diff --git a/.github/workflows/cd-deploy-main.yaml b/.github/workflows/cd-deploy-main.yaml index 9b6217c5ab30..5959d0abd9c6 100644 --- a/.github/workflows/cd-deploy-main.yaml +++ b/.github/workflows/cd-deploy-main.yaml @@ -5,6 +5,7 @@ on: - main jobs: deploy-main: + timeout-minutes: 3 runs-on: ubuntu-latest steps: - name: Repository Dispatch diff --git a/.github/workflows/cd-deploy-tag.yaml b/.github/workflows/cd-deploy-tag.yaml index 17bee3d1a34f..cb07fe44687e 100644 --- a/.github/workflows/cd-deploy-tag.yaml +++ b/.github/workflows/cd-deploy-tag.yaml @@ -5,6 +5,7 @@ on: - 'v*' jobs: deploy-tag: + timeout-minutes: 3 runs-on: ubuntu-latest steps: - name: Repository Dispatch diff --git a/.github/workflows/ci-chrome-extension.yaml b/.github/workflows/ci-chrome-extension.yaml index 6f99f922a94d..63c7094f7044 100644 --- a/.github/workflows/ci-chrome-extension.yaml +++ b/.github/workflows/ci-chrome-extension.yaml @@ -12,6 +12,7 @@ concurrency: jobs: chrome-extension-build: + timeout-minutes: 15 runs-on: ubuntu-latest env: VITE_SERVER_BASE_URL: http://localhost:3000 diff --git a/.github/workflows/playwright.yml.bak b/.github/workflows/ci-e2e.yml.bak similarity index 59% rename from .github/workflows/playwright.yml.bak rename to .github/workflows/ci-e2e.yml.bak index fc45955bc7ab..7bba72f1e77b 100644 --- a/.github/workflows/playwright.yml.bak +++ b/.github/workflows/ci-e2e.yml.bak @@ -1,34 +1,43 @@ -name: Playwright Tests +name: CI E2E Tests on: push: - branches: [ main, master ] + branches: + - main pull_request: - branches: [ main, master ] + branches: + - '**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: test: - timeout-minutes: 60 + timeout-minutes: 30 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - 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 + id: changed-files + uses: tj-actions/changed-files@v11 + with: + files: | + packages/** + playwright.config.ts - name: Skip if no relevant changes - if: steps.changed-files.outputs.any_changed != 'true' - run: echo "No relevant changes detected. Marking as valid." + if: steps.changed-files.outputs.any_changed == 'false' + 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 + uses: ./.github/workflows/actions/yarn-install - name: Install Playwright Browsers if: steps.changed-files.outputs.any_changed == 'true' run: yarn playwright install --with-deps diff --git a/.github/workflows/ci-front.yaml b/.github/workflows/ci-front.yaml index 1aed46afe407..910286ff1beb 100644 --- a/.github/workflows/ci-front.yaml +++ b/.github/workflows/ci-front.yaml @@ -12,6 +12,7 @@ concurrency: jobs: front-sb-build: + timeout-minutes: 30 runs-on: ubuntu-latest env: REACT_APP_SERVER_BASE_URL: http://localhost:3000 @@ -58,8 +59,8 @@ jobs: if: steps.changed-files.outputs.any_changed == 'true' run: npx nx storybook:build twenty-front front-sb-test: + timeout-minutes: 30 runs-on: shipfox-8vcpu-ubuntu-2204 - timeout-minutes: 60 needs: front-sb-build strategy: matrix: @@ -101,8 +102,8 @@ jobs: 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: + timeout-minutes: 30 runs-on: shipfox-8vcpu-ubuntu-2204 - timeout-minutes: 60 env: REACT_APP_SERVER_BASE_URL: http://localhost:3000 NX_REJECT_UNKNOWN_LOCAL_CACHE: 0 @@ -135,6 +136,7 @@ jobs: if: steps.changed-files.outputs.any_changed == 'true' run: npx nx run twenty-front:storybook:serve-and-test:static:performance front-chromatic-deployment: + timeout-minutes: 30 if: contains(github.event.pull_request.labels.*.name, 'run-chromatic') || github.event_name == 'push' needs: front-sb-build runs-on: ubuntu-latest @@ -177,6 +179,7 @@ jobs: if: steps.changed-files.outputs.any_changed == 'true' run: npx nx run twenty-front:chromatic:ci front-task: + timeout-minutes: 30 runs-on: ubuntu-latest env: NX_REJECT_UNKNOWN_LOCAL_CACHE: 0 diff --git a/.github/workflows/ci-release-create.yaml b/.github/workflows/ci-release-create.yaml index 068d054cb30b..736c57352597 100644 --- a/.github/workflows/ci-release-create.yaml +++ b/.github/workflows/ci-release-create.yaml @@ -15,6 +15,7 @@ on: jobs: create_pr: + timeout-minutes: 10 runs-on: ubuntu-latest steps: - name: Checkout diff --git a/.github/workflows/ci-release-merge.yaml b/.github/workflows/ci-release-merge.yaml index 96b719318672..5b0bce577501 100644 --- a/.github/workflows/ci-release-merge.yaml +++ b/.github/workflows/ci-release-merge.yaml @@ -6,6 +6,7 @@ on: jobs: tag_and_release: + timeout-minutes: 10 runs-on: ubuntu-latest if: github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'release') steps: diff --git a/.github/workflows/ci-server.yaml b/.github/workflows/ci-server.yaml index 9b24cacc4445..5d7a35244f01 100644 --- a/.github/workflows/ci-server.yaml +++ b/.github/workflows/ci-server.yaml @@ -12,6 +12,7 @@ concurrency: jobs: server-setup: + timeout-minutes: 30 runs-on: ubuntu-latest env: NX_REJECT_UNKNOWN_LOCAL_CACHE: 0 @@ -67,6 +68,7 @@ jobs: run: npx nx run twenty-server:worker:ci server-test: + timeout-minutes: 30 runs-on: ubuntu-latest needs: server-setup env: @@ -102,6 +104,7 @@ jobs: tasks: test server-integration-test: + timeout-minutes: 30 runs-on: ubuntu-latest needs: server-setup services: @@ -146,7 +149,7 @@ jobs: uses: ./.github/workflows/actions/nx-affected with: tag: scope:backend - tasks: "test:integration" + tasks: "test:integration:with-db-reset" - name: Server / Upload reset-logs file if: always() uses: actions/upload-artifact@v4 diff --git a/.github/workflows/ci-test-docker-compose.yaml b/.github/workflows/ci-test-docker-compose.yaml index 50277ab7aa31..d7202857a7b4 100644 --- a/.github/workflows/ci-test-docker-compose.yaml +++ b/.github/workflows/ci-test-docker-compose.yaml @@ -8,6 +8,7 @@ concurrency: jobs: test: + timeout-minutes: 10 runs-on: ubuntu-latest steps: - name: Checkout diff --git a/.github/workflows/ci-tinybird.yaml b/.github/workflows/ci-tinybird.yaml index 8a83f3e8177b..44328556408c 100644 --- a/.github/workflows/ci-tinybird.yaml +++ b/.github/workflows/ci-tinybird.yaml @@ -12,7 +12,9 @@ concurrency: jobs: ci: + timeout-minutes: 10 runs-on: ubuntu-latest + uses: tinybirdco/ci/.github/workflows/ci.yml@main steps: - name: Check for changed files id: changed-files @@ -27,7 +29,6 @@ jobs: run: echo "No relevant changes. Skipping CI." - name: Check twenty-tinybird package - uses: tinybirdco/ci/.github/workflows/ci.yml@main with: data_project_dir: packages/twenty-tinybird tb_admin_token: ${{ secrets.TB_ADMIN_TOKEN }} diff --git a/.github/workflows/ci-utils.yaml b/.github/workflows/ci-utils.yaml index fccfca98d8ab..7a7ef38042dc 100644 --- a/.github/workflows/ci-utils.yaml +++ b/.github/workflows/ci-utils.yaml @@ -19,6 +19,7 @@ concurrency: jobs: danger-js: + timeout-minutes: 3 runs-on: ubuntu-latest if: github.event.action != 'closed' steps: @@ -31,6 +32,7 @@ jobs: DANGER_GITHUB_API_TOKEN: ${{ github.token }} congratulate: + timeout-minutes: 3 runs-on: ubuntu-latest if: github.event.action == 'closed' && github.event.pull_request.merged == true steps: diff --git a/.github/workflows/ci-website.yaml b/.github/workflows/ci-website.yaml index 1b015c2a34ed..770381855bd9 100644 --- a/.github/workflows/ci-website.yaml +++ b/.github/workflows/ci-website.yaml @@ -13,6 +13,7 @@ concurrency: jobs: website-build: + timeout-minutes: 3 runs-on: ubuntu-latest services: postgres: diff --git a/package.json b/package.json index 2d4484289984..c9c7f6f1ef34 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@linaria/core": "^6.2.0", "@linaria/react": "^6.2.1", "@mdx-js/react": "^3.0.0", + "@microsoft/microsoft-graph-client": "^3.0.7", "@nestjs/apollo": "^11.0.5", "@nestjs/axios": "^3.0.1", "@nestjs/cli": "^9.0.0", @@ -201,6 +202,7 @@ "@graphql-codegen/typescript": "^3.0.4", "@graphql-codegen/typescript-operations": "^3.0.4", "@graphql-codegen/typescript-react-apollo": "^3.3.7", + "@microsoft/microsoft-graph-types": "^2.40.0", "@nestjs/cli": "^9.0.0", "@nestjs/schematics": "^9.0.0", "@nestjs/testing": "^9.0.0", @@ -350,7 +352,7 @@ "version": "0.2.1", "nx": {}, "scripts": { - "start": "npx nx run-many -t start worker -p twenty-server twenty-front" + "start": "npx concurrently --kill-others 'npx nx run-many -t start -p twenty-server twenty-front' 'npx wait-on tcp:3000 && npx nx run twenty-server:worker'" }, "workspaces": { "packages": [ diff --git a/packages/twenty-e2e-testing/drivers/env_variables.ts b/packages/twenty-e2e-testing/drivers/env_variables.ts new file mode 100644 index 000000000000..2bb7f57d88fb --- /dev/null +++ b/packages/twenty-e2e-testing/drivers/env_variables.ts @@ -0,0 +1,22 @@ +import * as fs from 'fs'; +import path from 'path'; + +export const envVariables = (variables: string) => { + let payload = ` + PG_DATABASE_URL=postgres://twenty:twenty@localhost:5432/default + FRONT_BASE_URL=http://localhost:3001 + ACCESS_TOKEN_SECRET=replace_me_with_a_random_string_access + LOGIN_TOKEN_SECRET=replace_me_with_a_random_string_login + REFRESH_TOKEN_SECRET=replace_me_with_a_random_string_refresh + FILE_TOKEN_SECRET=replace_me_with_a_random_string_refresh + REDIS_URL=redis://localhost:6379 + `; + payload = payload.concat(variables); + fs.writeFile( + path.join(__dirname, '..', '..', 'twenty-server', '.env'), + payload, + (err) => { + throw err; + }, + ); +}; diff --git a/packages/twenty-e2e-testing/lib/pom/helper/confirmationModal.ts b/packages/twenty-e2e-testing/lib/pom/helper/confirmationModal.ts new file mode 100644 index 000000000000..225c6733b49c --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/helper/confirmationModal.ts @@ -0,0 +1,36 @@ +import { Locator, Page } from '@playwright/test'; + +export class ConfirmationModal { + private readonly input: Locator; + private readonly cancelButton: Locator; + private readonly confirmButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.input = page.getByTestId('confirmation-modal-input'); + this.cancelButton = page.getByRole('button', { name: 'Cancel' }); + this.confirmButton = page.getByTestId('confirmation-modal-confirm-button'); + } + + async typePlaceholderToInput() { + await this.page + .getByTestId('confirmation-modal-input') + .fill( + await this.page + .getByTestId('confirmation-modal-input') + .getAttribute('placeholder'), + ); + } + + async typePhraseToInput(value: string) { + await this.page.getByTestId('confirmation-modal-input').fill(value); + } + + async clickCancelButton() { + await this.page.getByRole('button', { name: 'Cancel' }).click(); + } + + async clickConfirmButton() { + await this.page.getByTestId('confirmation-modal-confirm-button').click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/helper/formatDate.function.ts b/packages/twenty-e2e-testing/lib/pom/helper/formatDate.function.ts new file mode 100644 index 000000000000..bffa490e80f4 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/helper/formatDate.function.ts @@ -0,0 +1,28 @@ +const nth = (d: number) => { + if (d > 3 && d < 21) return 'th'; + switch (d % 10) { + case 1: + return 'st'; + case 2: + return 'nd'; + case 3: + return 'rd'; + default: + return 'th'; + } +}; + +// label looks like this: Choose Wednesday, October 30th, 2024 +// eslint-disable-next-line prefer-arrow/prefer-arrow-functions +export function formatDate(value: string): string { + const date = new Date(value); + return 'Choose '.concat( + date.toLocaleDateString('en-US', { weekday: 'long' }), + ', ', + date.toLocaleDateString('en-US', { month: 'long' }), + ' ', + nth(date.getDate()), + ', ', + date.toLocaleDateString('en-US', { year: 'numeric' }), + ); +} diff --git a/packages/twenty-e2e-testing/lib/pom/helper/googleLogin.ts b/packages/twenty-e2e-testing/lib/pom/helper/googleLogin.ts new file mode 100644 index 000000000000..ca57fd6361f5 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/helper/googleLogin.ts @@ -0,0 +1,6 @@ +import { Locator, Page } from '@playwright/test'; + +export class GoogleLogin { + // TODO: map all things like inputs and buttons + // (what's the correct way for proceeding with Google interaction? log in each time test is performed?) +} diff --git a/packages/twenty-e2e-testing/lib/pom/helper/iconSelect.ts b/packages/twenty-e2e-testing/lib/pom/helper/iconSelect.ts new file mode 100644 index 000000000000..82b30c53c7af --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/helper/iconSelect.ts @@ -0,0 +1,23 @@ +import { Locator, Page } from '@playwright/test'; + +export class IconSelect { + private readonly iconSelectButton: Locator; + private readonly iconSearchInput: Locator; + + constructor(public readonly page: Page) { + this.iconSelectButton = page.getByLabel('Click to select icon ('); + this.iconSearchInput = page.getByPlaceholder('Search icon'); + } + + async selectIcon(name: string) { + await this.iconSelectButton.click(); + await this.iconSearchInput.fill(name); + await this.page.getByTitle(name).click(); + } + + async selectRelationIcon(name: string) { + await this.iconSelectButton.nth(1).click(); + await this.iconSearchInput.fill(name); + await this.page.getByTitle(name).click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/helper/insertFieldData.ts b/packages/twenty-e2e-testing/lib/pom/helper/insertFieldData.ts new file mode 100644 index 000000000000..a052eff68c03 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/helper/insertFieldData.ts @@ -0,0 +1,267 @@ +import { Locator, Page } from '@playwright/test'; +import { formatDate } from './formatDate.function'; + +export class InsertFieldData { + private readonly address1Input: Locator; + private readonly address2Input: Locator; + private readonly cityInput: Locator; + private readonly stateInput: Locator; + private readonly postCodeInput: Locator; + private readonly countrySelect: Locator; + private readonly arrayValueInput: Locator; + private readonly arrayAddValueButton: Locator; + // boolean react after click so no need to write special locator + private readonly currencySelect: Locator; + private readonly currencyAmountInput: Locator; + private readonly monthSelect: Locator; + private readonly yearSelect: Locator; + private readonly previousMonthButton: Locator; + private readonly nextMonthButton: Locator; + private readonly clearDateButton: Locator; + private readonly dateInput: Locator; + private readonly firstNameInput: Locator; + private readonly lastNameInput: Locator; + private readonly addURLButton: Locator; + private readonly setAsPrimaryButton: Locator; + private readonly addPhoneButton: Locator; + private readonly addMailButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.address1Input = page.locator( + '//label[contains(., "ADDRESS 1")]/../div[last()]/input', + ); + this.address2Input = page.locator( + '//label[contains(., "ADDRESS 2")]/../div[last()]/input', + ); + this.cityInput = page.locator( + '//label[contains(., "CITY")]/../div[last()]/input', + ); + this.stateInput = page.locator( + '//label[contains(., "STATE")]/../div[last()]/input', + ); + this.postCodeInput = page.locator( + '//label[contains(., "POST CODE")]/../div[last()]/input', + ); + this.countrySelect = page.locator( + '//span[contains(., "COUNTRY")]/../div[last()]/input', + ); + this.arrayValueInput = page.locator("//input[@placeholder='Enter value']"); + this.arrayAddValueButton = page.locator( + "//div[@data-testid='tooltip' and contains(.,'Add item')]", + ); + this.currencySelect = page.locator( + '//body/div[last()]/div/div/div[first()]/div/div', + ); + this.currencyAmountInput = page.locator("//input[@placeholder='Currency']"); + this.monthSelect; // TODO: add once some other attributes are added + this.yearSelect; + this.previousMonthButton; + this.nextMonthButton; + this.clearDateButton = page.locator( + "//div[@data-testid='tooltip' and contains(., 'Clear')]", + ); + this.dateInput = page.locator("//input[@placeholder='Type date and time']"); + this.firstNameInput = page.locator("//input[@placeholder='First name']"); // may fail if placeholder is `F‌‌irst name` instead of `First name` + this.lastNameInput = page.locator("//input[@placeholder='Last name']"); // may fail if placeholder is `L‌‌ast name` instead of `Last name` + this.addURLButton = page.locator( + "//div[@data-testid='tooltip' and contains(., 'Add URL')]", + ); + this.setAsPrimaryButton = page.locator( + "//div[@data-testid='tooltip' and contains(., 'Set as primary')]", + ); + this.addPhoneButton = page.locator( + "//div[@data-testid='tooltip' and contains(., 'Add Phone')]", + ); + this.addMailButton = page.locator( + "//div[@data-testid='tooltip' and contains(., 'Add Email')]", + ); + } + + // address + async typeAddress1(value: string) { + await this.address1Input.fill(value); + } + + async typeAddress2(value: string) { + await this.address2Input.fill(value); + } + + async typeCity(value: string) { + await this.cityInput.fill(value); + } + + async typeState(value: string) { + await this.stateInput.fill(value); + } + + async typePostCode(value: string) { + await this.postCodeInput.fill(value); + } + + async selectCountry(value: string) { + await this.countrySelect.click(); + await this.page + .locator(`//div[@data-testid='tooltip' and contains(., '${value}')]`) + .click(); + } + + // array + async typeArrayValue(value: string) { + await this.arrayValueInput.fill(value); + } + + async clickAddItemButton() { + await this.arrayAddValueButton.click(); + } + + // currency + async selectCurrency(value: string) { + await this.currencySelect.click(); + await this.page + .locator(`//div[@data-testid='tooltip' and contains(., '${value}')]`) + .click(); + } + + async typeCurrencyAmount(value: string) { + await this.currencyAmountInput.fill(value); + } + + // date(-time) + async typeDate(value: string) { + await this.dateInput.fill(value); + } + + async selectMonth(value: string) { + await this.monthSelect.click(); + await this.page + .locator(`//div[@data-testid='tooltip' and contains(., '${value}')]`) + .click(); + } + + async selectYear(value: string) { + await this.yearSelect.click(); + await this.page + .locator(`//div[@data-testid='tooltip' and contains(., '${value}')]`) + .click(); + } + + async clickPreviousMonthButton() { + await this.previousMonthButton.click(); + } + + async clickNextMonthButton() { + await this.nextMonthButton.click(); + } + + async selectDay(value: string) { + await this.page + .locator(`//div[@aria-label='${formatDate(value)}']`) + .click(); + } + + async clearDate() { + await this.clearDateButton.click(); + } + + // email + async typeEmail(value: string) { + await this.page.locator(`//input[@placeholder='Email']`).fill(value); + } + + async clickAddMailButton() { + await this.addMailButton.click(); + } + + // full name + async typeFirstName(name: string) { + await this.firstNameInput.fill(name); + } + + async typeLastName(name: string) { + await this.lastNameInput.fill(name); + } + + // JSON + // placeholder is dependent on the name of field + async typeJSON(placeholder: string, value: string) { + await this.page + .locator(`//input[@placeholder='${placeholder}']`) + .fill(value); + } + + // link + async typeLink(value: string) { + await this.page.locator("//input[@placeholder='URL']").fill(value); + } + + async clickAddURL() { + await this.addURLButton.click(); + } + + // (multi-)select + async selectValue(value: string) { + await this.page + .locator(`//div[@data-testid='tooltip' and contains(., '${value}')]`) + .click(); + } + + // number + // placeholder is dependent on the name of field + async typeNumber(placeholder: string, value: string) { + await this.page + .locator(`//input[@placeholder='${placeholder}']`) + .fill(value); + } + + // phones + async selectCountryPhoneCode(countryCode: string) { + await this.page + .locator( + `//div[@data-testid='tooltip' and contains(., '${countryCode}')]`, + ) + .click(); + } + + async typePhoneNumber(value: string) { + await this.page.locator(`//input[@placeholder='Phone']`).fill(value); + } + + async clickAddPhoneButton() { + await this.addPhoneButton.click(); + } + + // rating + // if adding rating for the first time, hover must be used + async selectRating(rating: number) { + await this.page.locator(`//div[@role='slider']/div[${rating}]`).click(); + } + + // text + // placeholder is dependent on the name of field + async typeText(placeholder: string, value: string) { + await this.page + .locator(`//input[@placeholder='${placeholder}']`) + .fill(value); + } + + async clickSetAsPrimaryButton() { + await this.setAsPrimaryButton.click(); + } + + async searchValue(value: string) { + await this.page.locator(`//div[@placeholder='Search']`).fill(value); + } + + async clickEditButton() { + await this.page + .locator("//div[@data-testid='tooltip' and contains(., 'Edit')]") + .click(); + } + + async clickDeleteButton() { + await this.page + .locator("//div[@data-testid='tooltip' and contains(., 'Delete')]") + .click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/helper/stripePage.ts b/packages/twenty-e2e-testing/lib/pom/helper/stripePage.ts new file mode 100644 index 000000000000..ccef759f916d --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/helper/stripePage.ts @@ -0,0 +1,5 @@ +import { Locator, Page } from '@playwright/test'; + +export class StripePage { + // TODO: implement all necessary methods (staging/sandbox page - does it differ anyhow from normal page?) +} diff --git a/packages/twenty-e2e-testing/lib/pom/helper/uploadImage.ts b/packages/twenty-e2e-testing/lib/pom/helper/uploadImage.ts new file mode 100644 index 000000000000..41493fdc0e19 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/helper/uploadImage.ts @@ -0,0 +1,25 @@ +import { Locator, Page } from '@playwright/test'; + +export class UploadImage { + private readonly imagePreview: Locator; + private readonly uploadButton: Locator; + private readonly removeButton: Locator; + + constructor(public readonly page: Page) { + this.imagePreview = page.locator('.css-6eut39'); //TODO: add attribute to make it independent of theme + this.uploadButton = page.getByRole('button', { name: 'Upload' }); + this.removeButton = page.getByRole('button', { name: 'Remove' }); + } + + async clickImagePreview() { + await this.imagePreview.click(); + } + + async clickUploadButton() { + await this.uploadButton.click(); + } + + async clickRemoveButton() { + await this.removeButton.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/leftMenu.ts b/packages/twenty-e2e-testing/lib/pom/leftMenu.ts new file mode 100644 index 000000000000..c58c3f7f1a02 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/leftMenu.ts @@ -0,0 +1,115 @@ +import { Locator, Page } from '@playwright/test'; + +export class LeftMenu { + private readonly workspaceDropdown: Locator; + private readonly leftMenu: Locator; + private readonly searchSubTab: Locator; + private readonly settingsTab: Locator; + private readonly peopleTab: Locator; + private readonly companiesTab: Locator; + private readonly opportunitiesTab: Locator; + private readonly opportunitiesTabAll: Locator; + private readonly opportunitiesTabByStage: Locator; + private readonly tasksTab: Locator; + private readonly tasksTabAll: Locator; + private readonly tasksTabByStatus: Locator; + private readonly notesTab: Locator; + private readonly rocketsTab: Locator; + private readonly workflowsTab: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.workspaceDropdown = page.getByTestId('workspace-dropdown'); + this.leftMenu = page.getByRole('button').first(); + this.searchSubTab = page.getByText('Search'); + this.settingsTab = page.getByRole('link', { name: 'Settings' }); + this.peopleTab = page.getByRole('link', { name: 'People' }); + this.companiesTab = page.getByRole('link', { name: 'Companies' }); + this.opportunitiesTab = page.getByRole('link', { name: 'Opportunities' }); + this.opportunitiesTabAll = page.getByRole('link', { + name: 'All', + exact: true, + }); + this.opportunitiesTabByStage = page.getByRole('link', { name: 'By Stage' }); + this.tasksTab = page.getByRole('link', { name: 'Tasks' }); + this.tasksTabAll = page.getByRole('link', { name: 'All tasks' }); + this.tasksTabByStatus = page.getByRole('link', { name: 'Notes' }); + this.notesTab = page.getByRole('link', { name: 'Notes' }); + this.rocketsTab = page.getByRole('link', { name: 'Rockets' }); + this.workflowsTab = page.getByRole('link', { name: 'Workflows' }); + } + + async selectWorkspace(workspaceName: string) { + await this.workspaceDropdown.click(); + await this.page + .getByTestId('tooltip') + .filter({ hasText: workspaceName }) + .click(); + } + + async changeLeftMenu() { + await this.leftMenu.click(); + } + + async openSearchTab() { + await this.searchSubTab.click(); + } + + async goToSettings() { + await this.settingsTab.click(); + } + + async goToPeopleTab() { + await this.peopleTab.click(); + } + + async goToCompaniesTab() { + await this.companiesTab.click(); + } + + async goToOpportunitiesTab() { + await this.opportunitiesTab.click(); + } + + async goToOpportunitiesTableView() { + await this.opportunitiesTabAll.click(); + } + + async goToOpportunitiesKanbanView() { + await this.opportunitiesTabByStage.click(); + } + + async goToTasksTab() { + await this.tasksTab.click(); + } + + async goToTasksTableView() { + await this.tasksTabAll.click(); + } + + async goToTasksKanbanView() { + await this.tasksTabByStatus.click(); + } + + async goToNotesTab() { + await this.notesTab.click(); + } + + async goToRocketsTab() { + await this.rocketsTab.click(); + } + + async goToWorkflowsTab() { + await this.workflowsTab.click(); + } + + async goToCustomObject(customObjectName: string) { + await this.page.getByRole('link', { name: customObjectName }).click(); + } + + async goToCustomObjectView(name: string) { + await this.page.getByRole('link', { name: name }).click(); + } +} + +export default LeftMenu; diff --git a/packages/twenty-e2e-testing/lib/pom/loginPage.ts b/packages/twenty-e2e-testing/lib/pom/loginPage.ts new file mode 100644 index 000000000000..dc60d3f7a799 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/loginPage.ts @@ -0,0 +1,187 @@ +import { Locator, Page } from '@playwright/test'; + +export class LoginPage { + private readonly loginWithGoogleButton: Locator; + private readonly loginWithEmailButton: Locator; + private readonly termsOfServiceLink: Locator; + private readonly privacyPolicyLink: Locator; + private readonly emailField: Locator; + private readonly continueButton: Locator; + private readonly forgotPasswordButton: Locator; + private readonly passwordField: Locator; + private readonly revealPasswordButton: Locator; + private readonly signInButton: Locator; + private readonly signUpButton: Locator; + private readonly previewImageButton: Locator; + private readonly uploadImageButton: Locator; + private readonly deleteImageButton: Locator; + private readonly workspaceNameField: Locator; + private readonly firstNameField: Locator; + private readonly lastNameField: Locator; + private readonly syncEverythingWithGoogleRadio: Locator; + private readonly syncSubjectAndMetadataWithGoogleRadio: Locator; + private readonly syncMetadataWithGoogleRadio: Locator; + private readonly syncWithGoogleButton: Locator; + private readonly noSyncButton: Locator; + private readonly inviteLinkField1: Locator; + private readonly inviteLinkField2: Locator; + private readonly inviteLinkField3: Locator; + private readonly copyInviteLink: Locator; + private readonly finishButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.loginWithGoogleButton = page.getByRole('button', { + name: 'Continue with Google', + }); + this.loginWithEmailButton = page.getByRole('button', { + name: 'Continue With Email', + }); + this.termsOfServiceLink = page.getByRole('link', { + name: 'Terms of Service', + }); + this.privacyPolicyLink = page.getByRole('link', { name: 'Privacy Policy' }); + this.emailField = page.getByPlaceholder('Email'); + this.continueButton = page.getByRole('button', { + name: 'Continue', + exact: true, + }); + this.forgotPasswordButton = page.getByText('Forgot your password?'); + this.passwordField = page.getByPlaceholder('Password'); + this.revealPasswordButton = page.getByTestId('reveal-password-button'); + this.signInButton = page.getByRole('button', { name: 'Sign in' }); + this.signUpButton = page.getByRole('button', { name: 'Sign up' }); + this.previewImageButton = page.locator('.css-1qzw107'); // TODO: fix + this.uploadImageButton = page.getByRole('button', { name: 'Upload' }); + this.deleteImageButton = page.getByRole('button', { name: 'Remove' }); + this.workspaceNameField = page.getByPlaceholder('Apple'); + this.firstNameField = page.getByPlaceholder('Tim'); + this.lastNameField = page.getByPlaceholder('Cook'); + this.syncEverythingWithGoogleRadio = page.locator( + 'input[value="SHARE_EVERYTHING"]', + ); + this.syncSubjectAndMetadataWithGoogleRadio = page.locator( + 'input[value="SUBJECT"]', + ); + this.syncMetadataWithGoogleRadio = page.locator('input[value="METADATA"]'); + this.syncWithGoogleButton = page.getByRole('button', { + name: 'Sync with Google', + }); + this.noSyncButton = page.getByText('Continue without sync'); + this.inviteLinkField1 = page.getByPlaceholder('tim@apple.dev'); + this.inviteLinkField2 = page.getByPlaceholder('craig@apple.dev'); + this.inviteLinkField3 = page.getByPlaceholder('mike@apple.dev'); + this.copyInviteLink = page.getByRole('button', { + name: 'Copy invitation link', + }); + this.finishButton = page.getByRole('button', { name: 'Finish' }); + } + + async loginWithGoogle() { + await this.loginWithGoogleButton.click(); + } + + async clickLoginWithEmail() { + await this.loginWithEmailButton.click(); + } + + async clickContinueButton() { + await this.continueButton.click(); + } + + async clickTermsLink() { + await this.termsOfServiceLink.click(); + } + + async clickPrivacyPolicyLink() { + await this.privacyPolicyLink.click(); + } + + async typeEmail(email: string) { + await this.emailField.fill(email); + } + + async typePassword(email: string) { + await this.passwordField.fill(email); + } + + async clickSignInButton() { + await this.signInButton.click(); + } + + async clickSignUpButton() { + await this.signUpButton.click(); + } + + async clickForgotPassword() { + await this.forgotPasswordButton.click(); + } + + async revealPassword() { + await this.revealPasswordButton.click(); + } + + async previewImage() { + await this.previewImageButton.click(); + } + + async clickUploadImage() { + await this.uploadImageButton.click(); + } + + async deleteImage() { + await this.deleteImageButton.click(); + } + + async typeWorkspaceName(workspaceName: string) { + await this.workspaceNameField.fill(workspaceName); + } + + async typeFirstName(firstName: string) { + await this.firstNameField.fill(firstName); + } + + async typeLastName(lastName: string) { + await this.lastNameField.fill(lastName); + } + + async clickSyncEverythingWithGoogleRadio() { + await this.syncEverythingWithGoogleRadio.click(); + } + + async clickSyncSubjectAndMetadataWithGoogleRadio() { + await this.syncSubjectAndMetadataWithGoogleRadio.click(); + } + + async clickSyncMetadataWithGoogleRadio() { + await this.syncMetadataWithGoogleRadio.click(); + } + + async clickSyncWithGoogleButton() { + await this.syncWithGoogleButton.click(); + } + + async noSyncWithGoogle() { + await this.noSyncButton.click(); + } + + async typeInviteLink1(email: string) { + await this.inviteLinkField1.fill(email); + } + + async typeInviteLink2(email: string) { + await this.inviteLinkField2.fill(email); + } + + async typeInviteLink3(email: string) { + await this.inviteLinkField3.fill(email); + } + + async clickCopyInviteLink() { + await this.copyInviteLink.click(); + } + + async clickFinishButton() { + await this.finishButton.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/mainPage.ts b/packages/twenty-e2e-testing/lib/pom/mainPage.ts new file mode 100644 index 000000000000..a28e133b575c --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/mainPage.ts @@ -0,0 +1,196 @@ +import { Locator, Page } from '@playwright/test'; + +export class MainPage { + // TODO: add missing elements (advanced filters, import/export popups) + private readonly tableViews: Locator; + private readonly addViewButton: Locator; + private readonly viewIconSelect: Locator; + private readonly viewNameInput: Locator; + private readonly viewTypeSelect: Locator; + private readonly createViewButton: Locator; + private readonly deleteViewButton: Locator; + private readonly filterButton: Locator; + private readonly searchFieldInput: Locator; + private readonly advancedFilterButton: Locator; + private readonly addFilterButton: Locator; + private readonly resetFilterButton: Locator; + private readonly saveFilterAsViewButton: Locator; + private readonly sortButton: Locator; + private readonly sortOrderButton: Locator; + private readonly optionsButton: Locator; + private readonly fieldsButton: Locator; + private readonly goBackButton: Locator; + private readonly hiddenFieldsButton: Locator; + private readonly editFieldsButton: Locator; + private readonly importButton: Locator; + private readonly exportButton: Locator; + private readonly deletedRecordsButton: Locator; + private readonly createNewRecordButton: Locator; + private readonly addToFavoritesButton: Locator; + private readonly deleteFromFavoritesButton: Locator; + private readonly exportBottomBarButton: Locator; + private readonly deleteRecordsButton: Locator; + + constructor(public readonly page: Page) { + this.tableViews = page.getByText('·'); + this.addViewButton = page + .getByTestId('tooltip') + .filter({ hasText: /^Add view$/ }); + this.viewIconSelect = page.getByLabel('Click to select icon ('); + this.viewNameInput; // can be selected using only actual value + this.viewTypeSelect = page.locator( + "//span[contains(., 'View type')]/../div", + ); + this.createViewButton = page.getByRole('button', { name: 'Create' }); + this.deleteViewButton = page.getByRole('button', { name: 'Delete' }); + this.filterButton = page.getByText('Filter'); + this.searchFieldInput = page.getByPlaceholder('Search fields'); + this.advancedFilterButton = page + .getByTestId('tooltip') + .filter({ hasText: /^Advanced filter$/ }); + this.addFilterButton = page.getByRole('button', { name: 'Add Filter' }); + this.resetFilterButton = page.getByTestId('cancel-button'); + this.saveFilterAsViewButton = page.getByRole('button', { + name: 'Save as new view', + }); + this.sortButton = page.getByText('Sort'); + this.sortOrderButton = page.locator('//li'); + this.optionsButton = page.getByText('Options'); + this.fieldsButton = page.getByText('Fields'); + this.goBackButton = page.getByTestId('dropdown-menu-header-end-icon'); + this.hiddenFieldsButton = page + .getByTestId('tooltip') + .filter({ hasText: /^Hidden Fields$/ }); + this.editFieldsButton = page + .getByTestId('tooltip') + .filter({ hasText: /^Edit Fields$/ }); + this.importButton = page + .getByTestId('tooltip') + .filter({ hasText: /^Import$/ }); + this.exportButton = page + .getByTestId('tooltip') + .filter({ hasText: /^Export$/ }); + this.deletedRecordsButton = page + .getByTestId('tooltip') + .filter({ hasText: /^Deleted */ }); + this.createNewRecordButton = page.getByTestId('add-button'); + this.addToFavoritesButton = page.getByText('Add to favorites'); + this.deleteFromFavoritesButton = page.getByText('Delete from favorites'); + this.exportBottomBarButton = page.getByText('Export'); + this.deleteRecordsButton = page.getByText('Delete'); + } + + async clickTableViews() { + await this.tableViews.click(); + } + + async clickAddViewButton() { + await this.addViewButton.click(); + } + + async typeViewName(name: string) { + await this.viewNameInput.clear(); + await this.viewNameInput.fill(name); + } + + // name can be either be 'Table' or 'Kanban' + async selectViewType(name: string) { + await this.viewTypeSelect.click(); + await this.page.getByTestId('tooltip').filter({ hasText: name }).click(); + } + + async createView() { + await this.createViewButton.click(); + } + + async deleteView() { + await this.deleteViewButton.click(); + } + + async clickFilterButton() { + await this.filterButton.click(); + } + + async searchFields(name: string) { + await this.searchFieldInput.clear(); + await this.searchFieldInput.fill(name); + } + + async clickAdvancedFilterButton() { + await this.advancedFilterButton.click(); + } + + async addFilter() { + await this.addFilterButton.click(); + } + + async resetFilter() { + await this.resetFilterButton.click(); + } + + async saveFilterAsView() { + await this.saveFilterAsViewButton.click(); + } + + async clickSortButton() { + await this.sortButton.click(); + } + + //can be Ascending or Descending + async setSortOrder(name: string) { + await this.sortOrderButton.click(); + await this.page.getByTestId('tooltip').filter({ hasText: name }).click(); + } + + async clickOptionsButton() { + await this.optionsButton.click(); + } + + async clickFieldsButton() { + await this.fieldsButton.click(); + } + + async clickBackButton() { + await this.goBackButton.click(); + } + + async clickHiddenFieldsButton() { + await this.hiddenFieldsButton.click(); + } + + async clickEditFieldsButton() { + await this.editFieldsButton.click(); + } + + async clickImportButton() { + await this.importButton.click(); + } + + async clickExportButton() { + await this.exportButton.click(); + } + + async clickDeletedRecordsButton() { + await this.deletedRecordsButton.click(); + } + + async clickCreateNewRecordButton() { + await this.createNewRecordButton.click(); + } + + async clickAddToFavoritesButton() { + await this.addToFavoritesButton.click(); + } + + async clickDeleteFromFavoritesButton() { + await this.deleteFromFavoritesButton.click(); + } + + async clickExportBottomBarButton() { + await this.exportBottomBarButton.click(); + } + + async clickDeleteRecordsButton() { + await this.deleteRecordsButton.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/recordDetails.ts b/packages/twenty-e2e-testing/lib/pom/recordDetails.ts new file mode 100644 index 000000000000..f22dd8a459b5 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/recordDetails.ts @@ -0,0 +1,150 @@ +import { Locator, Page } from '@playwright/test'; + +export class RecordDetails { + // TODO: add missing components in tasks, notes, files, emails, calendar tabs + private readonly closeRecordButton: Locator; + private readonly previousRecordButton: Locator; + private readonly nextRecordButton: Locator; + private readonly favoriteRecordButton: Locator; + private readonly addShowPageButton: Locator; + private readonly moreOptionsButton: Locator; + private readonly deleteButton: Locator; + private readonly uploadProfileImageButton: Locator; + private readonly timelineTab: Locator; + private readonly tasksTab: Locator; + private readonly notesTab: Locator; + private readonly filesTab: Locator; + private readonly emailsTab: Locator; + private readonly calendarTab: Locator; + private readonly detachRelationButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + } + + async clickCloseRecordButton() { + await this.closeRecordButton.click(); + } + + async clickPreviousRecordButton() { + await this.previousRecordButton.click(); + } + + async clickNextRecordButton() { + await this.nextRecordButton.click(); + } + + async clickFavoriteRecordButton() { + await this.favoriteRecordButton.click(); + } + + async createRelatedNote() { + await this.addShowPageButton.click(); + await this.page + .locator('//div[@data-testid="tooltip" and contains(., "Note")]') + .click(); + } + + async createRelatedTask() { + await this.addShowPageButton.click(); + await this.page + .locator('//div[@data-testid="tooltip" and contains(., "Task")]') + .click(); + } + + async clickMoreOptionsButton() { + await this.moreOptionsButton.click(); + } + + async clickDeleteRecordButton() { + await this.deleteButton.click(); + } + + async clickUploadProfileImageButton() { + await this.uploadProfileImageButton.click(); + } + + async goToTimelineTab() { + await this.timelineTab.click(); + } + + async goToTasksTab() { + await this.tasksTab.click(); + } + + async goToNotesTab() { + await this.notesTab.click(); + } + + async goToFilesTab() { + await this.filesTab.click(); + } + + async goToEmailsTab() { + await this.emailsTab.click(); + } + + async goToCalendarTab() { + await this.calendarTab.click(); + } + + async clickField(name: string) { + await this.page + .locator( + `//div[@data-testid='tooltip' and contains(., '${name}']/../../../div[last()]/div/div`, + ) + .click(); + } + + async clickFieldWithButton(name: string) { + await this.page + .locator( + `//div[@data-testid='tooltip' and contains(., '${name}']/../../../div[last()]/div/div`, + ) + .hover(); + await this.page + .locator( + `//div[@data-testid='tooltip' and contains(., '${name}']/../../../div[last()]/div/div[last()]/div/button`, + ) + .click(); + } + + async clickRelationEditButton(name: string) { + await this.page.getByRole('heading').filter({ hasText: name }).hover(); + await this.page + .locator(`//header[contains(., "${name}")]/div[last()]/div/button`) + .click(); + } + + async detachRelation(name: string) { + await this.page.locator(`//a[contains(., "${name}")]`).hover(); + await this.page + .locator(`, //a[contains(., "${name}")]/../div[last()]/div/div/button`) + .hover(); + await this.detachRelationButton.click(); + } + + async deleteRelationRecord(name: string) { + await this.page.locator(`//a[contains(., "${name}")]`).hover(); + await this.page + .locator(`, //a[contains(., "${name}")]/../div[last()]/div/div/button`) + .hover(); + await this.deleteButton.click(); + } + + async selectRelationRecord(name: string) { + await this.page + .locator(`//div[@data-testid="tooltip" and contains(., "${name}")]`) + .click(); + } + + async searchRelationRecord(name: string) { + await this.page.getByPlaceholder('Search').fill(name); + } + + async createNewRelationRecord() { + await this.page + .locator('//div[@data-testid="tooltip" and contains(., "Add New")]') + .click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/accountsSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/accountsSection.ts new file mode 100644 index 000000000000..703cdffa6ed6 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/accountsSection.ts @@ -0,0 +1,54 @@ +import { Locator, Page } from '@playwright/test'; + +export class AccountsSection { + private readonly addAccountButton: Locator; + private readonly deleteAccountButton: Locator; + private readonly addBlocklistField: Locator; + private readonly addBlocklistButton: Locator; + private readonly connectWithGoogleButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.addAccountButton = page.getByRole('button', { name: 'Add account' }); + this.deleteAccountButton = page + .getByTestId('tooltip') + .getByText('Remove account'); + this.addBlocklistField = page.getByPlaceholder( + 'eddy@gmail.com, @apple.com', + ); + this.addBlocklistButton = page.getByRole('button', { + name: 'Add to blocklist', + }); + this.connectWithGoogleButton = page.getByRole('button', { + name: 'Connect with Google', + }); + } + + async clickAddAccount() { + await this.addAccountButton.click(); + } + + async deleteAccount(email: string) { + await this.page + .locator(`//span[contains(., "${email}")]/../div/div/div/button`) + .click(); + await this.deleteAccountButton.click(); + } + + async addToBlockList(domain: string) { + await this.addBlocklistField.fill(domain); + await this.addBlocklistButton.click(); + } + + async removeFromBlocklist(domain: string) { + await this.page + .locator( + `//div[@data-testid='tooltip' and contains(., '${domain}')]/../../div[last()]/button`, + ) + .click(); + } + + async linkGoogleAccount() { + await this.connectWithGoogleButton.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/calendarSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/calendarSection.ts new file mode 100644 index 000000000000..98ccba0d06f8 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/calendarSection.ts @@ -0,0 +1,30 @@ +import { Locator, Page } from '@playwright/test'; + +export class CalendarSection { + private readonly eventVisibilityEverythingOption: Locator; + private readonly eventVisibilityMetadataOption: Locator; + private readonly contactAutoCreation: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.eventVisibilityEverythingOption = page.locator( + 'input[value="SHARE_EVERYTHING"]', + ); + this.eventVisibilityMetadataOption = page.locator( + 'input[value="METADATA"]', + ); + this.contactAutoCreation = page.getByRole('checkbox').nth(1); + } + + async changeVisibilityToEverything() { + await this.eventVisibilityEverythingOption.click(); + } + + async changeVisibilityToMetadata() { + await this.eventVisibilityMetadataOption.click(); + } + + async toggleAutoCreation() { + await this.contactAutoCreation.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/dataModelSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/dataModelSection.ts new file mode 100644 index 000000000000..0e36bd351d56 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/dataModelSection.ts @@ -0,0 +1,189 @@ +import { Locator, Page } from '@playwright/test'; + +export class DataModelSection { + private readonly searchObjectInput: Locator; + private readonly addObjectButton: Locator; + private readonly objectSingularNameInput: Locator; + private readonly objectPluralNameInput: Locator; + private readonly objectDescription: Locator; + private readonly synchronizeLabelAPIToggle: Locator; + private readonly objectAPISingularNameInput: Locator; + private readonly objectAPIPluralNameInput: Locator; + private readonly objectMoreOptionsButton: Locator; + private readonly editObjectButton: Locator; + private readonly deleteObjectButton: Locator; + private readonly activeSection: Locator; + private readonly inactiveSection: Locator; + private readonly searchFieldInput: Locator; + private readonly addFieldButton: Locator; + private readonly viewFieldDetailsMoreOptionsButton: Locator; + private readonly nameFieldInput: Locator; + private readonly descriptionFieldInput: Locator; + private readonly deactivateMoreOptionsButton: Locator; + private readonly activateMoreOptionsButton: Locator; + private readonly deactivateButton: Locator; // TODO: add attribute to make it one button + private readonly activateButton: Locator; + private readonly cancelButton: Locator; + private readonly saveButton: Locator; + + constructor(public readonly page: Page) { + this.searchObjectInput = page.getByPlaceholder('Search an object...'); + this.addObjectButton = page.getByRole('button', { name: 'Add object' }); + this.objectSingularNameInput = page.getByPlaceholder('Listing', { + exact: true, + }); + this.objectPluralNameInput = page.getByPlaceholder('Listings', { + exact: true, + }); + this.objectDescription = page.getByPlaceholder('Write a description'); + this.synchronizeLabelAPIToggle = page.getByRole('checkbox').nth(1); + this.objectAPISingularNameInput = page.getByPlaceholder('listing', { + exact: true, + }); + this.objectAPIPluralNameInput = page.getByPlaceholder('listings', { + exact: true, + }); + this.objectMoreOptionsButton = page.getByLabel('Object Options'); + this.editObjectButton = page.getByTestId('tooltip').getByText('Edit'); + this.deactivateMoreOptionsButton = page + .getByTestId('tooltip') + .getByText('Deactivate'); + this.activateMoreOptionsButton = page + .getByTestId('tooltip') + .getByText('Activate'); + this.deleteObjectButton = page.getByTestId('tooltip').getByText('Delete'); + this.activeSection = page.getByText('Active', { exact: true }); + this.inactiveSection = page.getByText('Inactive'); + this.searchFieldInput = page.getByPlaceholder('Search a field...'); + this.addFieldButton = page.getByRole('button', { name: 'Add field' }); + this.viewFieldDetailsMoreOptionsButton = page + .getByTestId('tooltip') + .getByText('View'); + this.nameFieldInput = page.getByPlaceholder('Employees'); + this.descriptionFieldInput = page.getByPlaceholder('Write a description'); + this.deactivateButton = page.getByRole('button', { name: 'Deactivate' }); + this.activateButton = page.getByRole('button', { name: 'Activate' }); + this.cancelButton = page.getByRole('button', { name: 'Cancel' }); + this.saveButton = page.getByRole('button', { name: 'Save' }); + } + + async searchObject(name: string) { + await this.searchObjectInput.fill(name); + } + + async clickAddObjectButton() { + await this.addObjectButton.click(); + } + + async typeObjectSingularName(name: string) { + await this.objectSingularNameInput.fill(name); + } + + async typeObjectPluralName(name: string) { + await this.objectPluralNameInput.fill(name); + } + + async typeObjectDescription(name: string) { + await this.objectDescription.fill(name); + } + + async toggleSynchronizeLabelAPI() { + await this.synchronizeLabelAPIToggle.click(); + } + + async typeObjectSingularAPIName(name: string) { + await this.objectAPISingularNameInput.fill(name); + } + + async typeObjectPluralAPIName(name: string) { + await this.objectAPIPluralNameInput.fill(name); + } + + async checkObjectDetails(name: string) { + await this.page.getByRole('link').filter({ hasText: name }).click(); + } + + async activateInactiveObject(name: string) { + await this.page + .locator(`//div[@title="${name}"]/../../div[last()]`) + .click(); + await this.activateButton.click(); + } + + // object can be deleted only if is custom and inactive + async deleteInactiveObject(name: string) { + await this.page + .locator(`//div[@title="${name}"]/../../div[last()]`) + .click(); + await this.deleteObjectButton.click(); + } + + async editObjectDetails() { + await this.objectMoreOptionsButton.click(); + await this.editObjectButton.click(); + } + + async deactivateObjectWithMoreOptions() { + await this.objectMoreOptionsButton.click(); + await this.deactivateButton.click(); + } + + async searchField(name: string) { + await this.searchFieldInput.fill(name); + } + + async checkFieldDetails(name: string) { + await this.page.locator(`//div[@title="${name}"]`).click(); + } + + async checkFieldDetailsWithButton(name: string) { + await this.page + .locator(`//div[@title="${name}"]/../../div[last()]`) + .click(); + await this.viewFieldDetailsMoreOptionsButton.click(); + } + + async deactivateFieldWithButton(name: string) { + await this.page + .locator(`//div[@title="${name}"]/../../div[last()]`) + .click(); + await this.deactivateMoreOptionsButton.click(); + } + + async activateFieldWithButton(name: string) { + await this.page + .locator(`//div[@title="${name}"]/../../div[last()]`) + .click(); + await this.activateMoreOptionsButton.click(); + } + + async clickAddFieldButton() { + await this.addFieldButton.click(); + } + + async typeFieldName(name: string) { + await this.nameFieldInput.clear(); + await this.nameFieldInput.fill(name); + } + + async typeFieldDescription(description: string) { + await this.descriptionFieldInput.clear(); + await this.descriptionFieldInput.fill(description); + } + + async clickInactiveSection() { + await this.inactiveSection.click(); + } + + async clickActiveSection() { + await this.activeSection.click(); + } + + async clickCancelButton() { + await this.cancelButton.click(); + } + + async clickSaveButton() { + await this.saveButton.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/developersSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/developersSection.ts new file mode 100644 index 000000000000..0f7fec96f697 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/developersSection.ts @@ -0,0 +1,123 @@ +import { Locator, Page } from '@playwright/test'; + +export class DevelopersSection { + private readonly readDocumentationButton: Locator; + private readonly createAPIKeyButton: Locator; + private readonly regenerateAPIKeyButton: Locator; + private readonly nameOfAPIKeyInput: Locator; + private readonly expirationDateAPIKeySelect: Locator; + private readonly createWebhookButton: Locator; + private readonly webhookURLInput: Locator; + private readonly webhookDescription: Locator; + private readonly webhookFilterObjectSelect: Locator; + private readonly webhookFilterActionSelect: Locator; + private readonly cancelButton: Locator; + private readonly saveButton: Locator; + private readonly deleteButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.readDocumentationButton = page.getByRole('link', { + name: 'Read documentation', + }); + this.createAPIKeyButton = page.getByRole('link', { + name: 'Create API Key', + }); + this.createWebhookButton = page.getByRole('link', { + name: 'Create Webhook', + }); + this.nameOfAPIKeyInput = page + .getByPlaceholder('E.g. backoffice integration') + .first(); + this.expirationDateAPIKeySelect = page + .locator('div') + .filter({ hasText: /^Never$/ }) + .nth(3); // good enough if expiration date will change only 1 time + this.regenerateAPIKeyButton = page.getByRole('button', { + name: 'Regenerate Key', + }); + this.webhookURLInput = page.getByPlaceholder('URL'); + this.webhookDescription = page.getByPlaceholder('Write a description'); + this.webhookFilterObjectSelect = page + .locator('div') + .filter({ hasText: /^All Objects$/ }) + .nth(3); // works only for first filter + this.webhookFilterActionSelect = page + .locator('div') + .filter({ hasText: /^All Actions$/ }) + .nth(3); // works only for first filter + this.cancelButton = page.getByRole('button', { name: 'Cancel' }); + this.saveButton = page.getByRole('button', { name: 'Save' }); + this.deleteButton = page.getByRole('button', { name: 'Delete' }); + } + + async openDocumentation() { + await this.readDocumentationButton.click(); + } + + async createAPIKey() { + await this.createAPIKeyButton.click(); + } + + async typeAPIKeyName(name: string) { + await this.nameOfAPIKeyInput.clear(); + await this.nameOfAPIKeyInput.fill(name); + } + + async selectAPIExpirationDate(date: string) { + await this.expirationDateAPIKeySelect.click(); + await this.page.getByText(date, { exact: true }).click(); + } + + async regenerateAPIKey() { + await this.regenerateAPIKeyButton.click(); + } + + async deleteAPIKey() { + await this.deleteButton.click(); + } + + async deleteWebhook() { + await this.deleteButton.click(); + } + + async createWebhook() { + await this.createWebhookButton.click(); + } + + async typeWebhookURL(url: string) { + await this.webhookURLInput.fill(url); + } + + async typeWebhookDescription(description: string) { + await this.webhookDescription.fill(description); + } + + async selectWebhookObject(object: string) { + // TODO: finish + } + + async selectWebhookAction(action: string) { + // TODO: finish + } + + async deleteWebhookFilter() { + // TODO: finish + } + + async clickCancelButton() { + await this.cancelButton.click(); + } + + async clickSaveButton() { + await this.saveButton.click(); + } + + async checkAPIKeyDetails(name: string) { + await this.page.locator(`//a/div[contains(.,'${name}')][first()]`).click(); + } + + async checkWebhookDetails(name: string) { + await this.page.locator(`//a/div[contains(.,'${name}')][first()]`).click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/emailsSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/emailsSection.ts new file mode 100644 index 000000000000..23a83f8db07e --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/emailsSection.ts @@ -0,0 +1,61 @@ +import { Locator, Page } from '@playwright/test'; + +export class EmailsSection { + private readonly visibilityEverythingRadio: Locator; + private readonly visibilitySubjectRadio: Locator; + private readonly visibilityMetadataRadio: Locator; + private readonly autoCreationReceivedRadio: Locator; + private readonly autoCreationSentRadio: Locator; + private readonly autoCreationNoneRadio: Locator; + private readonly excludeNonProfessionalToggle: Locator; + private readonly excludeGroupToggle: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.visibilityEverythingRadio = page.locator( + 'input[value="SHARE_EVERYTHING"]', + ); + this.visibilitySubjectRadio = page.locator('input[value="SUBJECT"]'); + this.visibilityMetadataRadio = page.locator('input[value="METADATA"]'); + this.autoCreationReceivedRadio = page.locator( + 'input[value="SENT_AND_RECEIVED"]', + ); + this.autoCreationSentRadio = page.locator('input[value="SENT"]'); + this.autoCreationNoneRadio = page.locator('input[value="NONE"]'); + // first checkbox is advanced settings toggle + this.excludeNonProfessionalToggle = page.getByRole('checkbox').nth(1); + this.excludeGroupToggle = page.getByRole('checkbox').nth(2); + } + + async changeVisibilityToEverything() { + await this.visibilityEverythingRadio.click(); + } + + async changeVisibilityToSubject() { + await this.visibilitySubjectRadio.click(); + } + + async changeVisibilityToMetadata() { + await this.visibilityMetadataRadio.click(); + } + + async changeAutoCreationToAll() { + await this.autoCreationReceivedRadio.click(); + } + + async changeAutoCreationToSent() { + await this.autoCreationSentRadio.click(); + } + + async changeAutoCreationToNone() { + await this.autoCreationNoneRadio.click(); + } + + async toggleExcludeNonProfessional() { + await this.excludeNonProfessionalToggle.click(); + } + + async toggleExcludeGroup() { + await this.excludeGroupToggle.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/experienceSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/experienceSection.ts new file mode 100644 index 000000000000..4ed6606c10c9 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/experienceSection.ts @@ -0,0 +1,55 @@ +import { Locator, Page } from '@playwright/test'; + +export class ExperienceSection { + private readonly lightThemeButton: Locator; + private readonly darkThemeButton: Locator; + private readonly timezoneDropdown: Locator; + private readonly dateFormatDropdown: Locator; + private readonly timeFormatDropdown: Locator; + private readonly searchInput: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.lightThemeButton = page.getByText('AaLight'); // it works + this.darkThemeButton = page.getByText('AaDark'); + this.timezoneDropdown = page.locator( + '//span[contains(., "Time zone")]/../div/div/div', + ); + this.dateFormatDropdown = page.locator( + '//span[contains(., "Date format")]/../div/div/div', + ); + this.timeFormatDropdown = page.locator( + '//span[contains(., "Time format")]/../div/div/div', + ); + this.searchInput = page.getByPlaceholder('Search'); + } + + async changeThemeToLight() { + await this.lightThemeButton.click(); + } + + async changeThemeToDark() { + await this.darkThemeButton.click(); + } + + async selectTimeZone(timezone: string) { + await this.timezoneDropdown.click(); + await this.page.getByText(timezone, { exact: true }).click(); + } + + async selectTimeZoneWithSearch(timezone: string) { + await this.timezoneDropdown.click(); + await this.searchInput.fill(timezone); + await this.page.getByText(timezone, { exact: true }).click(); + } + + async selectDateFormat(dateFormat: string) { + await this.dateFormatDropdown.click(); + await this.page.getByText(dateFormat, { exact: true }).click(); + } + + async selectTimeFormat(timeFormat: string) { + await this.timeFormatDropdown.click(); + await this.page.getByText(timeFormat, { exact: true }).click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/functionsSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/functionsSection.ts new file mode 100644 index 000000000000..f8f42418a17e --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/functionsSection.ts @@ -0,0 +1,159 @@ +import { Locator, Page } from '@playwright/test'; + +export class FunctionsSection { + private readonly newFunctionButton: Locator; + private readonly functionNameInput: Locator; + private readonly functionDescriptionInput: Locator; + private readonly editorTab: Locator; + private readonly codeEditorField: Locator; + private readonly resetButton: Locator; + private readonly publishButton: Locator; + private readonly testButton: Locator; + private readonly testTab: Locator; + private readonly runFunctionButton: Locator; + private readonly inputField: Locator; + private readonly settingsTab: Locator; + private readonly searchVariableInput: Locator; + private readonly addVariableButton: Locator; + private readonly nameVariableInput: Locator; + private readonly valueVariableInput: Locator; + private readonly cancelVariableButton: Locator; + private readonly saveVariableButton: Locator; + private readonly editVariableButton: Locator; + private readonly deleteVariableButton: Locator; + private readonly cancelButton: Locator; + private readonly saveButton: Locator; + private readonly deleteButton: Locator; + + constructor(public readonly page: Page) { + this.newFunctionButton = page.getByRole('button', { name: 'New Function' }); + this.functionNameInput = page.getByPlaceholder('Name'); + this.functionDescriptionInput = page.getByPlaceholder('Description'); + this.editorTab = page.getByTestId('tab-editor'); + this.codeEditorField = page.getByTestId('dummyInput'); // TODO: fix + this.resetButton = page.getByRole('button', { name: 'Reset' }); + this.publishButton = page.getByRole('button', { name: 'Publish' }); + this.testButton = page.getByRole('button', { name: 'Test' }); + this.testTab = page.getByTestId('tab-test'); + this.runFunctionButton = page.getByRole('button', { name: 'Run Function' }); + this.inputField = page.getByTestId('dummyInput'); // TODO: fix + this.settingsTab = page.getByTestId('tab-settings'); + this.searchVariableInput = page.getByPlaceholder('Search a variable'); + this.addVariableButton = page.getByRole('button', { name: 'Add Variable' }); + this.nameVariableInput = page.getByPlaceholder('Name').nth(1); + this.valueVariableInput = page.getByPlaceholder('Value'); + this.cancelVariableButton = page.locator('.css-uwqduk').first(); // TODO: fix + this.saveVariableButton = page.locator('.css-uwqduk').nth(1); // TODO: fix + this.editVariableButton = page.getByText('Edit', { exact: true }); + this.deleteVariableButton = page.getByText('Delete', { exact: true }); + this.cancelButton = page.getByRole('button', { name: 'Cancel' }); + this.saveButton = page.getByRole('button', { name: 'Save' }); + this.deleteButton = page.getByRole('button', { name: 'Delete function' }); + } + + async clickNewFunction() { + await this.newFunctionButton.click(); + } + + async typeFunctionName(name: string) { + await this.functionNameInput.fill(name); + } + + async typeFunctionDescription(description: string) { + await this.functionDescriptionInput.fill(description); + } + + async checkFunctionDetails(name: string) { + await this.page.getByRole('link', { name: `${name} nodejs18.x` }).click(); + } + + async clickEditorTab() { + await this.editorTab.click(); + } + + async clickResetButton() { + await this.resetButton.click(); + } + + async clickPublishButton() { + await this.publishButton.click(); + } + + async clickTestButton() { + await this.testButton.click(); + } + + async typeFunctionCode() { + // TODO: finish once utils are merged + } + + async clickTestTab() { + await this.testTab.click(); + } + + async runFunction() { + await this.runFunctionButton.click(); + } + + async typeFunctionInput() { + // TODO: finish once utils are merged + } + + async clickSettingsTab() { + await this.settingsTab.click(); + } + + async searchVariable(name: string) { + await this.searchVariableInput.fill(name); + } + + async addVariable() { + await this.addVariableButton.click(); + } + + async typeVariableName(name: string) { + await this.nameVariableInput.fill(name); + } + + async typeVariableValue(value: string) { + await this.valueVariableInput.fill(value); + } + + async editVariable(name: string) { + await this.page + .locator( + `//div[@data-testid='tooltip' and contains(., '${name}')]/../../div[last()]/div/div/button`, + ) + .click(); + await this.editVariableButton.click(); + } + + async deleteVariable(name: string) { + await this.page + .locator( + `//div[@data-testid='tooltip' and contains(., '${name}')]/../../div[last()]/div/div/button`, + ) + .click(); + await this.deleteVariableButton.click(); + } + + async cancelVariable() { + await this.cancelVariableButton.click(); + } + + async saveVariable() { + await this.saveVariableButton.click(); + } + + async clickCancelButton() { + await this.cancelButton.click(); + } + + async clickSaveButton() { + await this.saveButton.click(); + } + + async clickDeleteButton() { + await this.deleteButton.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/generalSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/generalSection.ts new file mode 100644 index 000000000000..d936a2e9e196 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/generalSection.ts @@ -0,0 +1,29 @@ +import { Locator, Page } from '@playwright/test'; + +export class GeneralSection { + private readonly workspaceNameField: Locator; + private readonly supportSwitch: Locator; + private readonly deleteWorkspaceButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.workspaceNameField = page.getByPlaceholder('Apple'); + this.supportSwitch = page.getByRole('checkbox').nth(1); + this.deleteWorkspaceButton = page.getByRole('button', { + name: 'Delete workspace', + }); + } + + async changeWorkspaceName(workspaceName: string) { + await this.workspaceNameField.clear(); + await this.workspaceNameField.fill(workspaceName); + } + + async changeSupportSwitchState() { + await this.supportSwitch.click(); + } + + async clickDeleteWorkSpaceButton() { + await this.deleteWorkspaceButton.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/membersSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/membersSection.ts new file mode 100644 index 000000000000..4f1086657028 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/membersSection.ts @@ -0,0 +1,48 @@ +import { Locator, Page } from '@playwright/test'; + +export class MembersSection { + private readonly inviteMembersField: Locator; + private readonly inviteMembersButton: Locator; + private readonly inviteLinkButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.inviteMembersField = page.getByPlaceholder( + 'tim@apple.com, jony.ive@apple', + ); + this.inviteMembersButton = page.getByRole('button', { name: 'Invite' }); + this.inviteLinkButton = page.getByRole('button', { name: 'Copy link' }); + } + + async copyInviteLink() { + await this.inviteLinkButton.click(); + } + + async sendInviteEmail(email: string) { + await this.inviteMembersField.click(); + await this.inviteMembersField.fill(email); + await this.inviteMembersButton.click(); + } + + async deleteMember(email: string) { + await this.page + .locator(`//div[contains(., '${email}')]/../../div[last()]/div/button`) + .click(); + } + + async deleteInviteEmail(email: string) { + await this.page + .locator( + `//div[contains(., '${email}')]/../../div[last()]/div/button[first()]`, + ) + .click(); + } + + async refreshInviteEmail(email: string) { + await this.page + .locator( + `//div[contains(., '${email}')]/../../div[last()]/div/button[last()]`, + ) + .click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/newFieldSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/newFieldSection.ts new file mode 100644 index 000000000000..50b37e03ac5b --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/newFieldSection.ts @@ -0,0 +1,250 @@ +import { Locator, Page } from '@playwright/test'; + +export class NewFieldSection { + private readonly searchTypeFieldInput: Locator; + private readonly currencyFieldLink: Locator; + private readonly currencyDefaultUnitSelect: Locator; + private readonly emailsFieldLink: Locator; + private readonly linksFieldLink: Locator; + private readonly phonesFieldLink: Locator; + private readonly addressFieldLink: Locator; + private readonly textFieldLink: Locator; + private readonly numberFieldLink: Locator; + private readonly decreaseDecimalsButton: Locator; + private readonly decimalsNumberInput: Locator; + private readonly increaseDecimalsButton: Locator; + private readonly booleanFieldLink: Locator; + private readonly defaultBooleanSelect: Locator; + private readonly dateTimeFieldLink: Locator; + private readonly dateFieldLink: Locator; + private readonly relativeDateToggle: Locator; + private readonly selectFieldLink: Locator; + private readonly multiSelectFieldLink: Locator; + private readonly setAsDefaultOptionButton: Locator; + private readonly removeOptionButton: Locator; + private readonly addOptionButton: Locator; + private readonly ratingFieldLink: Locator; + private readonly JSONFieldLink: Locator; + private readonly arrayFieldLink: Locator; + private readonly relationFieldLink: Locator; + private readonly relationTypeSelect: Locator; + private readonly objectDestinationSelect: Locator; + private readonly relationFieldNameInput: Locator; + private readonly fullNameFieldLink: Locator; + private readonly UUIDFieldLink: Locator; + private readonly nameFieldInput: Locator; + private readonly descriptionFieldInput: Locator; + + constructor(public readonly page: Page) { + this.searchTypeFieldInput = page.getByPlaceholder('Search a type'); + this.currencyFieldLink = page.getByRole('link', { name: 'Currency' }); + this.currencyDefaultUnitSelect = page.locator( + "//span[contains(., 'Default Unit')]/../div", + ); + this.emailsFieldLink = page.getByRole('link', { name: 'Emails' }).nth(1); + this.linksFieldLink = page.getByRole('link', { name: 'Links' }); + this.phonesFieldLink = page.getByRole('link', { name: 'Phones' }); + this.addressFieldLink = page.getByRole('link', { name: 'Address' }); + this.textFieldLink = page.getByRole('link', { name: 'Text' }); + this.numberFieldLink = page.getByRole('link', { name: 'Number' }); + this.decreaseDecimalsButton = page.locator( + "//div[contains(., 'Number of decimals')]/../div[last()]/div/div/button[2]", + ); + this.decimalsNumberInput = page.locator( + // would be better if first div was span tag + "//div[contains(., 'Number of decimals')]/../div[last()]/div/div/div/div/input[2]", + ); + this.increaseDecimalsButton = page.locator( + "//div[contains(., 'Number of decimals')]/../div[last()]/div/div/button[3]", + ); + this.booleanFieldLink = page.getByRole('link', { name: 'True/False' }); + this.defaultBooleanSelect = page.locator( + "//span[contains(., 'Default Value')]/../div", + ); + this.dateTimeFieldLink = page.getByRole('link', { name: 'Date and Time' }); + this.dateFieldLink = page.getByRole('link', { name: 'Date' }); + this.relativeDateToggle = page.getByRole('checkbox').nth(1); + this.selectFieldLink = page.getByRole('link', { name: 'Select' }); + this.multiSelectFieldLink = page.getByRole('link', { + name: 'Multi-select', + }); + this.setAsDefaultOptionButton = page + .getByTestId('tooltip') + .getByText('Set as default'); + this.removeOptionButton = page + .getByTestId('tooltip') + .getByText('Remove option'); + this.addOptionButton = page.getByRole('button', { name: 'Add option' }); + this.ratingFieldLink = page.getByRole('link', { name: 'Rating' }); + this.JSONFieldLink = page.getByRole('link', { name: 'JSON' }); + this.arrayFieldLink = page.getByRole('link', { name: 'Array' }); + this.relationFieldLink = page.getByRole('link', { name: 'Relation' }); + this.relationTypeSelect = page.locator( + "//span[contains(., 'Relation type')]/../div", + ); + this.objectDestinationSelect = page.locator( + "//span[contains(., 'Object destination')]/../div", + ); + this.relationIconSelect = page.getByLabel('Click to select icon (').nth(1); + this.relationFieldNameInput = page.getByPlaceholder('Field name'); + this.fullNameFieldLink = page.getByRole('link', { name: 'Full Name' }); + this.UUIDFieldLink = page.getByRole('link', { name: 'Unique ID' }); + this.nameFieldInput = page.getByPlaceholder('Employees'); + this.descriptionFieldInput = page.getByPlaceholder('Write a description'); + } + + async searchTypeField(name: string) { + await this.searchTypeFieldInput.fill(name); + } + + async clickCurrencyType() { + await this.currencyFieldLink.click(); + } + + async selectDefaultUnit(name: string) { + await this.currencyDefaultUnitSelect.click(); + await this.page.getByTestId('tooltip').filter({ hasText: name }).click(); + } + + async clickEmailsType() { + await this.emailsFieldLink.click(); + } + + async clickLinksType() { + await this.linksFieldLink.click(); + } + + async clickPhonesType() { + await this.phonesFieldLink.click(); + } + + async clickAddressType() { + await this.addressFieldLink.click(); + } + + async clickTextType() { + await this.textFieldLink.click(); + } + + async clickNumberType() { + await this.numberFieldLink.click(); + } + + async decreaseDecimals() { + await this.decreaseDecimalsButton.click(); + } + + async typeNumberOfDecimals(amount: number) { + await this.decimalsNumberInput.fill(String(amount)); + } + + async increaseDecimals() { + await this.increaseDecimalsButton.click(); + } + + async clickBooleanType() { + await this.booleanFieldLink.click(); + } + + // either True of False + async selectDefaultBooleanValue(value: string) { + await this.defaultBooleanSelect.click(); + await this.page.getByTestId('tooltip').filter({ hasText: value }).click(); + } + + async clickDateTimeType() { + await this.dateTimeFieldLink.click(); + } + + async clickDateType() { + await this.dateFieldLink.click(); + } + + async toggleRelativeDate() { + await this.relativeDateToggle.click(); + } + + async clickSelectType() { + await this.selectFieldLink.click(); + } + + async clickMultiSelectType() { + await this.multiSelectFieldLink.click(); + } + + async addSelectOption() { + await this.addOptionButton.click(); + } + + async setOptionAsDefault() { + // TODO: finish + await this.setAsDefaultOptionButton.click(); + } + + async deleteSelectOption() { + // TODO: finish + await this.removeOptionButton.click(); + } + + async changeOptionAPIName() { + // TODO: finish + } + + async changeOptionColor() { + // TODO: finish + } + + async changeOptionName() { + // TODO: finish + } + + async clickRatingType() { + await this.ratingFieldLink.click(); + } + + async clickJSONType() { + await this.JSONFieldLink.click(); + } + + async clickArrayType() { + await this.arrayFieldLink.click(); + } + + async clickRelationType() { + await this.relationFieldLink.click(); + } + + // either 'Has many' or 'Belongs to one' + async selectRelationType(name: string) { + await this.relationTypeSelect.click(); + await this.page.getByTestId('tooltip').filter({ hasText: name }).click(); + } + + async selectObjectDestination(name: string) { + await this.objectDestinationSelect.click(); + await this.page.getByTestId('tooltip').filter({ hasText: name }).click(); + } + + async typeRelationName(name: string) { + await this.relationFieldNameInput.clear(); + await this.relationFieldNameInput.fill(name); + } + + async clickFullNameType() { + await this.fullNameFieldLink.click(); + } + + async clickUUIDType() { + await this.UUIDFieldLink.click(); + } + + async typeFieldName(name: string) { + await this.nameFieldInput.clear(); + await this.nameFieldInput.fill(name); + } + + async typeFieldDescription(description: string) { + await this.descriptionFieldInput.clear(); + await this.descriptionFieldInput.fill(description); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/profileSection.ts b/packages/twenty-e2e-testing/lib/pom/settings/profileSection.ts new file mode 100644 index 000000000000..32feb9ac586c --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/profileSection.ts @@ -0,0 +1,44 @@ +import { Locator, Page } from '@playwright/test'; + +export class ProfileSection { + private readonly firstNameField: Locator; + private readonly lastNameField: Locator; + private readonly emailField: Locator; + private readonly changePasswordButton: Locator; + private readonly deleteAccountButton: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.firstNameField = page.getByPlaceholder('Tim'); + this.lastNameField = page.getByPlaceholder('Cook'); + this.emailField = page.getByRole('textbox').nth(2); + this.changePasswordButton = page.getByRole('button', { + name: 'Change Password', + }); + this.deleteAccountButton = page.getByRole('button', { + name: 'Delete account', + }); + } + + async changeFirstName(firstName: string) { + await this.firstNameField.clear(); + await this.firstNameField.fill(firstName); + } + + async changeLastName(lastName: string) { + await this.lastNameField.clear(); + await this.lastNameField.fill(lastName); + } + + async getEmail() { + await this.emailField.textContent(); + } + + async sendChangePasswordEmail() { + await this.changePasswordButton.click(); + } + + async deleteAccount() { + await this.deleteAccountButton.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settings/securitySection.ts b/packages/twenty-e2e-testing/lib/pom/settings/securitySection.ts new file mode 100644 index 000000000000..01b83b515578 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settings/securitySection.ts @@ -0,0 +1,13 @@ +import { Locator, Page } from '@playwright/test'; + +export class SecuritySection { + private readonly inviteByLinkToggle: Locator; + + constructor(public readonly page: Page) { + this.inviteByLinkToggle = page.locator('input[type="checkbox"]').nth(1); + } + + async toggleInviteByLink() { + await this.inviteByLinkToggle.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/pom/settingsPage.ts b/packages/twenty-e2e-testing/lib/pom/settingsPage.ts new file mode 100644 index 000000000000..c753bb8d0d1e --- /dev/null +++ b/packages/twenty-e2e-testing/lib/pom/settingsPage.ts @@ -0,0 +1,104 @@ +import { Locator, Page } from '@playwright/test'; + +export class SettingsPage { + private readonly exitSettingsLink: Locator; + private readonly profileLink: Locator; + private readonly experienceLink: Locator; + private readonly accountsLink: Locator; + private readonly emailsLink: Locator; + private readonly calendarsLink: Locator; + private readonly generalLink: Locator; + private readonly membersLink: Locator; + private readonly dataModelLink: Locator; + private readonly developersLink: Locator; + private readonly functionsLink: Locator; + private readonly securityLink: Locator; + private readonly integrationsLink: Locator; + private readonly releasesLink: Locator; + private readonly logoutLink: Locator; + private readonly advancedToggle: Locator; + + constructor(public readonly page: Page) { + this.page = page; + this.exitSettingsLink = page.getByRole('button', { name: 'Exit Settings' }); + this.profileLink = page.getByRole('link', { name: 'Profile' }); + this.experienceLink = page.getByRole('link', { name: 'Experience' }); + this.accountsLink = page.getByRole('link', { name: 'Accounts' }); + this.emailsLink = page.getByRole('link', { name: 'Emails', exact: true }); + this.calendarsLink = page.getByRole('link', { name: 'Calendars' }); + this.generalLink = page.getByRole('link', { name: 'General' }); + this.membersLink = page.getByRole('link', { name: 'Members' }); + this.dataModelLink = page.getByRole('link', { name: 'Data model' }); + this.developersLink = page.getByRole('link', { name: 'Developers' }); + this.functionsLink = page.getByRole('link', { name: 'Functions' }); + this.integrationsLink = page.getByRole('link', { name: 'Integrations' }); + this.securityLink = page.getByRole('link', { name: 'Security' }); + this.releasesLink = page.getByRole('link', { name: 'Releases' }); + this.logoutLink = page.getByText('Logout'); + this.advancedToggle = page.locator('input[type="checkbox"]').first(); + } + + async leaveSettingsPage() { + await this.exitSettingsLink.click(); + } + + async goToProfileSection() { + await this.profileLink.click(); + } + + async goToExperienceSection() { + await this.experienceLink.click(); + } + + async goToAccountsSection() { + await this.accountsLink.click(); + } + + async goToEmailsSection() { + await this.emailsLink.click(); + } + + async goToCalendarsSection() { + await this.calendarsLink.click(); + } + + async goToGeneralSection() { + await this.generalLink.click(); + } + + async goToMembersSection() { + await this.membersLink.click(); + } + + async goToDataModelSection() { + await this.dataModelLink.click(); + } + + async goToDevelopersSection() { + await this.developersLink.click(); + } + + async goToFunctionsSection() { + await this.functionsLink.click(); + } + + async goToSecuritySection() { + await this.securityLink.click(); + } + + async goToIntegrationsSection() { + await this.integrationsLink.click(); + } + + async goToReleasesIntegration() { + await this.releasesLink.click(); + } + + async logout() { + await this.logoutLink.click(); + } + + async toggleAdvancedSettings() { + await this.advancedToggle.click(); + } +} diff --git a/packages/twenty-e2e-testing/lib/utils/keyboardShortcuts.ts b/packages/twenty-e2e-testing/lib/utils/keyboardShortcuts.ts new file mode 100644 index 000000000000..470e63c1a5d8 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/utils/keyboardShortcuts.ts @@ -0,0 +1,94 @@ +import { Page } from '@playwright/test'; + +const MAC = process.platform === 'darwin'; + +async function keyDownCtrlOrMeta(page: Page) { + if (MAC) { + await page.keyboard.down('Meta'); + } else { + await page.keyboard.down('Control'); + } +} + +async function keyUpCtrlOrMeta(page: Page) { + if (MAC) { + await page.keyboard.up('Meta'); + } else { + await page.keyboard.up('Control'); + } +} + +export async function withCtrlOrMeta(page: Page, key: () => Promise) { + await keyDownCtrlOrMeta(page); + await key(); + await keyUpCtrlOrMeta(page); +} + +export async function selectAllByKeyboard(page: Page) { + await keyDownCtrlOrMeta(page); + await page.keyboard.press('a', { delay: 50 }); + await keyUpCtrlOrMeta(page); +} + +export async function copyByKeyboard(page: Page) { + await keyDownCtrlOrMeta(page); + await page.keyboard.press('c', { delay: 50 }); + await keyUpCtrlOrMeta(page); +} + +export async function cutByKeyboard(page: Page) { + await keyDownCtrlOrMeta(page); + await page.keyboard.press('x', { delay: 50 }); + await keyUpCtrlOrMeta(page); +} + +export async function pasteByKeyboard(page: Page) { + await keyDownCtrlOrMeta(page); + await page.keyboard.press('v', { delay: 50 }); + await keyUpCtrlOrMeta(page); +} + +export async function companiesShortcut(page: Page) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press('c'); +} + +export async function notesShortcut(page: Page) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press('n'); +} + +export async function opportunitiesShortcut(page: Page) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press('o'); +} + +export async function peopleShortcut(page: Page) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press('p'); +} + +export async function rocketsShortcut(page: Page) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press('r'); +} + +export async function tasksShortcut(page: Page) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press('t'); +} + +export async function workflowsShortcut(page: Page) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press('w'); +} + +export async function settingsShortcut(page: Page) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press('s'); +} + +export async function customShortcut(page: Page, shortcut: string) { + await page.keyboard.press('g', { delay: 50 }); + await page.keyboard.press(shortcut); +} diff --git a/packages/twenty-e2e-testing/lib/utils/pasteCodeToCodeEditor.ts b/packages/twenty-e2e-testing/lib/utils/pasteCodeToCodeEditor.ts new file mode 100644 index 000000000000..f67defef9047 --- /dev/null +++ b/packages/twenty-e2e-testing/lib/utils/pasteCodeToCodeEditor.ts @@ -0,0 +1,14 @@ +import { Locator, Page } from '@playwright/test'; +import { selectAllByKeyboard } from './keyboardShortcuts'; + +// https://github.com/microsoft/playwright/issues/14126 +// code must have \n at the end of lines otherwise everything will be in one line +export const pasteCodeToCodeEditor = async ( + page: Page, + locator: Locator, + code: string, +) => { + await locator.click(); + await selectAllByKeyboard(page); + await page.keyboard.type(code); +}; diff --git a/packages/twenty-e2e-testing/lib/utils/uploadFile.ts b/packages/twenty-e2e-testing/lib/utils/uploadFile.ts new file mode 100644 index 000000000000..81898bc2ba7d --- /dev/null +++ b/packages/twenty-e2e-testing/lib/utils/uploadFile.ts @@ -0,0 +1,15 @@ +import { Page } from '@playwright/test'; +import path from 'path'; + +export const fileUploader = async ( + page: Page, + trigger: () => Promise, + filename: string, +) => { + const fileChooserPromise = page.waitForEvent('filechooser'); + await trigger(); + const fileChooser = await fileChooserPromise; + await fileChooser.setFiles( + path.join(__dirname, '..', 'test_files', filename), + ); +}; diff --git a/packages/twenty-e2e-testing/tests/companies.spec.ts b/packages/twenty-e2e-testing/tests/companies.spec.ts index b8f78c7ecad7..1aa53d61322e 100644 --- a/packages/twenty-e2e-testing/tests/companies.spec.ts +++ b/packages/twenty-e2e-testing/tests/companies.spec.ts @@ -1,7 +1,4 @@ import { test, expect } from '../lib/fixtures/screenshot'; -import { config } from 'dotenv'; -import path = require('path'); -config({ path: path.resolve(__dirname, '..', '.env') }); test.describe('Basic check', () => { test('Checking if table in Companies is visible', async ({ page }) => { diff --git a/packages/twenty-emails/package.json b/packages/twenty-emails/package.json index 02e7b81936ef..45a1068a96a9 100644 --- a/packages/twenty-emails/package.json +++ b/packages/twenty-emails/package.json @@ -1,6 +1,6 @@ { "name": "twenty-emails", - "version": "0.32.0", + "version": "0.33.0-canaray", "description": "", "author": "", "private": true, diff --git a/packages/twenty-front/.env.example b/packages/twenty-front/.env.example index 3fccb201c4b9..345d0fb92ad7 100644 --- a/packages/twenty-front/.env.example +++ b/packages/twenty-front/.env.example @@ -2,6 +2,7 @@ REACT_APP_SERVER_BASE_URL=http://localhost:3000 GENERATE_SOURCEMAP=false # ———————— Optional ———————— +# REACT_APP_PORT=3001 # CHROMATIC_PROJECT_TOKEN= # VITE_DISABLE_TYPESCRIPT_CHECKER=true # VITE_DISABLE_ESLINT_CHECKER=true \ No newline at end of file diff --git a/packages/twenty-front/package.json b/packages/twenty-front/package.json index d1a9d196a627..2d3b0086e398 100644 --- a/packages/twenty-front/package.json +++ b/packages/twenty-front/package.json @@ -1,6 +1,6 @@ { "name": "twenty-front", - "version": "0.32.0", + "version": "0.33.0-canaray", "private": true, "type": "module", "scripts": { diff --git a/packages/twenty-front/src/generated-metadata/gql.ts b/packages/twenty-front/src/generated-metadata/gql.ts index 54153628a72b..5d8e10a5ec05 100644 --- a/packages/twenty-front/src/generated-metadata/gql.ts +++ b/packages/twenty-front/src/generated-metadata/gql.ts @@ -33,7 +33,7 @@ const documents = { "\n mutation DeleteOneFieldMetadataItem($idToDelete: UUID!) {\n deleteOneField(input: { id: $idToDelete }) {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n settings\n }\n }\n": types.DeleteOneFieldMetadataItemDocument, "\n mutation DeleteOneRelationMetadataItem($idToDelete: UUID!) {\n deleteOneRelation(input: { id: $idToDelete }) {\n id\n }\n }\n": types.DeleteOneRelationMetadataItemDocument, "\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isRemote\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n shortcut\n isLabelSyncedWithName\n indexMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n name\n indexWhereClause\n indexType\n isUnique\n indexFieldMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n order\n fieldMetadataId\n }\n }\n }\n }\n }\n }\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n isUnique\n createdAt\n updatedAt\n defaultValue\n options\n settings\n relationDefinition {\n relationId\n direction\n sourceObjectMetadata {\n id\n nameSingular\n namePlural\n }\n sourceFieldMetadata {\n id\n name\n }\n targetObjectMetadata {\n id\n nameSingular\n namePlural\n }\n targetFieldMetadata {\n id\n name\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n": types.ObjectMetadataItemsDocument, - "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n latestVersionInputSchema {\n name\n type\n }\n publishedVersions\n createdAt\n updatedAt\n }\n": types.ServerlessFunctionFieldsFragmentDoc, + "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n latestVersionInputSchema\n publishedVersions\n createdAt\n updatedAt\n }\n": types.ServerlessFunctionFieldsFragmentDoc, "\n \n mutation CreateOneServerlessFunctionItem(\n $input: CreateServerlessFunctionInput!\n ) {\n createOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.CreateOneServerlessFunctionItemDocument, "\n \n mutation DeleteOneServerlessFunction($input: ServerlessFunctionIdInput!) {\n deleteOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.DeleteOneServerlessFunctionDocument, "\n mutation ExecuteOneServerlessFunction(\n $input: ExecuteServerlessFunctionInput!\n ) {\n executeOneServerlessFunction(input: $input) {\n data\n duration\n status\n error\n }\n }\n": types.ExecuteOneServerlessFunctionDocument, @@ -142,7 +142,7 @@ export function graphql(source: "\n query ObjectMetadataItems(\n $objectFilt /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n latestVersionInputSchema {\n name\n type\n }\n publishedVersions\n createdAt\n updatedAt\n }\n"): (typeof documents)["\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n latestVersionInputSchema {\n name\n type\n }\n publishedVersions\n createdAt\n updatedAt\n }\n"]; +export function graphql(source: "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n latestVersionInputSchema\n publishedVersions\n createdAt\n updatedAt\n }\n"): (typeof documents)["\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n latestVersionInputSchema\n publishedVersions\n createdAt\n updatedAt\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 04eb88d31957..b26b39ddc122 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -38,6 +38,16 @@ export type Analytics = { success: Scalars['Boolean']['output']; }; +export type AnalyticsTinybirdJwtMap = { + __typename?: 'AnalyticsTinybirdJwtMap'; + getPageviewsAnalytics: Scalars['String']['output']; + getServerlessFunctionDuration: Scalars['String']['output']; + getServerlessFunctionErrorCount: Scalars['String']['output']; + getServerlessFunctionSuccessRate: Scalars['String']['output']; + getUsersAnalytics: Scalars['String']['output']; + getWebhookAnalytics: Scalars['String']['output']; +}; + export type ApiConfig = { __typename?: 'ApiConfig'; mutationMaximumAffectedRecords: Scalars['Float']['output']; @@ -426,12 +436,6 @@ export type FullName = { lastName: Scalars['String']['output']; }; -export type FunctionParameter = { - __typename?: 'FunctionParameter'; - name: Scalars['String']['output']; - type: Scalars['String']['output']; -}; - export type GenerateJwt = GenerateJwtOutputWithAuthTokens | GenerateJwtOutputWithSsoauth; export type GenerateJwtOutputWithAuthTokens = { @@ -1233,7 +1237,7 @@ export type ServerlessFunction = { description?: Maybe; id: Scalars['UUID']['output']; latestVersion?: Maybe; - latestVersionInputSchema?: Maybe>; + latestVersionInputSchema?: Maybe; name: Scalars['String']['output']; publishedVersions: Array; runtime: Scalars['String']['output']; @@ -1497,7 +1501,7 @@ export type UpdateWorkspaceInput = { export type User = { __typename?: 'User'; - analyticsTinybirdJwt?: Maybe; + analyticsTinybirdJwts?: Maybe; canImpersonate: Scalars['Boolean']['output']; createdAt: Scalars['DateTime']['output']; defaultAvatarUrl?: Maybe; @@ -1987,21 +1991,21 @@ export type ObjectMetadataItemsQueryVariables = Exact<{ export type ObjectMetadataItemsQuery = { __typename?: 'Query', objects: { __typename?: 'ObjectConnection', edges: Array<{ __typename?: 'objectEdge', node: { __typename?: 'object', id: any, dataSourceId: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isRemote: boolean, isActive: boolean, isSystem: boolean, createdAt: any, updatedAt: any, labelIdentifierFieldMetadataId?: string | null, imageIdentifierFieldMetadataId?: string | null, shortcut?: string | null, isLabelSyncedWithName: boolean, indexMetadatas: { __typename?: 'ObjectIndexMetadatasConnection', edges: Array<{ __typename?: 'indexEdge', node: { __typename?: 'index', id: any, createdAt: any, updatedAt: any, name: string, indexWhereClause?: string | null, indexType: IndexType, isUnique: boolean, indexFieldMetadatas: { __typename?: 'IndexIndexFieldMetadatasConnection', edges: Array<{ __typename?: 'indexFieldEdge', node: { __typename?: 'indexField', id: any, createdAt: any, updatedAt: any, order: number, fieldMetadataId: any } }> } } }> }, fields: { __typename?: 'ObjectFieldsConnection', edges: Array<{ __typename?: 'fieldEdge', node: { __typename?: 'field', id: any, type: FieldMetadataType, name: string, label: string, description?: string | null, icon?: string | null, isCustom?: boolean | null, isActive?: boolean | null, isSystem?: boolean | null, isNullable?: boolean | null, isUnique?: boolean | null, createdAt: any, updatedAt: any, defaultValue?: any | null, options?: any | null, settings?: any | null, relationDefinition?: { __typename?: 'RelationDefinition', relationId: any, direction: RelationDefinitionType, sourceObjectMetadata: { __typename?: 'object', id: any, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'field', id: any, name: string }, targetObjectMetadata: { __typename?: 'object', id: any, nameSingular: string, namePlural: string }, targetFieldMetadata: { __typename?: 'field', id: any, name: string } } | null } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } }; -export type ServerlessFunctionFieldsFragment = { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, publishedVersions: Array, createdAt: any, updatedAt: any, latestVersionInputSchema?: Array<{ __typename?: 'FunctionParameter', name: string, type: string }> | null }; +export type ServerlessFunctionFieldsFragment = { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any }; export type CreateOneServerlessFunctionItemMutationVariables = Exact<{ input: CreateServerlessFunctionInput; }>; -export type CreateOneServerlessFunctionItemMutation = { __typename?: 'Mutation', createOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, publishedVersions: Array, createdAt: any, updatedAt: any, latestVersionInputSchema?: Array<{ __typename?: 'FunctionParameter', name: string, type: string }> | null } }; +export type CreateOneServerlessFunctionItemMutation = { __typename?: 'Mutation', createOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; export type DeleteOneServerlessFunctionMutationVariables = Exact<{ input: ServerlessFunctionIdInput; }>; -export type DeleteOneServerlessFunctionMutation = { __typename?: 'Mutation', deleteOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, publishedVersions: Array, createdAt: any, updatedAt: any, latestVersionInputSchema?: Array<{ __typename?: 'FunctionParameter', name: string, type: string }> | null } }; +export type DeleteOneServerlessFunctionMutation = { __typename?: 'Mutation', deleteOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; export type ExecuteOneServerlessFunctionMutationVariables = Exact<{ input: ExecuteServerlessFunctionInput; @@ -2015,14 +2019,14 @@ export type PublishOneServerlessFunctionMutationVariables = Exact<{ }>; -export type PublishOneServerlessFunctionMutation = { __typename?: 'Mutation', publishServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, publishedVersions: Array, createdAt: any, updatedAt: any, latestVersionInputSchema?: Array<{ __typename?: 'FunctionParameter', name: string, type: string }> | null } }; +export type PublishOneServerlessFunctionMutation = { __typename?: 'Mutation', publishServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; export type UpdateOneServerlessFunctionMutationVariables = Exact<{ input: UpdateServerlessFunctionInput; }>; -export type UpdateOneServerlessFunctionMutation = { __typename?: 'Mutation', updateOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, publishedVersions: Array, createdAt: any, updatedAt: any, latestVersionInputSchema?: Array<{ __typename?: 'FunctionParameter', name: string, type: string }> | null } }; +export type UpdateOneServerlessFunctionMutation = { __typename?: 'Mutation', updateOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; export type FindManyAvailablePackagesQueryVariables = Exact<{ [key: string]: never; }>; @@ -2032,14 +2036,14 @@ export type FindManyAvailablePackagesQuery = { __typename?: 'Query', getAvailabl export type GetManyServerlessFunctionsQueryVariables = Exact<{ [key: string]: never; }>; -export type GetManyServerlessFunctionsQuery = { __typename?: 'Query', findManyServerlessFunctions: Array<{ __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, publishedVersions: Array, createdAt: any, updatedAt: any, latestVersionInputSchema?: Array<{ __typename?: 'FunctionParameter', name: string, type: string }> | null }> }; +export type GetManyServerlessFunctionsQuery = { __typename?: 'Query', findManyServerlessFunctions: Array<{ __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any }> }; export type GetOneServerlessFunctionQueryVariables = Exact<{ input: ServerlessFunctionIdInput; }>; -export type GetOneServerlessFunctionQuery = { __typename?: 'Query', findOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, publishedVersions: Array, createdAt: any, updatedAt: any, latestVersionInputSchema?: Array<{ __typename?: 'FunctionParameter', name: string, type: string }> | null } }; +export type GetOneServerlessFunctionQuery = { __typename?: 'Query', findOneServerlessFunction: { __typename?: 'ServerlessFunction', id: any, name: string, description?: string | null, runtime: string, syncStatus: ServerlessFunctionSyncStatus, latestVersion?: string | null, latestVersionInputSchema?: any | null, publishedVersions: Array, createdAt: any, updatedAt: any } }; export type FindOneServerlessFunctionSourceCodeQueryVariables = Exact<{ input: GetServerlessFunctionSourceCodeInput; @@ -2050,7 +2054,7 @@ export type FindOneServerlessFunctionSourceCodeQuery = { __typename?: 'Query', g export const RemoteServerFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteServerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperId"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperOptions"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperType"}},{"kind":"Field","name":{"kind":"Name","value":"userMappingOptions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"}}]}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}}]} as unknown as DocumentNode; export const RemoteTableFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteTableFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTable"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"schemaPendingUpdates"}}]}}]} as unknown as DocumentNode; -export const ServerlessFunctionFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const ServerlessFunctionFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const CreateServerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"createServer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateRemoteServerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneRemoteServer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteServerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteServerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperId"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperOptions"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperType"}},{"kind":"Field","name":{"kind":"Name","value":"userMappingOptions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"}}]}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"label"}}]}}]} as unknown as DocumentNode; export const DeleteServerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"deleteServer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServerIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneRemoteServer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; export const SyncRemoteTableDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"syncRemoteTable"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTableInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"syncRemoteTable"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteTableFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteTableFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTable"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"schemaPendingUpdates"}}]}}]} as unknown as DocumentNode; @@ -2069,12 +2073,12 @@ export const DeleteOneObjectMetadataItemDocument = {"kind":"Document","definitio export const DeleteOneFieldMetadataItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneFieldMetadataItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneField"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"settings"}}]}}]}}]} as unknown as DocumentNode; export const DeleteOneRelationMetadataItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneRelationMetadataItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneRelation"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; export const ObjectMetadataItemsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ObjectMetadataItems"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"objectFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"objectFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"fieldFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"fieldFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"objects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"objectFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isRemote"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"labelIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"imageIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"shortcut"}},{"kind":"Field","name":{"kind":"Name","value":"isLabelSyncedWithName"}},{"kind":"Field","name":{"kind":"Name","value":"indexMetadatas"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"indexWhereClause"}},{"kind":"Field","name":{"kind":"Name","value":"indexType"}},{"kind":"Field","name":{"kind":"Name","value":"isUnique"}},{"kind":"Field","name":{"kind":"Name","value":"indexFieldMetadatas"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"100"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"fieldMetadataId"}}]}}]}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"fields"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"paging"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"first"},"value":{"kind":"IntValue","value":"1000"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"fieldFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isSystem"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"isUnique"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"defaultValue"}},{"kind":"Field","name":{"kind":"Name","value":"options"}},{"kind":"Field","name":{"kind":"Name","value":"settings"}},{"kind":"Field","name":{"kind":"Name","value":"relationDefinition"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"relationId"}},{"kind":"Field","name":{"kind":"Name","value":"direction"}},{"kind":"Field","name":{"kind":"Name","value":"sourceObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}}]}},{"kind":"Field","name":{"kind":"Name","value":"sourceFieldMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"targetObjectMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}}]}},{"kind":"Field","name":{"kind":"Name","value":"targetFieldMetadata"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]} as unknown as DocumentNode; -export const CreateOneServerlessFunctionItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOneServerlessFunctionItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; -export const DeleteOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunctionIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const CreateOneServerlessFunctionItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOneServerlessFunctionItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const DeleteOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunctionIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const ExecuteOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ExecuteOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ExecuteServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"executeOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"data"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"error"}}]}}]}}]} as unknown as DocumentNode; -export const PublishOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"PublishOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PublishServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"publishServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; -export const UpdateOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const PublishOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"PublishOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PublishServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"publishServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const UpdateOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateServerlessFunctionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const FindManyAvailablePackagesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindManyAvailablePackages"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getAvailablePackages"}}]}}]} as unknown as DocumentNode; -export const GetManyServerlessFunctionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetManyServerlessFunctions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findManyServerlessFunctions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; -export const GetOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunctionIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const GetManyServerlessFunctionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetManyServerlessFunctions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findManyServerlessFunctions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; +export const GetOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunctionIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode; export const FindOneServerlessFunctionSourceCodeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindOneServerlessFunctionSourceCode"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GetServerlessFunctionSourceCodeInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getServerlessFunctionSourceCode"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 766a07067bab..de35163609bc 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -31,6 +31,16 @@ export type Analytics = { success: Scalars['Boolean']; }; +export type AnalyticsTinybirdJwtMap = { + __typename?: 'AnalyticsTinybirdJwtMap'; + getPageviewsAnalytics: Scalars['String']; + getServerlessFunctionDuration: Scalars['String']; + getServerlessFunctionErrorCount: Scalars['String']; + getServerlessFunctionSuccessRate: Scalars['String']; + getUsersAnalytics: Scalars['String']; + getWebhookAnalytics: Scalars['String']; +}; + export type ApiConfig = { __typename?: 'ApiConfig'; mutationMaximumAffectedRecords: Scalars['Float']; @@ -321,12 +331,6 @@ export type FullName = { lastName: Scalars['String']; }; -export type FunctionParameter = { - __typename?: 'FunctionParameter'; - name: Scalars['String']; - type: Scalars['String']; -}; - export type GenerateJwt = GenerateJwtOutputWithAuthTokens | GenerateJwtOutputWithSsoauth; export type GenerateJwtOutputWithAuthTokens = { @@ -973,7 +977,7 @@ export type ServerlessFunction = { description?: Maybe; id: Scalars['UUID']; latestVersion?: Maybe; - latestVersionInputSchema?: Maybe>; + latestVersionInputSchema?: Maybe; name: Scalars['String']; publishedVersions: Array; runtime: Scalars['String']; @@ -1207,7 +1211,7 @@ export type UpdateWorkspaceInput = { export type User = { __typename?: 'User'; - analyticsTinybirdJwt?: Maybe; + analyticsTinybirdJwts?: Maybe; canImpersonate: Scalars['Boolean']; createdAt: Scalars['DateTime']; defaultAvatarUrl?: Maybe; @@ -1696,7 +1700,7 @@ export type ImpersonateMutationVariables = Exact<{ }>; -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, hasValidEntrepriseKey: 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 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, analyticsTinybirdJwts?: { __typename?: 'AnalyticsTinybirdJwtMap', getWebhookAnalytics: string, getPageviewsAnalytics: string, getUsersAnalytics: string, getServerlessFunctionDuration: string, getServerlessFunctionSuccessRate: string, getServerlessFunctionErrorCount: string } | null, 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, hasValidEntrepriseKey: 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']; @@ -1729,7 +1733,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, 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, hasValidEntrepriseKey: 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 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, analyticsTinybirdJwts?: { __typename?: 'AnalyticsTinybirdJwtMap', getWebhookAnalytics: string, getPageviewsAnalytics: string, getUsersAnalytics: string, getServerlessFunctionDuration: string, getServerlessFunctionSuccessRate: string, getServerlessFunctionErrorCount: string } | null, 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, hasValidEntrepriseKey: 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']; @@ -1816,7 +1820,7 @@ export type ListSsoIdentityProvidersByWorkspaceIdQueryVariables = Exact<{ [key: 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, hasValidEntrepriseKey: 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 UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, analyticsTinybirdJwts?: { __typename?: 'AnalyticsTinybirdJwtMap', getWebhookAnalytics: string, getPageviewsAnalytics: string, getUsersAnalytics: string, getServerlessFunctionDuration: string, getServerlessFunctionSuccessRate: string, getServerlessFunctionErrorCount: string } | null, 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, hasValidEntrepriseKey: 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; }>; @@ -1833,7 +1837,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, 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, hasValidEntrepriseKey: 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 GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, analyticsTinybirdJwts?: { __typename?: 'AnalyticsTinybirdJwtMap', getWebhookAnalytics: string, getPageviewsAnalytics: string, getUsersAnalytics: string, getServerlessFunctionDuration: string, getServerlessFunctionSuccessRate: string, getServerlessFunctionErrorCount: string } | null, 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, hasValidEntrepriseKey: 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']; @@ -2060,7 +2064,14 @@ export const UserQueryFragmentFragmentDoc = gql` email canImpersonate supportUserHash - analyticsTinybirdJwt + analyticsTinybirdJwts { + getWebhookAnalytics + getPageviewsAnalytics + getUsersAnalytics + getServerlessFunctionDuration + getServerlessFunctionSuccessRate + getServerlessFunctionErrorCount + } onboardingStatus workspaceMember { ...WorkspaceMemberQueryFragment diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/DeleteRecordsActionEffect.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/DeleteRecordsActionEffect.tsx index b4c7e585c17f..515852428a20 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/DeleteRecordsActionEffect.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/DeleteRecordsActionEffect.tsx @@ -1,5 +1,6 @@ import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries'; +import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState'; import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters'; @@ -45,8 +46,13 @@ export const DeleteRecordsActionEffect = ({ contextStoreTargetedRecordsRuleComponentState, ); + const contextStoreFilters = useRecoilComponentValueV2( + contextStoreFiltersComponentState, + ); + const graphqlFilter = computeContextStoreFilters( contextStoreTargetedRecordsRule, + contextStoreFilters, objectMetadataItem, ); diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter.tsx index 2bcc8400d259..7d64ec72c0de 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter.tsx @@ -8,14 +8,17 @@ import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMeta import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; -const globalRecordActionEffects = [ExportRecordsActionEffect]; +const noSelectionRecordActionEffects = [ExportRecordsActionEffect]; const singleRecordActionEffects = [ ManageFavoritesActionEffect, DeleteRecordsActionEffect, ]; -const multipleRecordActionEffects = [DeleteRecordsActionEffect]; +const multipleRecordActionEffects = [ + ExportRecordsActionEffect, + DeleteRecordsActionEffect, +]; export const RecordActionMenuEntriesSetter = () => { const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2( @@ -39,23 +42,18 @@ export const RecordActionMenuEntriesSetter = () => { } const actions = - contextStoreNumberOfSelectedRecords === 1 - ? singleRecordActionEffects - : multipleRecordActionEffects; + contextStoreNumberOfSelectedRecords === 0 + ? noSelectionRecordActionEffects + : contextStoreNumberOfSelectedRecords === 1 + ? singleRecordActionEffects + : multipleRecordActionEffects; return ( <> - {globalRecordActionEffects.map((ActionEffect, index) => ( - - ))} {actions.map((ActionEffect, index) => ( ))} diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx index 7bd2b0898531..1a62cab35e7d 100644 --- a/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/RecordIndexActionMenuDropdown.tsx @@ -10,10 +10,10 @@ import { ActionMenuDropdownHotkeyScope } from '@/action-menu/types/ActionMenuDro import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId'; 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'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; +import { MenuItem } from 'twenty-ui'; type StyledContainerProps = { position: PositionType; diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordShowActionMenuBarEntry.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordShowActionMenuBarEntry.tsx deleted file mode 100644 index debc175b7c6d..000000000000 --- a/packages/twenty-front/src/modules/action-menu/components/RecordShowActionMenuBarEntry.tsx +++ /dev/null @@ -1,53 +0,0 @@ -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 ( - entry.onClick?.()} - > - {entry.Icon && } - {entry.label} - - ); -}; diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordShowRightDrawerActionMenu.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordShowRightDrawerActionMenu.tsx index 510104926c90..ba964f27a8d8 100644 --- a/packages/twenty-front/src/modules/action-menu/components/RecordShowRightDrawerActionMenu.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/RecordShowRightDrawerActionMenu.tsx @@ -1,7 +1,7 @@ import { GlobalActionMenuEntriesSetter } from '@/action-menu/actions/global-actions/components/GlobalActionMenuEntriesSetter'; import { RecordActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter'; import { ActionMenuConfirmationModals } from '@/action-menu/components/ActionMenuConfirmationModals'; -import { RecordShowRightDrawerActionMenuBar } from '@/action-menu/components/RecordShowRightDrawerActionMenuBar'; +import { RightDrawerActionMenuDropdown } from '@/action-menu/components/RightDrawerActionMenuDropdown'; import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState'; @@ -21,7 +21,7 @@ export const RecordShowRightDrawerActionMenu = () => { onActionExecutedCallback: () => {}, }} > - + diff --git a/packages/twenty-front/src/modules/action-menu/components/RecordShowRightDrawerActionMenuBar.tsx b/packages/twenty-front/src/modules/action-menu/components/RecordShowRightDrawerActionMenuBar.tsx deleted file mode 100644 index 8f9540e4d623..000000000000 --- a/packages/twenty-front/src/modules/action-menu/components/RecordShowRightDrawerActionMenuBar.tsx +++ /dev/null @@ -1,21 +0,0 @@ -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 RecordShowRightDrawerActionMenuBar = () => { - const actionMenuEntries = useRecoilComponentValueV2( - actionMenuEntriesComponentSelector, - ); - - const standardActionMenuEntries = actionMenuEntries.filter( - (actionMenuEntry) => actionMenuEntry.type === 'standard', - ); - - return ( - <> - {standardActionMenuEntries.map((actionMenuEntry) => ( - - ))} - - ); -}; diff --git a/packages/twenty-front/src/modules/action-menu/components/RightDrawerActionMenuDropdown.tsx b/packages/twenty-front/src/modules/action-menu/components/RightDrawerActionMenuDropdown.tsx new file mode 100644 index 000000000000..ed3c1ee2277f --- /dev/null +++ b/packages/twenty-front/src/modules/action-menu/components/RightDrawerActionMenuDropdown.tsx @@ -0,0 +1,87 @@ +import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector'; +import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; +import { RightDrawerActionMenuDropdownHotkeyScope } from '@/action-menu/types/RightDrawerActionMenuDropdownHotkeyScope'; +import { getRightDrawerActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getRightDrawerActionMenuDropdownIdFromActionMenuId'; +import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { useDropdownV2 } from '@/ui/layout/dropdown/hooks/useDropdownV2'; +import { RightDrawerHotkeyScope } from '@/ui/layout/right-drawer/types/RightDrawerHotkeyScope'; +import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useTheme } from '@emotion/react'; +import { Key } from 'ts-key-enum'; +import { Button, MenuItem } from 'twenty-ui'; + +export const RightDrawerActionMenuDropdown = () => { + const actionMenuEntries = useRecoilComponentValueV2( + actionMenuEntriesComponentSelector, + ); + + const actionMenuId = useAvailableComponentInstanceIdOrThrow( + ActionMenuComponentInstanceContext, + ); + + const { closeDropdown, openDropdown } = useDropdownV2(); + + const theme = useTheme(); + + useScopedHotkeys( + [Key.Escape, 'ctrl+o,meta+o'], + () => { + closeDropdown( + getRightDrawerActionMenuDropdownIdFromActionMenuId(actionMenuId), + ); + }, + RightDrawerActionMenuDropdownHotkeyScope.RightDrawerActionMenuDropdown, + [closeDropdown], + ); + + useScopedHotkeys( + ['ctrl+o,meta+o'], + () => { + openDropdown( + getRightDrawerActionMenuDropdownIdFromActionMenuId(actionMenuId), + ); + }, + RightDrawerHotkeyScope.RightDrawer, + [openDropdown], + ); + + return ( + } + dropdownPlacement="top-end" + dropdownOffset={{ + y: parseInt(theme.spacing(2)), + }} + dropdownComponents={ + + {actionMenuEntries.map((item, index) => ( + { + closeDropdown( + getRightDrawerActionMenuDropdownIdFromActionMenuId( + actionMenuId, + ), + ); + item.onClick?.(); + }} + text={item.label} + /> + ))} + + } + /> + ); +}; diff --git a/packages/twenty-front/src/modules/action-menu/components/__stories__/RecordShowActionMenuBar.stories.tsx b/packages/twenty-front/src/modules/action-menu/components/__stories__/RecordShowActionMenuBar.stories.tsx index f2391b558b07..4a86046bd508 100644 --- a/packages/twenty-front/src/modules/action-menu/components/__stories__/RecordShowActionMenuBar.stories.tsx +++ b/packages/twenty-front/src/modules/action-menu/components/__stories__/RecordShowActionMenuBar.stories.tsx @@ -2,28 +2,28 @@ import { expect, jest } from '@storybook/jest'; import { Meta, StoryObj } from '@storybook/react'; import { RecoilRoot } from 'recoil'; -import { RecordShowRightDrawerActionMenuBar } from '@/action-menu/components/RecordShowRightDrawerActionMenuBar'; +import { RightDrawerActionMenuDropdown } from '@/action-menu/components/RightDrawerActionMenuDropdown'; import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState'; import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry'; 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, + MenuItemAccent, } from 'twenty-ui'; const deleteMock = jest.fn(); const addToFavoritesMock = jest.fn(); const exportMock = jest.fn(); -const meta: Meta = { - title: 'Modules/ActionMenu/RecordShowRightDrawerActionMenuBar', - component: RecordShowRightDrawerActionMenuBar, +const meta: Meta = { + title: 'Modules/ActionMenu/RightDrawerActionMenuDropdown', + component: RightDrawerActionMenuDropdown, decorators: [ (Story) => ( = { export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Default: Story = { args: { @@ -113,12 +113,21 @@ export const WithButtonClicks: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); + let actionButton = await canvas.findByText('Actions'); + await userEvent.click(actionButton); + const deleteButton = await canvas.findByText('Delete'); await userEvent.click(deleteButton); + actionButton = await canvas.findByText('Actions'); + await userEvent.click(actionButton); + const addToFavoritesButton = await canvas.findByText('Add to favorites'); await userEvent.click(addToFavoritesButton); + actionButton = await canvas.findByText('Actions'); + await userEvent.click(actionButton); + const exportButton = await canvas.findByText('Export'); await userEvent.click(exportButton); diff --git a/packages/twenty-front/src/modules/action-menu/types/ActionMenuEntry.ts b/packages/twenty-front/src/modules/action-menu/types/ActionMenuEntry.ts index 7cef4b2fead8..568bd3a33b83 100644 --- a/packages/twenty-front/src/modules/action-menu/types/ActionMenuEntry.ts +++ b/packages/twenty-front/src/modules/action-menu/types/ActionMenuEntry.ts @@ -1,7 +1,5 @@ import { MouseEvent, ReactNode } from 'react'; -import { IconComponent } from 'twenty-ui'; - -import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent'; +import { IconComponent, MenuItemAccent } from 'twenty-ui'; export type ActionMenuEntry = { type: 'standard' | 'workflow-run'; diff --git a/packages/twenty-front/src/modules/action-menu/types/RightDrawerActionMenuDropdownHotkeyScope.ts b/packages/twenty-front/src/modules/action-menu/types/RightDrawerActionMenuDropdownHotkeyScope.ts new file mode 100644 index 000000000000..74505c320f16 --- /dev/null +++ b/packages/twenty-front/src/modules/action-menu/types/RightDrawerActionMenuDropdownHotkeyScope.ts @@ -0,0 +1,3 @@ +export enum RightDrawerActionMenuDropdownHotkeyScope { + RightDrawerActionMenuDropdown = 'right-drawer-action-menu-dropdown', +} diff --git a/packages/twenty-front/src/modules/action-menu/utils/__tests__/getRightDrawerActionMenuDropdownIdFromActionMenuId.test.ts b/packages/twenty-front/src/modules/action-menu/utils/__tests__/getRightDrawerActionMenuDropdownIdFromActionMenuId.test.ts new file mode 100644 index 000000000000..209bdf99afb6 --- /dev/null +++ b/packages/twenty-front/src/modules/action-menu/utils/__tests__/getRightDrawerActionMenuDropdownIdFromActionMenuId.test.ts @@ -0,0 +1,9 @@ +import { getRightDrawerActionMenuDropdownIdFromActionMenuId } from '../getRightDrawerActionMenuDropdownIdFromActionMenuId'; + +describe('getRightDrawerActionMenuDropdownIdFromActionMenuId', () => { + it('should return the right drawer action menu dropdown id', () => { + expect( + getRightDrawerActionMenuDropdownIdFromActionMenuId('action-menu-id'), + ).toBe('right-drawer-action-menu-dropdown-action-menu-id'); + }); +}); diff --git a/packages/twenty-front/src/modules/action-menu/utils/getRightDrawerActionMenuDropdownIdFromActionMenuId.ts b/packages/twenty-front/src/modules/action-menu/utils/getRightDrawerActionMenuDropdownIdFromActionMenuId.ts new file mode 100644 index 000000000000..8e1d49133d50 --- /dev/null +++ b/packages/twenty-front/src/modules/action-menu/utils/getRightDrawerActionMenuDropdownIdFromActionMenuId.ts @@ -0,0 +1,5 @@ +export const getRightDrawerActionMenuDropdownIdFromActionMenuId = ( + actionMenuId: string, +) => { + return `right-drawer-action-menu-dropdown-${actionMenuId}`; +}; diff --git a/packages/twenty-front/src/modules/activities/calendar/components/Calendar.tsx b/packages/twenty-front/src/modules/activities/calendar/components/Calendar.tsx index 29beb1225811..0be4c731adda 100644 --- a/packages/twenty-front/src/modules/activities/calendar/components/Calendar.tsx +++ b/packages/twenty-front/src/modules/activities/calendar/components/Calendar.tsx @@ -38,6 +38,10 @@ const StyledYear = styled.span` color: ${({ theme }) => theme.font.color.light}; `; +const StyledTitleContainer = styled.div` + margin-bottom: ${({ theme }) => theme.spacing(4)}; +`; + export const Calendar = ({ targetableObject, }: { @@ -131,14 +135,16 @@ export const Calendar = ({ return (
- - {monthLabel} - {isLastMonthOfYear && {year}} - - } - /> + + + {monthLabel} + {isLastMonthOfYear && {year}} + + } + /> +
); diff --git a/packages/twenty-front/src/modules/activities/components/RichTextEditor.tsx b/packages/twenty-front/src/modules/activities/components/RichTextEditor.tsx index 26dd4204e631..bff2bef3cb1b 100644 --- a/packages/twenty-front/src/modules/activities/components/RichTextEditor.tsx +++ b/packages/twenty-front/src/modules/activities/components/RichTextEditor.tsx @@ -350,6 +350,10 @@ export const RichTextEditor = ({ editor.focus(); }, RightDrawerHotkeyScope.RightDrawer, + [], + { + preventDefault: false, + }, ); const handleBlockEditorFocus = () => { diff --git a/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscriberDropdownAddSubscriberMenuItem.tsx b/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscriberDropdownAddSubscriberMenuItem.tsx index b56cdc0809cb..04de8d7184c6 100644 --- a/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscriberDropdownAddSubscriberMenuItem.tsx +++ b/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscriberDropdownAddSubscriberMenuItem.tsx @@ -1,9 +1,8 @@ import { MessageThreadSubscriber } from '@/activities/emails/types/MessageThreadSubscriber'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord'; -import { MenuItemAvatar } from '@/ui/navigation/menu-item/components/MenuItemAvatar'; import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; -import { IconPlus } from 'twenty-ui'; +import { IconPlus, MenuItemAvatar } from 'twenty-ui'; export const MessageThreadSubscriberDropdownAddSubscriberMenuItem = ({ workspaceMember, diff --git a/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersDropdownButton.tsx b/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersDropdownButton.tsx index 7f7c42e7ae26..2fdf1d634fdd 100644 --- a/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersDropdownButton.tsx +++ b/packages/twenty-front/src/modules/activities/emails/components/MessageThreadSubscribersDropdownButton.tsx @@ -1,5 +1,5 @@ import { offset } from '@floating-ui/react'; -import { IconMinus, IconPlus } from 'twenty-ui'; +import { IconMinus, IconPlus, MenuItem, MenuItemAvatar } from 'twenty-ui'; import { MessageThreadSubscriberDropdownAddSubscriber } from '@/activities/emails/components/MessageThreadSubscriberDropdownAddSubscriber'; import { MessageThreadSubscribersChip } from '@/activities/emails/components/MessageThreadSubscribersChip'; @@ -10,8 +10,6 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useListenRightDrawerClose } from '@/ui/layout/right-drawer/hooks/useListenRightDrawerClose'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; -import { MenuItemAvatar } from '@/ui/navigation/menu-item/components/MenuItemAvatar'; import { useState } from 'react'; export const MESSAGE_THREAD_SUBSCRIBER_DROPDOWN_ID = diff --git a/packages/twenty-front/src/modules/activities/files/components/AttachmentDropdown.tsx b/packages/twenty-front/src/modules/activities/files/components/AttachmentDropdown.tsx index a52aa4c910bd..18f926d6425a 100644 --- a/packages/twenty-front/src/modules/activities/files/components/AttachmentDropdown.tsx +++ b/packages/twenty-front/src/modules/activities/files/components/AttachmentDropdown.tsx @@ -4,13 +4,12 @@ import { IconPencil, IconTrash, LightIconButton, + MenuItem, } from 'twenty-ui'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; -import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; type AttachmentDropdownProps = { onDownload: () => void; @@ -50,27 +49,26 @@ export const AttachmentDropdown = ({ clickableComponent={ } + dropdownMenuWidth={160} dropdownComponents={ - - - - - - - + + + + + } dropdownHotkeyScope={{ scope: dropdownId, diff --git a/packages/twenty-front/src/modules/activities/inline-cell/components/ActivityTargetInlineCellEditMode.tsx b/packages/twenty-front/src/modules/activities/inline-cell/components/ActivityTargetInlineCellEditMode.tsx index 5b9d62b6cd7a..7719f4576bed 100644 --- a/packages/twenty-front/src/modules/activities/inline-cell/components/ActivityTargetInlineCellEditMode.tsx +++ b/packages/twenty-front/src/modules/activities/inline-cell/components/ActivityTargetInlineCellEditMode.tsx @@ -27,6 +27,7 @@ import { import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { ActivityTargetInlineCellEditModeMultiRecordsEffect } from '@/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsEffect'; +import { ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect } from '@/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect'; import { MultiRecordSelect } from '@/object-record/relation-picker/components/MultiRecordSelect'; import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope'; import { prefillRecord } from '@/object-record/utils/prefillRecord'; @@ -287,6 +288,7 @@ export const ActivityTargetInlineCellEditMode = ({ + diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/components/EventList.tsx b/packages/twenty-front/src/modules/activities/timeline-activities/components/EventList.tsx index 86b053536d88..a19f0af6a7e0 100644 --- a/packages/twenty-front/src/modules/activities/timeline-activities/components/EventList.tsx +++ b/packages/twenty-front/src/modules/activities/timeline-activities/components/EventList.tsx @@ -26,8 +26,6 @@ const StyledTimelineContainer = styled.div` flex-direction: column; gap: ${({ theme }) => theme.spacing(1)}; justify-content: flex-start; - - width: calc(100% - ${({ theme }) => theme.spacing(8)}); `; export const EventList = ({ events, targetableObject }: EventListProps) => { diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/components/EventRow.tsx b/packages/twenty-front/src/modules/activities/timeline-activities/components/EventRow.tsx index e2cd91889021..e78e4a136d0f 100644 --- a/packages/twenty-front/src/modules/activities/timeline-activities/components/EventRow.tsx +++ b/packages/twenty-front/src/modules/activities/timeline-activities/components/EventRow.tsx @@ -11,6 +11,7 @@ import { TimelineActivity } from '@/activities/timeline-activities/types/Timelin import { getTimelineActivityAuthorFullName } from '@/activities/timeline-activities/utils/getTimelineActivityAuthorFullName'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { MOBILE_VIEWPORT } from 'twenty-ui'; import { beautifyPastDateRelativeToNow } from '~/utils/date-utils'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; @@ -62,6 +63,7 @@ const StyledSummary = styled.summary` flex: 1; flex-direction: row; gap: ${({ theme }) => theme.spacing(1)}; + width: 100%; `; const StyledItemContainer = styled.div<{ isMarginBottom?: boolean }>` @@ -77,6 +79,9 @@ const StyledItemContainer = styled.div<{ isMarginBottom?: boolean }>` `; const StyledItemTitleDate = styled.div` + @media (max-width: ${MOBILE_VIEWPORT}px) { + display: none; + } align-items: flex-start; padding-top: ${({ theme }) => theme.spacing(1)}; color: ${({ theme }) => theme.font.color.tertiary}; diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/components/TimelineActivities.tsx b/packages/twenty-front/src/modules/activities/timeline-activities/components/TimelineActivities.tsx index a0f8bf327f4d..9695f98c58f2 100644 --- a/packages/twenty-front/src/modules/activities/timeline-activities/components/TimelineActivities.tsx +++ b/packages/twenty-front/src/modules/activities/timeline-activities/components/TimelineActivities.tsx @@ -14,6 +14,7 @@ import { AnimatedPlaceholderEmptyTextContainer, AnimatedPlaceholderEmptyTitle, EMPTY_PLACEHOLDER_TRANSITION_PROPS, + MOBILE_VIEWPORT, } from 'twenty-ui'; const StyledMainContainer = styled.div` @@ -31,6 +32,11 @@ const StyledMainContainer = styled.div` padding-right: ${({ theme }) => theme.spacing(6)}; padding-left: ${({ theme }) => theme.spacing(6)}; gap: ${({ theme }) => theme.spacing(4)}; + + @media (max-width: ${MOBILE_VIEWPORT}px) { + padding-right: ${({ theme }) => theme.spacing(1)}; + padding-left: ${({ theme }) => theme.spacing(1)}; + } `; export const TimelineActivities = ({ diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/rows/activity/components/EventRowActivity.tsx b/packages/twenty-front/src/modules/activities/timeline-activities/rows/activity/components/EventRowActivity.tsx index 24322ac7c598..47f95270d6a4 100644 --- a/packages/twenty-front/src/modules/activities/timeline-activities/rows/activity/components/EventRowActivity.tsx +++ b/packages/twenty-front/src/modules/activities/timeline-activities/rows/activity/components/EventRowActivity.tsx @@ -16,6 +16,10 @@ const StyledLinkedActivity = styled.span` color: ${({ theme }) => theme.font.color.primary}; cursor: pointer; text-decoration: underline; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; `; export const StyledEventRowItemText = styled.span` diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/rows/components/EventCard.tsx b/packages/twenty-front/src/modules/activities/timeline-activities/rows/components/EventCard.tsx index 52b8fa0de84e..267e56f2d83c 100644 --- a/packages/twenty-front/src/modules/activities/timeline-activities/rows/components/EventCard.tsx +++ b/packages/twenty-front/src/modules/activities/timeline-activities/rows/components/EventCard.tsx @@ -1,6 +1,6 @@ import styled from '@emotion/styled'; -import { Card } from 'twenty-ui'; +import { Card, MOBILE_VIEWPORT } from 'twenty-ui'; type EventCardProps = { children: React.ReactNode; @@ -16,6 +16,10 @@ const StyledCardContainer = styled.div` width: 400px; padding: ${({ theme }) => theme.spacing(2)} 0px ${({ theme }) => theme.spacing(1)} 0px; + + @media (max-width: ${MOBILE_VIEWPORT}px) { + width: 300px; + } `; const StyledCard = styled(Card)` diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/rows/main-object/components/EventFieldDiff.tsx b/packages/twenty-front/src/modules/activities/timeline-activities/rows/main-object/components/EventFieldDiff.tsx index f1a50f9128e7..e212b15c5576 100644 --- a/packages/twenty-front/src/modules/activities/timeline-activities/rows/main-object/components/EventFieldDiff.tsx +++ b/packages/twenty-front/src/modules/activities/timeline-activities/rows/main-object/components/EventFieldDiff.tsx @@ -20,7 +20,10 @@ const StyledEventFieldDiffContainer = styled.div` flex-direction: row; gap: ${({ theme }) => theme.spacing(1)}; height: 24px; - width: 380px; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; `; const StyledEmptyValue = styled.div` diff --git a/packages/twenty-front/src/modules/settings/developers/webhook/components/SettingsDevelopersWebhookUsageGraph.tsx b/packages/twenty-front/src/modules/analytics/components/AnalyticsActivityGraph.tsx similarity index 67% rename from packages/twenty-front/src/modules/settings/developers/webhook/components/SettingsDevelopersWebhookUsageGraph.tsx rename to packages/twenty-front/src/modules/analytics/components/AnalyticsActivityGraph.tsx index 9626c6712eef..1a124ad90c52 100644 --- a/packages/twenty-front/src/modules/settings/developers/webhook/components/SettingsDevelopersWebhookUsageGraph.tsx +++ b/packages/twenty-front/src/modules/analytics/components/AnalyticsActivityGraph.tsx @@ -1,23 +1,19 @@ -import { SettingsDevelopersWebhookTooltip } from '@/settings/developers/webhook/components/SettingsDevelopersWebhookTooltip'; -import { useGraphData } from '@/settings/developers/webhook/hooks/useGraphData'; -import { webhookGraphDataState } from '@/settings/developers/webhook/states/webhookGraphDataState'; +import { WebhookAnalyticsTooltip } from '@/analytics/components/WebhookAnalyticsTooltip'; +import { ANALYTICS_GRAPH_DESCRIPTION_MAP } from '@/analytics/constants/AnalyticsGraphDescriptionMap'; +import { ANALYTICS_GRAPH_TITLE_MAP } from '@/analytics/constants/AnalyticsGraphTitleMap'; +import { useGraphData } from '@/analytics/hooks/useGraphData'; +import { analyticsGraphDataComponentState } from '@/analytics/states/analyticsGraphDataComponentState'; +import { AnalyticsComponentProps as AnalyticsActivityGraphProps } from '@/analytics/types/AnalyticsComponentProps'; +import { computeAnalyticsGraphDataFunction } from '@/analytics/utils/computeAnalyticsGraphDataFunction'; import { Select } from '@/ui/input/components/Select'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { ResponsiveLine } from '@nivo/line'; import { Section } from '@react-email/components'; -import { useState } from 'react'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; +import { useId, useState } from 'react'; import { H2Title } from 'twenty-ui'; -export type NivoLineInput = { - id: string | number; - color?: string; - data: Array<{ - x: number | string | Date; - y: number | string | Date; - }>; -}; const StyledGraphContainer = styled.div` background-color: ${({ theme }) => theme.background.secondary}; border: 1px solid ${({ theme }) => theme.border.color.medium}; @@ -33,34 +29,38 @@ const StyledTitleContainer = styled.div` justify-content: space-between; `; -type SettingsDevelopersWebhookUsageGraphProps = { - webhookId: string; -}; - -export const SettingsDevelopersWebhookUsageGraph = ({ - webhookId, -}: SettingsDevelopersWebhookUsageGraphProps) => { - const webhookGraphData = useRecoilValue(webhookGraphDataState); - const setWebhookGraphData = useSetRecoilState(webhookGraphDataState); +export const AnalyticsActivityGraph = ({ + recordId, + endpointName, +}: AnalyticsActivityGraphProps) => { + const [analyticsGraphData, setAnalyticsGraphData] = useRecoilComponentStateV2( + analyticsGraphDataComponentState, + ); const theme = useTheme(); const [windowLengthGraphOption, setWindowLengthGraphOption] = useState< '7D' | '1D' | '12H' | '4H' >('7D'); - const { fetchGraphData } = useGraphData(webhookId); + const { fetchGraphData } = useGraphData({ + recordId, + endpointName, + }); + + const transformDataFunction = computeAnalyticsGraphDataFunction(endpointName); + const dropdownId = useId(); return ( <> - {webhookGraphData.length ? ( + {analyticsGraphData.length ? (
updateOperation(index, 'object', object)} @@ -260,7 +269,9 @@ export const SettingsDevelopersWebhooksDetail = () => { />