mirror of
https://github.com/twentyhq/twenty.git
synced 2024-11-25 09:13:22 +03:00
Merge branch 'main' into c--build-twenty-query-builder-for-event-emitter
This commit is contained in:
commit
22f829b311
26
.github/workflows/ci-chrome-extension.yaml
vendored
26
.github/workflows/ci-chrome-extension.yaml
vendored
@ -3,13 +3,9 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'package.json'
|
||||
- 'packages/twenty-chrome-extension/**'
|
||||
|
||||
pull_request:
|
||||
paths:
|
||||
- 'package.json'
|
||||
- 'packages/twenty-chrome-extension/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@ -26,7 +22,25 @@ jobs:
|
||||
with:
|
||||
access_token: ${{ github.token }}
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: |
|
||||
package.json
|
||||
packages/twenty-chrome-extension/**
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Chrome Extension / Run build
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: npx nx build twenty-chrome-extension
|
||||
|
||||
- name: Mark as Valid if No Changes
|
||||
if: steps.changed-files.outputs.changed != 'true'
|
||||
run: |
|
||||
echo "No relevant changes detected. Marking as valid."
|
||||
|
100
.github/workflows/ci-front.yaml
vendored
100
.github/workflows/ci-front.yaml
vendored
@ -3,15 +3,9 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'package.json'
|
||||
- 'packages/twenty-front/**'
|
||||
- 'packages/twenty-ui/**'
|
||||
|
||||
pull_request:
|
||||
paths:
|
||||
- 'package.json'
|
||||
- 'packages/twenty-front/**'
|
||||
- 'packages/twenty-ui/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@ -29,21 +23,43 @@ jobs:
|
||||
access_token: ${{ github.token }}
|
||||
- name: Fetch local actions
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: |
|
||||
package.json
|
||||
packages/twenty-front/**
|
||||
packages/twenty-ui/**
|
||||
|
||||
- name: Skip if no relevant changes
|
||||
if: steps.changed-files.outputs.any_changed == 'false'
|
||||
run: echo "No relevant changes. Skipping CI."
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Diagnostic disk space issue
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: df -h
|
||||
- name: Front / Restore Storybook Task Cache
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/task-cache
|
||||
with:
|
||||
tag: scope:frontend
|
||||
tasks: storybook:build
|
||||
- name: Front / Write .env
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: npx nx reset:env twenty-front
|
||||
- name: Front / Build storybook
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: npx nx storybook:build twenty-front
|
||||
front-sb-test:
|
||||
runs-on: shipfox-8vcpu-ubuntu-2204
|
||||
timeout-minutes: 60
|
||||
needs: front-sb-build
|
||||
strategy:
|
||||
matrix:
|
||||
@ -54,35 +70,70 @@ jobs:
|
||||
steps:
|
||||
- name: Fetch local actions
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Check for changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: |
|
||||
packages/twenty-front/**
|
||||
- name: Skip if no relevant changes
|
||||
if: steps.changed-files.outputs.any_changed == 'false'
|
||||
run: echo "No relevant changes. Skipping CI."
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Install Playwright
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: cd packages/twenty-front && npx playwright install
|
||||
- name: Front / Restore Storybook Task Cache
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/task-cache
|
||||
with:
|
||||
tag: scope:frontend
|
||||
tasks: storybook:build
|
||||
- name: Front / Write .env
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: npx nx reset:env twenty-front
|
||||
- name: Run storybook tests
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: npx nx storybook:serve-and-test:static twenty-front --configuration=${{ matrix.storybook_scope }}
|
||||
front-sb-test-performance:
|
||||
runs-on: shipfox-8vcpu-ubuntu-2204
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
REACT_APP_SERVER_BASE_URL: http://localhost:3000
|
||||
NX_REJECT_UNKNOWN_LOCAL_CACHE: 0
|
||||
steps:
|
||||
- name: Fetch local actions
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Check for changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: |
|
||||
packages/twenty-front/**
|
||||
|
||||
- name: Skip if no relevant changes
|
||||
if: steps.changed-files.outputs.any_changed == 'false'
|
||||
run: echo "No relevant changes. Skipping CI."
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Install Playwright
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: cd packages/twenty-front && npx playwright install
|
||||
- name: Front / Write .env
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: npx nx reset:env twenty-front
|
||||
- name: Run storybook tests
|
||||
run: npx nx storybook:serve-and-test:static:performance twenty-front
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: npx nx run twenty-front:storybook:serve-and-test:static:performance
|
||||
front-chromatic-deployment:
|
||||
if: contains(github.event.pull_request.labels.*.name, 'run-chromatic') || github.event_name == 'push'
|
||||
needs: front-sb-build
|
||||
@ -95,19 +146,35 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: |
|
||||
packages/twenty-front/**
|
||||
|
||||
- name: Skip if no relevant changes
|
||||
if: steps.changed-files.outputs.any_changed == 'false'
|
||||
run: echo "No relevant changes. Skipping CI."
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Front / Restore Storybook Task Cache
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/task-cache
|
||||
with:
|
||||
tag: scope:frontend
|
||||
tasks: storybook:build
|
||||
- name: Front / Write .env
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: |
|
||||
cd packages/twenty-front
|
||||
touch .env
|
||||
echo "REACT_APP_SERVER_BASE_URL: $REACT_APP_SERVER_BASE_URL" >> .env
|
||||
- name: Publish to Chromatic
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: npx nx run twenty-front:chromatic:ci
|
||||
front-task:
|
||||
runs-on: ubuntu-latest
|
||||
@ -125,19 +192,34 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Check for changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: |
|
||||
packages/twenty-front/**
|
||||
|
||||
- name: Skip if no relevant changes
|
||||
if: steps.changed-files.outputs.any_changed == 'false'
|
||||
run: echo "No relevant changes. Skipping CI."
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Front / Restore ${{ matrix.task }} task cache
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/task-cache
|
||||
with:
|
||||
tag: scope:frontend
|
||||
tasks: ${{ matrix.task }}
|
||||
- name: Reset .env
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/nx-affected
|
||||
with:
|
||||
tag: scope:frontend
|
||||
tasks: reset:env
|
||||
- name: Run ${{ matrix.task }} task
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/nx-affected
|
||||
with:
|
||||
tag: scope:frontend
|
||||
|
52
.github/workflows/ci-server.yaml
vendored
52
.github/workflows/ci-server.yaml
vendored
@ -3,15 +3,9 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'package.json'
|
||||
- 'packages/twenty-server/**'
|
||||
- 'packages/twenty-emails/**'
|
||||
|
||||
pull_request:
|
||||
paths:
|
||||
- 'package.json'
|
||||
- 'packages/twenty-server/**'
|
||||
- 'packages/twenty-emails/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@ -38,22 +32,38 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: |
|
||||
package.json
|
||||
packages/twenty-server/**
|
||||
packages/twenty-emails/**
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Server / Restore Task Cache
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/task-cache
|
||||
with:
|
||||
tag: scope:backend
|
||||
- name: Server / Run lint & typecheck
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/nx-affected
|
||||
with:
|
||||
tag: scope:backend
|
||||
tasks: lint,typecheck
|
||||
- name: Server / Build
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: npx nx build twenty-server
|
||||
- name: Server / Write .env
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: npx nx reset:env twenty-server
|
||||
- name: Worker / Run
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: npx nx run twenty-server:worker:ci
|
||||
|
||||
server-test:
|
||||
@ -66,13 +76,26 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: |
|
||||
package.json
|
||||
packages/twenty-server/**
|
||||
packages/twenty-emails/**
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Server / Restore Task Cache
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/task-cache
|
||||
with:
|
||||
tag: scope:backend
|
||||
- name: Server / Run Tests
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/nx-affected
|
||||
with:
|
||||
tag: scope:backend
|
||||
@ -100,13 +123,26 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: |
|
||||
package.json
|
||||
packages/twenty-server/**
|
||||
packages/twenty-emails/**
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Server / Restore Task Cache
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/task-cache
|
||||
with:
|
||||
tag: scope:backend
|
||||
- name: Server / Run Integration Tests
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
uses: ./.github/workflows/actions/nx-affected
|
||||
with:
|
||||
tag: scope:backend
|
||||
|
14
.github/workflows/ci-test-docker-compose.yaml
vendored
14
.github/workflows/ci-test-docker-compose.yaml
vendored
@ -1,8 +1,7 @@
|
||||
name: 'Test Docker Compose'
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'packages/twenty-docker/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@ -13,8 +12,19 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Check for changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: |
|
||||
packages/twenty-docker/**
|
||||
docker-compose.yml
|
||||
- name: Skip if no relevant changes
|
||||
if: steps.changed-files.outputs.any_changed != 'true'
|
||||
run: echo "No relevant changes detected. Marking as valid."
|
||||
|
||||
- name: Run compose
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: |
|
||||
echo "Patching docker-compose.yml..."
|
||||
# change image to localbuild using yq
|
||||
|
14
.github/workflows/ci-utils.yaml
vendored
14
.github/workflows/ci-utils.yaml
vendored
@ -23,9 +23,16 @@ jobs:
|
||||
if: github.event.action != 'closed'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check for changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: 'packages/twenty-utils/**'
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Utils / Run Danger.js
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
run: cd packages/twenty-utils && npx nx danger:ci
|
||||
env:
|
||||
DANGER_GITHUB_API_TOKEN: ${{ github.token }}
|
||||
@ -35,9 +42,16 @@ jobs:
|
||||
if: github.event.action == 'closed' && github.event.pull_request.merged == true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check for changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: 'packages/twenty-utils/**'
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
- name: Run congratulate-dangerfile.js
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
run: cd packages/twenty-utils && npx nx danger:congratulate
|
||||
env:
|
||||
DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
27
.github/workflows/ci-website.yaml
vendored
27
.github/workflows/ci-website.yaml
vendored
@ -3,13 +3,10 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'package.json'
|
||||
- 'packages/twenty-website/**'
|
||||
|
||||
pull_request:
|
||||
paths:
|
||||
- 'package.json'
|
||||
- 'packages/twenty-website/**'
|
||||
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
@ -27,13 +24,29 @@ jobs:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Check for changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: 'package.json, packages/twenty-website/**'
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
uses: ./.github/workflows/actions/yarn-install
|
||||
|
||||
- name: Website / Run migrations
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
run: npx nx database:migrate twenty-website
|
||||
env:
|
||||
DATABASE_PG_URL: postgres://twenty:twenty@localhost:5432/default
|
||||
- name: Website / Build Website
|
||||
if: steps.changed-files.outputs.changed == 'true'
|
||||
run: npx nx build twenty-website
|
||||
env:
|
||||
DATABASE_PG_URL: postgres://twenty:twenty@localhost:5432/default
|
||||
DATABASE_PG_URL: postgres://twenty:twenty@localhost:5432/default
|
||||
|
||||
- name: Mark as VALID
|
||||
if: steps.changed-files.outputs.changed != 'true' # If no changes, mark as valid
|
||||
run: echo "No relevant changes detected. CI is valid."
|
16
.github/workflows/playwright.yml.bak
vendored
16
.github/workflows/playwright.yml.bak
vendored
@ -13,11 +13,27 @@ jobs:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
|
||||
- name: Check for changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v11
|
||||
with:
|
||||
files: |
|
||||
packages/** # Adjust this to your relevant directories
|
||||
playwright.config.ts # Include any relevant config files
|
||||
|
||||
- name: Skip if no relevant changes
|
||||
if: steps.changed-files.outputs.any_changed != 'true'
|
||||
run: echo "No relevant changes detected. Marking as valid."
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: npm install -g yarn && yarn
|
||||
- name: Install Playwright Browsers
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: yarn playwright install --with-deps
|
||||
- name: Run Playwright tests
|
||||
if: steps.changed-files.outputs.any_changed == 'true'
|
||||
run: yarn test:e2e companies
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,6 +9,7 @@
|
||||
|
||||
.nx/installation
|
||||
.nx/cache
|
||||
projectStructure.cache.json
|
||||
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -45,5 +45,5 @@
|
||||
"search.exclude": {
|
||||
"**/.yarn": true,
|
||||
},
|
||||
"eslint.debug": true
|
||||
"eslint.debug": true,
|
||||
}
|
||||
|
49
LICENSE
49
LICENSE
@ -1,3 +1,8 @@
|
||||
|
||||
This project is mostly licensed under the GNU General Public License (GPL) as described below. However, certain files within this project are licensed under a different commercial license. These files are clearly marked with the following comment at the top of the file: /* @license Enterprise */
|
||||
Files with this comment are not licensed under the aGPL v3, but instead are subject to the commercial license terms defined later in this file.
|
||||
|
||||
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
@ -659,3 +664,47 @@ specific requirements.
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
|
||||
------------------------------------
|
||||
|
||||
|
||||
|
||||
The Twenty.com Commercial License (the “Commercial License”)
|
||||
Copyright (c) 2023-present Twenty.com, PBC
|
||||
|
||||
With regard to Twenty's Software:
|
||||
|
||||
This part of the software and associated documentation files (the "Software") may only be
|
||||
used in production, if you (and any entity that you represent) have agreed to,
|
||||
and are in compliance with, the Terms available
|
||||
at https://twenty.com/legal/terms, or other agreements governing
|
||||
the use of the Software, as mutually agreed by you and Twenty.com, PBC ("Twenty"),
|
||||
and otherwise have a valid Twenty Enterprise Edition subscription
|
||||
for the correct number of hosts and seats as defined in the Commercial Terms.
|
||||
Subject to the foregoing sentence,
|
||||
you are free to modify this Software and publish patches to the Software. You agree
|
||||
that Twenty and/or its licensors (as applicable) retain all right, title and interest in
|
||||
and to all such modifications and/or patches, and all such modifications and/or
|
||||
patches may only be used, copied, modified, displayed, distributed, or otherwise
|
||||
exploited with a valid Commercial Subscription for the correct number of hosts and seats.
|
||||
Notwithstanding the foregoing, you may copy and modify the Software for development
|
||||
and testing purposes, without requiring a subscription. You agree that Twenty.Com and/or
|
||||
its licensors (as applicable) retain all right, title and interest in and to all such
|
||||
modifications. You are not granted any other rights beyond what is expressly stated herein.
|
||||
Subject to the foregoing, it is forbidden to copy, merge, publish, distribute, sublicense,
|
||||
and/or sell the Software.
|
||||
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
For all third party components incorporated into the Twenty Software, those
|
||||
components are licensed under the original license provided by the owner of the
|
||||
applicable component.
|
@ -45,7 +45,7 @@ trap on_exit EXIT
|
||||
|
||||
# Use environment variables VERSION and BRANCH, with defaults if not set
|
||||
version=${VERSION:-$(curl -s https://api.github.com/repos/twentyhq/twenty/releases/latest | grep '"tag_name":' | cut -d '"' -f 4)}
|
||||
branch=${BRANCH:-main}
|
||||
branch=${BRANCH:-$version}
|
||||
|
||||
echo "🚀 Using version $version and branch $branch"
|
||||
|
||||
@ -72,7 +72,7 @@ done
|
||||
echo "📁 Creating directory '$dir_name'"
|
||||
mkdir -p "$dir_name" && cd "$dir_name" || { echo "❌ Failed to create/access directory '$dir_name'"; exit 1; }
|
||||
|
||||
# Copy the twenty/packages/twenty-docker/docker-compose.yml file in it
|
||||
# Copy twenty/packages/twenty-docker/docker-compose.yml in it
|
||||
echo -e "\t• Copying docker-compose.yml"
|
||||
curl -sLo docker-compose.yml https://raw.githubusercontent.com/twentyhq/twenty/$branch/packages/twenty-docker/docker-compose.yml
|
||||
|
||||
|
14
nx.json
14
nx.json
@ -108,12 +108,11 @@
|
||||
"storybook:build": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"dependsOn": ["^build"],
|
||||
"inputs": ["^default", "excludeTests"],
|
||||
"outputs": ["{projectRoot}/{options.output-dir}"],
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"command": "storybook build",
|
||||
"command": "VITE_DISABLE_ESLINT_CHECKER=true storybook build",
|
||||
"output-dir": "storybook-static",
|
||||
"config-dir": ".storybook"
|
||||
}
|
||||
@ -192,16 +191,7 @@
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"commands": [
|
||||
"npx concurrently --kill-others --success=first -n SB,TEST 'nx storybook:serve:static {projectName} --port={args.port}' 'npx wait-on tcp:{args.port} && nx storybook:test {projectName} --port={args.port}'"
|
||||
],
|
||||
"port": 6006
|
||||
}
|
||||
},
|
||||
"storybook:serve-and-test:static:performance": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"commands": [
|
||||
"npx concurrently --kill-others --success=first -n SB,TEST 'nx storybook:serve:dev {projectName} --configuration=performance --port={args.port}' 'npx wait-on tcp:{args.port} && nx storybook:test:no-coverage {projectName} --port={args.port} --configuration=performance'"
|
||||
"npx concurrently --kill-others --success=first -n SB,TEST 'nx storybook:serve:static {projectName} --port={args.port} --configuration={args.performance}' 'npx wait-on tcp:{args.port} && nx storybook:test {projectName} --port={args.port} --configuration={args.scope}'"
|
||||
],
|
||||
"port": 6006
|
||||
}
|
||||
|
@ -18,4 +18,6 @@ Your turn 👇
|
||||
|
||||
» 02-October-2024 by [yourhandle](https://oss.gg/yourhandle) YouTube Link: [YouTube](https://twenty.com/)
|
||||
|
||||
---
|
||||
» 19-October-2024 by [Thefool76](https://oss.gg/thefool76) YouTube Link: [YouTube](https://youtu.be/KuAycGuW698?si=q-YxcukbbYuO8BWf)
|
||||
|
||||
---
|
||||
|
@ -18,4 +18,10 @@ Your turn 👇
|
||||
|
||||
» 02-October-2024 by [yourhandle](https://oss.gg/yourhandle) blog Link: [blog](https://twenty.com/)
|
||||
|
||||
---
|
||||
» 19-October-2024 by [Thefool76](https://oss.gg/thefool76) blog Link: [blog](https://k5lo7h.hashnode.dev/twenty-crm-a-fresh-start-for-modern-businesses)
|
||||
|
||||
» 21-October-2024 by [sateshcharan](https://oss.gg/sateshcharan) blog Link: [blog](https://dev.to/sateshcharan/twenty-crm-a-fresh-start-for-modern-businesses-46kf)
|
||||
|
||||
» 22-October-2024 by [rajeevDewangan](https://oss.gg/rajeevDewangan) blog Link: [blog](https://open.substack.com/pub/rajeevdewangan/p/comprehensive-guide-to-self-hosting?r=4lly3x&utm_campaign=post&utm_medium=web&showWelcomeOnShare=true)
|
||||
|
||||
» 22-October-2024 by [Khaan25](https://oss.gg/Khaan25) blog Link: [blog](https://medium.com/@ziaurzai/twenty-crm-modern-solution-for-modern-problems-a0b65fec9d6c)
|
||||
|
@ -18,4 +18,10 @@ Your turn 👇
|
||||
|
||||
» 02-October-2024 by [yourhandle](https://oss.gg/yourhandle) blog Link: [blog](https://twenty.com/)
|
||||
|
||||
---
|
||||
» 21-October-2024 by [sateshcharan](https://oss.gg/sateshcharan) blog Link: [blog](https://dev.to/sateshcharan/streamlined-self-hosting-with-twenty-crm-1-click-docker-compose-setup-188o)
|
||||
|
||||
» 23-October-2024 by [Thefool76](https://oss.gg/thefool76) blog Link: [blog](https://k5lo7h.hashnode.dev/a-detailed-guide-to-self-host-twenty-crm-on-you-local-server)
|
||||
|
||||
» 24-October-2024 by [Khaan25](https://oss.gg/Khaan25) blog Link: [blog](https://medium.com/@ziaurzai/detailed-guide-on-self-hosting-twenty-crm-on-your-server-troubleshooting-and-best-practices-1f2ca15cd6eb)
|
||||
|
||||
---
|
||||
|
@ -1,5 +1,5 @@
|
||||
**Side Quest**: Design a promotional poster of Twenty and share it on social media.
|
||||
**Points**: 300 Points
|
||||
**Points**: 50 Points
|
||||
**Proof**: Add your oss handle and poster link to the list below.
|
||||
|
||||
Please follow the following schema:
|
||||
@ -25,4 +25,11 @@ Your turn 👇
|
||||
» 14-October-2024 by [AliYar-Khan](https://oss.gg/AliYar-Khan) poster Link: [poster](https://x.com/Mr_Programmer14/status/1845888855183884352)
|
||||
|
||||
» 16-October-2024 by [Harsh BHat](https://oss.gg/harshsbhat) poster Link: [poster](https://x.com/HarshBhatX/status/1846233330435477531)
|
||||
---
|
||||
|
||||
» 17-October-2024 by [Atharva Deshmukh](https://oss.gg/Atharva-3000) poster Link: [poster](https://x.com/0x_atharva/status/1846915861191577697)
|
||||
|
||||
» 20-October-2024 by [Naprila](https://oss.gg/Naprila) poster Link: [poster](https://x.com/mkprasad_821/status/1848037527921254625)
|
||||
|
||||
» 21-October-2024 by [sateshcharan](https://oss.gg/sateshcharan) poster Link: [poster](https://x.com/sateshcharans/status/1848358958970396727)
|
||||
|
||||
» 22-October-2024 by [Khaan25](https://oss.gg/Khaan25) poster Link: [poster](https://drive.google.com/file/d/1IFtzwzKa0C_hT9cL4o3ChsKwVNRP33G_/view?usp=sharing) - [Tweet Link](https://x.com/zia_webdev/status/1848764487081619470)
|
||||
|
@ -1,5 +1,5 @@
|
||||
**Side Quest**: Design/Create new Twenty logo, tweet your design, and mention @twentycrm.
|
||||
**Points**: 300 Points
|
||||
**Points**: 50 Points
|
||||
**Proof**: Create a logo upload it on any of the platform and add your oss handle and logo link to the list below.
|
||||
|
||||
Please follow the following schema:
|
||||
@ -20,10 +20,18 @@ Your turn 👇
|
||||
|
||||
» 11-October-2024 by [thefool76](https://oss.gg/thefool76) Logo Link: [logo](https://drive.google.com/file/d/1DxSwNY_i90kGgWzPQj5SxScBz_6r02l4/view?usp=sharing) » tweet Link: [tweet](https://x.com/thefool1135/status/1844693487067034008)
|
||||
|
||||
» 22-October-2024 by [Khaan25](https://oss.gg/Khaan25) Logo Link: [logo](https://drive.google.com/drive/folders/1yaegQ7Hr8YraMNs50AHZmDprvzLn6A90?usp=sharing) » tweet Link: [tweet](https://x.com/zia_webdev/status/1848754055717212388)
|
||||
|
||||
» 13-October-2024 by [Atharva_404](https://oss.gg/Atharva-3000) Logo Link: [logo](https://drive.google.com/drive/folders/1XB7ELR7kPA4x7Fx5RQr8wo5etdZAZgcs?usp=drive_link) » tweet Link: [tweet](https://x.com/0x_atharva/status/1845421218914095453)
|
||||
|
||||
» 13-October-2024 by [Ionfinisher](https://oss.gg/Ionfinisher) Logo Link: [logo](https://drive.google.com/file/d/1l9vE8CIjW9KfdioI5WKzxrdmvO8LR4j7/view?usp=drive_link) » tweet Link: [tweet](https://x.com/ion_finisher/status/1845466470429442163)
|
||||
|
||||
» 16-October-2024 by [harshsbhat](https://oss.gg/harshsbhat) Logo Link: [logo](https://drive.google.com/file/d/1jmqwNvlSyWSY1-pCG63TAtDvCoVa8xg-/view?usp=sharing) » tweet Link: [tweet](https://x.com/HarshBhatX/status/1846234658712772977)
|
||||
|
||||
» 17-October-2024 by [shlok-py](https://oss.gg/shlok-py) Logo Link: [logo](https://drive.google.com/file/d/1BakHRLJul6DcNbLyeOXgJO9Ap4DpUxO9/view?usp=sharing) » tweet Link: [tweet](https://x.com/koirala_shlok/status/1846910669658247201)
|
||||
|
||||
» 20-October-2024 by [Naprila](https://oss.gg/Naprila) Logo Link: [logo](https://drive.google.com/file/d/105fWXNtOkOPkU31AV0FDZKOdrJ8XLwBb/view?usp=drivesdk) » tweet Link: [tweet](https://x.com/mkprasad_821/status/1847978789713695133)
|
||||
|
||||
» 21-October-2024 by [sateshcharan](https://oss.gg/sateshcharan) Logo Link: [logo](https://drive.google.com/file/d/1fwvOcg8oQZC3NlTNV8EcyJxh9v_OYdpY/view?usp=sharing) » tweet Link: [tweet](https://x.com/sateshcharans/status/1848344729483690455)
|
||||
|
||||
---
|
||||
|
@ -1,12 +1,13 @@
|
||||
**Side Quest**: Duplicate the Figma file from the main repo and customize the variables to create a unique interface theme for Twenty.
|
||||
**Points**: 750 Points
|
||||
**Proof**: Add your oss handle and Figma link to the list below.
|
||||
**Side Quest**: Duplicate the Figma file from the main repo and customize the variables to create a unique interface theme for Twenty. <br/>
|
||||
**Points**: 750 Points <br/>
|
||||
**Proof**: Add your oss handle and Figma link to the list below. <br/>
|
||||
**Figma Link**: https://www.figma.com/design/xt8O9mFeLl46C5InWwoMrN/Twenty?t=YIFyswta6Xf6sSYK-0
|
||||
|
||||
Please follow the following schema:
|
||||
|
||||
---
|
||||
|
||||
» 05-April-2024 by YOUR oss.gg HANDLE » Figma Link: https://link.to/content
|
||||
» 05-April-2024 by YOUR oss.gg HANDLE] Figma Link: https://www.figma.com/design/xt8O9mFeLl46C5InWwoMrN/Twenty?t=YIFyswta6Xf6sSYK-0
|
||||
|
||||
---
|
||||
|
||||
@ -18,4 +19,6 @@ Your turn 👇
|
||||
|
||||
» 02-October-2024 by [yourhandle](https://oss.gg/yourhandle) Figma Link: [Figma](https://twenty.com/)
|
||||
|
||||
---
|
||||
» 24-October-2024 by [Khaan25](https://oss.gg/Khaan25) Figma Link: [Figma](https://www.figma.com/design/HqYQrzel3e2TjzujwfdCXZ/Twenty-(Copy)---Khaan25?node-id=478-19796&t=QTB8gzKTudbVNeNs-1)
|
||||
|
||||
---
|
||||
|
@ -18,4 +18,5 @@ Your turn 👇
|
||||
|
||||
» 02-October-2024 by [yourhandle](https://oss.gg/yourhandle) video Link: [video](https://twenty.com/)
|
||||
|
||||
» 22-October-2024 by [FaheemOnHub](https://oss.gg/FaheemOnHub) video Link: [video](https://drive.google.com/file/d/1bR59Q5gqoqHjzgdrF6K68U2hloexkQYM/view)
|
||||
---
|
@ -46,3 +46,17 @@ Your turn 👇
|
||||
|
||||
» 16-October-2024 by Harsh Bhat
|
||||
» Link to Tweet: https://x.com/HarshBhatX/status/1846252536241508392
|
||||
|
||||
» 20-October-2024 by Naprila
|
||||
» Link to Tweet: https://x.com/mkprasad_821/status/1847886807314120762
|
||||
|
||||
» 22-October-2024 by Zia Ur Rehman Khan
|
||||
» Link to Tweet: https://x.com/zia_webdev/status/1848659210243871165x
|
||||
|
||||
» 22-October-2024 by Ritansh Rajput
|
||||
» Link to Tweet: https://x.com/Ritansh_Dev/status/1848641904511975838
|
||||
|
||||
» 23-October-2024 by Rajeev Dewangan
|
||||
» Link to Tweet: https://x.com/rajeevdew/status/1849109074685907374
|
||||
|
||||
|
||||
|
@ -28,4 +28,12 @@ Your turn 👇
|
||||
|
||||
» 16-October-2024 by Harsh Bhat
|
||||
» Link to Tweet: https://x.com/HarshBhatX/status/1846075312691413066
|
||||
---
|
||||
|
||||
» 20-October-2024 by Naprila
|
||||
» Link to Tweet: https://x.com/mkprasad_821/status/1847895747707953205
|
||||
|
||||
» 22-October-2024 by Zia Ur Rehman Khan
|
||||
» Link to Tweet: https://x.com/zia_webdev/status/1848660000190697633
|
||||
|
||||
» 23-October-2024 by Rajeev Dewangan
|
||||
» Link to Tweet: https://x.com/rajeevdew/status/1849110473272442991
|
||||
|
@ -34,4 +34,15 @@ Your turn 👇
|
||||
|
||||
» 16-October-2024 by Harsh Bhat
|
||||
» Link to Tweet: https://x.com/HarshBhatX/status/1844698253104709899
|
||||
---
|
||||
|
||||
» 20-October-2024 by Poorvi Bajpai
|
||||
» Link to Tweet: https://x.com/poorvi_bajpai/status/1847881362038308992
|
||||
|
||||
» 20-October-2024 by Satesh Charan
|
||||
» Link to Tweet: https://x.com/sateshcharans/status/1847760124267389357
|
||||
|
||||
» 20-October-2024 by Naprila
|
||||
» Link to Tweet: https://x.com/mkprasad_821/status/1847900277510123706
|
||||
|
||||
» 22-October-2024 by Zia Ur Rehman Khan
|
||||
» Link to Tweet: https://x.com/zia_webdev/status/1846954638953926675
|
@ -34,4 +34,12 @@ Your turn 👇
|
||||
|
||||
» 16-October-2024 by Harsh Bhat
|
||||
» Link to gif: https://giphy.com/gifs/oss-twentycrm-mgoYSDrjIalUL7XJzm
|
||||
---
|
||||
|
||||
» 20-October-2024 by Satesh Charan
|
||||
» Link to gif: https://giphy.com/gifs/rXjvGBrTqu7vvhEsvR
|
||||
|
||||
» 20-October-2024 by Naprila
|
||||
» Link to gif: https://giphy.com/gifs/uiTAwFJ0BWQsQb7jbM
|
||||
|
||||
» 22-October-2024 by Zia Ur Rehman Khan
|
||||
» Link to gif: https://giphy.com/gifs/MG5FQSrG1mxf1N5qnA
|
||||
|
@ -16,4 +16,6 @@ Your turn 👇
|
||||
|
||||
» 01-October-2024 by X
|
||||
|
||||
---
|
||||
» 21-October-2024 by [sateshcharan](https://oss.gg/sateshcharan)
|
||||
|
||||
» 22-October-2024 by [Khaan25](https://oss.gg/Khaan25)
|
@ -176,6 +176,7 @@
|
||||
"scroll-into-view": "^1.16.2",
|
||||
"semver": "^7.5.4",
|
||||
"sharp": "^0.32.1",
|
||||
"slash": "^5.1.0",
|
||||
"stripe": "^14.17.0",
|
||||
"ts-key-enum": "^2.0.12",
|
||||
"tslib": "^2.3.0",
|
||||
@ -294,6 +295,7 @@
|
||||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||
"eslint-plugin-prefer-arrow": "^1.2.3",
|
||||
"eslint-plugin-prettier": "^5.1.2",
|
||||
"eslint-plugin-project-structure": "^3.7.2",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.4",
|
||||
@ -347,7 +349,7 @@
|
||||
"version": "0.2.1",
|
||||
"nx": {},
|
||||
"scripts": {
|
||||
"start": "npx nx run-many -t start -p twenty-server twenty-front"
|
||||
"start": "npx nx run-many -t start worker -p twenty-server twenty-front"
|
||||
},
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
|
@ -21,7 +21,14 @@ module.exports = {
|
||||
parserOptions: {
|
||||
project: ['packages/twenty-front/tsconfig.{json,*.json}'],
|
||||
},
|
||||
rules: {},
|
||||
plugins: ['project-structure'],
|
||||
settings: {
|
||||
'project-structure/folder-structure-config-path':
|
||||
'packages/twenty-front/folderStructure.json',
|
||||
},
|
||||
rules: {
|
||||
'project-structure/folder-structure': 'error',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
@ -50,7 +50,11 @@ const config: StorybookConfig = {
|
||||
const { mergeConfig } = await import('vite');
|
||||
|
||||
return mergeConfig(config, {
|
||||
// Add dependencies to pre-optimization
|
||||
resolve: {
|
||||
alias: {
|
||||
'react-dom/client': 'react-dom/profiling',
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
81
packages/twenty-front/folderStructure.json
Normal file
81
packages/twenty-front/folderStructure.json
Normal file
@ -0,0 +1,81 @@
|
||||
{
|
||||
"$schema": "../../node_modules/eslint-plugin-project-structure/folderStructure.schema.json",
|
||||
"regexParameters": {
|
||||
"camelCase": "^[a-z]+[A-Za-z0-9]+"
|
||||
},
|
||||
"structure": [
|
||||
{
|
||||
"name": "packages",
|
||||
"children": [
|
||||
{
|
||||
"name": "twenty-front",
|
||||
"children": [
|
||||
{ "name": "*", "children": [] },
|
||||
{ "name": "*" },
|
||||
{
|
||||
"name": "src",
|
||||
"children": [
|
||||
{ "name": "*", "children": [] },
|
||||
{ "name": "*" },
|
||||
{
|
||||
"name": "modules",
|
||||
"children": [
|
||||
{ "ruleId": "moduleFolderRule" },
|
||||
{ "name": "types", "ruleId": "doNotCheckLeafFolderRule" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"rules": {
|
||||
"moduleFolderRule": {
|
||||
"name": "^(?!utils$|hooks$|states$|types$|graphql$|components$|effect-components$|constants$|validation-schemas$|contexts$|scopes$|services$|errors$)[a-z][a-z0-9]**(?:-[a-z0-9]+)**$",
|
||||
"folderRecursionLimit": 6,
|
||||
"children": [
|
||||
{ "ruleId": "moduleFolderRule" },
|
||||
{ "name": "hooks", "ruleId": "hooksLeafFolderRule" },
|
||||
{ "name": "utils", "ruleId": "utilsLeafFolderRule" },
|
||||
{ "name": "states", "ruleId": "doNotCheckLeafFolderRule" },
|
||||
{ "name": "types", "ruleId": "doNotCheckLeafFolderRule" },
|
||||
{ "name": "graphql", "ruleId": "doNotCheckLeafFolderRule" },
|
||||
{ "name": "components", "ruleId": "doNotCheckLeafFolderRule" },
|
||||
{ "name": "effect-components", "ruleId": "doNotCheckLeafFolderRule" },
|
||||
{ "name": "constants", "ruleId": "doNotCheckLeafFolderRule" },
|
||||
{ "name": "validation-schemas", "ruleId": "doNotCheckLeafFolderRule" },
|
||||
{ "name": "contexts", "ruleId": "doNotCheckLeafFolderRule" },
|
||||
{ "name": "scopes", "ruleId": "doNotCheckLeafFolderRule" },
|
||||
{ "name": "services", "ruleId": "doNotCheckLeafFolderRule" },
|
||||
{ "name": "errors", "ruleId": "doNotCheckLeafFolderRule" }
|
||||
]
|
||||
},
|
||||
"hooksLeafFolderRule": {
|
||||
"folderRecursionLimit": 2,
|
||||
"children": [
|
||||
{ "name": "use{PascalCase}.(ts|tsx)" },
|
||||
{
|
||||
"name": "__tests__",
|
||||
"children": [{ "name": "use{PascalCase}.test.(ts|tsx)" }]
|
||||
},
|
||||
{ "name": "internal", "ruleId": "hooksLeafFolderRule" }
|
||||
]
|
||||
},
|
||||
"doNotCheckLeafFolderRule": {
|
||||
"folderRecursionLimit": 1,
|
||||
"children": [{ "name": "*" }, { "name": "*", "children": [] }]
|
||||
},
|
||||
"utilsLeafFolderRule": {
|
||||
"folderRecursionLimit": 1,
|
||||
"children": [
|
||||
{ "name": "{camelCase}.ts" },
|
||||
{
|
||||
"name": "__tests__",
|
||||
"children": [{ "name": "{camelCase}.test.ts" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -25,9 +25,9 @@ const jestConfig: JestConfigWithTsJest = {
|
||||
extensionsToTreatAsEsm: ['.ts', '.tsx'],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
statements: 60,
|
||||
statements: 59,
|
||||
lines: 55,
|
||||
functions: 50,
|
||||
functions: 49,
|
||||
},
|
||||
},
|
||||
collectCoverageFrom: ['<rootDir>/src/**/*.ts'],
|
||||
|
@ -33,6 +33,12 @@
|
||||
"@nivo/calendar": "^0.87.0",
|
||||
"@nivo/core": "^0.87.0",
|
||||
"@nivo/line": "^0.87.0",
|
||||
"@tiptap/extension-document": "^2.9.0",
|
||||
"@tiptap/extension-paragraph": "^2.9.0",
|
||||
"@tiptap/extension-placeholder": "^2.9.0",
|
||||
"@tiptap/extension-text": "^2.9.0",
|
||||
"@tiptap/extension-text-style": "^2.8.0",
|
||||
"@tiptap/react": "^2.8.0",
|
||||
"@xyflow/react": "^12.0.4",
|
||||
"transliteration": "^2.3.5"
|
||||
}
|
||||
|
@ -52,7 +52,9 @@
|
||||
"reportUnusedDisableDirectives": "error"
|
||||
},
|
||||
"configurations": {
|
||||
"ci": { "eslintConfig": "{projectRoot}/.eslintrc-ci.cjs" },
|
||||
"ci": {
|
||||
"eslintConfig": "{projectRoot}/.eslintrc-ci.cjs"
|
||||
},
|
||||
"fix": {}
|
||||
}
|
||||
},
|
||||
@ -68,6 +70,12 @@
|
||||
"storybook:build": {
|
||||
"options": {
|
||||
"env": { "NODE_OPTIONS": "--max_old_space_size=6500" }
|
||||
},
|
||||
"configurations": {
|
||||
"docs": { "env": { "STORYBOOK_SCOPE": "ui-docs" } },
|
||||
"modules": { "env": { "STORYBOOK_SCOPE": "modules" } },
|
||||
"pages": { "env": { "STORYBOOK_SCOPE": "pages" } },
|
||||
"performance": { "env": { "STORYBOOK_SCOPE": "performance" } }
|
||||
}
|
||||
},
|
||||
"storybook:serve:dev": {
|
||||
@ -80,7 +88,13 @@
|
||||
}
|
||||
},
|
||||
"storybook:serve:static": {
|
||||
"options": { "port": 6006 }
|
||||
"options": { "port": 6006 },
|
||||
"configurations": {
|
||||
"docs": { "env": { "STORYBOOK_SCOPE": "ui-docs" } },
|
||||
"modules": { "env": { "STORYBOOK_SCOPE": "modules" } },
|
||||
"pages": { "env": { "STORYBOOK_SCOPE": "pages" } },
|
||||
"performance": { "env": { "STORYBOOK_SCOPE": "performance" } }
|
||||
}
|
||||
},
|
||||
"storybook:coverage": {
|
||||
"configurations": {
|
||||
@ -102,9 +116,6 @@
|
||||
},
|
||||
"storybook:serve-and-test:static": {
|
||||
"options": {
|
||||
"commands": [
|
||||
"npx concurrently --kill-others --success=first -n SB,TEST 'nx storybook:serve:static {projectName} --port={args.port}' 'npx wait-on tcp:{args.port} && nx storybook:test {projectName} --port={args.port} --configuration={args.scope}'"
|
||||
],
|
||||
"port": 6006
|
||||
},
|
||||
"configurations": {
|
||||
@ -114,15 +125,6 @@
|
||||
"performance": { "scope": "performance" }
|
||||
}
|
||||
},
|
||||
"storybook:serve-and-test:static:performance": {},
|
||||
"storybook:test:no-coverage": {
|
||||
"configurations": {
|
||||
"docs": { "env": { "STORYBOOK_SCOPE": "ui-docs" } },
|
||||
"modules": { "env": { "STORYBOOK_SCOPE": "modules" } },
|
||||
"pages": { "env": { "STORYBOOK_SCOPE": "pages" } },
|
||||
"performance": { "env": { "STORYBOOK_SCOPE": "performance" } }
|
||||
}
|
||||
},
|
||||
"graphql:generate": {
|
||||
"executor": "nx:run-commands",
|
||||
"defaultConfiguration": "data",
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
BIN
packages/twenty-front/public/logos/20-high-resolution-logo.png
Normal file
BIN
packages/twenty-front/public/logos/20-high-resolution-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -64,6 +64,7 @@ export type AuthProviders = {
|
||||
magicLink: Scalars['Boolean'];
|
||||
microsoft: Scalars['Boolean'];
|
||||
password: Scalars['Boolean'];
|
||||
sso: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export type AuthToken = {
|
||||
@ -141,6 +142,7 @@ export enum CaptchaDriverType {
|
||||
|
||||
export type ClientConfig = {
|
||||
__typename?: 'ClientConfig';
|
||||
analyticsEnabled: Scalars['Boolean'];
|
||||
api: ApiConfig;
|
||||
authProviders: AuthProviders;
|
||||
billing: Billing;
|
||||
@ -174,9 +176,13 @@ export type DeleteOneObjectInput = {
|
||||
id: Scalars['UUID'];
|
||||
};
|
||||
|
||||
export type DeleteServerlessFunctionInput = {
|
||||
/** The id of the function. */
|
||||
id: Scalars['ID'];
|
||||
export type DeleteSsoInput = {
|
||||
identityProviderId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type DeleteSsoOutput = {
|
||||
__typename?: 'DeleteSsoOutput';
|
||||
identityProviderId: Scalars['String'];
|
||||
};
|
||||
|
||||
/** Schema update on a table */
|
||||
@ -187,6 +193,20 @@ export enum DistantTableUpdate {
|
||||
TableDeleted = 'TABLE_DELETED'
|
||||
}
|
||||
|
||||
export type EditSsoInput = {
|
||||
id: Scalars['String'];
|
||||
status: SsoIdentityProviderStatus;
|
||||
};
|
||||
|
||||
export type EditSsoOutput = {
|
||||
__typename?: 'EditSsoOutput';
|
||||
id: Scalars['String'];
|
||||
issuer: Scalars['String'];
|
||||
name: Scalars['String'];
|
||||
status: SsoIdentityProviderStatus;
|
||||
type: IdpType;
|
||||
};
|
||||
|
||||
export type EmailPasswordResetLink = {
|
||||
__typename?: 'EmailPasswordResetLink';
|
||||
/** Boolean that confirms query was dispatched */
|
||||
@ -276,12 +296,53 @@ export enum FileFolder {
|
||||
WorkspaceLogo = 'WorkspaceLogo'
|
||||
}
|
||||
|
||||
export type FindAvailableSsoidpInput = {
|
||||
email: Scalars['String'];
|
||||
};
|
||||
|
||||
export type FindAvailableSsoidpOutput = {
|
||||
__typename?: 'FindAvailableSSOIDPOutput';
|
||||
id: Scalars['String'];
|
||||
issuer: Scalars['String'];
|
||||
name: Scalars['String'];
|
||||
status: SsoIdentityProviderStatus;
|
||||
type: IdpType;
|
||||
workspace: WorkspaceNameAndId;
|
||||
};
|
||||
|
||||
export type FullName = {
|
||||
__typename?: 'FullName';
|
||||
firstName: Scalars['String'];
|
||||
lastName: Scalars['String'];
|
||||
};
|
||||
|
||||
export type GenerateJwt = GenerateJwtOutputWithAuthTokens | GenerateJwtOutputWithSsoauth;
|
||||
|
||||
export type GenerateJwtOutputWithAuthTokens = {
|
||||
__typename?: 'GenerateJWTOutputWithAuthTokens';
|
||||
authTokens: AuthTokens;
|
||||
reason: Scalars['String'];
|
||||
success: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export type GenerateJwtOutputWithSsoauth = {
|
||||
__typename?: 'GenerateJWTOutputWithSSOAUTH';
|
||||
availableSSOIDPs: Array<FindAvailableSsoidpOutput>;
|
||||
reason: Scalars['String'];
|
||||
success: Scalars['Boolean'];
|
||||
};
|
||||
|
||||
export type GetAuthorizationUrlInput = {
|
||||
identityProviderId: Scalars['String'];
|
||||
};
|
||||
|
||||
export type GetAuthorizationUrlOutput = {
|
||||
__typename?: 'GetAuthorizationUrlOutput';
|
||||
authorizationURL: Scalars['String'];
|
||||
id: Scalars['String'];
|
||||
type: Scalars['String'];
|
||||
};
|
||||
|
||||
export type GetServerlessFunctionSourceCodeInput = {
|
||||
/** The id of the function. */
|
||||
id: Scalars['ID'];
|
||||
@ -289,6 +350,11 @@ export type GetServerlessFunctionSourceCodeInput = {
|
||||
version?: Scalars['String'];
|
||||
};
|
||||
|
||||
export enum IdpType {
|
||||
Oidc = 'OIDC',
|
||||
Saml = 'SAML'
|
||||
}
|
||||
|
||||
export type IndexConnection = {
|
||||
__typename?: 'IndexConnection';
|
||||
/** Array of edges. */
|
||||
@ -358,23 +424,29 @@ export type Mutation = {
|
||||
authorizeApp: AuthorizeApp;
|
||||
challenge: LoginToken;
|
||||
checkoutSession: SessionEntity;
|
||||
createOIDCIdentityProvider: SetupSsoOutput;
|
||||
createOneAppToken: AppToken;
|
||||
createOneObject: Object;
|
||||
createOneServerlessFunction: ServerlessFunction;
|
||||
createSAMLIdentityProvider: SetupSsoOutput;
|
||||
deactivateWorkflowVersion: Scalars['Boolean'];
|
||||
deleteCurrentWorkspace: Workspace;
|
||||
deleteOneObject: Object;
|
||||
deleteOneServerlessFunction: ServerlessFunction;
|
||||
deleteSSOIdentityProvider: DeleteSsoOutput;
|
||||
deleteUser: User;
|
||||
deleteWorkspaceInvitation: Scalars['String'];
|
||||
disablePostgresProxy: PostgresCredentials;
|
||||
editSSOIdentityProvider: EditSsoOutput;
|
||||
emailPasswordResetLink: EmailPasswordResetLink;
|
||||
enablePostgresProxy: PostgresCredentials;
|
||||
exchangeAuthorizationCode: ExchangeAuthCode;
|
||||
executeOneServerlessFunction: ServerlessFunctionExecutionResult;
|
||||
findAvailableSSOIdentityProviders: Array<FindAvailableSsoidpOutput>;
|
||||
generateApiKeyToken: ApiKeyToken;
|
||||
generateJWT: AuthTokens;
|
||||
generateJWT: GenerateJwt;
|
||||
generateTransientToken: TransientToken;
|
||||
getAuthorizationUrl: GetAuthorizationUrlOutput;
|
||||
impersonate: Verify;
|
||||
publishServerlessFunction: ServerlessFunction;
|
||||
renewToken: AuthTokens;
|
||||
@ -437,11 +509,21 @@ export type MutationCheckoutSessionArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateOidcIdentityProviderArgs = {
|
||||
input: SetupOidcSsoInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateOneServerlessFunctionArgs = {
|
||||
input: CreateServerlessFunctionInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateSamlIdentityProviderArgs = {
|
||||
input: SetupSamlSsoInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeactivateWorkflowVersionArgs = {
|
||||
workflowVersionId: Scalars['String'];
|
||||
};
|
||||
@ -453,7 +535,12 @@ export type MutationDeleteOneObjectArgs = {
|
||||
|
||||
|
||||
export type MutationDeleteOneServerlessFunctionArgs = {
|
||||
input: DeleteServerlessFunctionInput;
|
||||
input: ServerlessFunctionIdInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeleteSsoIdentityProviderArgs = {
|
||||
input: DeleteSsoInput;
|
||||
};
|
||||
|
||||
|
||||
@ -462,6 +549,11 @@ export type MutationDeleteWorkspaceInvitationArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationEditSsoIdentityProviderArgs = {
|
||||
input: EditSsoInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationEmailPasswordResetLinkArgs = {
|
||||
email: Scalars['String'];
|
||||
};
|
||||
@ -479,6 +571,11 @@ export type MutationExecuteOneServerlessFunctionArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationFindAvailableSsoIdentityProvidersArgs = {
|
||||
input: FindAvailableSsoidpInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationGenerateApiKeyTokenArgs = {
|
||||
apiKeyId: Scalars['String'];
|
||||
expiresAt: Scalars['String'];
|
||||
@ -490,6 +587,11 @@ export type MutationGenerateJwtArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationGetAuthorizationUrlArgs = {
|
||||
input: GetAuthorizationUrlInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationImpersonateArgs = {
|
||||
userId: Scalars['String'];
|
||||
};
|
||||
@ -669,6 +771,8 @@ export type Query = {
|
||||
clientConfig: ClientConfig;
|
||||
currentUser: User;
|
||||
currentWorkspace: Workspace;
|
||||
findManyServerlessFunctions: Array<ServerlessFunction>;
|
||||
findOneServerlessFunction: ServerlessFunction;
|
||||
findWorkspaceFromInviteHash: Workspace;
|
||||
findWorkspaceInvitations: Array<WorkspaceInvitation>;
|
||||
getAvailablePackages: Scalars['JSON'];
|
||||
@ -681,10 +785,9 @@ export type Query = {
|
||||
getTimelineThreadsFromPersonId: TimelineThreadsWithTotal;
|
||||
index: Index;
|
||||
indexMetadatas: IndexConnection;
|
||||
listSSOIdentityProvidersByWorkspaceId: Array<FindAvailableSsoidpOutput>;
|
||||
object: Object;
|
||||
objects: ObjectConnection;
|
||||
serverlessFunction: ServerlessFunction;
|
||||
serverlessFunctions: ServerlessFunctionConnection;
|
||||
validatePasswordResetToken: ValidatePasswordResetToken;
|
||||
};
|
||||
|
||||
@ -705,6 +808,11 @@ export type QueryCheckWorkspaceInviteHashIsValidArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type QueryFindOneServerlessFunctionArgs = {
|
||||
input: ServerlessFunctionIdInput;
|
||||
};
|
||||
|
||||
|
||||
export type QueryFindWorkspaceFromInviteHashArgs = {
|
||||
inviteHash: Scalars['String'];
|
||||
};
|
||||
@ -821,6 +929,12 @@ export type RunWorkflowVersionInput = {
|
||||
workflowVersionId: Scalars['String'];
|
||||
};
|
||||
|
||||
export enum SsoIdentityProviderStatus {
|
||||
Active = 'Active',
|
||||
Error = 'Error',
|
||||
Inactive = 'Inactive'
|
||||
}
|
||||
|
||||
export type SendInvitationsOutput = {
|
||||
__typename?: 'SendInvitationsOutput';
|
||||
errors: Array<Scalars['String']>;
|
||||
@ -843,27 +957,12 @@ export type ServerlessFunction = {
|
||||
id: Scalars['UUID'];
|
||||
latestVersion?: Maybe<Scalars['String']>;
|
||||
name: Scalars['String'];
|
||||
publishedVersions: Array<Scalars['String']>;
|
||||
runtime: Scalars['String'];
|
||||
syncStatus: ServerlessFunctionSyncStatus;
|
||||
updatedAt: Scalars['DateTime'];
|
||||
};
|
||||
|
||||
export type ServerlessFunctionConnection = {
|
||||
__typename?: 'ServerlessFunctionConnection';
|
||||
/** Array of edges. */
|
||||
edges: Array<ServerlessFunctionEdge>;
|
||||
/** Paging information */
|
||||
pageInfo: PageInfo;
|
||||
};
|
||||
|
||||
export type ServerlessFunctionEdge = {
|
||||
__typename?: 'ServerlessFunctionEdge';
|
||||
/** Cursor for this node. */
|
||||
cursor: Scalars['ConnectionCursor'];
|
||||
/** The node containing the ServerlessFunction */
|
||||
node: ServerlessFunction;
|
||||
};
|
||||
|
||||
export type ServerlessFunctionExecutionResult = {
|
||||
__typename?: 'ServerlessFunctionExecutionResult';
|
||||
/** Execution result in JSON format */
|
||||
@ -882,6 +981,11 @@ export enum ServerlessFunctionExecutionStatus {
|
||||
Success = 'SUCCESS'
|
||||
}
|
||||
|
||||
export type ServerlessFunctionIdInput = {
|
||||
/** The id of the function. */
|
||||
id: Scalars['ID'];
|
||||
};
|
||||
|
||||
/** SyncStatus of the serverlessFunction */
|
||||
export enum ServerlessFunctionSyncStatus {
|
||||
NotReady = 'NOT_READY',
|
||||
@ -893,6 +997,31 @@ export type SessionEntity = {
|
||||
url?: Maybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type SetupOidcSsoInput = {
|
||||
clientID: Scalars['String'];
|
||||
clientSecret: Scalars['String'];
|
||||
issuer: Scalars['String'];
|
||||
name: Scalars['String'];
|
||||
};
|
||||
|
||||
export type SetupSamlSsoInput = {
|
||||
certificate: Scalars['String'];
|
||||
fingerprint?: InputMaybe<Scalars['String']>;
|
||||
id: Scalars['String'];
|
||||
issuer: Scalars['String'];
|
||||
name: Scalars['String'];
|
||||
ssoURL: Scalars['String'];
|
||||
};
|
||||
|
||||
export type SetupSsoOutput = {
|
||||
__typename?: 'SetupSsoOutput';
|
||||
id: Scalars['String'];
|
||||
issuer: Scalars['String'];
|
||||
name: Scalars['String'];
|
||||
status: SsoIdentityProviderStatus;
|
||||
type: IdpType;
|
||||
};
|
||||
|
||||
/** Sort Directions */
|
||||
export enum SortDirection {
|
||||
Asc = 'ASC',
|
||||
@ -1031,6 +1160,7 @@ export type UpdateObjectPayload = {
|
||||
labelSingular?: InputMaybe<Scalars['String']>;
|
||||
namePlural?: InputMaybe<Scalars['String']>;
|
||||
nameSingular?: InputMaybe<Scalars['String']>;
|
||||
shouldSyncLabelAndName?: InputMaybe<Scalars['Boolean']>;
|
||||
};
|
||||
|
||||
export type UpdateOneObjectInput = {
|
||||
@ -1052,11 +1182,13 @@ export type UpdateWorkspaceInput = {
|
||||
displayName?: InputMaybe<Scalars['String']>;
|
||||
domainName?: InputMaybe<Scalars['String']>;
|
||||
inviteHash?: InputMaybe<Scalars['String']>;
|
||||
isPublicInviteLinkEnabled?: InputMaybe<Scalars['Boolean']>;
|
||||
logo?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
export type User = {
|
||||
__typename?: 'User';
|
||||
analyticsTinybirdJwt?: Maybe<Scalars['String']>;
|
||||
canImpersonate: Scalars['Boolean'];
|
||||
createdAt: Scalars['DateTime'];
|
||||
defaultAvatarUrl?: Maybe<Scalars['String']>;
|
||||
@ -1141,6 +1273,7 @@ export type Workspace = {
|
||||
featureFlags?: Maybe<Array<FeatureFlag>>;
|
||||
id: Scalars['UUID'];
|
||||
inviteHash?: Maybe<Scalars['String']>;
|
||||
isPublicInviteLinkEnabled: Scalars['Boolean'];
|
||||
logo?: Maybe<Scalars['String']>;
|
||||
metadataVersion: Scalars['Float'];
|
||||
updatedAt: Scalars['DateTime'];
|
||||
@ -1213,6 +1346,12 @@ export enum WorkspaceMemberTimeFormatEnum {
|
||||
System = 'SYSTEM'
|
||||
}
|
||||
|
||||
export type WorkspaceNameAndId = {
|
||||
__typename?: 'WorkspaceNameAndId';
|
||||
displayName?: Maybe<Scalars['String']>;
|
||||
id: Scalars['String'];
|
||||
};
|
||||
|
||||
export type Field = {
|
||||
__typename?: 'field';
|
||||
createdAt: Scalars['DateTime'];
|
||||
@ -1338,6 +1477,7 @@ export type Object = {
|
||||
labelSingular: Scalars['String'];
|
||||
namePlural: Scalars['String'];
|
||||
nameSingular: Scalars['String'];
|
||||
shouldSyncLabelAndName: Scalars['Boolean'];
|
||||
updatedAt: Scalars['DateTime'];
|
||||
};
|
||||
|
||||
@ -1469,6 +1609,8 @@ export type AuthTokenFragmentFragment = { __typename?: 'AuthToken', token: strin
|
||||
|
||||
export type AuthTokensFragmentFragment = { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } };
|
||||
|
||||
export type AvailableSsoIdentityProvidersFragmentFragment = { __typename?: 'FindAvailableSSOIDPOutput', id: string, issuer: string, name: string, status: SsoIdentityProviderStatus, workspace: { __typename?: 'WorkspaceNameAndId', id: string, displayName?: string | null } };
|
||||
|
||||
export type AuthorizeAppMutationVariables = Exact<{
|
||||
clientId: Scalars['String'];
|
||||
codeChallenge: Scalars['String'];
|
||||
@ -1494,6 +1636,13 @@ export type EmailPasswordResetLinkMutationVariables = Exact<{
|
||||
|
||||
export type EmailPasswordResetLinkMutation = { __typename?: 'Mutation', emailPasswordResetLink: { __typename?: 'EmailPasswordResetLink', success: boolean } };
|
||||
|
||||
export type FindAvailableSsoIdentityProvidersMutationVariables = Exact<{
|
||||
input: FindAvailableSsoidpInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type FindAvailableSsoIdentityProvidersMutation = { __typename?: 'Mutation', findAvailableSSOIdentityProviders: Array<{ __typename?: 'FindAvailableSSOIDPOutput', id: string, issuer: string, name: string, status: SsoIdentityProviderStatus, workspace: { __typename?: 'WorkspaceNameAndId', id: string, displayName?: string | null } }> };
|
||||
|
||||
export type GenerateApiKeyTokenMutationVariables = Exact<{
|
||||
apiKeyId: Scalars['String'];
|
||||
expiresAt: Scalars['String'];
|
||||
@ -1507,19 +1656,26 @@ export type GenerateJwtMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type GenerateJwtMutation = { __typename?: 'Mutation', generateJWT: { __typename?: 'AuthTokens', tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
export type GenerateJwtMutation = { __typename?: 'Mutation', generateJWT: { __typename?: 'GenerateJWTOutputWithAuthTokens', success: boolean, reason: string, authTokens: { __typename?: 'AuthTokens', tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } } | { __typename?: 'GenerateJWTOutputWithSSOAUTH', success: boolean, reason: string, availableSSOIDPs: Array<{ __typename?: 'FindAvailableSSOIDPOutput', id: string, issuer: string, name: string, status: SsoIdentityProviderStatus, workspace: { __typename?: 'WorkspaceNameAndId', id: string, displayName?: string | null } }> } };
|
||||
|
||||
export type GenerateTransientTokenMutationVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GenerateTransientTokenMutation = { __typename?: 'Mutation', generateTransientToken: { __typename?: 'TransientToken', transientToken: { __typename?: 'AuthToken', token: string } } };
|
||||
|
||||
export type GetAuthorizationUrlMutationVariables = Exact<{
|
||||
input: GetAuthorizationUrlInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type GetAuthorizationUrlMutation = { __typename?: 'Mutation', getAuthorizationUrl: { __typename?: 'GetAuthorizationUrlOutput', id: string, type: string, authorizationURL: string } };
|
||||
|
||||
export type ImpersonateMutationVariables = Exact<{
|
||||
userId: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, analyticsTinybirdJwt?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
|
||||
export type RenewTokenMutationVariables = Exact<{
|
||||
appToken: Scalars['String'];
|
||||
@ -1552,7 +1708,7 @@ export type VerifyMutationVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, analyticsTinybirdJwt?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } };
|
||||
|
||||
export type CheckUserExistsQueryVariables = Exact<{
|
||||
email: Scalars['String'];
|
||||
@ -1599,14 +1755,47 @@ export type UpdateBillingSubscriptionMutation = { __typename?: 'Mutation', updat
|
||||
export type GetClientConfigQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, signUpDisabled: boolean, debugMode: boolean, chromeExtensionId?: string | null, authProviders: { __typename?: 'AuthProviders', google: boolean, password: boolean, microsoft: boolean }, billing: { __typename?: 'Billing', isBillingEnabled: boolean, billingUrl?: string | null, billingFreeTrialDurationInDays?: number | null }, support: { __typename?: 'Support', supportDriver: string, supportFrontChatId?: string | null }, sentry: { __typename?: 'Sentry', dsn?: string | null, environment?: string | null, release?: string | null }, captcha: { __typename?: 'Captcha', provider?: CaptchaDriverType | null, siteKey?: string | null }, api: { __typename?: 'ApiConfig', mutationMaximumAffectedRecords: number } } };
|
||||
export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, signUpDisabled: boolean, debugMode: boolean, analyticsEnabled: boolean, chromeExtensionId?: string | null, authProviders: { __typename?: 'AuthProviders', google: boolean, password: boolean, microsoft: boolean, sso: boolean }, billing: { __typename?: 'Billing', isBillingEnabled: boolean, billingUrl?: string | null, billingFreeTrialDurationInDays?: number | null }, support: { __typename?: 'Support', supportDriver: string, supportFrontChatId?: string | null }, sentry: { __typename?: 'Sentry', dsn?: string | null, environment?: string | null, release?: string | null }, captcha: { __typename?: 'Captcha', provider?: CaptchaDriverType | null, siteKey?: string | null }, api: { __typename?: 'ApiConfig', mutationMaximumAffectedRecords: number } } };
|
||||
|
||||
export type SkipSyncEmailOnboardingStepMutationVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type SkipSyncEmailOnboardingStepMutation = { __typename?: 'Mutation', skipSyncEmailOnboardingStep: { __typename?: 'OnboardingStepSuccess', success: boolean } };
|
||||
|
||||
export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> };
|
||||
export type CreateOidcIdentityProviderMutationVariables = Exact<{
|
||||
input: SetupOidcSsoInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateOidcIdentityProviderMutation = { __typename?: 'Mutation', createOIDCIdentityProvider: { __typename?: 'SetupSsoOutput', id: string, type: IdpType, issuer: string, name: string, status: SsoIdentityProviderStatus } };
|
||||
|
||||
export type CreateSamlIdentityProviderMutationVariables = Exact<{
|
||||
input: SetupSamlSsoInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type CreateSamlIdentityProviderMutation = { __typename?: 'Mutation', createSAMLIdentityProvider: { __typename?: 'SetupSsoOutput', id: string, type: IdpType, issuer: string, name: string, status: SsoIdentityProviderStatus } };
|
||||
|
||||
export type DeleteSsoIdentityProviderMutationVariables = Exact<{
|
||||
input: DeleteSsoInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type DeleteSsoIdentityProviderMutation = { __typename?: 'Mutation', deleteSSOIdentityProvider: { __typename?: 'DeleteSsoOutput', identityProviderId: string } };
|
||||
|
||||
export type EditSsoIdentityProviderMutationVariables = Exact<{
|
||||
input: EditSsoInput;
|
||||
}>;
|
||||
|
||||
|
||||
export type EditSsoIdentityProviderMutation = { __typename?: 'Mutation', editSSOIdentityProvider: { __typename?: 'EditSsoOutput', id: string, type: IdpType, issuer: string, name: string, status: SsoIdentityProviderStatus } };
|
||||
|
||||
export type ListSsoIdentityProvidersByWorkspaceIdQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type ListSsoIdentityProvidersByWorkspaceIdQuery = { __typename?: 'Query', listSSOIdentityProvidersByWorkspaceId: Array<{ __typename?: 'FindAvailableSSOIDPOutput', type: IdpType, id: string, name: string, issuer: string, status: SsoIdentityProviderStatus }> };
|
||||
|
||||
export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, analyticsTinybirdJwt?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> };
|
||||
|
||||
export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
@ -1623,7 +1812,7 @@ export type UploadProfilePictureMutation = { __typename?: 'Mutation', uploadProf
|
||||
export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> } };
|
||||
export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, analyticsTinybirdJwt?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> } };
|
||||
|
||||
export type ActivateWorkflowVersionMutationVariables = Exact<{
|
||||
workflowVersionId: Scalars['String'];
|
||||
@ -1801,6 +1990,18 @@ export const AuthTokensFragmentFragmentDoc = gql`
|
||||
}
|
||||
}
|
||||
${AuthTokenFragmentFragmentDoc}`;
|
||||
export const AvailableSsoIdentityProvidersFragmentFragmentDoc = gql`
|
||||
fragment AvailableSSOIdentityProvidersFragment on FindAvailableSSOIDPOutput {
|
||||
id
|
||||
issuer
|
||||
name
|
||||
status
|
||||
workspace {
|
||||
id
|
||||
displayName
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const WorkspaceMemberQueryFragmentFragmentDoc = gql`
|
||||
fragment WorkspaceMemberQueryFragment on WorkspaceMember {
|
||||
id
|
||||
@ -1824,6 +2025,7 @@ export const UserQueryFragmentFragmentDoc = gql`
|
||||
email
|
||||
canImpersonate
|
||||
supportUserHash
|
||||
analyticsTinybirdJwt
|
||||
onboardingStatus
|
||||
workspaceMember {
|
||||
...WorkspaceMemberQueryFragment
|
||||
@ -1839,6 +2041,7 @@ export const UserQueryFragmentFragmentDoc = gql`
|
||||
inviteHash
|
||||
allowImpersonation
|
||||
activationStatus
|
||||
isPublicInviteLinkEnabled
|
||||
featureFlags {
|
||||
id
|
||||
key
|
||||
@ -2235,6 +2438,39 @@ export function useEmailPasswordResetLinkMutation(baseOptions?: Apollo.MutationH
|
||||
export type EmailPasswordResetLinkMutationHookResult = ReturnType<typeof useEmailPasswordResetLinkMutation>;
|
||||
export type EmailPasswordResetLinkMutationResult = Apollo.MutationResult<EmailPasswordResetLinkMutation>;
|
||||
export type EmailPasswordResetLinkMutationOptions = Apollo.BaseMutationOptions<EmailPasswordResetLinkMutation, EmailPasswordResetLinkMutationVariables>;
|
||||
export const FindAvailableSsoIdentityProvidersDocument = gql`
|
||||
mutation FindAvailableSSOIdentityProviders($input: FindAvailableSSOIDPInput!) {
|
||||
findAvailableSSOIdentityProviders(input: $input) {
|
||||
...AvailableSSOIdentityProvidersFragment
|
||||
}
|
||||
}
|
||||
${AvailableSsoIdentityProvidersFragmentFragmentDoc}`;
|
||||
export type FindAvailableSsoIdentityProvidersMutationFn = Apollo.MutationFunction<FindAvailableSsoIdentityProvidersMutation, FindAvailableSsoIdentityProvidersMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useFindAvailableSsoIdentityProvidersMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useFindAvailableSsoIdentityProvidersMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useFindAvailableSsoIdentityProvidersMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [findAvailableSsoIdentityProvidersMutation, { data, loading, error }] = useFindAvailableSsoIdentityProvidersMutation({
|
||||
* variables: {
|
||||
* input: // value for 'input'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useFindAvailableSsoIdentityProvidersMutation(baseOptions?: Apollo.MutationHookOptions<FindAvailableSsoIdentityProvidersMutation, FindAvailableSsoIdentityProvidersMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<FindAvailableSsoIdentityProvidersMutation, FindAvailableSsoIdentityProvidersMutationVariables>(FindAvailableSsoIdentityProvidersDocument, options);
|
||||
}
|
||||
export type FindAvailableSsoIdentityProvidersMutationHookResult = ReturnType<typeof useFindAvailableSsoIdentityProvidersMutation>;
|
||||
export type FindAvailableSsoIdentityProvidersMutationResult = Apollo.MutationResult<FindAvailableSsoIdentityProvidersMutation>;
|
||||
export type FindAvailableSsoIdentityProvidersMutationOptions = Apollo.BaseMutationOptions<FindAvailableSsoIdentityProvidersMutation, FindAvailableSsoIdentityProvidersMutationVariables>;
|
||||
export const GenerateApiKeyTokenDocument = gql`
|
||||
mutation GenerateApiKeyToken($apiKeyId: String!, $expiresAt: String!) {
|
||||
generateApiKeyToken(apiKeyId: $apiKeyId, expiresAt: $expiresAt) {
|
||||
@ -2272,12 +2508,26 @@ export type GenerateApiKeyTokenMutationOptions = Apollo.BaseMutationOptions<Gene
|
||||
export const GenerateJwtDocument = gql`
|
||||
mutation GenerateJWT($workspaceId: String!) {
|
||||
generateJWT(workspaceId: $workspaceId) {
|
||||
tokens {
|
||||
...AuthTokensFragment
|
||||
... on GenerateJWTOutputWithAuthTokens {
|
||||
success
|
||||
reason
|
||||
authTokens {
|
||||
tokens {
|
||||
...AuthTokensFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
... on GenerateJWTOutputWithSSOAUTH {
|
||||
success
|
||||
reason
|
||||
availableSSOIDPs {
|
||||
...AvailableSSOIdentityProvidersFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${AuthTokensFragmentFragmentDoc}`;
|
||||
${AuthTokensFragmentFragmentDoc}
|
||||
${AvailableSsoIdentityProvidersFragmentFragmentDoc}`;
|
||||
export type GenerateJwtMutationFn = Apollo.MutationFunction<GenerateJwtMutation, GenerateJwtMutationVariables>;
|
||||
|
||||
/**
|
||||
@ -2338,6 +2588,41 @@ export function useGenerateTransientTokenMutation(baseOptions?: Apollo.MutationH
|
||||
export type GenerateTransientTokenMutationHookResult = ReturnType<typeof useGenerateTransientTokenMutation>;
|
||||
export type GenerateTransientTokenMutationResult = Apollo.MutationResult<GenerateTransientTokenMutation>;
|
||||
export type GenerateTransientTokenMutationOptions = Apollo.BaseMutationOptions<GenerateTransientTokenMutation, GenerateTransientTokenMutationVariables>;
|
||||
export const GetAuthorizationUrlDocument = gql`
|
||||
mutation GetAuthorizationUrl($input: GetAuthorizationUrlInput!) {
|
||||
getAuthorizationUrl(input: $input) {
|
||||
id
|
||||
type
|
||||
authorizationURL
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type GetAuthorizationUrlMutationFn = Apollo.MutationFunction<GetAuthorizationUrlMutation, GetAuthorizationUrlMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useGetAuthorizationUrlMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useGetAuthorizationUrlMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useGetAuthorizationUrlMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [getAuthorizationUrlMutation, { data, loading, error }] = useGetAuthorizationUrlMutation({
|
||||
* variables: {
|
||||
* input: // value for 'input'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useGetAuthorizationUrlMutation(baseOptions?: Apollo.MutationHookOptions<GetAuthorizationUrlMutation, GetAuthorizationUrlMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<GetAuthorizationUrlMutation, GetAuthorizationUrlMutationVariables>(GetAuthorizationUrlDocument, options);
|
||||
}
|
||||
export type GetAuthorizationUrlMutationHookResult = ReturnType<typeof useGetAuthorizationUrlMutation>;
|
||||
export type GetAuthorizationUrlMutationResult = Apollo.MutationResult<GetAuthorizationUrlMutation>;
|
||||
export type GetAuthorizationUrlMutationOptions = Apollo.BaseMutationOptions<GetAuthorizationUrlMutation, GetAuthorizationUrlMutationVariables>;
|
||||
export const ImpersonateDocument = gql`
|
||||
mutation Impersonate($userId: String!) {
|
||||
impersonate(userId: $userId) {
|
||||
@ -2756,6 +3041,7 @@ export const GetClientConfigDocument = gql`
|
||||
google
|
||||
password
|
||||
microsoft
|
||||
sso
|
||||
}
|
||||
billing {
|
||||
isBillingEnabled
|
||||
@ -2765,6 +3051,7 @@ export const GetClientConfigDocument = gql`
|
||||
signInPrefilled
|
||||
signUpDisabled
|
||||
debugMode
|
||||
analyticsEnabled
|
||||
support {
|
||||
supportDriver
|
||||
supportFrontChatId
|
||||
@ -2844,6 +3131,188 @@ export function useSkipSyncEmailOnboardingStepMutation(baseOptions?: Apollo.Muta
|
||||
export type SkipSyncEmailOnboardingStepMutationHookResult = ReturnType<typeof useSkipSyncEmailOnboardingStepMutation>;
|
||||
export type SkipSyncEmailOnboardingStepMutationResult = Apollo.MutationResult<SkipSyncEmailOnboardingStepMutation>;
|
||||
export type SkipSyncEmailOnboardingStepMutationOptions = Apollo.BaseMutationOptions<SkipSyncEmailOnboardingStepMutation, SkipSyncEmailOnboardingStepMutationVariables>;
|
||||
export const CreateOidcIdentityProviderDocument = gql`
|
||||
mutation CreateOIDCIdentityProvider($input: SetupOIDCSsoInput!) {
|
||||
createOIDCIdentityProvider(input: $input) {
|
||||
id
|
||||
type
|
||||
issuer
|
||||
name
|
||||
status
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type CreateOidcIdentityProviderMutationFn = Apollo.MutationFunction<CreateOidcIdentityProviderMutation, CreateOidcIdentityProviderMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useCreateOidcIdentityProviderMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useCreateOidcIdentityProviderMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useCreateOidcIdentityProviderMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [createOidcIdentityProviderMutation, { data, loading, error }] = useCreateOidcIdentityProviderMutation({
|
||||
* variables: {
|
||||
* input: // value for 'input'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useCreateOidcIdentityProviderMutation(baseOptions?: Apollo.MutationHookOptions<CreateOidcIdentityProviderMutation, CreateOidcIdentityProviderMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<CreateOidcIdentityProviderMutation, CreateOidcIdentityProviderMutationVariables>(CreateOidcIdentityProviderDocument, options);
|
||||
}
|
||||
export type CreateOidcIdentityProviderMutationHookResult = ReturnType<typeof useCreateOidcIdentityProviderMutation>;
|
||||
export type CreateOidcIdentityProviderMutationResult = Apollo.MutationResult<CreateOidcIdentityProviderMutation>;
|
||||
export type CreateOidcIdentityProviderMutationOptions = Apollo.BaseMutationOptions<CreateOidcIdentityProviderMutation, CreateOidcIdentityProviderMutationVariables>;
|
||||
export const CreateSamlIdentityProviderDocument = gql`
|
||||
mutation CreateSAMLIdentityProvider($input: SetupSAMLSsoInput!) {
|
||||
createSAMLIdentityProvider(input: $input) {
|
||||
id
|
||||
type
|
||||
issuer
|
||||
name
|
||||
status
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type CreateSamlIdentityProviderMutationFn = Apollo.MutationFunction<CreateSamlIdentityProviderMutation, CreateSamlIdentityProviderMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useCreateSamlIdentityProviderMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useCreateSamlIdentityProviderMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useCreateSamlIdentityProviderMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [createSamlIdentityProviderMutation, { data, loading, error }] = useCreateSamlIdentityProviderMutation({
|
||||
* variables: {
|
||||
* input: // value for 'input'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useCreateSamlIdentityProviderMutation(baseOptions?: Apollo.MutationHookOptions<CreateSamlIdentityProviderMutation, CreateSamlIdentityProviderMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<CreateSamlIdentityProviderMutation, CreateSamlIdentityProviderMutationVariables>(CreateSamlIdentityProviderDocument, options);
|
||||
}
|
||||
export type CreateSamlIdentityProviderMutationHookResult = ReturnType<typeof useCreateSamlIdentityProviderMutation>;
|
||||
export type CreateSamlIdentityProviderMutationResult = Apollo.MutationResult<CreateSamlIdentityProviderMutation>;
|
||||
export type CreateSamlIdentityProviderMutationOptions = Apollo.BaseMutationOptions<CreateSamlIdentityProviderMutation, CreateSamlIdentityProviderMutationVariables>;
|
||||
export const DeleteSsoIdentityProviderDocument = gql`
|
||||
mutation DeleteSSOIdentityProvider($input: DeleteSsoInput!) {
|
||||
deleteSSOIdentityProvider(input: $input) {
|
||||
identityProviderId
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type DeleteSsoIdentityProviderMutationFn = Apollo.MutationFunction<DeleteSsoIdentityProviderMutation, DeleteSsoIdentityProviderMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useDeleteSsoIdentityProviderMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useDeleteSsoIdentityProviderMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useDeleteSsoIdentityProviderMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [deleteSsoIdentityProviderMutation, { data, loading, error }] = useDeleteSsoIdentityProviderMutation({
|
||||
* variables: {
|
||||
* input: // value for 'input'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useDeleteSsoIdentityProviderMutation(baseOptions?: Apollo.MutationHookOptions<DeleteSsoIdentityProviderMutation, DeleteSsoIdentityProviderMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<DeleteSsoIdentityProviderMutation, DeleteSsoIdentityProviderMutationVariables>(DeleteSsoIdentityProviderDocument, options);
|
||||
}
|
||||
export type DeleteSsoIdentityProviderMutationHookResult = ReturnType<typeof useDeleteSsoIdentityProviderMutation>;
|
||||
export type DeleteSsoIdentityProviderMutationResult = Apollo.MutationResult<DeleteSsoIdentityProviderMutation>;
|
||||
export type DeleteSsoIdentityProviderMutationOptions = Apollo.BaseMutationOptions<DeleteSsoIdentityProviderMutation, DeleteSsoIdentityProviderMutationVariables>;
|
||||
export const EditSsoIdentityProviderDocument = gql`
|
||||
mutation EditSSOIdentityProvider($input: EditSsoInput!) {
|
||||
editSSOIdentityProvider(input: $input) {
|
||||
id
|
||||
type
|
||||
issuer
|
||||
name
|
||||
status
|
||||
}
|
||||
}
|
||||
`;
|
||||
export type EditSsoIdentityProviderMutationFn = Apollo.MutationFunction<EditSsoIdentityProviderMutation, EditSsoIdentityProviderMutationVariables>;
|
||||
|
||||
/**
|
||||
* __useEditSsoIdentityProviderMutation__
|
||||
*
|
||||
* To run a mutation, you first call `useEditSsoIdentityProviderMutation` within a React component and pass it any options that fit your needs.
|
||||
* When your component renders, `useEditSsoIdentityProviderMutation` returns a tuple that includes:
|
||||
* - A mutate function that you can call at any time to execute the mutation
|
||||
* - An object with fields that represent the current status of the mutation's execution
|
||||
*
|
||||
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||
*
|
||||
* @example
|
||||
* const [editSsoIdentityProviderMutation, { data, loading, error }] = useEditSsoIdentityProviderMutation({
|
||||
* variables: {
|
||||
* input: // value for 'input'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useEditSsoIdentityProviderMutation(baseOptions?: Apollo.MutationHookOptions<EditSsoIdentityProviderMutation, EditSsoIdentityProviderMutationVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useMutation<EditSsoIdentityProviderMutation, EditSsoIdentityProviderMutationVariables>(EditSsoIdentityProviderDocument, options);
|
||||
}
|
||||
export type EditSsoIdentityProviderMutationHookResult = ReturnType<typeof useEditSsoIdentityProviderMutation>;
|
||||
export type EditSsoIdentityProviderMutationResult = Apollo.MutationResult<EditSsoIdentityProviderMutation>;
|
||||
export type EditSsoIdentityProviderMutationOptions = Apollo.BaseMutationOptions<EditSsoIdentityProviderMutation, EditSsoIdentityProviderMutationVariables>;
|
||||
export const ListSsoIdentityProvidersByWorkspaceIdDocument = gql`
|
||||
query ListSSOIdentityProvidersByWorkspaceId {
|
||||
listSSOIdentityProvidersByWorkspaceId {
|
||||
type
|
||||
id
|
||||
name
|
||||
issuer
|
||||
status
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useListSsoIdentityProvidersByWorkspaceIdQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useListSsoIdentityProvidersByWorkspaceIdQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useListSsoIdentityProvidersByWorkspaceIdQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useListSsoIdentityProvidersByWorkspaceIdQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useListSsoIdentityProvidersByWorkspaceIdQuery(baseOptions?: Apollo.QueryHookOptions<ListSsoIdentityProvidersByWorkspaceIdQuery, ListSsoIdentityProvidersByWorkspaceIdQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ListSsoIdentityProvidersByWorkspaceIdQuery, ListSsoIdentityProvidersByWorkspaceIdQueryVariables>(ListSsoIdentityProvidersByWorkspaceIdDocument, options);
|
||||
}
|
||||
export function useListSsoIdentityProvidersByWorkspaceIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ListSsoIdentityProvidersByWorkspaceIdQuery, ListSsoIdentityProvidersByWorkspaceIdQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ListSsoIdentityProvidersByWorkspaceIdQuery, ListSsoIdentityProvidersByWorkspaceIdQueryVariables>(ListSsoIdentityProvidersByWorkspaceIdDocument, options);
|
||||
}
|
||||
export type ListSsoIdentityProvidersByWorkspaceIdQueryHookResult = ReturnType<typeof useListSsoIdentityProvidersByWorkspaceIdQuery>;
|
||||
export type ListSsoIdentityProvidersByWorkspaceIdLazyQueryHookResult = ReturnType<typeof useListSsoIdentityProvidersByWorkspaceIdLazyQuery>;
|
||||
export type ListSsoIdentityProvidersByWorkspaceIdQueryResult = Apollo.QueryResult<ListSsoIdentityProvidersByWorkspaceIdQuery, ListSsoIdentityProvidersByWorkspaceIdQueryVariables>;
|
||||
export const DeleteUserAccountDocument = gql`
|
||||
mutation DeleteUserAccount {
|
||||
deleteUser {
|
||||
|
@ -1,51 +1,97 @@
|
||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||
import { contextStoreCurrentObjectMetadataIdState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdState';
|
||||
import { contextStoreTargetedRecordIdsState } from '@/context-store/states/contextStoreTargetedRecordIdsState';
|
||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||
import { ActionMenuType } from '@/action-menu/types/ActionMenuType';
|
||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { DELETE_MAX_COUNT } from '@/object-record/constants/DeleteMaxCount';
|
||||
import { useDeleteTableData } from '@/object-record/record-index/options/hooks/useDeleteTableData';
|
||||
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
||||
import { useFetchAllRecordIds } from '@/object-record/hooks/useFetchAllRecordIds';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconTrash } from 'twenty-ui';
|
||||
import { IconTrash, isDefined } from 'twenty-ui';
|
||||
|
||||
export const DeleteRecordsActionEffect = ({
|
||||
position,
|
||||
objectMetadataItem,
|
||||
actionMenuType,
|
||||
}: {
|
||||
position: number;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
actionMenuType: ActionMenuType;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
|
||||
const contextStoreTargetedRecordIds = useRecoilValue(
|
||||
contextStoreTargetedRecordIdsState,
|
||||
);
|
||||
|
||||
const contextStoreCurrentObjectMetadataId = useRecoilValue(
|
||||
contextStoreCurrentObjectMetadataIdState,
|
||||
);
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItemById({
|
||||
objectId: contextStoreCurrentObjectMetadataId,
|
||||
});
|
||||
|
||||
const [isDeleteRecordsModalOpen, setIsDeleteRecordsModalOpen] =
|
||||
useState(false);
|
||||
|
||||
const { deleteTableData } = useDeleteTableData({
|
||||
objectNameSingular: objectMetadataItem?.nameSingular ?? '',
|
||||
recordIndexId: objectMetadataItem?.namePlural ?? '',
|
||||
const { resetTableRowSelection } = useRecordTable({
|
||||
recordTableId: objectMetadataItem.namePlural,
|
||||
});
|
||||
|
||||
const handleDeleteClick = useCallback(() => {
|
||||
deleteTableData(contextStoreTargetedRecordIds);
|
||||
}, [deleteTableData, contextStoreTargetedRecordIds]);
|
||||
const { deleteManyRecords } = useDeleteManyRecords({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
});
|
||||
|
||||
const isRemoteObject = objectMetadataItem?.isRemote ?? false;
|
||||
const { favorites, deleteFavorite } = useFavorites();
|
||||
|
||||
const numberOfSelectedRecords = contextStoreTargetedRecordIds.length;
|
||||
const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
|
||||
contextStoreNumberOfSelectedRecordsComponentState,
|
||||
);
|
||||
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const graphqlFilter = computeContextStoreFilters(
|
||||
contextStoreTargetedRecordsRule,
|
||||
objectMetadataItem,
|
||||
);
|
||||
|
||||
const { fetchAllRecordIds } = useFetchAllRecordIds({
|
||||
objectNameSingular: objectMetadataItem.nameSingular,
|
||||
filter: graphqlFilter,
|
||||
});
|
||||
|
||||
const { closeRightDrawer } = useRightDrawer();
|
||||
|
||||
const handleDeleteClick = useCallback(async () => {
|
||||
const recordIdsToDelete = await fetchAllRecordIds();
|
||||
|
||||
resetTableRowSelection();
|
||||
|
||||
for (const recordIdToDelete of recordIdsToDelete) {
|
||||
const foundFavorite = favorites?.find(
|
||||
(favorite) => favorite.recordId === recordIdToDelete,
|
||||
);
|
||||
|
||||
if (foundFavorite !== undefined) {
|
||||
deleteFavorite(foundFavorite.id);
|
||||
}
|
||||
}
|
||||
|
||||
await deleteManyRecords(recordIdsToDelete, {
|
||||
delayInMsBetweenRequests: 50,
|
||||
});
|
||||
}, [
|
||||
deleteFavorite,
|
||||
deleteManyRecords,
|
||||
favorites,
|
||||
fetchAllRecordIds,
|
||||
resetTableRowSelection,
|
||||
]);
|
||||
|
||||
const isRemoteObject = objectMetadataItem.isRemote;
|
||||
|
||||
const canDelete =
|
||||
!isRemoteObject && numberOfSelectedRecords < DELETE_MAX_COUNT;
|
||||
!isRemoteObject &&
|
||||
isDefined(contextStoreNumberOfSelectedRecords) &&
|
||||
contextStoreNumberOfSelectedRecords < DELETE_MAX_COUNT &&
|
||||
contextStoreNumberOfSelectedRecords > 0;
|
||||
|
||||
useEffect(() => {
|
||||
if (canDelete) {
|
||||
@ -62,32 +108,49 @@ export const DeleteRecordsActionEffect = ({
|
||||
<ConfirmationModal
|
||||
isOpen={isDeleteRecordsModalOpen}
|
||||
setIsOpen={setIsDeleteRecordsModalOpen}
|
||||
title={`Delete ${numberOfSelectedRecords} ${
|
||||
numberOfSelectedRecords === 1 ? `record` : 'records'
|
||||
title={`Delete ${contextStoreNumberOfSelectedRecords} ${
|
||||
contextStoreNumberOfSelectedRecords === 1 ? `record` : 'records'
|
||||
}`}
|
||||
subtitle={`Are you sure you want to delete ${
|
||||
numberOfSelectedRecords === 1 ? 'this record' : 'these records'
|
||||
contextStoreNumberOfSelectedRecords === 1
|
||||
? 'this record'
|
||||
: 'these records'
|
||||
}? ${
|
||||
numberOfSelectedRecords === 1 ? 'It' : 'They'
|
||||
contextStoreNumberOfSelectedRecords === 1 ? 'It' : 'They'
|
||||
} can be recovered from the Options menu.`}
|
||||
onConfirmClick={() => handleDeleteClick()}
|
||||
onConfirmClick={() => {
|
||||
handleDeleteClick();
|
||||
|
||||
if (actionMenuType === 'recordShow') {
|
||||
closeRightDrawer();
|
||||
}
|
||||
}}
|
||||
deleteButtonText={`Delete ${
|
||||
numberOfSelectedRecords > 1 ? 'Records' : 'Record'
|
||||
contextStoreNumberOfSelectedRecords > 1 ? 'Records' : 'Record'
|
||||
}`}
|
||||
modalVariant={
|
||||
actionMenuType === 'recordShow' ? 'tertiary' : 'primary'
|
||||
}
|
||||
/>
|
||||
),
|
||||
});
|
||||
} else {
|
||||
removeActionMenuEntry('delete');
|
||||
}
|
||||
|
||||
return () => {
|
||||
removeActionMenuEntry('delete');
|
||||
};
|
||||
}, [
|
||||
canDelete,
|
||||
actionMenuType,
|
||||
addActionMenuEntry,
|
||||
removeActionMenuEntry,
|
||||
isDeleteRecordsModalOpen,
|
||||
numberOfSelectedRecords,
|
||||
canDelete,
|
||||
closeRightDrawer,
|
||||
contextStoreNumberOfSelectedRecords,
|
||||
handleDeleteClick,
|
||||
isDeleteRecordsModalOpen,
|
||||
position,
|
||||
removeActionMenuEntry,
|
||||
]);
|
||||
|
||||
return null;
|
||||
|
@ -1,38 +1,27 @@
|
||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||
import { contextStoreCurrentObjectMetadataIdState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdState';
|
||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||
import {
|
||||
displayedExportProgress,
|
||||
useExportTableData,
|
||||
} from '@/object-record/record-index/options/hooks/useExportTableData';
|
||||
useExportRecordData,
|
||||
} from '@/action-menu/hooks/useExportRecordData';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconFileExport } from 'twenty-ui';
|
||||
|
||||
export const ExportRecordsActionEffect = ({
|
||||
position,
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
position: number;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
|
||||
const contextStoreCurrentObjectMetadataId = useRecoilValue(
|
||||
contextStoreCurrentObjectMetadataIdState,
|
||||
);
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItemById({
|
||||
objectId: contextStoreCurrentObjectMetadataId,
|
||||
});
|
||||
|
||||
const baseTableDataParams = {
|
||||
const { progress, download } = useExportRecordData({
|
||||
delayMs: 100,
|
||||
objectNameSingular: objectMetadataItem?.nameSingular ?? '',
|
||||
recordIndexId: objectMetadataItem?.namePlural ?? '',
|
||||
};
|
||||
|
||||
const { progress, download } = useExportTableData({
|
||||
...baseTableDataParams,
|
||||
filename: `${objectMetadataItem?.nameSingular}.csv`,
|
||||
objectMetadataItem,
|
||||
recordIndexId: objectMetadataItem.namePlural,
|
||||
filename: `${objectMetadataItem.nameSingular}.csv`,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -1,39 +1,37 @@
|
||||
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
|
||||
import { contextStoreCurrentObjectMetadataIdState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdState';
|
||||
import { contextStoreTargetedRecordIdsState } from '@/context-store/states/contextStoreTargetedRecordIdsState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconHeart, IconHeartOff, isDefined } from 'twenty-ui';
|
||||
|
||||
export const ManageFavoritesActionEffect = ({
|
||||
position,
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
position: number;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
|
||||
|
||||
const contextStoreTargetedRecordIds = useRecoilValue(
|
||||
contextStoreTargetedRecordIdsState,
|
||||
);
|
||||
const contextStoreCurrentObjectMetadataId = useRecoilValue(
|
||||
contextStoreCurrentObjectMetadataIdState,
|
||||
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
|
||||
contextStoreTargetedRecordsRuleComponentState,
|
||||
);
|
||||
|
||||
const { favorites, createFavorite, deleteFavorite } = useFavorites();
|
||||
|
||||
const selectedRecordId = contextStoreTargetedRecordIds[0];
|
||||
const selectedRecordId =
|
||||
contextStoreTargetedRecordsRule.mode === 'selection'
|
||||
? contextStoreTargetedRecordsRule.selectedRecordIds[0]
|
||||
: undefined;
|
||||
|
||||
const selectedRecord = useRecoilValue(
|
||||
recordStoreFamilyState(selectedRecordId),
|
||||
recordStoreFamilyState(selectedRecordId ?? ''),
|
||||
);
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItemById({
|
||||
objectId: contextStoreCurrentObjectMetadataId,
|
||||
});
|
||||
|
||||
const foundFavorite = favorites?.find(
|
||||
(favorite) => favorite.recordId === selectedRecordId,
|
||||
);
|
||||
|
@ -1,13 +1,26 @@
|
||||
import { DeleteRecordsActionEffect } from '@/action-menu/actions/record-actions/components/DeleteRecordsActionEffect';
|
||||
import { ExportRecordsActionEffect } from '@/action-menu/actions/record-actions/components/ExportRecordsActionEffect';
|
||||
import { ActionMenuType } from '@/action-menu/types/ActionMenuType';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
const actionEffects = [ExportRecordsActionEffect, DeleteRecordsActionEffect];
|
||||
|
||||
export const MultipleRecordsActionMenuEntriesSetter = () => {
|
||||
export const MultipleRecordsActionMenuEntriesSetter = ({
|
||||
objectMetadataItem,
|
||||
actionMenuType,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
actionMenuType: ActionMenuType;
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
{actionEffects.map((ActionEffect, index) => (
|
||||
<ActionEffect key={index} position={index} />
|
||||
<ActionEffect
|
||||
key={index}
|
||||
position={index}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
actionMenuType={actionMenuType}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
@ -1,20 +1,51 @@
|
||||
import { MultipleRecordsActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/MultipleRecordsActionMenuEntriesSetter';
|
||||
import { SingleRecordActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/SingleRecordActionMenuEntriesSetter';
|
||||
import { contextStoreTargetedRecordIdsState } from '@/context-store/states/contextStoreTargetedRecordIdsState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { ActionMenuType } from '@/action-menu/types/ActionMenuType';
|
||||
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
|
||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const RecordActionMenuEntriesSetter = () => {
|
||||
const contextStoreTargetedRecordIds = useRecoilValue(
|
||||
contextStoreTargetedRecordIdsState,
|
||||
export const RecordActionMenuEntriesSetter = ({
|
||||
actionMenuType,
|
||||
}: {
|
||||
actionMenuType: ActionMenuType;
|
||||
}) => {
|
||||
const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
|
||||
contextStoreNumberOfSelectedRecordsComponentState,
|
||||
);
|
||||
|
||||
if (contextStoreTargetedRecordIds.length === 0) {
|
||||
const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentObjectMetadataIdComponentState,
|
||||
);
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItemById({
|
||||
objectId: contextStoreCurrentObjectMetadataId ?? '',
|
||||
});
|
||||
|
||||
if (!objectMetadataItem) {
|
||||
throw new Error(
|
||||
`Object metadata item not found for id ${contextStoreCurrentObjectMetadataId}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!contextStoreNumberOfSelectedRecords) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (contextStoreTargetedRecordIds.length === 1) {
|
||||
return <SingleRecordActionMenuEntriesSetter />;
|
||||
if (contextStoreNumberOfSelectedRecords === 1) {
|
||||
return (
|
||||
<SingleRecordActionMenuEntriesSetter
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
actionMenuType={actionMenuType}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <MultipleRecordsActionMenuEntriesSetter />;
|
||||
return (
|
||||
<MultipleRecordsActionMenuEntriesSetter
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
actionMenuType={actionMenuType}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,17 +1,30 @@
|
||||
import { DeleteRecordsActionEffect } from '@/action-menu/actions/record-actions/components/DeleteRecordsActionEffect';
|
||||
import { ExportRecordsActionEffect } from '@/action-menu/actions/record-actions/components/ExportRecordsActionEffect';
|
||||
import { ManageFavoritesActionEffect } from '@/action-menu/actions/record-actions/components/ManageFavoritesActionEffect';
|
||||
import { ActionMenuType } from '@/action-menu/types/ActionMenuType';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
export const SingleRecordActionMenuEntriesSetter = () => {
|
||||
export const SingleRecordActionMenuEntriesSetter = ({
|
||||
objectMetadataItem,
|
||||
actionMenuType,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
actionMenuType: ActionMenuType;
|
||||
}) => {
|
||||
const actionEffects = [
|
||||
ManageFavoritesActionEffect,
|
||||
ExportRecordsActionEffect,
|
||||
DeleteRecordsActionEffect,
|
||||
ManageFavoritesActionEffect,
|
||||
];
|
||||
return (
|
||||
<>
|
||||
{actionEffects.map((ActionEffect, index) => (
|
||||
<ActionEffect key={index} position={index} />
|
||||
<ActionEffect
|
||||
key={index}
|
||||
position={index}
|
||||
objectMetadataItem={objectMetadataItem}
|
||||
actionMenuType={actionMenuType}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
@ -0,0 +1,35 @@
|
||||
import { RecordActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter';
|
||||
import { ActionMenuConfirmationModals } from '@/action-menu/components/ActionMenuConfirmationModals';
|
||||
import { RecordIndexActionMenuBar } from '@/action-menu/components/RecordIndexActionMenuBar';
|
||||
import { RecordIndexActionMenuDropdown } from '@/action-menu/components/RecordIndexActionMenuDropdown';
|
||||
import { RecordIndexActionMenuEffect } from '@/action-menu/components/RecordIndexActionMenuEffect';
|
||||
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const RecordIndexActionMenu = ({
|
||||
actionMenuId,
|
||||
}: {
|
||||
actionMenuId: string;
|
||||
}) => {
|
||||
const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentObjectMetadataIdComponentState,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{contextStoreCurrentObjectMetadataId && (
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{ instanceId: actionMenuId }}
|
||||
>
|
||||
<RecordIndexActionMenuBar />
|
||||
<RecordIndexActionMenuDropdown />
|
||||
<ActionMenuConfirmationModals />
|
||||
<RecordIndexActionMenuEffect />
|
||||
<RecordActionMenuEntriesSetter actionMenuType="recordIndex" />
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,14 +1,13 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { ActionMenuBarEntry } from '@/action-menu/components/ActionMenuBarEntry';
|
||||
import { RecordIndexActionMenuBarEntry } from '@/action-menu/components/RecordIndexActionMenuBarEntry';
|
||||
import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { ActionBarHotkeyScope } from '@/action-menu/types/ActionBarHotKeyScope';
|
||||
import { contextStoreTargetedRecordIdsState } from '@/context-store/states/contextStoreTargetedRecordIdsState';
|
||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||
import { BottomBar } from '@/ui/layout/bottom-bar/components/BottomBar';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
const StyledLabel = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
@ -18,9 +17,9 @@ const StyledLabel = styled.div`
|
||||
padding-right: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
export const ActionMenuBar = () => {
|
||||
const contextStoreTargetedRecordIds = useRecoilValue(
|
||||
contextStoreTargetedRecordIdsState,
|
||||
export const RecordIndexActionMenuBar = () => {
|
||||
const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
|
||||
contextStoreNumberOfSelectedRecordsComponentState,
|
||||
);
|
||||
|
||||
const actionMenuId = useAvailableComponentInstanceIdOrThrow(
|
||||
@ -42,11 +41,9 @@ export const ActionMenuBar = () => {
|
||||
scope: ActionBarHotkeyScope.ActionBar,
|
||||
}}
|
||||
>
|
||||
<StyledLabel>
|
||||
{contextStoreTargetedRecordIds.length} selected:
|
||||
</StyledLabel>
|
||||
<StyledLabel>{contextStoreNumberOfSelectedRecords} selected:</StyledLabel>
|
||||
{actionMenuEntries.map((entry, index) => (
|
||||
<ActionMenuBarEntry key={index} entry={entry} />
|
||||
<RecordIndexActionMenuBarEntry key={index} entry={entry} />
|
||||
))}
|
||||
</BottomBar>
|
||||
);
|
@ -4,7 +4,7 @@ import styled from '@emotion/styled';
|
||||
import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry';
|
||||
import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent';
|
||||
|
||||
type ActionMenuBarEntryProps = {
|
||||
type RecordIndexActionMenuBarEntryProps = {
|
||||
entry: ActionMenuEntry;
|
||||
};
|
||||
|
||||
@ -35,7 +35,9 @@ const StyledButtonLabel = styled.div`
|
||||
margin-left: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
export const ActionMenuBarEntry = ({ entry }: ActionMenuBarEntryProps) => {
|
||||
export const RecordIndexActionMenuBarEntry = ({
|
||||
entry,
|
||||
}: RecordIndexActionMenuBarEntryProps) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<StyledButton
|
@ -3,11 +3,12 @@ import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { PositionType } from '../types/PositionType';
|
||||
|
||||
import { actionMenuDropdownPositionComponentState } from '@/action-menu/states/actionMenuDropdownPositionComponentState';
|
||||
import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
|
||||
import { ActionMenuDropdownHotkeyScope } from '@/action-menu/types/ActionMenuDropdownHotKeyScope';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
@ -34,7 +35,7 @@ const StyledContainerActionMenuDropdown = styled.div<StyledContainerProps>`
|
||||
width: auto;
|
||||
`;
|
||||
|
||||
export const ActionMenuDropdown = () => {
|
||||
export const RecordIndexActionMenuDropdown = () => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentSelector,
|
||||
);
|
||||
@ -45,7 +46,7 @@ export const ActionMenuDropdown = () => {
|
||||
|
||||
const actionMenuDropdownPosition = useRecoilValue(
|
||||
extractComponentState(
|
||||
actionMenuDropdownPositionComponentState,
|
||||
recordIndexActionMenuDropdownPositionComponentState,
|
||||
`action-menu-dropdown-${actionMenuId}`,
|
||||
),
|
||||
);
|
||||
@ -64,7 +65,7 @@ export const ActionMenuDropdown = () => {
|
||||
return (
|
||||
<StyledContainerActionMenuDropdown
|
||||
position={actionMenuDropdownPosition}
|
||||
className="context-menu"
|
||||
className="action-menu-dropdown"
|
||||
>
|
||||
<Dropdown
|
||||
dropdownId={`action-menu-dropdown-${actionMenuId}`}
|
||||
@ -73,15 +74,19 @@ export const ActionMenuDropdown = () => {
|
||||
}}
|
||||
data-select-disable
|
||||
dropdownMenuWidth={width}
|
||||
dropdownComponents={actionMenuEntries.map((item, index) => (
|
||||
<MenuItem
|
||||
key={index}
|
||||
LeftIcon={item.Icon}
|
||||
onClick={item.onClick}
|
||||
accent={item.accent}
|
||||
text={item.label}
|
||||
/>
|
||||
))}
|
||||
dropdownComponents={
|
||||
<DropdownMenuItemsContainer>
|
||||
{actionMenuEntries.map((item, index) => (
|
||||
<MenuItem
|
||||
key={index}
|
||||
LeftIcon={item.Icon}
|
||||
onClick={item.onClick}
|
||||
accent={item.accent}
|
||||
text={item.label}
|
||||
/>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
}
|
||||
/>
|
||||
</StyledContainerActionMenuDropdown>
|
||||
);
|
@ -1,15 +1,16 @@
|
||||
import { useActionMenu } from '@/action-menu/hooks/useActionMenu';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { contextStoreTargetedRecordIdsState } from '@/context-store/states/contextStoreTargetedRecordIdsState';
|
||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export const ActionMenuEffect = () => {
|
||||
const contextStoreTargetedRecordIds = useRecoilValue(
|
||||
contextStoreTargetedRecordIdsState,
|
||||
export const RecordIndexActionMenuEffect = () => {
|
||||
const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
|
||||
contextStoreNumberOfSelectedRecordsComponentState,
|
||||
);
|
||||
|
||||
const actionMenuId = useAvailableComponentInstanceIdOrThrow(
|
||||
@ -26,17 +27,17 @@ export const ActionMenuEffect = () => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (contextStoreTargetedRecordIds.length > 0 && !isDropdownOpen) {
|
||||
if (contextStoreNumberOfSelectedRecords > 0 && !isDropdownOpen) {
|
||||
// We only handle opening the ActionMenuBar here, not the Dropdown.
|
||||
// The Dropdown is already managed by sync handlers for events like
|
||||
// right-click to open and click outside to close.
|
||||
openActionBar();
|
||||
}
|
||||
if (contextStoreTargetedRecordIds.length === 0) {
|
||||
if (contextStoreNumberOfSelectedRecords === 0 && isDropdownOpen) {
|
||||
closeActionBar();
|
||||
}
|
||||
}, [
|
||||
contextStoreTargetedRecordIds,
|
||||
contextStoreNumberOfSelectedRecords,
|
||||
openActionBar,
|
||||
closeActionBar,
|
||||
isDropdownOpen,
|
@ -0,0 +1,31 @@
|
||||
import { RecordActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter';
|
||||
import { ActionMenuConfirmationModals } from '@/action-menu/components/ActionMenuConfirmationModals';
|
||||
import { RecordShowActionMenuBar } from '@/action-menu/components/RecordShowActionMenuBar';
|
||||
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const RecordShowActionMenu = ({
|
||||
actionMenuId,
|
||||
}: {
|
||||
actionMenuId: string;
|
||||
}) => {
|
||||
const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2(
|
||||
contextStoreCurrentObjectMetadataIdComponentState,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{contextStoreCurrentObjectMetadataId && (
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{ instanceId: actionMenuId }}
|
||||
>
|
||||
<RecordShowActionMenuBar />
|
||||
<ActionMenuConfirmationModals />
|
||||
<RecordActionMenuEntriesSetter actionMenuType="recordShow" />
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,16 @@
|
||||
import { RecordShowActionMenuBarEntry } from '@/action-menu/components/RecordShowActionMenuBarEntry';
|
||||
import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
|
||||
export const RecordShowActionMenuBar = () => {
|
||||
const actionMenuEntries = useRecoilComponentValueV2(
|
||||
actionMenuEntriesComponentSelector,
|
||||
);
|
||||
return (
|
||||
<>
|
||||
{actionMenuEntries.map((actionMenuEntry) => (
|
||||
<RecordShowActionMenuBarEntry entry={actionMenuEntry} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1,53 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry';
|
||||
import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent';
|
||||
|
||||
type RecordShowActionMenuBarEntryProps = {
|
||||
entry: ActionMenuEntry;
|
||||
};
|
||||
|
||||
const StyledButton = styled.div<{ accent: MenuItemAccent }>`
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
color: ${(props) =>
|
||||
props.accent === 'danger'
|
||||
? props.theme.color.red
|
||||
: props.theme.font.color.secondary};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
transition: background 0.1s ease;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
background: ${({ theme, accent }) =>
|
||||
accent === 'danger'
|
||||
? theme.background.danger
|
||||
: theme.background.tertiary};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledButtonLabel = styled.div`
|
||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||
margin-left: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
// For now, this component is the same as RecordIndexActionMenuBarEntry but they
|
||||
// will probably diverge in the future
|
||||
export const RecordShowActionMenuBarEntry = ({
|
||||
entry,
|
||||
}: RecordShowActionMenuBarEntryProps) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<StyledButton
|
||||
accent={entry.accent ?? 'default'}
|
||||
onClick={() => entry.onClick?.()}
|
||||
>
|
||||
{entry.Icon && <entry.Icon size={theme.icon.size.md} />}
|
||||
<StyledButtonLabel>{entry.label}</StyledButtonLabel>
|
||||
</StyledButton>
|
||||
);
|
||||
};
|
@ -1,111 +0,0 @@
|
||||
import { expect, jest } from '@storybook/jest';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { ActionMenuBar } from '@/action-menu/components/ActionMenuBar';
|
||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { contextStoreTargetedRecordIdsState } from '@/context-store/states/contextStoreTargetedRecordIdsState';
|
||||
import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState';
|
||||
import { userEvent, waitFor, within } from '@storybook/test';
|
||||
import { IconCheckbox, IconTrash } from 'twenty-ui';
|
||||
|
||||
const deleteMock = jest.fn();
|
||||
const markAsDoneMock = jest.fn();
|
||||
|
||||
const meta: Meta<typeof ActionMenuBar> = {
|
||||
title: 'Modules/ActionMenu/ActionMenuBar',
|
||||
component: ActionMenuBar,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<RecoilRoot
|
||||
initializeState={({ set }) => {
|
||||
set(contextStoreTargetedRecordIdsState, ['1', '2', '3']);
|
||||
set(
|
||||
actionMenuEntriesComponentState.atomFamily({
|
||||
instanceId: 'story-action-menu',
|
||||
}),
|
||||
new Map([
|
||||
[
|
||||
'delete',
|
||||
{
|
||||
key: 'delete',
|
||||
label: 'Delete',
|
||||
position: 0,
|
||||
Icon: IconTrash,
|
||||
onClick: deleteMock,
|
||||
},
|
||||
],
|
||||
[
|
||||
'markAsDone',
|
||||
{
|
||||
key: 'markAsDone',
|
||||
label: 'Mark as done',
|
||||
position: 1,
|
||||
Icon: IconCheckbox,
|
||||
onClick: markAsDoneMock,
|
||||
},
|
||||
],
|
||||
]),
|
||||
);
|
||||
set(
|
||||
isBottomBarOpenedComponentState.atomFamily({
|
||||
instanceId: 'action-bar-story-action-menu',
|
||||
}),
|
||||
true,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story-action-menu' }}
|
||||
>
|
||||
<Story />
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
),
|
||||
],
|
||||
args: {
|
||||
actionMenuId: 'story-action-menu',
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof ActionMenuBar>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
actionMenuId: 'story-action-menu',
|
||||
},
|
||||
};
|
||||
|
||||
export const WithCustomSelection: Story = {
|
||||
args: {
|
||||
actionMenuId: 'story-action-menu',
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const selectionText = await canvas.findByText('3 selected:');
|
||||
expect(selectionText).toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
export const WithButtonClicks: Story = {
|
||||
args: {
|
||||
actionMenuId: 'story-action-menu',
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const deleteButton = await canvas.findByText('Delete');
|
||||
await userEvent.click(deleteButton);
|
||||
|
||||
const markAsDoneButton = await canvas.findByText('Mark as done');
|
||||
await userEvent.click(markAsDoneButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(deleteMock).toHaveBeenCalled();
|
||||
expect(markAsDoneMock).toHaveBeenCalled();
|
||||
});
|
||||
},
|
||||
};
|
@ -0,0 +1,131 @@
|
||||
import { expect, jest } from '@storybook/jest';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { RecordIndexActionMenuBar } from '@/action-menu/components/RecordIndexActionMenuBar';
|
||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
|
||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState';
|
||||
import { userEvent, waitFor, within } from '@storybook/test';
|
||||
import { IconCheckbox, IconTrash } from 'twenty-ui';
|
||||
|
||||
const deleteMock = jest.fn();
|
||||
const markAsDoneMock = jest.fn();
|
||||
|
||||
const meta: Meta<typeof RecordIndexActionMenuBar> = {
|
||||
title: 'Modules/ActionMenu/RecordIndexActionMenuBar',
|
||||
component: RecordIndexActionMenuBar,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<ContextStoreComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story-action-menu' }}
|
||||
>
|
||||
<RecoilRoot
|
||||
initializeState={({ set }) => {
|
||||
set(
|
||||
contextStoreTargetedRecordsRuleComponentState.atomFamily({
|
||||
instanceId: 'story-action-menu',
|
||||
}),
|
||||
{
|
||||
mode: 'selection',
|
||||
selectedRecordIds: ['1', '2', '3'],
|
||||
},
|
||||
);
|
||||
set(
|
||||
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
|
||||
instanceId: 'story-action-menu',
|
||||
}),
|
||||
3,
|
||||
);
|
||||
set(
|
||||
actionMenuEntriesComponentState.atomFamily({
|
||||
instanceId: 'story-action-menu',
|
||||
}),
|
||||
new Map([
|
||||
[
|
||||
'delete',
|
||||
{
|
||||
key: 'delete',
|
||||
label: 'Delete',
|
||||
position: 0,
|
||||
Icon: IconTrash,
|
||||
onClick: deleteMock,
|
||||
},
|
||||
],
|
||||
[
|
||||
'markAsDone',
|
||||
{
|
||||
key: 'markAsDone',
|
||||
label: 'Mark as done',
|
||||
position: 1,
|
||||
Icon: IconCheckbox,
|
||||
onClick: markAsDoneMock,
|
||||
},
|
||||
],
|
||||
]),
|
||||
);
|
||||
set(
|
||||
isBottomBarOpenedComponentState.atomFamily({
|
||||
instanceId: 'action-bar-story-action-menu',
|
||||
}),
|
||||
true,
|
||||
);
|
||||
}}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story-action-menu' }}
|
||||
>
|
||||
<Story />
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
</ContextStoreComponentInstanceContext.Provider>
|
||||
),
|
||||
],
|
||||
args: {
|
||||
actionMenuId: 'story-action-menu',
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof RecordIndexActionMenuBar>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
actionMenuId: 'story-action-menu',
|
||||
},
|
||||
};
|
||||
|
||||
export const WithCustomSelection: Story = {
|
||||
args: {
|
||||
actionMenuId: 'story-action-menu',
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const selectionText = await canvas.findByText('3 selected:');
|
||||
expect(selectionText).toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
export const WithButtonClicks: Story = {
|
||||
args: {
|
||||
actionMenuId: 'story-action-menu',
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const deleteButton = await canvas.findByText('Delete');
|
||||
await userEvent.click(deleteButton);
|
||||
|
||||
const markAsDoneButton = await canvas.findByText('Mark as done');
|
||||
await userEvent.click(markAsDoneButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(deleteMock).toHaveBeenCalled();
|
||||
expect(markAsDoneMock).toHaveBeenCalled();
|
||||
});
|
||||
},
|
||||
};
|
@ -1,19 +1,19 @@
|
||||
import { RecordIndexActionMenuBarEntry } from '@/action-menu/components/RecordIndexActionMenuBarEntry';
|
||||
import { expect, jest } from '@storybook/jest';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { userEvent, within } from '@storybook/testing-library';
|
||||
|
||||
import { ComponentDecorator, IconCheckbox, IconTrash } from 'twenty-ui';
|
||||
import { ActionMenuBarEntry } from '../ActionMenuBarEntry';
|
||||
|
||||
const meta: Meta<typeof ActionMenuBarEntry> = {
|
||||
title: 'Modules/ActionMenu/ActionMenuBarEntry',
|
||||
component: ActionMenuBarEntry,
|
||||
const meta: Meta<typeof RecordIndexActionMenuBarEntry> = {
|
||||
title: 'Modules/ActionMenu/RecordIndexActionMenuBarEntry',
|
||||
component: RecordIndexActionMenuBarEntry,
|
||||
decorators: [ComponentDecorator],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof ActionMenuBarEntry>;
|
||||
type Story = StoryObj<typeof RecordIndexActionMenuBarEntry>;
|
||||
|
||||
const deleteMock = jest.fn();
|
||||
const markAsDoneMock = jest.fn();
|
@ -3,10 +3,10 @@ import { Meta, StoryObj } from '@storybook/react';
|
||||
import { userEvent, within } from '@storybook/testing-library';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { ActionMenuDropdown } from '@/action-menu/components/ActionMenuDropdown';
|
||||
import { actionMenuDropdownPositionComponentState } from '@/action-menu/states/actionMenuDropdownPositionComponentState';
|
||||
import { RecordIndexActionMenuDropdown } from '@/action-menu/components/RecordIndexActionMenuDropdown';
|
||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
|
||||
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
|
||||
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
|
||||
import { IconCheckbox, IconHeart, IconTrash } from 'twenty-ui';
|
||||
@ -15,16 +15,16 @@ const deleteMock = jest.fn();
|
||||
const markAsDoneMock = jest.fn();
|
||||
const addToFavoritesMock = jest.fn();
|
||||
|
||||
const meta: Meta<typeof ActionMenuDropdown> = {
|
||||
title: 'Modules/ActionMenu/ActionMenuDropdown',
|
||||
component: ActionMenuDropdown,
|
||||
const meta: Meta<typeof RecordIndexActionMenuDropdown> = {
|
||||
title: 'Modules/ActionMenu/RecordIndexActionMenuDropdown',
|
||||
component: RecordIndexActionMenuDropdown,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<RecoilRoot
|
||||
initializeState={({ set }) => {
|
||||
set(
|
||||
extractComponentState(
|
||||
actionMenuDropdownPositionComponentState,
|
||||
recordIndexActionMenuDropdownPositionComponentState,
|
||||
'action-menu-dropdown-story',
|
||||
),
|
||||
{ x: 10, y: 10 },
|
||||
@ -87,7 +87,7 @@ const meta: Meta<typeof ActionMenuDropdown> = {
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof ActionMenuDropdown>;
|
||||
type Story = StoryObj<typeof RecordIndexActionMenuDropdown>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
@ -0,0 +1,131 @@
|
||||
import { expect, jest } from '@storybook/jest';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
|
||||
import { RecordShowActionMenuBar } from '@/action-menu/components/RecordShowActionMenuBar';
|
||||
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
|
||||
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
|
||||
import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent';
|
||||
import { userEvent, waitFor, within } from '@storybook/test';
|
||||
import {
|
||||
ComponentDecorator,
|
||||
IconFileExport,
|
||||
IconHeart,
|
||||
IconTrash,
|
||||
} from 'twenty-ui';
|
||||
|
||||
const deleteMock = jest.fn();
|
||||
const addToFavoritesMock = jest.fn();
|
||||
const exportMock = jest.fn();
|
||||
|
||||
const meta: Meta<typeof RecordShowActionMenuBar> = {
|
||||
title: 'Modules/ActionMenu/RecordShowActionMenuBar',
|
||||
component: RecordShowActionMenuBar,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<RecoilRoot
|
||||
initializeState={({ set }) => {
|
||||
set(
|
||||
contextStoreTargetedRecordsRuleComponentState.atomFamily({
|
||||
instanceId: 'story-action-menu',
|
||||
}),
|
||||
{
|
||||
mode: 'selection',
|
||||
selectedRecordIds: ['1'],
|
||||
},
|
||||
);
|
||||
set(
|
||||
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
|
||||
instanceId: 'story-action-menu',
|
||||
}),
|
||||
1,
|
||||
);
|
||||
set(
|
||||
actionMenuEntriesComponentState.atomFamily({
|
||||
instanceId: 'story-action-menu',
|
||||
}),
|
||||
new Map([
|
||||
[
|
||||
'addToFavorites',
|
||||
{
|
||||
key: 'addToFavorites',
|
||||
label: 'Add to favorites',
|
||||
position: 0,
|
||||
Icon: IconHeart,
|
||||
onClick: addToFavoritesMock,
|
||||
},
|
||||
],
|
||||
[
|
||||
'export',
|
||||
{
|
||||
key: 'export',
|
||||
label: 'Export',
|
||||
position: 1,
|
||||
Icon: IconFileExport,
|
||||
onClick: exportMock,
|
||||
},
|
||||
],
|
||||
[
|
||||
'delete',
|
||||
{
|
||||
key: 'delete',
|
||||
label: 'Delete',
|
||||
position: 2,
|
||||
Icon: IconTrash,
|
||||
onClick: deleteMock,
|
||||
accent: 'danger' as MenuItemAccent,
|
||||
},
|
||||
],
|
||||
]),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story-action-menu' }}
|
||||
>
|
||||
<Story />
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
),
|
||||
ComponentDecorator,
|
||||
],
|
||||
args: {
|
||||
actionMenuId: 'story-action-menu',
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof RecordShowActionMenuBar>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
actionMenuId: 'story-action-menu',
|
||||
},
|
||||
};
|
||||
|
||||
export const WithButtonClicks: Story = {
|
||||
args: {
|
||||
actionMenuId: 'story-action-menu',
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
const deleteButton = await canvas.findByText('Delete');
|
||||
await userEvent.click(deleteButton);
|
||||
|
||||
const addToFavoritesButton = await canvas.findByText('Add to favorites');
|
||||
await userEvent.click(addToFavoritesButton);
|
||||
|
||||
const exportButton = await canvas.findByText('Export');
|
||||
await userEvent.click(exportButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(deleteMock).toHaveBeenCalled();
|
||||
expect(addToFavoritesMock).toHaveBeenCalled();
|
||||
expect(exportMock).toHaveBeenCalled();
|
||||
});
|
||||
},
|
||||
};
|
@ -7,7 +7,7 @@ import {
|
||||
displayedExportProgress,
|
||||
download,
|
||||
generateCsv,
|
||||
} from '../useExportTableData';
|
||||
} from '../useExportRecordData';
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
@ -4,10 +4,11 @@ import { useMemo } from 'react';
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { EXPORT_TABLE_DATA_DEFAULT_PAGE_SIZE } from '@/object-record/record-index/options/constants/ExportTableDataDefaultPageSize';
|
||||
import { useProcessRecordsForCSVExport } from '@/object-record/record-index/options/hooks/useProcessRecordsForCSVExport';
|
||||
|
||||
import {
|
||||
useTableData,
|
||||
UseTableDataOptions,
|
||||
} from '@/object-record/record-index/options/hooks/useTableData';
|
||||
UseRecordDataOptions,
|
||||
useRecordData,
|
||||
} from '@/object-record/record-index/options/hooks/useRecordData';
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { RelationDefinitionType } from '~/generated-metadata/graphql';
|
||||
@ -134,21 +135,22 @@ const downloader = (mimeType: string, generator: GenerateExport) => {
|
||||
|
||||
export const csvDownloader = downloader('text/csv', generateCsv);
|
||||
|
||||
type UseExportTableDataOptions = Omit<UseTableDataOptions, 'callback'> & {
|
||||
type UseExportTableDataOptions = Omit<UseRecordDataOptions, 'callback'> & {
|
||||
filename: string;
|
||||
};
|
||||
|
||||
export const useExportTableData = ({
|
||||
export const useExportRecordData = ({
|
||||
delayMs,
|
||||
filename,
|
||||
maximumRequests = 100,
|
||||
objectNameSingular,
|
||||
objectMetadataItem,
|
||||
pageSize = EXPORT_TABLE_DATA_DEFAULT_PAGE_SIZE,
|
||||
recordIndexId,
|
||||
viewType,
|
||||
}: UseExportTableDataOptions) => {
|
||||
const { processRecordsForCSVExport } =
|
||||
useProcessRecordsForCSVExport(objectNameSingular);
|
||||
const { processRecordsForCSVExport } = useProcessRecordsForCSVExport(
|
||||
objectMetadataItem.nameSingular,
|
||||
);
|
||||
|
||||
const downloadCsv = useMemo(
|
||||
() =>
|
||||
@ -160,10 +162,10 @@ export const useExportTableData = ({
|
||||
[filename, processRecordsForCSVExport],
|
||||
);
|
||||
|
||||
const { getTableData: download, progress } = useTableData({
|
||||
const { getTableData: download, progress } = useRecordData({
|
||||
delayMs,
|
||||
maximumRequests,
|
||||
objectNameSingular,
|
||||
objectMetadataItem,
|
||||
pageSize,
|
||||
recordIndexId,
|
||||
callback: downloadCsv,
|
@ -1,9 +1,9 @@
|
||||
import { PositionType } from '@/action-menu/types/PositionType';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const actionMenuDropdownPositionComponentState =
|
||||
export const recordIndexActionMenuDropdownPositionComponentState =
|
||||
createComponentState<PositionType>({
|
||||
key: 'actionMenuDropdownPositionComponentState',
|
||||
key: 'recordIndexActionMenuDropdownPositionComponentState',
|
||||
defaultValue: {
|
||||
x: null,
|
||||
y: null,
|
@ -0,0 +1 @@
|
||||
export type ActionMenuType = 'recordIndex' | 'recordShow';
|
@ -3,14 +3,14 @@ import styled from '@emotion/styled';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { ChangeEvent, useRef } from 'react';
|
||||
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { AppThemeProvider } from '@/ui/theme/components/AppThemeProvider';
|
||||
import { Button } from 'twenty-ui';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
import { AttachmentIcon } from '../files/components/AttachmentIcon';
|
||||
import { AttachmentType } from '../files/types/Attachment';
|
||||
import { getFileType } from '../files/utils/getFileType';
|
||||
import { AttachmentIcon } from '../../files/components/AttachmentIcon';
|
||||
import { AttachmentType } from '../../files/types/Attachment';
|
||||
import { getFileType } from '../../files/utils/getFileType';
|
||||
|
||||
const StyledFileInput = styled.input`
|
||||
display: none;
|
@ -1,8 +1,8 @@
|
||||
import { BlockNoteSchema, defaultBlockSpecs } from '@blocknote/core';
|
||||
|
||||
import { FileBlock } from './FileBlock';
|
||||
import { FileBlock } from '../components/FileBlock';
|
||||
|
||||
export const blockSchema = BlockNoteSchema.create({
|
||||
export const BLOCK_SCHEMA = BlockNoteSchema.create({
|
||||
blockSpecs: {
|
||||
...defaultBlockSpecs,
|
||||
file: FileBlock,
|
@ -18,7 +18,7 @@ import {
|
||||
|
||||
import { SuggestionItem } from '@/ui/input/editor/components/CustomSlashMenu';
|
||||
|
||||
import { blockSchema } from './schema';
|
||||
import { BLOCK_SCHEMA } from '../constants/Schema';
|
||||
|
||||
const Icons: Record<string, IconComponent> = {
|
||||
'Heading 1': IconH1,
|
||||
@ -35,7 +35,7 @@ const Icons: Record<string, IconComponent> = {
|
||||
Emoji: IconMoodSmile,
|
||||
};
|
||||
|
||||
export const getSlashMenu = (editor: typeof blockSchema.BlockNoteEditor) => {
|
||||
export const getSlashMenu = (editor: typeof BLOCK_SCHEMA.BlockNoteEditor) => {
|
||||
const items: SuggestionItem[] = [
|
||||
...getDefaultReactSlashMenuItems(editor).map((x) => ({
|
||||
...x,
|
@ -1,26 +1,26 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { format, getYear } from 'date-fns';
|
||||
import { H3Title } from 'twenty-ui';
|
||||
|
||||
import { CalendarMonthCard } from '@/activities/calendar/components/CalendarMonthCard';
|
||||
import { TIMELINE_CALENDAR_EVENTS_DEFAULT_PAGE_SIZE } from '@/activities/calendar/constants/Calendar';
|
||||
import { CalendarContext } from '@/activities/calendar/contexts/CalendarContext';
|
||||
import { useCalendarEvents } from '@/activities/calendar/hooks/useCalendarEvents';
|
||||
import { getTimelineCalendarEventsFromCompanyId } from '@/activities/calendar/queries/getTimelineCalendarEventsFromCompanyId';
|
||||
import { getTimelineCalendarEventsFromPersonId } from '@/activities/calendar/queries/getTimelineCalendarEventsFromPersonId';
|
||||
import { CustomResolverFetchMoreLoader } from '@/activities/components/CustomResolverFetchMoreLoader';
|
||||
import { SkeletonLoader } from '@/activities/components/SkeletonLoader';
|
||||
import { useCustomResolver } from '@/activities/hooks/useCustomResolver';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
|
||||
import {
|
||||
AnimatedPlaceholder,
|
||||
AnimatedPlaceholderEmptyContainer,
|
||||
AnimatedPlaceholderEmptySubTitle,
|
||||
AnimatedPlaceholderEmptyTextContainer,
|
||||
AnimatedPlaceholderEmptyTitle,
|
||||
EMPTY_PLACEHOLDER_TRANSITION_PROPS,
|
||||
} from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled';
|
||||
H3Title,
|
||||
} from 'twenty-ui';
|
||||
|
||||
import { CalendarMonthCard } from '@/activities/calendar/components/CalendarMonthCard';
|
||||
import { TIMELINE_CALENDAR_EVENTS_DEFAULT_PAGE_SIZE } from '@/activities/calendar/constants/Calendar';
|
||||
import { CalendarContext } from '@/activities/calendar/contexts/CalendarContext';
|
||||
import { getTimelineCalendarEventsFromCompanyId } from '@/activities/calendar/graphql/queries/getTimelineCalendarEventsFromCompanyId';
|
||||
import { getTimelineCalendarEventsFromPersonId } from '@/activities/calendar/graphql/queries/getTimelineCalendarEventsFromPersonId';
|
||||
import { useCalendarEvents } from '@/activities/calendar/hooks/useCalendarEvents';
|
||||
import { CustomResolverFetchMoreLoader } from '@/activities/components/CustomResolverFetchMoreLoader';
|
||||
import { SkeletonLoader } from '@/activities/components/SkeletonLoader';
|
||||
import { useCustomResolver } from '@/activities/hooks/useCustomResolver';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { TimelineCalendarEventsWithTotal } from '~/generated/graphql';
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { graphql, HttpResponse } from 'msw';
|
||||
import { HttpResponse, graphql } from 'msw';
|
||||
import { ComponentDecorator } from 'twenty-ui';
|
||||
|
||||
import { Calendar } from '@/activities/calendar/components/Calendar';
|
||||
import { getTimelineCalendarEventsFromCompanyId } from '@/activities/calendar/queries/getTimelineCalendarEventsFromCompanyId';
|
||||
import { getTimelineCalendarEventsFromCompanyId } from '@/activities/calendar/graphql/queries/getTimelineCalendarEventsFromCompanyId';
|
||||
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
|
||||
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { timelineCalendarEventParticipantFragment } from '@/activities/calendar/graphql/queries/fragments/timelineCalendarEventParticipantFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { timelineCalendarEventParticipantFragment } from '@/activities/calendar/queries/fragments/timelineCalendarEventParticipantFragment';
|
||||
|
||||
export const timelineCalendarEventFragment = gql`
|
||||
fragment TimelineCalendarEventFragment on TimelineCalendarEvent {
|
||||
id
|
@ -1,7 +1,6 @@
|
||||
import { timelineCalendarEventFragment } from '@/activities/calendar/graphql/queries/fragments/timelineCalendarEventFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { timelineCalendarEventFragment } from '@/activities/calendar/queries/fragments/timelineCalendarEventFragment';
|
||||
|
||||
export const timelineCalendarEventWithTotalFragment = gql`
|
||||
fragment TimelineCalendarEventsWithTotalFragment on TimelineCalendarEventsWithTotal {
|
||||
totalNumberOfCalendarEvents
|
@ -1,7 +1,6 @@
|
||||
import { timelineCalendarEventWithTotalFragment } from '@/activities/calendar/graphql/queries/fragments/timelineCalendarEventWithTotalFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { timelineCalendarEventWithTotalFragment } from '@/activities/calendar/queries/fragments/timelineCalendarEventWithTotalFragment';
|
||||
|
||||
export const getTimelineCalendarEventsFromCompanyId = gql`
|
||||
query GetTimelineCalendarEventsFromCompanyId(
|
||||
$companyId: UUID!
|
@ -1,7 +1,6 @@
|
||||
import { timelineCalendarEventWithTotalFragment } from '@/activities/calendar/graphql/queries/fragments/timelineCalendarEventWithTotalFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { timelineCalendarEventWithTotalFragment } from '@/activities/calendar/queries/fragments/timelineCalendarEventWithTotalFragment';
|
||||
|
||||
export const getTimelineCalendarEventsFromPersonId = gql`
|
||||
query GetTimelineCalendarEventsFromPersonId(
|
||||
$personId: UUID!
|
@ -7,7 +7,6 @@ import { Key } from 'ts-key-enum';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { blockSchema } from '@/activities/blocks/schema';
|
||||
import { useUpsertActivity } from '@/activities/hooks/useUpsertActivity';
|
||||
import { activityBodyFamilyState } from '@/activities/states/activityBodyFamilyState';
|
||||
import { activityTitleHasBeenSetFamilyState } from '@/activities/states/activityTitleHasBeenSetFamilyState';
|
||||
@ -27,6 +26,7 @@ import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
import { getFileType } from '../files/utils/getFileType';
|
||||
|
||||
import { BLOCK_SCHEMA } from '@/activities/blocks/constants/Schema';
|
||||
import { Note } from '@/activities/types/Note';
|
||||
import { Task } from '@/activities/types/Task';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
@ -287,7 +287,7 @@ export const RichTextEditor = ({
|
||||
const editor = useCreateBlockNote({
|
||||
initialContent: initialBody,
|
||||
domAttributes: { editor: { class: 'editor' } },
|
||||
schema: blockSchema,
|
||||
schema: BLOCK_SCHEMA,
|
||||
uploadFile: handleUploadAttachment,
|
||||
});
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { Loader } from '@/ui/feedback/loader/components/Loader';
|
||||
import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
|
||||
import {
|
||||
AnimatedPlaceholder,
|
||||
AnimatedPlaceholderEmptyContainer,
|
||||
AnimatedPlaceholderEmptyTextContainer,
|
||||
AnimatedPlaceholderEmptyTitle,
|
||||
} from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled';
|
||||
} from 'twenty-ui';
|
||||
|
||||
export const EmailLoader = ({ loadingText }: { loadingText?: string }) => (
|
||||
<AnimatedPlaceholderEmptyContainer>
|
||||
|
@ -1,7 +1,5 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { IconArrowBackUp, IconUserCircle } from 'twenty-ui';
|
||||
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { Button, IconArrowBackUp, IconUserCircle } from 'twenty-ui';
|
||||
|
||||
const StyledThreadBottomBar = styled.div`
|
||||
align-items: center;
|
||||
|
@ -1,8 +1,6 @@
|
||||
import React from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
import { AnimatedEaseInOut } from '@/ui/utilities/animation/components/AnimatedEaseInOut';
|
||||
import { AnimatedEaseInOut } from 'twenty-ui';
|
||||
|
||||
const StyledThreadMessageBody = styled(motion.div)`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
|
@ -1,24 +1,25 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { H1Title, H1TitleFontColor } from 'twenty-ui';
|
||||
import {
|
||||
AnimatedPlaceholder,
|
||||
AnimatedPlaceholderEmptyContainer,
|
||||
AnimatedPlaceholderEmptySubTitle,
|
||||
AnimatedPlaceholderEmptyTextContainer,
|
||||
AnimatedPlaceholderEmptyTitle,
|
||||
EMPTY_PLACEHOLDER_TRANSITION_PROPS,
|
||||
H1Title,
|
||||
H1TitleFontColor,
|
||||
} from 'twenty-ui';
|
||||
|
||||
import { ActivityList } from '@/activities/components/ActivityList';
|
||||
import { CustomResolverFetchMoreLoader } from '@/activities/components/CustomResolverFetchMoreLoader';
|
||||
import { SkeletonLoader } from '@/activities/components/SkeletonLoader';
|
||||
import { EmailThreadPreview } from '@/activities/emails/components/EmailThreadPreview';
|
||||
import { TIMELINE_THREADS_DEFAULT_PAGE_SIZE } from '@/activities/emails/constants/Messaging';
|
||||
import { getTimelineThreadsFromCompanyId } from '@/activities/emails/queries/getTimelineThreadsFromCompanyId';
|
||||
import { getTimelineThreadsFromPersonId } from '@/activities/emails/queries/getTimelineThreadsFromPersonId';
|
||||
import { getTimelineThreadsFromCompanyId } from '@/activities/emails/graphql/queries/getTimelineThreadsFromCompanyId';
|
||||
import { getTimelineThreadsFromPersonId } from '@/activities/emails/graphql/queries/getTimelineThreadsFromPersonId';
|
||||
import { useCustomResolver } from '@/activities/hooks/useCustomResolver';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
|
||||
import {
|
||||
AnimatedPlaceholderEmptyContainer,
|
||||
AnimatedPlaceholderEmptySubTitle,
|
||||
AnimatedPlaceholderEmptyTextContainer,
|
||||
AnimatedPlaceholderEmptyTitle,
|
||||
EMPTY_PLACEHOLDER_TRANSITION_PROPS,
|
||||
} from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled';
|
||||
import { Section } from '@/ui/layout/section/components/Section';
|
||||
import { TimelineThread, TimelineThreadsWithTotal } from '~/generated/graphql';
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { participantFragment } from '@/activities/emails/queries/fragments/participantFragment';
|
||||
import { participantFragment } from '@/activities/emails/graphql/queries/fragments/participantFragment';
|
||||
|
||||
export const timelineThreadFragment = gql`
|
||||
fragment TimelineThreadFragment on TimelineThread {
|
@ -1,7 +1,6 @@
|
||||
import { timelineThreadFragment } from '@/activities/emails/graphql/queries/fragments/timelineThreadFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { timelineThreadFragment } from '@/activities/emails/queries/fragments/timelineThreadFragment';
|
||||
|
||||
export const timelineThreadWithTotalFragment = gql`
|
||||
fragment TimelineThreadsWithTotalFragment on TimelineThreadsWithTotal {
|
||||
totalNumberOfThreads
|
@ -1,7 +1,6 @@
|
||||
import { timelineThreadWithTotalFragment } from '@/activities/emails/graphql/queries/fragments/timelineThreadWithTotalFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { timelineThreadWithTotalFragment } from '@/activities/emails/queries/fragments/timelineThreadWithTotalFragment';
|
||||
|
||||
export const getTimelineThreadsFromCompanyId = gql`
|
||||
query GetTimelineThreadsFromCompanyId(
|
||||
$companyId: UUID!
|
@ -1,6 +1,6 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { timelineThreadWithTotalFragment } from '@/activities/emails/queries/fragments/timelineThreadWithTotalFragment';
|
||||
import { timelineThreadWithTotalFragment } from '@/activities/emails/graphql/queries/fragments/timelineThreadWithTotalFragment';
|
||||
|
||||
export const getTimelineThreadsFromPersonId = gql`
|
||||
query GetTimelineThreadsFromPersonId(
|
@ -1,10 +1,9 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useState } from 'react';
|
||||
import { IconArrowsVertical } from 'twenty-ui';
|
||||
import { Button, IconArrowsVertical } from 'twenty-ui';
|
||||
|
||||
import { EmailThreadMessage } from '@/activities/emails/components/EmailThreadMessage';
|
||||
import { EmailThreadMessageWithSender } from '@/activities/emails/types/EmailThreadMessageWithSender';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
|
||||
const StyledButtonContainer = styled.div`
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
|
@ -9,12 +9,11 @@ import { EmailThreadMessage } from '@/activities/emails/components/EmailThreadMe
|
||||
import { IntermediaryMessages } from '@/activities/emails/right-drawer/components/IntermediaryMessages';
|
||||
import { useRightDrawerEmailThread } from '@/activities/emails/right-drawer/hooks/useRightDrawerEmailThread';
|
||||
import { emailThreadIdWhenEmailThreadWasClosedState } from '@/activities/emails/states/lastViewableEmailThreadIdState';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import { RIGHT_DRAWER_CLICK_OUTSIDE_LISTENER_ID } from '@/ui/layout/right-drawer/constants/RightDrawerClickOutsideListener';
|
||||
import { messageThreadState } from '@/ui/layout/right-drawer/states/messageThreadState';
|
||||
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
|
||||
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
|
||||
import { IconArrowBackUp } from 'twenty-ui';
|
||||
import { Button, IconArrowBackUp } from 'twenty-ui';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
display: flex;
|
||||
|
@ -3,9 +3,9 @@ import {
|
||||
IconDownload,
|
||||
IconPencil,
|
||||
IconTrash,
|
||||
LightIconButton,
|
||||
} from 'twenty-ui';
|
||||
|
||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
|
@ -1,6 +1,15 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { ChangeEvent, useRef, useState } from 'react';
|
||||
import { IconPlus } from 'twenty-ui';
|
||||
import {
|
||||
AnimatedPlaceholder,
|
||||
AnimatedPlaceholderEmptyContainer,
|
||||
AnimatedPlaceholderEmptySubTitle,
|
||||
AnimatedPlaceholderEmptyTextContainer,
|
||||
AnimatedPlaceholderEmptyTitle,
|
||||
Button,
|
||||
EMPTY_PLACEHOLDER_TRANSITION_PROPS,
|
||||
IconPlus,
|
||||
} from 'twenty-ui';
|
||||
|
||||
import { SkeletonLoader } from '@/activities/components/SkeletonLoader';
|
||||
import { AttachmentList } from '@/activities/files/components/AttachmentList';
|
||||
@ -8,15 +17,6 @@ import { DropZone } from '@/activities/files/components/DropZone';
|
||||
import { useAttachments } from '@/activities/files/hooks/useAttachments';
|
||||
import { useUploadAttachmentFile } from '@/activities/files/hooks/useUploadAttachmentFile';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
|
||||
import {
|
||||
AnimatedPlaceholderEmptyContainer,
|
||||
AnimatedPlaceholderEmptySubTitle,
|
||||
AnimatedPlaceholderEmptyTextContainer,
|
||||
AnimatedPlaceholderEmptyTitle,
|
||||
EMPTY_PLACEHOLDER_TRANSITION_PROPS,
|
||||
} from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
const StyledAttachmentsContainer = styled.div`
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { usePrepareFindManyActivitiesQuery } from '@/activities/hooks/usePrepareFindManyActivitiesQuery';
|
||||
import { objectShowPageTargetableObjectState } from '@/activities/timelineActivities/states/objectShowPageTargetableObjectIdState';
|
||||
import { objectShowPageTargetableObjectState } from '@/activities/timeline-activities/states/objectShowPageTargetableObjectIdState';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
|
@ -4,7 +4,7 @@ import { useCreateActivityInDB } from '@/activities/hooks/useCreateActivityInDB'
|
||||
import { useRefreshShowPageFindManyActivitiesQueries } from '@/activities/hooks/useRefreshShowPageFindManyActivitiesQueries';
|
||||
import { isActivityInCreateModeState } from '@/activities/states/isActivityInCreateModeState';
|
||||
import { isUpsertingActivityInDBState } from '@/activities/states/isCreatingActivityInDBState';
|
||||
import { objectShowPageTargetableObjectState } from '@/activities/timelineActivities/states/objectShowPageTargetableObjectIdState';
|
||||
import { objectShowPageTargetableObjectState } from '@/activities/timeline-activities/states/objectShowPageTargetableObjectIdState';
|
||||
import { Note } from '@/activities/types/Note';
|
||||
import { Task } from '@/activities/types/Task';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
|
@ -1,21 +1,20 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { IconPlus } from 'twenty-ui';
|
||||
|
||||
import { SkeletonLoader } from '@/activities/components/SkeletonLoader';
|
||||
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
||||
import { NoteList } from '@/activities/notes/components/NoteList';
|
||||
import { useNotes } from '@/activities/notes/hooks/useNotes';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
|
||||
import styled from '@emotion/styled';
|
||||
import {
|
||||
AnimatedPlaceholder,
|
||||
AnimatedPlaceholderEmptyContainer,
|
||||
AnimatedPlaceholderEmptySubTitle,
|
||||
AnimatedPlaceholderEmptyTextContainer,
|
||||
AnimatedPlaceholderEmptyTitle,
|
||||
Button,
|
||||
EMPTY_PLACEHOLDER_TRANSITION_PROPS,
|
||||
} from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled';
|
||||
IconPlus,
|
||||
} from 'twenty-ui';
|
||||
|
||||
const StyledNotesContainer = styled.div`
|
||||
display: flex;
|
||||
|
@ -3,7 +3,7 @@ import { useRecoilState } from 'recoil';
|
||||
|
||||
import { useActivities } from '@/activities/hooks/useActivities';
|
||||
import { currentNotesQueryVariablesState } from '@/activities/notes/states/currentNotesQueryVariablesState';
|
||||
import { FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY } from '@/activities/timelineActivities/constants/FindManyTimelineActivitiesOrderBy';
|
||||
import { FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY } from '@/activities/timeline-activities/constants/FindManyTimelineActivitiesOrderBy';
|
||||
import { Note } from '@/activities/types/Note';
|
||||
import { RecordGqlOperationVariables } from '@/object-record/graphql/types/RecordGqlOperationVariables';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { isNonEmptyArray } from '@sniptt/guards';
|
||||
import { IconPlus } from 'twenty-ui';
|
||||
import { Button, IconPlus } from 'twenty-ui';
|
||||
|
||||
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
|
||||
export const AddTaskButton = ({
|
||||
activityTargetableObjects,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { PageAddButton } from '@/ui/layout/page/PageAddButton';
|
||||
import { PageAddButton } from '@/ui/layout/page/components/PageAddButton';
|
||||
|
||||
export const PageAddTaskButton = () => {
|
||||
const openCreateActivity = useOpenCreateActivityDrawer({
|
||||
|
@ -1,25 +1,24 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { IconPlus } from 'twenty-ui';
|
||||
import {
|
||||
AnimatedPlaceholder,
|
||||
AnimatedPlaceholderEmptyContainer,
|
||||
AnimatedPlaceholderEmptySubTitle,
|
||||
AnimatedPlaceholderEmptyTextContainer,
|
||||
AnimatedPlaceholderEmptyTitle,
|
||||
Button,
|
||||
EMPTY_PLACEHOLDER_TRANSITION_PROPS,
|
||||
IconPlus,
|
||||
} from 'twenty-ui';
|
||||
|
||||
import { SkeletonLoader } from '@/activities/components/SkeletonLoader';
|
||||
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
||||
import { TASKS_TAB_LIST_COMPONENT_ID } from '@/activities/tasks/constants/TasksTabListComponentId';
|
||||
import { useTasks } from '@/activities/tasks/hooks/useTasks';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { Button } from '@/ui/input/button/components/Button';
|
||||
import AnimatedPlaceholder from '@/ui/layout/animated-placeholder/components/AnimatedPlaceholder';
|
||||
import {
|
||||
AnimatedPlaceholderEmptyContainer,
|
||||
AnimatedPlaceholderEmptySubTitle,
|
||||
AnimatedPlaceholderEmptyTextContainer,
|
||||
AnimatedPlaceholderEmptyTitle,
|
||||
EMPTY_PLACEHOLDER_TRANSITION_PROPS,
|
||||
} from '@/ui/layout/animated-placeholder/components/EmptyPlaceholderStyled';
|
||||
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
|
||||
|
||||
import { Task } from '@/activities/types/Task';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
|
||||
import groupBy from 'lodash.groupby';
|
||||
import { AddTaskButton } from './AddTaskButton';
|
||||
import { TaskList } from './TaskList';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useActivities } from '@/activities/hooks/useActivities';
|
||||
import { FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY } from '@/activities/timelineActivities/constants/FindManyTimelineActivitiesOrderBy';
|
||||
import { FIND_MANY_TIMELINE_ACTIVITIES_ORDER_BY } from '@/activities/timeline-activities/constants/FindManyTimelineActivitiesOrderBy';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { Task } from '@/activities/types/Task';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
|
@ -1,10 +1,10 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { ReactElement } from 'react';
|
||||
|
||||
import { EventsGroup } from '@/activities/timelineActivities/components/EventsGroup';
|
||||
import { TimelineActivity } from '@/activities/timelineActivities/types/TimelineActivity';
|
||||
import { filterOutInvalidTimelineActivities } from '@/activities/timelineActivities/utils/filterOutInvalidTimelineActivities';
|
||||
import { groupEventsByMonth } from '@/activities/timelineActivities/utils/groupEventsByMonth';
|
||||
import { EventsGroup } from '@/activities/timeline-activities/components/EventsGroup';
|
||||
import { TimelineActivity } from '@/activities/timeline-activities/types/TimelineActivity';
|
||||
import { filterOutInvalidTimelineActivities } from '@/activities/timeline-activities/utils/filterOutInvalidTimelineActivities';
|
||||
import { groupEventsByMonth } from '@/activities/timeline-activities/utils/groupEventsByMonth';
|
||||
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
@ -2,13 +2,13 @@ import styled from '@emotion/styled';
|
||||
import { useContext } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { TimelineActivityContext } from '@/activities/timelineActivities/contexts/TimelineActivityContext';
|
||||
import { TimelineActivityContext } from '@/activities/timeline-activities/contexts/TimelineActivityContext';
|
||||
|
||||
import { useLinkedObjectObjectMetadataItem } from '@/activities/timelineActivities/hooks/useLinkedObjectObjectMetadataItem';
|
||||
import { EventIconDynamicComponent } from '@/activities/timelineActivities/rows/components/EventIconDynamicComponent';
|
||||
import { EventRowDynamicComponent } from '@/activities/timelineActivities/rows/components/EventRowDynamicComponent';
|
||||
import { TimelineActivity } from '@/activities/timelineActivities/types/TimelineActivity';
|
||||
import { getTimelineActivityAuthorFullName } from '@/activities/timelineActivities/utils/getTimelineActivityAuthorFullName';
|
||||
import { useLinkedObjectObjectMetadataItem } from '@/activities/timeline-activities/hooks/useLinkedObjectObjectMetadataItem';
|
||||
import { EventIconDynamicComponent } from '@/activities/timeline-activities/rows/components/EventIconDynamicComponent';
|
||||
import { EventRowDynamicComponent } from '@/activities/timeline-activities/rows/components/EventRowDynamicComponent';
|
||||
import { TimelineActivity } from '@/activities/timeline-activities/types/TimelineActivity';
|
||||
import { getTimelineActivityAuthorFullName } from '@/activities/timeline-activities/utils/getTimelineActivityAuthorFullName';
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { beautifyPastDateRelativeToNow } from '~/utils/date-utils';
|
@ -1,7 +1,7 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { EventRow } from '@/activities/timelineActivities/components/EventRow';
|
||||
import { EventGroup } from '@/activities/timelineActivities/utils/groupEventsByMonth';
|
||||
import { EventRow } from '@/activities/timeline-activities/components/EventRow';
|
||||
import { EventGroup } from '@/activities/timeline-activities/utils/groupEventsByMonth';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
|
||||
type EventsGroupProps = {
|
||||
@ -20,8 +20,8 @@ const StyledActivityGroup = styled.div`
|
||||
`;
|
||||
|
||||
const StyledActivityGroupContainer = styled.div`
|
||||
padding-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||
margin-bottom: ${({ theme }) => theme.spacing(3)};
|
||||
margin-top: ${({ theme }) => theme.spacing(3)};
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
@ -29,7 +29,7 @@ const StyledActivityGroupBar = styled.div`
|
||||
align-items: center;
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
border-radius: ${({ theme }) => theme.border.radius.xl};
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user