diff --git a/package.json b/package.json index c22f9e5b706e..c3329532b348 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,6 @@ "@sentry/tracing": "^7.99.0", "@sniptt/guards": "^0.2.0", "@stoplight/elements": "^8.0.5", - "@storybook/icons": "^1.2.9", "@swc/jest": "^0.2.29", "@tabler/icons-react": "^2.44.0", "@types/dompurify": "^3.0.5", @@ -190,7 +189,7 @@ "stripe": "^14.17.0", "ts-key-enum": "^2.0.12", "tslib": "^2.3.0", - "tsup": "^8.0.1", + "tsup": "^8.2.4", "type-fest": "4.10.1", "typeorm": "patch:typeorm@0.3.20#./packages/twenty-server/patches/typeorm+0.3.20.patch", "typescript": "5.3.3", @@ -234,6 +233,7 @@ "@storybook/addon-onboarding": "^1.0.10", "@storybook/blocks": "^7.6.3", "@storybook/core-server": "7.6.3", + "@storybook/icons": "^1.2.9", "@storybook/jest": "^0.2.3", "@storybook/react": "^7.6.3", "@storybook/react-vite": "^7.6.3", @@ -297,7 +297,7 @@ "danger": "^11.3.0", "dotenv-cli": "^7.2.1", "drizzle-kit": "^0.20.14", - "esbuild": "^0.20.2", + "esbuild": "^0.23.0", "eslint": "^8.53.0", "eslint-config-next": "14.0.4", "eslint-config-prettier": "^9.1.0", @@ -329,14 +329,14 @@ "storybook": "^7.6.3", "storybook-addon-cookie": "^3.2.0", "storybook-addon-pseudo-states": "^2.1.2", - "storybook-dark-mode": "^4.0.1", + "storybook-dark-mode": "^3.0.3", "supertest": "^6.1.3", "ts-jest": "^29.1.1", "ts-loader": "^9.2.3", "ts-node": "10.9.1", "tsconfig-paths": "^4.2.0", - "tsx": "^4.7.2", - "vite": "^5.0.0", + "tsx": "^4.17.0", + "vite": "^5.4.0", "vite-plugin-checker": "^0.6.2", "vite-plugin-dts": "3.8.1", "vite-plugin-svgr": "^4.2.0", diff --git a/packages/twenty-chrome-extension/src/options/modules/ui/input/components/Toggle.tsx b/packages/twenty-chrome-extension/src/options/modules/ui/input/components/Toggle.tsx index b390a94b9cbf..7decf1d13ff4 100644 --- a/packages/twenty-chrome-extension/src/options/modules/ui/input/components/Toggle.tsx +++ b/packages/twenty-chrome-extension/src/options/modules/ui/input/components/Toggle.tsx @@ -15,7 +15,7 @@ type ContainerProps = { const StyledContainer = styled.div` align-items: center; background-color: ${({ theme, isOn, color }) => - isOn ? color ?? theme.color.blue : theme.background.quaternary}; + isOn ? (color ?? theme.color.blue) : theme.background.quaternary}; border-radius: 10px; cursor: pointer; display: flex; diff --git a/packages/twenty-front/jest.config.ts b/packages/twenty-front/jest.config.ts index 40bf33ba3760..b9c8205189c7 100644 --- a/packages/twenty-front/jest.config.ts +++ b/packages/twenty-front/jest.config.ts @@ -24,9 +24,9 @@ const jestConfig: JestConfigWithTsJest = { extensionsToTreatAsEsm: ['.ts', '.tsx'], coverageThreshold: { global: { - statements: 64, - lines: 63, - functions: 55, + statements: 62, + lines: 61, + functions: 52, }, }, collectCoverageFrom: ['/src/**/*.ts'], diff --git a/packages/twenty-front/src/loading/components/__stories__/PrefetchLoading.stories.tsx b/packages/twenty-front/src/loading/components/__stories__/PrefetchLoading.stories.tsx index 919b284a4ce5..847680fbae61 100644 --- a/packages/twenty-front/src/loading/components/__stories__/PrefetchLoading.stories.tsx +++ b/packages/twenty-front/src/loading/components/__stories__/PrefetchLoading.stories.tsx @@ -1,4 +1,3 @@ -import { expect } from '@storybook/jest'; import { Meta, StoryObj } from '@storybook/react'; import { within } from '@storybook/test'; @@ -7,7 +6,6 @@ import { PageDecorator, PageDecoratorArgs, } from '~/testing/decorators/PageDecorator'; -import { PrefetchLoadingDecorator } from '~/testing/decorators/PrefetchLoadingDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; const meta: Meta = { @@ -31,17 +29,15 @@ export type Story = StoryObj; export const Default: Story = { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - decorators: [PrefetchLoadingDecorator, PageDecorator], + decorators: [PageDecorator], play: async ({ canvasElement }) => { const canvas = within(canvasElement); await canvas.findByText('Search'); await canvas.findByText('Settings'); await canvas.findByText('Tasks'); - - expect(canvas.queryByText('People')).toBeNull(); - expect(canvas.queryByText('Opportunities')).toBeNull(); - expect(canvas.queryByText('Listings')).toBeNull(); - expect(canvas.queryByText('My Customs')).toBeNull(); + await canvas.findByText('People'); + await canvas.findByText('Opportunities'); + await canvas.findByText('My Customs'); }, }; diff --git a/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts b/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts index 0999bb0e58f1..65f69f6d290e 100644 --- a/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts +++ b/packages/twenty-front/src/modules/apollo/services/apollo.factory.ts @@ -23,7 +23,7 @@ import { loggerLink } from '../utils'; const logger = loggerLink(() => 'Twenty'); export interface Options extends ApolloClientOptions { - onError?: (err: GraphQLFormattedError | undefined) => void; + onError?: (err: readonly GraphQLFormattedError[] | undefined) => void; onNetworkError?: (err: Error | ServerParseError | ServerError) => void; onTokenPairChange?: (tokenPair: AuthTokenPair) => void; onUnauthenticatedError?: () => void; @@ -80,9 +80,8 @@ export class ApolloFactory implements ApolloManager { const errorLink = onError( ({ graphQLErrors, networkError, forward, operation }) => { if (isDefined(graphQLErrors)) { + onErrorCb?.(graphQLErrors); for (const graphQLError of graphQLErrors) { - onErrorCb?.(graphQLError); - if (graphQLError.message === 'Unauthorized') { return fromPromise( renewToken(uri, this.tokenPair) diff --git a/packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx b/packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx index f3f96062b2c9..506920b959a6 100644 --- a/packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx @@ -1,7 +1,7 @@ -import { useEffect } from 'react'; import { action } from '@storybook/addon-actions'; import { Meta, StoryObj } from '@storybook/react'; import { expect, userEvent, within } from '@storybook/test'; +import { useEffect } from 'react'; import { useSetRecoilState } from 'recoil'; import { IconCheckbox, IconNotes } from 'twenty-ui'; @@ -14,7 +14,6 @@ import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadat import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; import { getCompaniesMock } from '~/testing/mock-data/companies'; -import { getPeopleMock } from '~/testing/mock-data/people'; import { mockDefaultWorkspace, mockedWorkspaceMemberData, @@ -23,7 +22,6 @@ import { sleep } from '~/utils/sleep'; import { CommandMenu } from '../CommandMenu'; -const peopleMock = getPeopleMock(); const companiesMock = getCompaniesMock(); const openTimeout = 50; @@ -99,13 +97,8 @@ export const MatchingPersonCompanyActivityCreateNavigate: Story = { const searchInput = await canvas.findByPlaceholderText('Search'); await sleep(openTimeout); await userEvent.type(searchInput, 'n'); - expect( - await canvas.findByText( - peopleMock[0].name.firstName + ' ' + peopleMock[0].name.lastName, - ), - ).toBeInTheDocument(); + expect(await canvas.findByText('Linkedin')).toBeInTheDocument(); expect(await canvas.findByText(companiesMock[0].name)).toBeInTheDocument(); - expect(await canvas.findByText('My very first note')).toBeInTheDocument(); expect(await canvas.findByText('Create Note')).toBeInTheDocument(); expect(await canvas.findByText('Go to Companies')).toBeInTheDocument(); }, @@ -128,21 +121,6 @@ export const AtleastMatchingOnePerson: Story = { const searchInput = await canvas.findByPlaceholderText('Search'); await sleep(openTimeout); await userEvent.type(searchInput, 'alex'); - expect( - await canvas.findByText( - peopleMock[0].name.firstName + ' ' + peopleMock[0].name.lastName, - ), - ).toBeInTheDocument(); - }, -}; - -export const NotMatchingAnything: Story = { - play: async () => { - const canvas = within(document.body); - const searchInput = await canvas.findByPlaceholderText('Search'); - await sleep(openTimeout); - await userEvent.type(searchInput, 'asdasdasd'); - // FIXME: We need to fix the filters in graphql - // expect(await canvas.findByText('No results found')).toBeInTheDocument(); + expect(await canvas.findByText('Sylvie Palmer')).toBeInTheDocument(); }, }; diff --git a/packages/twenty-front/src/modules/object-metadata/components/__stories__/ObjectMetadataNavItems.stories.tsx b/packages/twenty-front/src/modules/object-metadata/components/__stories__/ObjectMetadataNavItems.stories.tsx index 6efdf175bb5a..7e74871a5fee 100644 --- a/packages/twenty-front/src/modules/object-metadata/components/__stories__/ObjectMetadataNavItems.stories.tsx +++ b/packages/twenty-front/src/modules/object-metadata/components/__stories__/ObjectMetadataNavItems.stories.tsx @@ -9,6 +9,7 @@ import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadat import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; +import { PrefetchLoadedDecorator } from '~/testing/decorators/PrefetchLoadedDecorator'; import { ObjectMetadataNavItems } from '../ObjectMetadataNavItems'; const meta: Meta = { @@ -20,6 +21,7 @@ const meta: Meta = { ComponentWithRouterDecorator, ComponentWithRecoilScopeDecorator, SnackBarDecorator, + PrefetchLoadedDecorator, ], parameters: { msw: graphqlMocks, diff --git a/packages/twenty-front/src/modules/object-record/hooks/__mocks__/useExecuteQuickActionOnOneRecord.ts b/packages/twenty-front/src/modules/object-record/hooks/__mocks__/useExecuteQuickActionOnOneRecord.ts deleted file mode 100644 index 65a05a028bcb..000000000000 --- a/packages/twenty-front/src/modules/object-record/hooks/__mocks__/useExecuteQuickActionOnOneRecord.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { PERSON_FRAGMENT } from '@/object-record/hooks/__mocks__/personFragment'; -import { gql } from '@apollo/client'; - -export { responseData } from './useUpdateOneRecord'; - -export const query = gql` - mutation ExecuteQuickActionOnOnePerson($idToExecuteQuickActionOn: ID!) { - executeQuickActionOnPerson(id: $idToExecuteQuickActionOn) { - ${PERSON_FRAGMENT} - } - } -`; - -export const variables = { - idToExecuteQuickActionOn: 'a7286b9a-c039-4a89-9567-2dfa7953cda9', -}; diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useExecuteQuickActionOnOneRecord.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useExecuteQuickActionOnOneRecord.test.tsx deleted file mode 100644 index 100825c3edd6..000000000000 --- a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useExecuteQuickActionOnOneRecord.test.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { ReactNode } from 'react'; -import { MockedProvider } from '@apollo/client/testing'; -import { act, renderHook } from '@testing-library/react'; -import { RecoilRoot } from 'recoil'; - -import { - query, - responseData, - variables, -} from '@/object-record/hooks/__mocks__/useExecuteQuickActionOnOneRecord'; -import { useExecuteQuickActionOnOneRecord } from '@/object-record/hooks/useExecuteQuickActionOnOneRecord'; - -const idToExecuteQuickActionOn = 'a7286b9a-c039-4a89-9567-2dfa7953cda9'; - -const mocks = [ - { - request: { - query, - variables, - }, - result: jest.fn(() => ({ - data: { - executeQuickActionOnPerson: { - ...responseData, - id: idToExecuteQuickActionOn, - }, - }, - })), - }, -]; - -const Wrapper = ({ children }: { children: ReactNode }) => ( - - - {children} - - -); - -describe('useExecuteQuickActionOnOneRecord', () => { - it('should work as expected', async () => { - const { result } = renderHook( - () => - useExecuteQuickActionOnOneRecord({ - objectNameSingular: 'person', - }), - { - wrapper: Wrapper, - }, - ); - - await act(async () => { - const res = await result.current.executeQuickActionOnOneRecord( - idToExecuteQuickActionOn, - ); - - expect(res).toHaveProperty('id', idToExecuteQuickActionOn); - }); - - expect(mocks[0].result).toHaveBeenCalled(); - }); -}); diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useExecuteQuickActionOnOneRecordMutation.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useExecuteQuickActionOnOneRecordMutation.test.tsx deleted file mode 100644 index b48a31a8ddf1..000000000000 --- a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useExecuteQuickActionOnOneRecordMutation.test.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { renderHook } from '@testing-library/react'; -import { print } from 'graphql'; -import { RecoilRoot } from 'recoil'; - -import { PERSON_FRAGMENT } from '@/object-record/hooks/__mocks__/personFragment'; -import { useExecuteQuickActionOnOneRecordMutation } from '@/object-record/hooks/useExecuteQuickActionOnOneRecordMutation'; - -const expectedQueryTemplate = ` - mutation ExecuteQuickActionOnOnePerson($idToExecuteQuickActionOn: ID!) { - executeQuickActionOnPerson(id: $idToExecuteQuickActionOn) { - ${PERSON_FRAGMENT} - } - } -`.replace(/\s/g, ''); - -describe('useExecuteQuickActionOnOneRecordMutation', () => { - it('should return a valid executeQuickActionOnOneRecordMutation', () => { - const objectNameSingular = 'person'; - - const { result } = renderHook( - () => - useExecuteQuickActionOnOneRecordMutation({ - objectNameSingular, - }), - { - wrapper: RecoilRoot, - }, - ); - - const { executeQuickActionOnOneRecordMutation } = result.current; - - expect(executeQuickActionOnOneRecordMutation).toBeDefined(); - - const printedReceivedQuery = print( - executeQuickActionOnOneRecordMutation, - ).replace(/\s/g, ''); - - expect(printedReceivedQuery).toEqual(expectedQueryTemplate); - }); -}); diff --git a/packages/twenty-front/src/modules/object-record/hooks/useExecuteQuickActionOnOneRecord.ts b/packages/twenty-front/src/modules/object-record/hooks/useExecuteQuickActionOnOneRecord.ts deleted file mode 100644 index 10b67d64182a..000000000000 --- a/packages/twenty-front/src/modules/object-record/hooks/useExecuteQuickActionOnOneRecord.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { useCallback } from 'react'; -import { useApolloClient } from '@apollo/client'; -import { getOperationName } from '@apollo/client/utilities'; - -import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { useExecuteQuickActionOnOneRecordMutation } from '@/object-record/hooks/useExecuteQuickActionOnOneRecordMutation'; -import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery'; -import { capitalize } from '~/utils/string/capitalize'; - -type useExecuteQuickActionOnOneRecordProps = { - objectNameSingular: string; -}; - -export const useExecuteQuickActionOnOneRecord = ({ - objectNameSingular, -}: useExecuteQuickActionOnOneRecordProps) => { - const { objectMetadataItem } = useObjectMetadataItem({ - objectNameSingular, - }); - - const { executeQuickActionOnOneRecordMutation } = - useExecuteQuickActionOnOneRecordMutation({ - objectNameSingular, - }); - - const { findManyRecordsQuery } = useFindManyRecordsQuery({ - objectNameSingular, - }); - - const apolloClient = useApolloClient(); - - const executeQuickActionOnOneRecord = useCallback( - async (idToExecuteQuickActionOn: string) => { - const executeQuickActionOnRecord = await apolloClient.mutate({ - mutation: executeQuickActionOnOneRecordMutation, - variables: { - idToExecuteQuickActionOn, - }, - refetchQueries: [getOperationName(findManyRecordsQuery) ?? ''], - }); - - return executeQuickActionOnRecord.data[ - `executeQuickActionOn${capitalize(objectMetadataItem.nameSingular)}` - ] as T; - }, - [ - objectMetadataItem.nameSingular, - apolloClient, - executeQuickActionOnOneRecordMutation, - findManyRecordsQuery, - ], - ); - - return { - executeQuickActionOnOneRecord, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useExecuteQuickActionOnOneRecordMutation.ts b/packages/twenty-front/src/modules/object-record/hooks/useExecuteQuickActionOnOneRecordMutation.ts deleted file mode 100644 index 4785029e819b..000000000000 --- a/packages/twenty-front/src/modules/object-record/hooks/useExecuteQuickActionOnOneRecordMutation.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { gql } from '@apollo/client'; -import { useRecoilValue } from 'recoil'; - -import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; -import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery'; -import { EMPTY_MUTATION } from '@/object-record/constants/EmptyMutation'; -import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; -import { capitalize } from '~/utils/string/capitalize'; - -export const getExecuteQuickActionOnOneRecordMutationGraphQLField = ({ - objectNameSingular, -}: { - objectNameSingular: string; -}) => { - return `executeQuickActionOn${capitalize(objectNameSingular)}`; -}; - -export const useExecuteQuickActionOnOneRecordMutation = ({ - objectNameSingular, -}: { - objectNameSingular: string; -}) => { - const { objectMetadataItem } = useObjectMetadataItem({ - objectNameSingular, - }); - - const objectMetadataItems = useRecoilValue(objectMetadataItemsState); - - if (isUndefinedOrNull(objectMetadataItem)) { - return { executeQuickActionOnOneRecordMutation: EMPTY_MUTATION }; - } - - const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular); - - const graphQLFieldForExecuteQuickActionOnOneRecordMutation = - getExecuteQuickActionOnOneRecordMutationGraphQLField({ - objectNameSingular: objectMetadataItem.nameSingular, - }); - - const executeQuickActionOnOneRecordMutation = gql` - mutation ExecuteQuickActionOnOne${capitalizedObjectName}($idToExecuteQuickActionOn: ID!) { - ${graphQLFieldForExecuteQuickActionOnOneRecordMutation}(id: $idToExecuteQuickActionOn) ${mapObjectMetadataToGraphQLQuery( - { - objectMetadataItems, - objectMetadataItem, - }, - )} - } - `; - - return { executeQuickActionOnOneRecordMutation }; -}; diff --git a/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts b/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts index da5dc238af89..c5f22c9f640e 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts +++ b/packages/twenty-front/src/modules/object-record/hooks/useFindManyRecords.ts @@ -90,8 +90,9 @@ export const useFindManyRecords = ({ objectMetadataItem, }); - const pageInfo = data?.[objectMetadataItem.namePlural].pageInfo; - const totalCount = data?.[objectMetadataItem.namePlural].totalCount; + const pageInfo = data?.[objectMetadataItem.namePlural]?.pageInfo; + + const totalCount = data?.[objectMetadataItem.namePlural]?.totalCount; return { objectMetadataItem, diff --git a/packages/twenty-front/src/modules/object-record/record-action-bar/hooks/useRecordActionBar.tsx b/packages/twenty-front/src/modules/object-record/record-action-bar/hooks/useRecordActionBar.tsx index 77bf9bf09f95..24230af6b59a 100644 --- a/packages/twenty-front/src/modules/object-record/record-action-bar/hooks/useRecordActionBar.tsx +++ b/packages/twenty-front/src/modules/object-record/record-action-bar/hooks/useRecordActionBar.tsx @@ -1,20 +1,11 @@ import { isNonEmptyString } from '@sniptt/guards'; import { useCallback, useMemo, useState } from 'react'; import { useRecoilCallback, useSetRecoilState } from 'recoil'; -import { - IconClick, - IconFileExport, - IconHeart, - IconHeartOff, - IconMail, - IconPuzzle, - IconTrash, -} from 'twenty-ui'; +import { IconFileExport, IconHeart, IconHeartOff, IconTrash } from 'twenty-ui'; import { useFavorites } from '@/favorites/hooks/useFavorites'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { DELETE_MAX_COUNT } from '@/object-record/constants/DeleteMaxCount'; -import { useExecuteQuickActionOnOneRecord } from '@/object-record/hooks/useExecuteQuickActionOnOneRecord'; import { useDeleteTableData } from '@/object-record/record-index/options/hooks/useDeleteTableData'; import { displayedExportProgress, @@ -25,7 +16,6 @@ import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModa import { actionBarEntriesState } from '@/ui/navigation/action-bar/states/actionBarEntriesState'; import { contextMenuEntriesState } from '@/ui/navigation/context-menu/states/contextMenuEntriesState'; import { ContextMenuEntry } from '@/ui/navigation/context-menu/types/ContextMenuEntry'; -import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { isDefined } from '~/utils/isDefined'; type useRecordActionBarProps = { @@ -48,10 +38,6 @@ export const useRecordActionBar = ({ const { createFavorite, favorites, deleteFavorite } = useFavorites(); - const { executeQuickActionOnOneRecord } = useExecuteQuickActionOnOneRecord({ - objectNameSingular: objectMetadataItem.nameSingular, - }); - const handleFavoriteButtonClick = useRecoilCallback( ({ snapshot }) => () => { @@ -99,15 +85,6 @@ export const useRecordActionBar = ({ deleteTableData(selectedRecordIds); }, [deleteTableData, selectedRecordIds]); - const handleExecuteQuickActionOnClick = useCallback(async () => { - callback?.(); - await Promise.all( - selectedRecordIds.map(async (recordId) => { - await executeQuickActionOnOneRecord(recordId); - }), - ); - }, [callback, executeQuickActionOnOneRecord, selectedRecordIds]); - const { progress, download } = useExportTableData({ ...baseTableDataParams, filename: `${objectMetadataItem.nameSingular}.csv`, @@ -168,10 +145,6 @@ export const useRecordActionBar = ({ ], ); - const dataExecuteQuickActionOnmentEnabled = useIsFeatureEnabled( - 'IS_QUICK_ACTIONS_ENABLED', - ); - const hasOnlyOneRecordSelected = selectedRecordIds.length === 1; const isFavorite = @@ -212,12 +185,13 @@ export const useRecordActionBar = ({ setActionBarEntries: useCallback(() => { setActionBarEntriesState([ - ...(dataExecuteQuickActionOnmentEnabled - ? [ + /* { label: 'Actions', Icon: IconClick, - subActions: [ + subActions: + + /* [ { label: 'Enrich', Icon: IconPuzzle, @@ -228,16 +202,9 @@ export const useRecordActionBar = ({ Icon: IconMail, }, ], - }, - ] - : []), + */ ...menuActions, ]); - }, [ - menuActions, - dataExecuteQuickActionOnmentEnabled, - handleExecuteQuickActionOnClick, - setActionBarEntriesState, - ]), + }, [menuActions, setActionBarEntriesState]), }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/hooks/__tests__/useIsFieldReadOnly.test.tsx b/packages/twenty-front/src/modules/object-record/record-field/hooks/__tests__/useIsFieldReadOnly.test.tsx index 2dbee3f3c98d..2baa6c2d34d8 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/hooks/__tests__/useIsFieldReadOnly.test.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/hooks/__tests__/useIsFieldReadOnly.test.tsx @@ -11,7 +11,7 @@ import { useIsFieldReadOnly } from '@/object-record/record-field/hooks/useIsFiel import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; -const entityId = 'entityId'; +const recordId = 'recordId'; const getWrapper = (fieldDefinition: FieldDefinition) => @@ -19,7 +19,7 @@ const getWrapper = ({ + data: { + people: { + ...defaultResponseData, + edges: [ + { + node: mockPerson, + cursor: '1', + }, + ], + }, + }, + })), + }, +]; + +const Wrapper = ({ children }: { children: ReactNode }) => ( + + + + + {children} + + + + +); + +const graphqlEmptyResponse = [ + { + ...mocks[0], + result: jest.fn(() => ({ + data: { + people: { + ...defaultResponseData, + edges: [], + }, + }, + })), + }, +]; + +const WrapperWithEmptyResponse = ({ children }: { children: ReactNode }) => ( + + + + + {children} + + + + +); + +describe('useTableData', () => { + const recordIndexId = 'people'; + const objectNameSingular = 'person'; + describe('data fetching', () => { + it('should handle no records', async () => { + const callback = jest.fn(); + const { result } = renderHook( + () => + useTableData({ + recordIndexId, + objectNameSingular, + callback, + + delayMs: 0, + viewType: ViewType.Kanban, + }), + { wrapper: WrapperWithEmptyResponse }, + ); + + await act(async () => { + result.current.getTableData(); + }); + + await waitFor(() => { + expect(callback).toHaveBeenCalledWith([], []); + }); + }); + + it('should call the callback function with fetched data', async () => { + const callback = jest.fn(); + const { result } = renderHook( + () => + useTableData({ + recordIndexId, + objectNameSingular, + callback, + + delayMs: 0, + }), + { wrapper: Wrapper }, + ); + + await act(async () => { + result.current.getTableData(); + }); + + await waitFor(() => { + expect(callback).toHaveBeenCalledWith([mockPerson], []); + }); + }); + + it('should call the callback function with kanban field included as column if view type is kanban', async () => { + const callback = jest.fn(); + const { result } = renderHook( + () => { + const kanbanFieldNameState = extractComponentState( + recordBoardKanbanFieldMetadataNameComponentState, + recordIndexId, + ); + return { + tableData: useTableData({ + recordIndexId, + objectNameSingular, + callback, + pageSize: 30, + maximumRequests: 100, + delayMs: 0, + viewType: ViewType.Kanban, + }), + setKanbanFieldName: useRecordBoard(recordIndexId), + kanbanFieldName: useRecoilValue(kanbanFieldNameState), + kanbanData: useRecordIndexOptionsForBoard({ + objectNameSingular, + recordBoardId: recordIndexId, + viewBarId: recordIndexId, + }), + }; + }, + { + wrapper: Wrapper, + }, + ); + + await act(async () => { + result.current.setKanbanFieldName.setKanbanFieldMetadataName( + result.current.kanbanData.hiddenBoardFields[0].metadata.fieldName, + ); + }); + + await act(async () => { + result.current.tableData.getTableData(); + }); + + await waitFor(async () => { + expect(callback).toHaveBeenCalledWith( + [mockPerson], + [ + { + defaultValue: 'now', + editButtonIcon: undefined, + fieldMetadataId: '102963b7-3e77-4293-a1e6-1ab59a02b663', + iconName: 'IconCalendarClock', + isFilterable: true, + isLabelIdentifier: false, + isSortable: true, + isVisible: false, + label: 'Last update', + labelWidth: undefined, + metadata: { + fieldName: 'updatedAt', + isNullable: false, + objectMetadataNameSingular: 'person', + options: null, + placeHolder: 'Last update', + relationFieldMetadataId: undefined, + relationObjectMetadataNamePlural: '', + relationObjectMetadataNameSingular: '', + relationType: undefined, + targetFieldMetadataName: '', + }, + position: 0, + showLabel: undefined, + size: 100, + type: 'DATE_TIME', + }, + ], + ); + }); + }); + + it('should not call the callback function with kanban field included as column if view type is table', async () => { + const callback = jest.fn(); + const { result } = renderHook( + () => { + const kanbanFieldNameState = extractComponentState( + recordBoardKanbanFieldMetadataNameComponentState, + recordIndexId, + ); + return { + tableData: useTableData({ + recordIndexId, + objectNameSingular, + callback, + pageSize: 30, + maximumRequests: 100, + delayMs: 0, + viewType: ViewType.Table, + }), + setKanbanFieldName: useRecordBoard(recordIndexId), + kanbanFieldName: useRecoilValue(kanbanFieldNameState), + kanbanData: useRecordIndexOptionsForBoard({ + objectNameSingular, + recordBoardId: recordIndexId, + viewBarId: recordIndexId, + }), + }; + }, + { + wrapper: Wrapper, + }, + ); + + await act(async () => { + result.current.setKanbanFieldName.setKanbanFieldMetadataName( + result.current.kanbanData.hiddenBoardFields[0].metadata.fieldName, + ); + }); + + await act(async () => { + result.current.tableData.getTableData(); + }); + + await waitFor(async () => { + expect(callback).toHaveBeenCalledWith([mockPerson], []); + }); + }); + }); + + describe('utils', () => { + it('should correctly calculate percentage', () => { + expect(percentage(50, 200)).toBe(25); + expect(percentage(1, 3)).toBe(33); + }); + + it('should resolve sleep after given time', async () => { + jest.useFakeTimers(); + const sleepPromise = sleep(1000); + jest.advanceTimersByTime(1000); + await expect(sleepPromise).resolves.toBeUndefined(); + }); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/record-index/options/hooks/useExportTableData.ts b/packages/twenty-front/src/modules/object-record/record-index/options/hooks/useExportTableData.ts index e62a90ea5b0f..d6f6f83f3feb 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/options/hooks/useExportTableData.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/options/hooks/useExportTableData.ts @@ -143,6 +143,7 @@ export const useExportTableData = ({ objectNameSingular, pageSize = 30, recordIndexId, + viewType, }: UseExportTableDataOptions) => { const { processRecordsForCSVExport } = useProcessRecordsForCSVExport(objectNameSingular); @@ -164,6 +165,7 @@ export const useExportTableData = ({ pageSize, recordIndexId, callback: downloadCsv, + viewType, }); return { progress, download }; diff --git a/packages/twenty-front/src/modules/object-record/record-index/options/hooks/useTableData.ts b/packages/twenty-front/src/modules/object-record/record-index/options/hooks/useTableData.ts index b389d906dbb8..46572dffe1bf 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/options/hooks/useTableData.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/options/hooks/useTableData.ts @@ -8,6 +8,9 @@ import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefin import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { isDefined } from '~/utils/isDefined'; +import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; +import { useRecordIndexOptionsForBoard } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard'; +import { ViewType } from '@/views/types/ViewType'; import { useFindManyParams } from '../../hooks/useLoadRecordIndexTable'; export const sleep = (ms: number) => @@ -27,6 +30,7 @@ export type UseTableDataOptions = { rows: ObjectRecord[], columns: ColumnDefinition[], ) => void | Promise; + viewType?: ViewType; }; type ExportProgress = { @@ -42,6 +46,7 @@ export const useTableData = ({ pageSize = 30, recordIndexId, callback, + viewType = ViewType.Table, }: UseTableDataOptions) => { const [isDownloading, setIsDownloading] = useState(false); const [inflight, setInflight] = useState(false); @@ -58,6 +63,17 @@ export const useTableData = ({ hasUserSelectedAllRowsState, } = useRecordTableStates(recordIndexId); + const { hiddenBoardFields } = useRecordIndexOptionsForBoard({ + objectNameSingular, + recordBoardId: recordIndexId, + viewBarId: recordIndexId, + }); + + const { kanbanFieldMetadataNameState } = useRecordBoardStates(recordIndexId); + const kanbanFieldMetadataName = useRecoilValue(kanbanFieldMetadataNameState); + const hiddenKanbanFieldColumn = hiddenBoardFields.find( + (column) => column.metadata.fieldName === kanbanFieldMetadataName, + ); const columns = useRecoilValue(visibleTableColumnsSelector()); const selectedRowIds = useRecoilValue(selectedRowIdsSelector()); @@ -165,7 +181,14 @@ export const useTableData = ({ }); }; - const res = callback(records, columns); + const finalColumns = [ + ...columns, + ...(hiddenKanbanFieldColumn && viewType === ViewType.Kanban + ? [hiddenKanbanFieldColumn] + : []), + ]; + + const res = callback(records, finalColumns); if (res instanceof Promise) { res.then(complete); @@ -189,6 +212,8 @@ export const useTableData = ({ loading, callback, previousRecordCount, + hiddenKanbanFieldColumn, + viewType, ]); return { diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellBaseContainer.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellBaseContainer.tsx index 3a2a8c5c9bf8..1e977ecaec4e 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellBaseContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/components/RecordTableCellBaseContainer.tsx @@ -1,5 +1,5 @@ -import { ReactNode, useContext } from 'react'; import { styled } from '@linaria/react'; +import { ReactNode, useContext } from 'react'; import { BORDER_COMMON, ThemeContext } from 'twenty-ui'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; @@ -79,7 +79,7 @@ export const RecordTableCellBaseContainer = ({ const { hotkeyScope } = useContext(FieldContext); - const editHotkeyScope = { scope: hotkeyScope } ?? DEFAULT_CELL_SCOPE; + const editHotkeyScope = { scope: hotkeyScope ?? DEFAULT_CELL_SCOPE }; return ( diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/preview/components/__stories__/SettingsDataModelFieldPreviewCard.stories.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/preview/components/__stories__/SettingsDataModelFieldPreviewCard.stories.tsx index a494de18409b..3df8c6e7664a 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/preview/components/__stories__/SettingsDataModelFieldPreviewCard.stories.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/preview/components/__stories__/SettingsDataModelFieldPreviewCard.stories.tsx @@ -83,26 +83,16 @@ export const Date: Story = { }, }; -export const Link: Story = { +export const Links: Story = { args: { fieldMetadataItem: mockedCompanyObjectMetadataItem.fields.find( ({ name, type }) => - name === 'linkedinLink' && type === FieldMetadataType.Link, + name === 'linkedinLink' && type === FieldMetadataType.Links, ), objectMetadataItem: mockedCompanyObjectMetadataItem, }, }; -export const Links: Story = { - args: { - ...Link.args, - fieldMetadataItem: { - ...Link.args!.fieldMetadataItem!, - type: FieldMetadataType.Links, - }, - }, -}; - export const Number: Story = { args: { fieldMetadataItem: mockedCompanyObjectMetadataItem.fields.find( diff --git a/packages/twenty-front/src/modules/support/components/__stories__/SupportButton.stories.tsx b/packages/twenty-front/src/modules/support/components/__stories__/SupportDropdown.stories.tsx similarity index 91% rename from packages/twenty-front/src/modules/support/components/__stories__/SupportButton.stories.tsx rename to packages/twenty-front/src/modules/support/components/__stories__/SupportDropdown.stories.tsx index 06cb0fe3d9ea..5301812356fe 100644 --- a/packages/twenty-front/src/modules/support/components/__stories__/SupportButton.stories.tsx +++ b/packages/twenty-front/src/modules/support/components/__stories__/SupportDropdown.stories.tsx @@ -1,6 +1,6 @@ import { expect } from '@storybook/jest'; import { Meta, StoryObj } from '@storybook/react'; -import { within, userEvent } from '@storybook/test'; +import { userEvent, within } from '@storybook/test'; import { useSetRecoilState } from 'recoil'; import { currentUserState } from '@/auth/states/currentUserState'; @@ -15,6 +15,7 @@ import { } from '~/testing/mock-data/users'; import { SupportDropdown } from '@/support/components/SupportDropdown'; +import { PrefetchLoadedDecorator } from '~/testing/decorators/PrefetchLoadedDecorator'; const meta: Meta = { title: 'Modules/Support/SupportDropdown', @@ -35,6 +36,7 @@ const meta: Meta = { return ; }, + PrefetchLoadedDecorator, ], parameters: { msw: graphqlMocks, diff --git a/packages/twenty-front/src/modules/workspace/types/FeatureFlagKey.ts b/packages/twenty-front/src/modules/workspace/types/FeatureFlagKey.ts index 53f29d4b287c..1185aa04f0c5 100644 --- a/packages/twenty-front/src/modules/workspace/types/FeatureFlagKey.ts +++ b/packages/twenty-front/src/modules/workspace/types/FeatureFlagKey.ts @@ -1,6 +1,5 @@ export type FeatureFlagKey = | 'IS_BLOCKLIST_ENABLED' - | 'IS_QUICK_ACTIONS_ENABLED' | 'IS_EVENT_OBJECT_ENABLED' | 'IS_AIRTABLE_INTEGRATION_ENABLED' | 'IS_POSTGRESQL_INTEGRATION_ENABLED' diff --git a/packages/twenty-front/src/pages/object-record/__stories__/RecordShowPage.stories.tsx b/packages/twenty-front/src/pages/object-record/__stories__/RecordShowPage.stories.tsx index c23d9d6fdd0e..48f628dbf1bd 100644 --- a/packages/twenty-front/src/pages/object-record/__stories__/RecordShowPage.stories.tsx +++ b/packages/twenty-front/src/pages/object-record/__stories__/RecordShowPage.stories.tsx @@ -87,30 +87,12 @@ export const Default: Story = { play: async ({ canvasElement }) => { const canvas = within(canvasElement); - await canvas.findAllByText( - peopleMock[0].name.firstName + ' ' + peopleMock[0].name.lastName, - ); + // await canvas.findAllByText(peopleMock[0].name.firstName); expect( - await canvas.findByText('Add your first Activity', undefined, { + await canvas.findByText('Twenty', undefined, { timeout: 3000, }), ).toBeInTheDocument(); - }, -}; - -export const Loading: Story = { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - decorators: [PageDecorator], - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - expect( - canvas.queryByText( - peopleMock[0].name.firstName + ' ' + peopleMock[0].name.lastName, - ), - ).toBeNull(); - expect( await canvas.findByText('Add your first Activity', undefined, { timeout: 3000, diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjects.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjects.tsx index b3549ff54dde..efb5856ea5e3 100644 --- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjects.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjects.tsx @@ -60,7 +60,7 @@ export const SettingsObjects = () => { /> -
+ <>
@@ -122,7 +122,7 @@ export const SettingsObjects = () => { )}
-
+ ); diff --git a/packages/twenty-front/src/pages/settings/developers/webhooks/SettingsDevelopersWebhooksNew.tsx b/packages/twenty-front/src/pages/settings/developers/webhooks/SettingsDevelopersWebhooksNew.tsx index c83640d9dfcb..ba471414b551 100644 --- a/packages/twenty-front/src/pages/settings/developers/webhooks/SettingsDevelopersWebhooksNew.tsx +++ b/packages/twenty-front/src/pages/settings/developers/webhooks/SettingsDevelopersWebhooksNew.tsx @@ -12,7 +12,6 @@ import { TextInput } from '@/ui/input/components/TextInput'; import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; import { Section } from '@/ui/layout/section/components/Section'; import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb'; -import { isURL } from '~/utils/is-url'; export const SettingsDevelopersWebhooksNew = () => { const navigate = useNavigate(); @@ -23,18 +22,10 @@ export const SettingsDevelopersWebhooksNew = () => { targetUrl: '', operation: '*.*', }); - const [errorMessage, setErrorMessage] = useState(); const { createOneRecord: createOneWebhook } = useCreateOneRecord({ objectNameSingular: CoreObjectNameSingular.Webhook, }); const handleSave = async () => { - setErrorMessage(undefined); - - if (!isURL(formValues.targetUrl)) { - setErrorMessage('Invalid webhook URL'); - return; - } - const newWebhook = await createOneWebhook?.(formValues); if (!newWebhook) { @@ -80,7 +71,6 @@ export const SettingsDevelopersWebhooksNew = () => { targetUrl: value, })); }} - error={errorMessage} fullWidth /> diff --git a/packages/twenty-front/src/testing/decorators/PrefetchLoadedDecorator.tsx b/packages/twenty-front/src/testing/decorators/PrefetchLoadedDecorator.tsx new file mode 100644 index 000000000000..8079adc3475f --- /dev/null +++ b/packages/twenty-front/src/testing/decorators/PrefetchLoadedDecorator.tsx @@ -0,0 +1,19 @@ +import { Decorator } from '@storybook/react'; +import { useSetRecoilState } from 'recoil'; + +import { prefetchIsLoadedFamilyState } from '@/prefetch/states/prefetchIsLoadedFamilyState'; +import { PrefetchKey } from '@/prefetch/types/PrefetchKey'; + +export const PrefetchLoadedDecorator: Decorator = (Story) => { + const setAreViewsPrefetched = useSetRecoilState( + prefetchIsLoadedFamilyState(PrefetchKey.AllViews), + ); + const setAreFavoritesPrefetched = useSetRecoilState( + prefetchIsLoadedFamilyState(PrefetchKey.AllFavorites), + ); + + setAreViewsPrefetched(true); + setAreFavoritesPrefetched(true); + + return ; +}; diff --git a/packages/twenty-front/src/testing/mock-data/config.ts b/packages/twenty-front/src/testing/mock-data/config.ts index a14ea35bf85e..656dcbb80b76 100644 --- a/packages/twenty-front/src/testing/mock-data/config.ts +++ b/packages/twenty-front/src/testing/mock-data/config.ts @@ -1,9 +1,10 @@ -import { CaptchaDriverType } from '~/generated/graphql'; import { ClientConfig } from '~/generated-metadata/graphql'; +import { CaptchaDriverType } from '~/generated/graphql'; export const mockedClientConfig: ClientConfig = { signInPrefilled: true, signUpDisabled: false, + chromeExtensionId: 'MOCKED_EXTENSION_ID', debugMode: false, authProviders: { google: true, diff --git a/packages/twenty-front/src/testing/mock-data/generated/standard-metadata-query-result.ts b/packages/twenty-front/src/testing/mock-data/generated/standard-metadata-query-result.ts index 8bf7799370dd..250e1b6e6f1b 100644 --- a/packages/twenty-front/src/testing/mock-data/generated/standard-metadata-query-result.ts +++ b/packages/twenty-front/src/testing/mock-data/generated/standard-metadata-query-result.ts @@ -7918,7 +7918,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = "__typename": "fieldEdge", "node": { "__typename": "field", - "id": "af19a3ba-b725-4b9d-a0b7-8bf6b04fadbf", + "id": "5dcef112-ce1b-46c1-a33a-4d1394628c34", "type": "DATE_TIME", "name": "updatedAt", "label": "Update date", @@ -7941,7 +7941,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = "__typename": "fieldEdge", "node": { "__typename": "field", - "id": "af19a3ba-b725-4b9d-a0b7-8bf6b04fadds", + "id": "277d8939-1ead-4cdb-a560-854644219779", "type": "MULTI_SELECT", "name": "testMultiSelect", "label": "Test Multi Select", @@ -7964,7 +7964,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = "__typename": "fieldEdge", "node": { "__typename": "field", - "id": "tt929592-4f74-419e-8b26-6d216859078f", + "id": "f207dd14-f05e-4f29-b222-8993d4680f31", "type": "RAW_JSON", "name": "testRawJson", "label": "Test Raw Json", @@ -7987,7 +7987,7 @@ export const mockedStandardObjectMetadataQueryResult: ObjectMetadataItemsQuery = "__typename": "fieldEdge", "node": { "__typename": "field", - "id": "vv929592-4f74-419e-8b26-6d216859078f", + "id": "81db846a-a2f9-4b31-8931-81fac5cdd1b6", "type": "RATING", "name": "testRating", "label": "Rating", diff --git a/packages/twenty-front/src/testing/mock-data/metadata.ts b/packages/twenty-front/src/testing/mock-data/metadata.ts index d6ec68da1d70..c86d17d47afb 100644 --- a/packages/twenty-front/src/testing/mock-data/metadata.ts +++ b/packages/twenty-front/src/testing/mock-data/metadata.ts @@ -266,7 +266,7 @@ const customObjectMetadataItemEdge: ObjectEdge = { color: 'yellow', }, { - id: '3', + id: '6f6e1421-8a42-4d4a-bf76-465b5f84b6d2', value: 'HIGH', label: 'High', color: 'red', diff --git a/packages/twenty-front/src/testing/mock-data/users.ts b/packages/twenty-front/src/testing/mock-data/users.ts index 2bbe2185f983..5d6514c42867 100644 --- a/packages/twenty-front/src/testing/mock-data/users.ts +++ b/packages/twenty-front/src/testing/mock-data/users.ts @@ -6,6 +6,8 @@ import { User, Workspace, WorkspaceActivationStatus, + WorkspaceMemberDateFormatEnum, + WorkspaceMemberTimeFormatEnum, } from '~/generated/graphql'; type MockedUser = Pick< @@ -85,6 +87,9 @@ export const mockedWorkspaceMemberData: WorkspaceMember = { updatedAt: '2023-04-26T10:23:42.33625+00:00', userId: '2603c1f9-0172-4ea6-986c-eeaccdf7f4cf', userEmail: 'charles@test.com', + dateFormat: WorkspaceMemberDateFormatEnum.DayFirst, + timeFormat: WorkspaceMemberTimeFormatEnum.Hour_24, + timeZone: 'America/New_York', }; export const mockedUserData: MockedUser = { diff --git a/packages/twenty-front/src/utils/__tests__/is-url.test.ts b/packages/twenty-front/src/utils/__tests__/is-url.test.ts deleted file mode 100644 index a31cf05240b2..000000000000 --- a/packages/twenty-front/src/utils/__tests__/is-url.test.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { isURL } from '~/utils/is-url'; - -describe('isURL', () => { - it(`should return false if null`, () => { - expect(isURL(null)).toBeFalsy(); - }); - - it(`should return false if undefined`, () => { - expect(isURL(undefined)).toBeFalsy(); - }); - - it(`should return true if string google`, () => { - expect(isURL('google')).toBeFalsy(); - }); - - it(`should return true if string google.com`, () => { - expect(isURL('google.com')).toBeTruthy(); - }); - - it(`should return true if string bbc.co.uk`, () => { - expect(isURL('bbc.co.uk')).toBeTruthy(); - }); - - it(`should return true if string web.io`, () => { - expect(isURL('web.io')).toBeTruthy(); - }); - - it(`should return true if string x.com`, () => { - expect(isURL('x.com')).toBeTruthy(); - }); - - it(`should return true if string 2.com`, () => { - expect(isURL('2.com')).toBeTruthy(); - }); - - it(`should return true if string https://2.com/test/`, () => { - expect(isURL('https://2.com/test/')).toBeTruthy(); - }); - - it(`should return true if string is https://www.linkedin.com/company/b%C3%B6ke-&-partner-sdft-partmbb/`, () => { - expect( - isURL( - 'https://www.linkedin.com/company/b%C3%B6ke-&-partner-sdft-partmbb/', - ), - ).toBeTruthy(); - }); - - it('should return true if the TLD is long', () => { - expect(isURL('https://example.travelinsurance')).toBeTruthy(); - }); - - it('should return true if the TLD is internationalized', () => { - // The longest TLD as of now - // https://stackoverflow.com/questions/9238640/how-long-can-a-tld-possibly-be - // curl -s http://data.iana.org/TLD/tlds-alpha-by-domain.txt \ - // | tail -n+2 \ - // | awk '{ print length, $0 }' \ - // | sort --numeric-sort --reverse \ - // | head -n 5 - expect(isURL('https://example.xn--vermgensberatung-pwb')).toBeTruthy(); - }); -}); diff --git a/packages/twenty-front/src/utils/is-url.ts b/packages/twenty-front/src/utils/is-url.ts deleted file mode 100644 index bb6e8f7ee613..000000000000 --- a/packages/twenty-front/src/utils/is-url.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { isDefined } from './isDefined'; - -export const isURL = (url: string | undefined | null) => - isDefined(url) && - url.match( - /^(https?:\/\/)?(www.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-z]{2,63}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/i, - ); diff --git a/packages/twenty-front/tsup.config.ts b/packages/twenty-front/tsup.config.ts index 5f0aab5fc40c..565c6d450dc2 100644 --- a/packages/twenty-front/tsup.config.ts +++ b/packages/twenty-front/tsup.config.ts @@ -1,6 +1,6 @@ -import { defineConfig } from 'tsup'; -import svgr from 'esbuild-plugin-svgr'; import { Config } from '@svgr/core'; +import svgr from 'esbuild-plugin-svgr'; +import { defineConfig } from 'tsup'; const template: Config['template'] = (variables, { tpl }) => { return tpl` diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/compute-pg-graphql-error.util.ts b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/compute-pg-graphql-error.util.ts index ebb8ae5e34ce..bab858d6c3b5 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/compute-pg-graphql-error.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-query-runner/utils/compute-pg-graphql-error.util.ts @@ -57,8 +57,8 @@ export const computePgGraphQLError = ( const error = errors[0]; const errorMessage = error?.message; - const mappedErrorKey = Object.keys(pgGraphQLErrorMapping).find( - (key) => errorMessage?.includes(key), + const mappedErrorKey = Object.keys(pgGraphQLErrorMapping).find((key) => + errorMessage?.includes(key), ); const mappedError = mappedErrorKey diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts index 6b40eb2db972..467488192455 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-many-resolver.factory.ts @@ -25,9 +25,9 @@ export class CreateManyResolverFactory ): Resolver { const internalContext = context; - return (_source, args, context, info) => { + return async (_source, args, context, info) => { try { - return this.workspaceQueryRunnerService.createMany(args, { + return await this.workspaceQueryRunnerService.createMany(args, { authContext: internalContext.authContext, objectMetadataItem: internalContext.objectMetadataItem, info, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts index 5ae1a939e6bf..24fdfe0239fd 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/create-one-resolver.factory.ts @@ -25,9 +25,9 @@ export class CreateOneResolverFactory ): Resolver { const internalContext = context; - return (_source, args, context, info) => { + return async (_source, args, context, info) => { try { - return this.workspaceQueryRunnerService.createOne(args, { + return await this.workspaceQueryRunnerService.createOne(args, { authContext: internalContext.authContext, objectMetadataItem: internalContext.objectMetadataItem, info, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory.ts index ba415d92cc14..a8d36f3e4900 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory.ts @@ -25,9 +25,9 @@ export class DeleteManyResolverFactory ): Resolver { const internalContext = context; - return (_source, args, context, info) => { + return async (_source, args, context, info) => { try { - return this.workspaceQueryRunnerService.deleteMany(args, { + return await this.workspaceQueryRunnerService.deleteMany(args, { authContext: internalContext.authContext, objectMetadataItem: internalContext.objectMetadataItem, info, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-one-resolver.factory.ts index f9bde1bf37ce..93f249cdd6ac 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/delete-one-resolver.factory.ts @@ -25,9 +25,9 @@ export class DeleteOneResolverFactory ): Resolver { const internalContext = context; - return (_source, args, context, info) => { + return async (_source, args, context, info) => { try { - return this.workspaceQueryRunnerService.deleteOne(args, { + return await this.workspaceQueryRunnerService.deleteOne(args, { authContext: internalContext.authContext, objectMetadataItem: internalContext.objectMetadataItem, info, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/execute-quick-action-on-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/execute-quick-action-on-one-resolver.factory.ts deleted file mode 100644 index 278dea7f50b4..000000000000 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/execute-quick-action-on-one-resolver.factory.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -import { Record as IRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface'; -import { WorkspaceQueryRunnerOptions } from 'src/engine/api/graphql/workspace-query-runner/interfaces/query-runner-option.interface'; -import { WorkspaceResolverBuilderFactoryInterface } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolver-builder-factory.interface'; -import { - DeleteOneResolverArgs, - ExecuteQuickActionOnOneResolverArgs, - FindOneResolverArgs, - Resolver, -} from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; -import { WorkspaceSchemaBuilderContext } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/workspace-schema-builder-context.interface'; - -import { workspaceQueryRunnerGraphqlApiExceptionHandler } from 'src/engine/api/graphql/workspace-query-runner/utils/workspace-query-runner-graphql-api-exception-handler.util'; -import { WorkspaceQueryRunnerService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service'; -import { QuickActionsService } from 'src/engine/core-modules/quick-actions/quick-actions.service'; - -@Injectable() -export class ExecuteQuickActionOnOneResolverFactory - implements WorkspaceResolverBuilderFactoryInterface -{ - public static methodName = 'executeQuickActionOnOne' as const; - - constructor( - private readonly workspaceQueryRunnerService: WorkspaceQueryRunnerService, - private readonly quickActionsService: QuickActionsService, - ) {} - - create( - context: WorkspaceSchemaBuilderContext, - ): Resolver { - const internalContext = context; - - return (_source, args, context, info) => { - try { - return this.executeQuickActionOnOne(args, { - authContext: internalContext.authContext, - objectMetadataItem: internalContext.objectMetadataItem, - info, - fieldMetadataCollection: internalContext.fieldMetadataCollection, - objectMetadataCollection: internalContext.objectMetadataCollection, - }); - } catch (error) { - workspaceQueryRunnerGraphqlApiExceptionHandler(error); - } - }; - } - - private async executeQuickActionOnOne( - args: DeleteOneResolverArgs, - options: WorkspaceQueryRunnerOptions, - ): Promise { - switch (options.objectMetadataItem.nameSingular) { - case 'company': { - await this.quickActionsService.executeQuickActionOnCompany( - args.id, - options.authContext.workspace.id, - options.objectMetadataItem, - ); - break; - } - case 'person': { - await this.quickActionsService.createCompanyFromPerson( - args.id, - options.authContext.workspace.id, - options.objectMetadataCollection, - ); - break; - } - default: - // TODO: different quick actions per object on frontend - break; - } - - return this.workspaceQueryRunnerService.findOne( - { filter: { id: { eq: args.id } } } as FindOneResolverArgs, - options, - ); - } -} diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/factories.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/factories.ts index 9bd0f4432e7c..2f4a88b34d2a 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/factories.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/factories.ts @@ -1,14 +1,13 @@ import { UpdateManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory'; +import { CreateManyResolverFactory } from './create-many-resolver.factory'; +import { CreateOneResolverFactory } from './create-one-resolver.factory'; +import { DeleteManyResolverFactory } from './delete-many-resolver.factory'; +import { DeleteOneResolverFactory } from './delete-one-resolver.factory'; import { FindDuplicatesResolverFactory } from './find-duplicates-resolver.factory'; import { FindManyResolverFactory } from './find-many-resolver.factory'; import { FindOneResolverFactory } from './find-one-resolver.factory'; -import { CreateManyResolverFactory } from './create-many-resolver.factory'; -import { CreateOneResolverFactory } from './create-one-resolver.factory'; import { UpdateOneResolverFactory } from './update-one-resolver.factory'; -import { DeleteOneResolverFactory } from './delete-one-resolver.factory'; -import { DeleteManyResolverFactory } from './delete-many-resolver.factory'; -import { ExecuteQuickActionOnOneResolverFactory } from './execute-quick-action-on-one-resolver.factory'; export const workspaceResolverBuilderFactories = [ FindManyResolverFactory, @@ -18,7 +17,6 @@ export const workspaceResolverBuilderFactories = [ CreateOneResolverFactory, UpdateOneResolverFactory, DeleteOneResolverFactory, - ExecuteQuickActionOnOneResolverFactory, UpdateManyResolverFactory, DeleteManyResolverFactory, ]; @@ -34,7 +32,6 @@ export const workspaceResolverBuilderMethodNames = { CreateOneResolverFactory.methodName, UpdateOneResolverFactory.methodName, DeleteOneResolverFactory.methodName, - ExecuteQuickActionOnOneResolverFactory.methodName, UpdateManyResolverFactory.methodName, DeleteManyResolverFactory.methodName, ], diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory.ts index bba60e72115a..0a1494efb666 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-duplicates-resolver.factory.ts @@ -25,9 +25,9 @@ export class FindDuplicatesResolverFactory ): Resolver { const internalContext = context; - return (_source, args, context, info) => { + return async (_source, args, context, info) => { try { - return this.workspaceQueryRunnerService.findDuplicates(args, { + return await this.workspaceQueryRunnerService.findDuplicates(args, { authContext: internalContext.authContext, objectMetadataItem: internalContext.objectMetadataItem, info, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-many-resolver.factory.ts index 24ee24f745f6..76391b27ed2f 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-many-resolver.factory.ts @@ -25,9 +25,9 @@ export class FindManyResolverFactory ): Resolver { const internalContext = context; - return (_source, args, context, info) => { + return async (_source, args, context, info) => { try { - return this.workspaceQueryRunnerService.findMany(args, { + return await this.workspaceQueryRunnerService.findMany(args, { authContext: internalContext.authContext, objectMetadataItem: internalContext.objectMetadataItem, info, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-one-resolver.factory.ts index 47a51e4ad8e0..d6a679593789 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/find-one-resolver.factory.ts @@ -25,9 +25,9 @@ export class FindOneResolverFactory ): Resolver { const internalContext = context; - return (_source, args, context, info) => { + return async (_source, args, context, info) => { try { - return this.workspaceQueryRunnerService.findOne(args, { + return await this.workspaceQueryRunnerService.findOne(args, { authContext: internalContext.authContext, objectMetadataItem: internalContext.objectMetadataItem, info, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory.ts index 888764ec2466..c2328d12ba05 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory.ts @@ -25,9 +25,9 @@ export class UpdateManyResolverFactory ): Resolver { const internalContext = context; - return (_source, args, context, info) => { + return async (_source, args, context, info) => { try { - return this.workspaceQueryRunnerService.updateMany(args, { + return await this.workspaceQueryRunnerService.updateMany(args, { authContext: internalContext.authContext, objectMetadataItem: internalContext.objectMetadataItem, info, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-one-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-one-resolver.factory.ts index 2e802c3ff13b..c7a7dc6bacd6 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-one-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/factories/update-one-resolver.factory.ts @@ -25,9 +25,9 @@ export class UpdateOneResolverFactory ): Resolver { const internalContext = context; - return (_source, args, context, info) => { + return async (_source, args, context, info) => { try { - return this.workspaceQueryRunnerService.updateOne(args, { + return await this.workspaceQueryRunnerService.updateOne(args, { authContext: internalContext.authContext, objectMetadataItem: internalContext.objectMetadataItem, info, diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface.ts index a2db909472bc..2727a3fdc605 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface.ts @@ -20,7 +20,6 @@ export enum ResolverArgsType { UpdateMany = 'UpdateMany', DeleteOne = 'DeleteOne', DeleteMany = 'DeleteMany', - ExecuteQuickActionOnOne = 'ExecuteQuickActionOnOne', } export interface FindManyResolverArgs< @@ -79,10 +78,6 @@ export interface DeleteOneResolverArgs { id: string; } -export interface ExecuteQuickActionOnOneResolverArgs { - id: string; -} - export interface DeleteManyResolverArgs { filter: Filter; } diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/workspace-resolver-builder.module.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/workspace-resolver-builder.module.ts index 85a84f17a647..7b87e49a8d76 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/workspace-resolver-builder.module.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/workspace-resolver-builder.module.ts @@ -1,14 +1,13 @@ import { Module } from '@nestjs/common'; import { WorkspaceQueryRunnerModule } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module'; -import { QuickActionsModule } from 'src/engine/core-modules/quick-actions/quick-actions.module'; import { WorkspaceResolverFactory } from './workspace-resolver.factory'; import { workspaceResolverBuilderFactories } from './factories/factories'; @Module({ - imports: [WorkspaceQueryRunnerModule, QuickActionsModule], + imports: [WorkspaceQueryRunnerModule], providers: [...workspaceResolverBuilderFactories, WorkspaceResolverFactory], exports: [WorkspaceResolverFactory], }) diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/workspace-resolver.factory.ts b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/workspace-resolver.factory.ts index 87e94673ea82..7fed8b96c613 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/workspace-resolver.factory.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-resolver-builder/workspace-resolver.factory.ts @@ -4,24 +4,23 @@ import { IResolvers } from '@graphql-tools/utils'; import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface'; -import { getResolverName } from 'src/engine/utils/get-resolver-name.util'; -import { UpdateManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory'; import { DeleteManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/delete-many-resolver.factory'; -import { ExecuteQuickActionOnOneResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/execute-quick-action-on-one-resolver.factory'; +import { UpdateManyResolverFactory } from 'src/engine/api/graphql/workspace-resolver-builder/factories/update-many-resolver.factory'; import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; +import { getResolverName } from 'src/engine/utils/get-resolver-name.util'; +import { CreateManyResolverFactory } from './factories/create-many-resolver.factory'; +import { CreateOneResolverFactory } from './factories/create-one-resolver.factory'; +import { DeleteOneResolverFactory } from './factories/delete-one-resolver.factory'; import { FindDuplicatesResolverFactory } from './factories/find-duplicates-resolver.factory'; import { FindManyResolverFactory } from './factories/find-many-resolver.factory'; import { FindOneResolverFactory } from './factories/find-one-resolver.factory'; -import { CreateManyResolverFactory } from './factories/create-many-resolver.factory'; -import { CreateOneResolverFactory } from './factories/create-one-resolver.factory'; import { UpdateOneResolverFactory } from './factories/update-one-resolver.factory'; -import { DeleteOneResolverFactory } from './factories/delete-one-resolver.factory'; +import { WorkspaceResolverBuilderFactoryInterface } from './interfaces/workspace-resolver-builder-factory.interface'; import { WorkspaceResolverBuilderMethodNames, WorkspaceResolverBuilderMethods, } from './interfaces/workspace-resolvers-builder.interface'; -import { WorkspaceResolverBuilderFactoryInterface } from './interfaces/workspace-resolver-builder-factory.interface'; @Injectable() export class WorkspaceResolverFactory { @@ -35,7 +34,6 @@ export class WorkspaceResolverFactory { private readonly createOneResolverFactory: CreateOneResolverFactory, private readonly updateOneResolverFactory: UpdateOneResolverFactory, private readonly deleteOneResolverFactory: DeleteOneResolverFactory, - private readonly executeQuickActionOnOneResolverFactory: ExecuteQuickActionOnOneResolverFactory, private readonly updateManyResolverFactory: UpdateManyResolverFactory, private readonly deleteManyResolverFactory: DeleteManyResolverFactory, ) {} @@ -56,7 +54,6 @@ export class WorkspaceResolverFactory { ['createOne', this.createOneResolverFactory], ['updateOne', this.updateOneResolverFactory], ['deleteOne', this.deleteOneResolverFactory], - ['executeQuickActionOnOne', this.executeQuickActionOnOneResolverFactory], ['updateMany', this.updateManyResolverFactory], ['deleteMany', this.deleteManyResolverFactory], ]); diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/utils/__tests__/get-resolver-args.spec.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/utils/__tests__/get-resolver-args.spec.ts index 26652d04a69a..72a9de127dc4 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/utils/__tests__/get-resolver-args.spec.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/utils/__tests__/get-resolver-args.spec.ts @@ -50,9 +50,6 @@ describe('getResolverArgs', () => { deleteOne: { id: { type: GraphQLID, isNullable: false }, }, - executeQuickActionOnOne: { - id: { type: GraphQLID, isNullable: false }, - }, }; // Test each resolver type diff --git a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util.ts b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util.ts index 609f268fd81f..a279282b50dc 100644 --- a/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util.ts +++ b/packages/twenty-server/src/engine/api/graphql/workspace-schema-builder/utils/get-resolver-args.util.ts @@ -1,4 +1,4 @@ -import { GraphQLString, GraphQLInt, GraphQLID, GraphQLBoolean } from 'graphql'; +import { GraphQLBoolean, GraphQLID, GraphQLInt, GraphQLString } from 'graphql'; import { WorkspaceResolverBuilderMethodNames } from 'src/engine/api/graphql/workspace-resolver-builder/interfaces/workspace-resolvers-builder.interface'; import { ArgMetadata } from 'src/engine/api/graphql/workspace-schema-builder/interfaces/param-metadata.interface'; @@ -105,13 +105,6 @@ export const getResolverArgs = ( isNullable: false, }, }; - case 'executeQuickActionOnOne': - return { - id: { - type: GraphQLID, - isNullable: false, - }, - }; case 'updateMany': return { data: { diff --git a/packages/twenty-server/src/engine/core-modules/auth/auth.exception.ts b/packages/twenty-server/src/engine/core-modules/auth/auth.exception.ts index c245db9717fe..6195fd37de77 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/auth.exception.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/auth.exception.ts @@ -12,6 +12,7 @@ export enum AuthExceptionCode { CLIENT_NOT_FOUND = 'CLIENT_NOT_FOUND', INVALID_INPUT = 'INVALID_INPUT', FORBIDDEN_EXCEPTION = 'FORBIDDEN_EXCEPTION', + UNAUTHENTICATED = 'UNAUTHENTICATED', INVALID_DATA = 'INVALID_DATA', INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR', } diff --git a/packages/twenty-server/src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter.ts b/packages/twenty-server/src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter.ts index ebabba88f127..c33c724f044a 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter.ts @@ -1,10 +1,11 @@ -import { Catch } from '@nestjs/common'; +import { Catch, ExceptionFilter } from '@nestjs/common'; import { AuthException, AuthExceptionCode, } from 'src/engine/core-modules/auth/auth.exception'; import { + AuthenticationError, ForbiddenError, InternalServerError, NotFoundError, @@ -12,7 +13,7 @@ import { } from 'src/engine/core-modules/graphql/utils/graphql-errors.util'; @Catch(AuthException) -export class AuthGraphqlApiExceptionFilter { +export class AuthGraphqlApiExceptionFilter implements ExceptionFilter { catch(exception: AuthException) { switch (exception.code) { case AuthExceptionCode.USER_NOT_FOUND: @@ -22,6 +23,8 @@ export class AuthGraphqlApiExceptionFilter { throw new UserInputError(exception.message); case AuthExceptionCode.FORBIDDEN_EXCEPTION: throw new ForbiddenError(exception.message); + case AuthExceptionCode.UNAUTHENTICATED: + throw new AuthenticationError(exception.message); case AuthExceptionCode.INVALID_DATA: case AuthExceptionCode.INTERNAL_SERVER_ERROR: default: diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/token.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/token.service.ts index 9a45a3a37abc..da571fbe5184 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/token.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/token.service.ts @@ -564,12 +564,12 @@ export class TokenService { if (error instanceof TokenExpiredError) { throw new AuthException( 'Token has expired.', - AuthExceptionCode.FORBIDDEN_EXCEPTION, + AuthExceptionCode.UNAUTHENTICATED, ); } else if (error instanceof JsonWebTokenError) { throw new AuthException( 'Token invalid.', - AuthExceptionCode.FORBIDDEN_EXCEPTION, + AuthExceptionCode.UNAUTHENTICATED, ); } else { throw new AuthException( diff --git a/packages/twenty-server/src/engine/core-modules/messaging/timeline-messaging.service.ts b/packages/twenty-server/src/engine/core-modules/messaging/timeline-messaging.service.ts index eeef55833439..5982b0d3148d 100644 --- a/packages/twenty-server/src/engine/core-modules/messaging/timeline-messaging.service.ts +++ b/packages/twenty-server/src/engine/core-modules/messaging/timeline-messaging.service.ts @@ -405,9 +405,9 @@ export class TimelineMessagingService { messageThreadIdsForWhichWorkspaceMemberIsNotInParticipants.includes( messageThreadId, ) - ? threadVisibilityByThreadIdForWhichWorkspaceMemberIsNotInParticipants?.[ + ? (threadVisibilityByThreadIdForWhichWorkspaceMemberIsNotInParticipants?.[ messageThreadId - ] ?? MessageChannelVisibility.METADATA + ] ?? MessageChannelVisibility.METADATA) : MessageChannelVisibility.SHARE_EVERYTHING; return threadVisibilityAcc; diff --git a/packages/twenty-server/src/engine/core-modules/quick-actions/intelligence.service.ts b/packages/twenty-server/src/engine/core-modules/quick-actions/intelligence.service.ts deleted file mode 100644 index 74c4d252fdfa..000000000000 --- a/packages/twenty-server/src/engine/core-modules/quick-actions/intelligence.service.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { HttpService } from '@nestjs/axios'; -import { Injectable } from '@nestjs/common'; - -import { CompanyInteface } from 'src/engine/core-modules/quick-actions/interfaces/company.interface'; - -import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; - -@Injectable() -export class IntelligenceService { - constructor( - private readonly environmentService: EnvironmentService, - private readonly httpService: HttpService, - ) {} - - async enrichCompany(domainName: string): Promise { - const enrichedCompany = await this.httpService.axiosRef.get( - `https://companies.twenty.com/${domainName}`, - { - validateStatus: function () { - // This ensures the promise is always resolved, preventing axios from throwing an error - return true; - }, - }, - ); - - if (enrichedCompany.status !== 200) { - return {}; - } - - return { - linkedinLinkPrimaryLinkUrl: - `https://linkedin.com/` + enrichedCompany.data.handle, - }; - } - - async completeWithAi(content: string) { - return this.httpService.axiosRef.post( - 'https://openrouter.ai/api/v1/chat/completions', - { - headers: { - Authorization: `Bearer ${this.environmentService.get( - 'OPENROUTER_API_KEY', - )}`, - 'HTTP-Referer': `https://twenty.com`, - 'X-Title': `Twenty CRM`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - model: 'mistralai/mixtral-8x7b-instruct', - messages: [{ role: 'user', content: content }], - }), - }, - ); - } -} diff --git a/packages/twenty-server/src/engine/core-modules/quick-actions/interfaces/company.interface.ts b/packages/twenty-server/src/engine/core-modules/quick-actions/interfaces/company.interface.ts deleted file mode 100644 index 0b765beb014d..000000000000 --- a/packages/twenty-server/src/engine/core-modules/quick-actions/interfaces/company.interface.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface CompanyInteface { - linkedinLinkPrimaryLinkUrl?: string; -} diff --git a/packages/twenty-server/src/engine/core-modules/quick-actions/quick-actions.module.ts b/packages/twenty-server/src/engine/core-modules/quick-actions/quick-actions.module.ts deleted file mode 100644 index 95fa48dbcd19..000000000000 --- a/packages/twenty-server/src/engine/core-modules/quick-actions/quick-actions.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Module } from '@nestjs/common'; -import { HttpModule } from '@nestjs/axios'; - -import { IntelligenceService } from 'src/engine/core-modules/quick-actions/intelligence.service'; -import { QuickActionsService } from 'src/engine/core-modules/quick-actions/quick-actions.service'; -import { WorkspaceQueryRunnerModule } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.module'; - -@Module({ - imports: [WorkspaceQueryRunnerModule, HttpModule], - controllers: [], - providers: [QuickActionsService, IntelligenceService], - exports: [QuickActionsService, IntelligenceService], -}) -export class QuickActionsModule {} diff --git a/packages/twenty-server/src/engine/core-modules/quick-actions/quick-actions.service.ts b/packages/twenty-server/src/engine/core-modules/quick-actions/quick-actions.service.ts deleted file mode 100644 index 63789e57ba68..000000000000 --- a/packages/twenty-server/src/engine/core-modules/quick-actions/quick-actions.service.ts +++ /dev/null @@ -1,181 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -import { v4 as uuidv4 } from 'uuid'; - -import { Record as IRecord } from 'src/engine/api/graphql/workspace-query-builder/interfaces/record.interface'; -import { ObjectMetadataInterface } from 'src/engine/metadata-modules/field-metadata/interfaces/object-metadata.interface'; - -import { stringifyWithoutKeyQuote } from 'src/engine/api/graphql/workspace-query-builder/utils/stringify-without-key-quote.util'; -import { WorkspaceQueryRunnerService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-runner.service'; -import { IntelligenceService } from 'src/engine/core-modules/quick-actions/intelligence.service'; -import { getCompanyNameFromDomainName } from 'src/modules/contact-creation-manager/utils/get-company-name-from-domain-name.util'; -import { capitalize } from 'src/utils/capitalize'; -import { getCompanyDomainName } from 'src/utils/getCompanyDomainName'; -import { isWorkEmail } from 'src/utils/is-work-email'; - -@Injectable() -export class QuickActionsService { - constructor( - private readonly workspaceQueryRunnunerService: WorkspaceQueryRunnerService, - private readonly intelligenceService: IntelligenceService, - ) {} - - async createCompanyFromPerson( - id: string, - workspaceId: string, - objectMetadataItemCollection: ObjectMetadataInterface[], - ) { - const personObjectMetadata = objectMetadataItemCollection.find( - (item) => item.nameSingular === 'person', - ); - - if (!personObjectMetadata) { - return; - } - - const personRequest = - await this.workspaceQueryRunnunerService.executeAndParse( - `query { - personCollection(filter: {id: {eq: "${id}"}}) { - edges { - node { - id - email - companyId - } - } - } - } - `, - personObjectMetadata, - '', - workspaceId, - ); - const person = personRequest.edges?.[0]?.node; - - if (!person) { - return; - } - - if (!person.companyId && person.email && isWorkEmail(person.email)) { - const companyDomainName = person.email.split('@')?.[1].toLowerCase(); - const companyName = capitalize(companyDomainName.split('.')[0]); - let relatedCompanyId = uuidv4(); - - const companyObjectMetadata = objectMetadataItemCollection.find( - (item) => item.nameSingular === 'company', - ); - - if (!companyObjectMetadata) { - return; - } - - const existingCompany = - await this.workspaceQueryRunnunerService.executeAndParse( - `query {companyCollection(filter: {domainName: {eq: "${companyDomainName}"}}) { - edges { - node { - id - } - } - } - } - `, - companyObjectMetadata, - '', - workspaceId, - ); - - if (existingCompany.edges?.length) { - relatedCompanyId = existingCompany.edges[0].node.id; - } - - await this.workspaceQueryRunnunerService.execute( - `mutation { - insertIntocompanyCollection(objects: ${stringifyWithoutKeyQuote([ - { - id: relatedCompanyId, - name: companyName, - domainName: companyDomainName, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - }, - ])}) { - affectedCount - records { - id - } - } - } - `, - workspaceId, - ); - - await this.workspaceQueryRunnunerService.execute( - `mutation { - updatepersonCollection(set: ${stringifyWithoutKeyQuote({ - companyId: relatedCompanyId, - })}, filter: { id: { eq: "${person.id}" } }) { - affectedCount - records { - id - } - } - } - `, - workspaceId, - ); - } - } - - async executeQuickActionOnCompany( - id: string, - workspaceId: string, - objectMetadataItem: ObjectMetadataInterface, - ) { - const companyQuery = `query { - companyCollection(filter: {id: {eq: "${id}"}}) { - edges { - node { - id - domainName - createdAt - linkedinLinkPrimaryLinkUrl - } - } - } - } - `; - - const companyRequest = - await this.workspaceQueryRunnunerService.executeAndParse( - companyQuery, - objectMetadataItem, - '', - workspaceId, - ); - const company = companyRequest.edges?.[0]?.node; - - if (!company) { - return; - } - - const enrichedData = await this.intelligenceService.enrichCompany( - getCompanyNameFromDomainName(getCompanyDomainName(company)), - ); - - await this.workspaceQueryRunnunerService.execute( - `mutation { - updatecompanyCollection(set: ${stringifyWithoutKeyQuote( - enrichedData, - )}, filter: { id: { eq: "${id}" } }) { - affectedCount - records { - id - } - } - }`, - workspaceId, - ); - } -} diff --git a/packages/twenty-server/src/engine/integrations/environment/environment-variables.ts b/packages/twenty-server/src/engine/integrations/environment/environment-variables.ts index c2e67a200fd0..85c21c1a37ba 100644 --- a/packages/twenty-server/src/engine/integrations/environment/environment-variables.ts +++ b/packages/twenty-server/src/engine/integrations/environment/environment-variables.ts @@ -2,17 +2,17 @@ import { LogLevel } from '@nestjs/common'; import { plainToClass } from 'class-transformer'; import { + IsBoolean, + IsDefined, IsEnum, + IsNumber, IsOptional, IsString, IsUrl, + Max, + Min, ValidateIf, validateSync, - IsBoolean, - IsNumber, - IsDefined, - Min, - Max, } from 'class-validator'; import { EmailDriver } from 'src/engine/integrations/email/interfaces/email.interface'; @@ -20,24 +20,24 @@ import { NodeEnvironment } from 'src/engine/integrations/environment/interfaces/ import { LLMChatModelDriver } from 'src/engine/integrations/llm-chat-model/interfaces/llm-chat-model.interface'; import { LLMTracingDriver } from 'src/engine/integrations/llm-tracing/interfaces/llm-tracing.interface'; -import { ServerlessDriverType } from 'src/engine/integrations/serverless/serverless.interface'; -import { assert } from 'src/utils/assert'; +import { CacheStorageType } from 'src/engine/integrations/cache-storage/types/cache-storage-type.enum'; +import { CaptchaDriverType } from 'src/engine/integrations/captcha/interfaces'; import { CastToStringArray } from 'src/engine/integrations/environment/decorators/cast-to-string-array.decorator'; +import { IsStrictlyLowerThan } from 'src/engine/integrations/environment/decorators/is-strictly-lower-than.decorator'; import { ExceptionHandlerDriver } from 'src/engine/integrations/exception-handler/interfaces'; import { StorageDriverType } from 'src/engine/integrations/file-storage/interfaces'; import { LoggerDriverType } from 'src/engine/integrations/logger/interfaces'; -import { IsStrictlyLowerThan } from 'src/engine/integrations/environment/decorators/is-strictly-lower-than.decorator'; -import { CaptchaDriverType } from 'src/engine/integrations/captcha/interfaces'; import { MessageQueueDriverType } from 'src/engine/integrations/message-queue/interfaces'; -import { CacheStorageType } from 'src/engine/integrations/cache-storage/types/cache-storage-type.enum'; +import { ServerlessDriverType } from 'src/engine/integrations/serverless/serverless.interface'; +import { assert } from 'src/utils/assert'; +import { CastToBoolean } from './decorators/cast-to-boolean.decorator'; +import { CastToLogLevelArray } from './decorators/cast-to-log-level-array.decorator'; +import { CastToPositiveNumber } from './decorators/cast-to-positive-number.decorator'; +import { IsAWSRegion } from './decorators/is-aws-region.decorator'; import { IsDuration } from './decorators/is-duration.decorator'; import { AwsRegion } from './interfaces/aws-region.interface'; -import { IsAWSRegion } from './decorators/is-aws-region.decorator'; -import { CastToBoolean } from './decorators/cast-to-boolean.decorator'; import { SupportDriver } from './interfaces/support.interface'; -import { CastToPositiveNumber } from './decorators/cast-to-positive-number.decorator'; -import { CastToLogLevelArray } from './decorators/cast-to-log-level-array.decorator'; export class EnvironmentVariables { // Misc @@ -394,8 +394,6 @@ export class EnvironmentVariables { EMAIL_SMTP_PASSWORD: string; - OPENROUTER_API_KEY: string; - LLM_CHAT_MODEL_DRIVER: LLMChatModelDriver; OPENAI_API_KEY: string; diff --git a/packages/twenty-server/src/engine/integrations/message-queue/message-queue-core.module.ts b/packages/twenty-server/src/engine/integrations/message-queue/message-queue-core.module.ts index c22b0abce39c..6c785e23529d 100644 --- a/packages/twenty-server/src/engine/integrations/message-queue/message-queue-core.module.ts +++ b/packages/twenty-server/src/engine/integrations/message-queue/message-queue-core.module.ts @@ -63,9 +63,12 @@ export class MessageQueueCoreModule extends ConfigurableModuleClass { const driverProvider: Provider = { provide: QUEUE_DRIVER, useFactory: async (...args: any[]) => { - const config = await options.useFactory!(...args); + if (options.useFactory) { + const config = await options.useFactory(...args); - return this.createDriver(config); + return this.createDriver(config); + } + throw new Error('useFactory is not defined'); }, inject: options.inject || [], }; diff --git a/packages/twenty-server/src/engine/middlewares/graphql-hydrate-request-from-token.middleware.ts b/packages/twenty-server/src/engine/middlewares/graphql-hydrate-request-from-token.middleware.ts index 4a402cb27749..a756b8cef472 100644 --- a/packages/twenty-server/src/engine/middlewares/graphql-hydrate-request-from-token.middleware.ts +++ b/packages/twenty-server/src/engine/middlewares/graphql-hydrate-request-from-token.middleware.ts @@ -1,13 +1,32 @@ import { Injectable, NestMiddleware } from '@nestjs/common'; -import { Request, Response, NextFunction } from 'express'; +import { NextFunction, Request, Response } from 'express'; +import { AuthGraphqlApiExceptionFilter } from 'src/engine/core-modules/auth/filters/auth-graphql-api-exception.filter'; import { TokenService } from 'src/engine/core-modules/auth/services/token.service'; import { AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type'; import { ExceptionHandlerService } from 'src/engine/integrations/exception-handler/exception-handler.service'; import { WorkspaceCacheVersionService } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.service'; import { handleExceptionAndConvertToGraphQLError } from 'src/engine/utils/global-exception-handler.util'; +class GraphqlTokenValidationProxy { + private tokenService: TokenService; + + constructor(tokenService: TokenService) { + this.tokenService = tokenService; + } + + async validateToken(req: Request) { + try { + return await this.tokenService.validateToken(req); + } catch (error) { + const authGraphqlApiExceptionFilter = new AuthGraphqlApiExceptionFilter(); + + throw authGraphqlApiExceptionFilter.catch(error); + } + } +} + @Injectable() export class GraphQLHydrateRequestFromTokenMiddleware implements NestMiddleware @@ -48,7 +67,11 @@ export class GraphQLHydrateRequestFromTokenMiddleware let data: AuthContext; try { - data = await this.tokenService.validateToken(req); + const graphqlTokenValidationProxy = new GraphqlTokenValidationProxy( + this.tokenService, + ); + + data = await graphqlTokenValidationProxy.validateToken(req); const cacheVersion = await this.workspaceCacheVersionService.getVersion( data.workspace.id, ); diff --git a/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts b/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts index c98949d2af54..d0dc9a5cb072 100644 --- a/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts +++ b/packages/twenty-server/src/engine/twenty-orm/repository/workspace.repository.ts @@ -18,9 +18,9 @@ import { SaveOptions, UpdateResult, } from 'typeorm'; -import { PickKeysByType } from 'typeorm/common/PickKeysByType'; import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity'; import { UpsertOptions } from 'typeorm/repository/UpsertOptions'; +import { PickKeysByType } from 'typeorm/common/PickKeysByType'; import { WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface'; diff --git a/packages/twenty-server/src/engine/utils/__tests__/get-resolver-name.spec.ts b/packages/twenty-server/src/engine/utils/__tests__/get-resolver-name.spec.ts index 16560a4e63d2..55f327d19651 100644 --- a/packages/twenty-server/src/engine/utils/__tests__/get-resolver-name.spec.ts +++ b/packages/twenty-server/src/engine/utils/__tests__/get-resolver-name.spec.ts @@ -15,7 +15,6 @@ describe('getResolverName', () => { ['createOne', 'createEntity'], ['updateOne', 'updateEntity'], ['deleteOne', 'deleteEntity'], - ['executeQuickActionOnOne', 'executeQuickActionOnEntity'], ])('should return correct name for %s resolver', (type, expectedResult) => { expect( getResolverName(metadata, type as WorkspaceResolverBuilderMethodNames), diff --git a/packages/twenty-server/src/engine/utils/get-resolver-name.util.ts b/packages/twenty-server/src/engine/utils/get-resolver-name.util.ts index 1f4d0ebc7b31..4c35f0edafd6 100644 --- a/packages/twenty-server/src/engine/utils/get-resolver-name.util.ts +++ b/packages/twenty-server/src/engine/utils/get-resolver-name.util.ts @@ -23,8 +23,6 @@ export const getResolverName = ( return `update${pascalCase(objectMetadata.nameSingular)}`; case 'deleteOne': return `delete${pascalCase(objectMetadata.nameSingular)}`; - case 'executeQuickActionOnOne': - return `executeQuickActionOn${pascalCase(objectMetadata.nameSingular)}`; case 'updateMany': return `update${pascalCase(objectMetadata.namePlural)}`; case 'deleteMany': diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service.ts index 117ca1e59172..2a22bac5965f 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service.ts @@ -1,7 +1,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { InjectDataSource } from '@nestjs/typeorm'; -import { DataSource } from 'typeorm'; +import { DataSource, QueryFailedError } from 'typeorm'; import { WorkspaceSyncContext } from 'src/engine/workspace-manager/workspace-sync-metadata/interfaces/workspace-sync-context.interface'; @@ -150,6 +150,10 @@ export class WorkspaceSyncMetadataService { ); } catch (error) { this.logger.error('Sync of standard objects failed with:', error); + + if (error instanceof QueryFailedError && (error as any).detail) { + this.logger.error((error as any).detail); + } await queryRunner.rollbackTransaction(); } finally { await queryRunner.release(); diff --git a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company-and-contact.service.ts b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company-and-contact.service.ts index 57ede6f2dec8..6435f0d65327 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company-and-contact.service.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company-and-contact.service.ts @@ -7,8 +7,10 @@ import compact from 'lodash.compact'; import { Any, EntityManager, Repository } from 'typeorm'; import { ObjectRecordCreateEvent } from 'src/engine/integrations/event-emitter/types/object-record-create.event'; +import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { STANDARD_OBJECT_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-object-ids'; import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity'; import { CONTACTS_CREATION_BATCH_SIZE } from 'src/modules/contact-creation-manager/constants/contacts-creation-batch-size.constant'; @@ -22,8 +24,6 @@ import { PersonWorkspaceEntity } from 'src/modules/person/standard-objects/perso import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { isWorkEmail } from 'src/utils/is-work-email'; -import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; -import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; @Injectable() export class CreateCompanyAndContactService { @@ -105,7 +105,7 @@ export class CreateCompanyAndContactService { filteredContactsToCreateWithCompanyDomainNames .filter((participant) => participant.companyDomainName) .map((participant) => ({ - domainName: participant.companyDomainName!, + domainName: participant.companyDomainName, createdBySource: source, createdByWorkspaceMember: connectedAccount.accountOwner, })), @@ -196,7 +196,7 @@ export class CreateCompanyAndContactService { name: 'person.created', workspaceId, // FixMe: TypeORM typing issue... id is always returned when using save - recordId: createdPerson.id!, + recordId: createdPerson.id as string, objectMetadata, properties: { after: createdPerson, diff --git a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts index e294637d5035..6e9f3c8956c8 100644 --- a/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts +++ b/packages/twenty-server/src/modules/contact-creation-manager/services/create-company.service.ts @@ -14,7 +14,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta import { computeDisplayName } from 'src/utils/compute-display-name'; type CompanyToCreate = { - domainName: string; + domainName: string | undefined; createdBySource: FieldActorSource; createdByWorkspaceMember?: WorkspaceMemberWorkspaceEntity | null; }; @@ -186,7 +186,9 @@ export class CreateCompanyService { return lastCompanyPosition ?? 0; } - private async getCompanyInfoFromDomainName(domainName: string): Promise<{ + private async getCompanyInfoFromDomainName( + domainName: string | undefined, + ): Promise<{ name: string; city: string; }> { @@ -196,12 +198,12 @@ export class CreateCompanyService { const data = response.data; return { - name: data.name ?? getCompanyNameFromDomainName(domainName), + name: data.name ?? getCompanyNameFromDomainName(domainName ?? ''), city: data.city, }; } catch (e) { return { - name: getCompanyNameFromDomainName(domainName), + name: getCompanyNameFromDomainName(domainName ?? ''), city: '', }; } diff --git a/packages/twenty-server/src/queue-worker/queue-worker.ts b/packages/twenty-server/src/queue-worker/queue-worker.ts index 0b2af24eccd6..d38f49d306d5 100644 --- a/packages/twenty-server/src/queue-worker/queue-worker.ts +++ b/packages/twenty-server/src/queue-worker/queue-worker.ts @@ -1,8 +1,8 @@ import { NestFactory } from '@nestjs/core'; -import { shouldFilterException } from 'src/engine/utils/global-exception-handler.util'; import { ExceptionHandlerService } from 'src/engine/integrations/exception-handler/exception-handler.service'; import { LoggerService } from 'src/engine/integrations/logger/logger.service'; +import { shouldFilterException } from 'src/engine/utils/global-exception-handler.util'; import { QueueWorkerModule } from 'src/queue-worker/queue-worker.module'; async function bootstrap() { @@ -18,7 +18,7 @@ async function bootstrap() { exceptionHandlerService = app.get(ExceptionHandlerService); // Inject our logger - app.useLogger(loggerService!); + app.useLogger(loggerService ?? false); } catch (err) { loggerService?.error(err?.message, err?.name); diff --git a/packages/twenty-website/src/app/_components/docs/AlgoliaDocSearch.tsx b/packages/twenty-website/src/app/_components/docs/AlgoliaDocSearch.tsx index cbd4b5b7a1e0..041a03240caa 100644 --- a/packages/twenty-website/src/app/_components/docs/AlgoliaDocSearch.tsx +++ b/packages/twenty-website/src/app/_components/docs/AlgoliaDocSearch.tsx @@ -1,13 +1,6 @@ import { DocSearch } from '@docsearch/react'; -import { StoredDocSearchHit } from '@docsearch/react/dist/esm/types'; import { env } from 'next-runtime-env'; -interface AlgoliaHit extends StoredDocSearchHit { - _snippetResult?: { - content: { value: string }; - }; -} - interface AlgoliaDocSearchProps { pathname: string; } @@ -18,7 +11,7 @@ export const AlgoliaDocSearch = ({ pathname }: AlgoliaDocSearchProps) => { : 'developer'; return ( ( + hitComponent={({ hit }: { hit: any }) => (
diff --git a/yarn.lock b/yarn.lock index 88ddfe8287f7..15ad5e151edd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4616,13 +4616,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/aix-ppc64@npm:0.20.2" - conditions: os=aix & cpu=ppc64 - languageName: node - linkType: hard - "@esbuild/aix-ppc64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/aix-ppc64@npm:0.21.5" @@ -4651,13 +4644,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/android-arm64@npm:0.20.2" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/android-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-arm64@npm:0.21.5" @@ -4686,13 +4672,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/android-arm@npm:0.20.2" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - "@esbuild/android-arm@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-arm@npm:0.21.5" @@ -4721,13 +4700,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/android-x64@npm:0.20.2" - conditions: os=android & cpu=x64 - languageName: node - linkType: hard - "@esbuild/android-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/android-x64@npm:0.21.5" @@ -4756,13 +4728,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/darwin-arm64@npm:0.20.2" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/darwin-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/darwin-arm64@npm:0.21.5" @@ -4791,13 +4756,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/darwin-x64@npm:0.20.2" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - "@esbuild/darwin-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/darwin-x64@npm:0.21.5" @@ -4826,13 +4784,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/freebsd-arm64@npm:0.20.2" - conditions: os=freebsd & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/freebsd-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/freebsd-arm64@npm:0.21.5" @@ -4861,13 +4812,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/freebsd-x64@npm:0.20.2" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/freebsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/freebsd-x64@npm:0.21.5" @@ -4896,13 +4840,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/linux-arm64@npm:0.20.2" - conditions: os=linux & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/linux-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-arm64@npm:0.21.5" @@ -4931,13 +4868,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/linux-arm@npm:0.20.2" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - "@esbuild/linux-arm@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-arm@npm:0.21.5" @@ -4966,13 +4896,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/linux-ia32@npm:0.20.2" - conditions: os=linux & cpu=ia32 - languageName: node - linkType: hard - "@esbuild/linux-ia32@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-ia32@npm:0.21.5" @@ -5001,13 +4924,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/linux-loong64@npm:0.20.2" - conditions: os=linux & cpu=loong64 - languageName: node - linkType: hard - "@esbuild/linux-loong64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-loong64@npm:0.21.5" @@ -5036,13 +4952,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/linux-mips64el@npm:0.20.2" - conditions: os=linux & cpu=mips64el - languageName: node - linkType: hard - "@esbuild/linux-mips64el@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-mips64el@npm:0.21.5" @@ -5071,13 +4980,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/linux-ppc64@npm:0.20.2" - conditions: os=linux & cpu=ppc64 - languageName: node - linkType: hard - "@esbuild/linux-ppc64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-ppc64@npm:0.21.5" @@ -5106,13 +5008,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/linux-riscv64@npm:0.20.2" - conditions: os=linux & cpu=riscv64 - languageName: node - linkType: hard - "@esbuild/linux-riscv64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-riscv64@npm:0.21.5" @@ -5141,13 +5036,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/linux-s390x@npm:0.20.2" - conditions: os=linux & cpu=s390x - languageName: node - linkType: hard - "@esbuild/linux-s390x@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-s390x@npm:0.21.5" @@ -5176,13 +5064,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/linux-x64@npm:0.20.2" - conditions: os=linux & cpu=x64 - languageName: node - linkType: hard - "@esbuild/linux-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/linux-x64@npm:0.21.5" @@ -5211,13 +5092,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/netbsd-x64@npm:0.20.2" - conditions: os=netbsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/netbsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/netbsd-x64@npm:0.21.5" @@ -5253,13 +5127,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/openbsd-x64@npm:0.20.2" - conditions: os=openbsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/openbsd-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/openbsd-x64@npm:0.21.5" @@ -5288,13 +5155,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/sunos-x64@npm:0.20.2" - conditions: os=sunos & cpu=x64 - languageName: node - linkType: hard - "@esbuild/sunos-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/sunos-x64@npm:0.21.5" @@ -5323,13 +5183,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/win32-arm64@npm:0.20.2" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/win32-arm64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-arm64@npm:0.21.5" @@ -5358,13 +5211,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/win32-ia32@npm:0.20.2" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - "@esbuild/win32-ia32@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-ia32@npm:0.21.5" @@ -5393,13 +5239,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.20.2": - version: 0.20.2 - resolution: "@esbuild/win32-x64@npm:0.20.2" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - "@esbuild/win32-x64@npm:0.21.5": version: 0.21.5 resolution: "@esbuild/win32-x64@npm:0.21.5" @@ -13785,6 +13624,17 @@ __metadata: languageName: node linkType: hard +"@storybook/addons@npm:^7.0.0": + version: 7.6.20 + resolution: "@storybook/addons@npm:7.6.20" + dependencies: + "@storybook/manager-api": "npm:7.6.20" + "@storybook/preview-api": "npm:7.6.20" + "@storybook/types": "npm:7.6.20" + checksum: 10c0/e05d797e4871258925988446fddde662b9086aba71d4351c25f3cce6d64e3b7b5a22d7879db1d0b70f7c81fa9c41a70f2be2309564574f729cea1f3153bb6579 + languageName: node + linkType: hard + "@storybook/blocks@npm:7.6.20, @storybook/blocks@npm:^7.6.3": version: 7.6.20 resolution: "@storybook/blocks@npm:7.6.20" @@ -14022,7 +13872,7 @@ __metadata: languageName: node linkType: hard -"@storybook/components@npm:7.6.20": +"@storybook/components@npm:7.6.20, @storybook/components@npm:^7.0.0": version: 7.6.20 resolution: "@storybook/components@npm:7.6.20" dependencies: @@ -14043,15 +13893,6 @@ __metadata: languageName: node linkType: hard -"@storybook/components@npm:^8.0.0": - version: 8.2.8 - resolution: "@storybook/components@npm:8.2.8" - peerDependencies: - storybook: ^8.2.8 - checksum: 10c0/6be24c9fac90673e5a061794007a3fad786c70537e850cec9ff294b92874682c43ad93ffa6af37f4697ddb10127f4ccb569f025ba831e4c345f55c3ac01e7bad - languageName: node - linkType: hard - "@storybook/core-client@npm:7.6.20": version: 7.6.20 resolution: "@storybook/core-client@npm:7.6.20" @@ -14124,7 +13965,7 @@ __metadata: languageName: node linkType: hard -"@storybook/core-events@npm:7.6.20": +"@storybook/core-events@npm:7.6.20, @storybook/core-events@npm:^7.0.0": version: 7.6.20 resolution: "@storybook/core-events@npm:7.6.20" dependencies: @@ -14142,15 +13983,6 @@ __metadata: languageName: node linkType: hard -"@storybook/core-events@npm:^8.0.0": - version: 8.2.8 - resolution: "@storybook/core-events@npm:8.2.8" - peerDependencies: - storybook: ^8.2.8 - checksum: 10c0/971cbb6ded2cbc136f54a61f539a4c45b44c640126ee83ecf96fe657d3029cda44eb3ae31164beca6ef8d623cb06d0e7bd698377522479a9ab0f4c1b0f5596c6 - languageName: node - linkType: hard - "@storybook/core-server@npm:7.6.20": version: 7.6.20 resolution: "@storybook/core-server@npm:7.6.20" @@ -14348,7 +14180,7 @@ __metadata: languageName: node linkType: hard -"@storybook/icons@npm:^1.2.5, @storybook/icons@npm:^1.2.9": +"@storybook/icons@npm:^1.2.9": version: 1.2.10 resolution: "@storybook/icons@npm:1.2.10" peerDependencies: @@ -14385,7 +14217,7 @@ __metadata: languageName: node linkType: hard -"@storybook/manager-api@npm:7.6.20": +"@storybook/manager-api@npm:7.6.20, @storybook/manager-api@npm:^7.0.0": version: 7.6.20 resolution: "@storybook/manager-api@npm:7.6.20" dependencies: @@ -14407,15 +14239,6 @@ __metadata: languageName: node linkType: hard -"@storybook/manager-api@npm:^8.0.0": - version: 8.2.8 - resolution: "@storybook/manager-api@npm:8.2.8" - peerDependencies: - storybook: ^8.2.8 - checksum: 10c0/0c0f3a5337fc8efa965aa6232d2b2a1bb864c5ebaca85bcd124f83644e6d19ad6eecf9556974b04803a4856def17db801dcce4944a9830b8e9c7a8482df6842e - languageName: node - linkType: hard - "@storybook/manager@npm:7.6.20": version: 7.6.20 resolution: "@storybook/manager@npm:7.6.20" @@ -14686,7 +14509,7 @@ __metadata: languageName: node linkType: hard -"@storybook/theming@npm:7.6.20": +"@storybook/theming@npm:7.6.20, @storybook/theming@npm:^7.0.0": version: 7.6.20 resolution: "@storybook/theming@npm:7.6.20" dependencies: @@ -14701,15 +14524,6 @@ __metadata: languageName: node linkType: hard -"@storybook/theming@npm:^8.0.0": - version: 8.2.8 - resolution: "@storybook/theming@npm:8.2.8" - peerDependencies: - storybook: ^8.2.8 - checksum: 10c0/d762d2715004ce7c1b9cdef3babf38ba4ac6fe53b7e9583aea737fa03d777f7a70fbc880fb4e7e7b64e7ff1471f3d2b67379a59170e0297015baa3e1beed02cf - languageName: node - linkType: hard - "@storybook/types@npm:7.6.20": version: 7.6.20 resolution: "@storybook/types@npm:7.6.20" @@ -26468,86 +26282,6 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.20.2": - version: 0.20.2 - resolution: "esbuild@npm:0.20.2" - dependencies: - "@esbuild/aix-ppc64": "npm:0.20.2" - "@esbuild/android-arm": "npm:0.20.2" - "@esbuild/android-arm64": "npm:0.20.2" - "@esbuild/android-x64": "npm:0.20.2" - "@esbuild/darwin-arm64": "npm:0.20.2" - "@esbuild/darwin-x64": "npm:0.20.2" - "@esbuild/freebsd-arm64": "npm:0.20.2" - "@esbuild/freebsd-x64": "npm:0.20.2" - "@esbuild/linux-arm": "npm:0.20.2" - "@esbuild/linux-arm64": "npm:0.20.2" - "@esbuild/linux-ia32": "npm:0.20.2" - "@esbuild/linux-loong64": "npm:0.20.2" - "@esbuild/linux-mips64el": "npm:0.20.2" - "@esbuild/linux-ppc64": "npm:0.20.2" - "@esbuild/linux-riscv64": "npm:0.20.2" - "@esbuild/linux-s390x": "npm:0.20.2" - "@esbuild/linux-x64": "npm:0.20.2" - "@esbuild/netbsd-x64": "npm:0.20.2" - "@esbuild/openbsd-x64": "npm:0.20.2" - "@esbuild/sunos-x64": "npm:0.20.2" - "@esbuild/win32-arm64": "npm:0.20.2" - "@esbuild/win32-ia32": "npm:0.20.2" - "@esbuild/win32-x64": "npm:0.20.2" - dependenciesMeta: - "@esbuild/aix-ppc64": - optional: true - "@esbuild/android-arm": - optional: true - "@esbuild/android-arm64": - optional: true - "@esbuild/android-x64": - optional: true - "@esbuild/darwin-arm64": - optional: true - "@esbuild/darwin-x64": - optional: true - "@esbuild/freebsd-arm64": - optional: true - "@esbuild/freebsd-x64": - optional: true - "@esbuild/linux-arm": - optional: true - "@esbuild/linux-arm64": - optional: true - "@esbuild/linux-ia32": - optional: true - "@esbuild/linux-loong64": - optional: true - "@esbuild/linux-mips64el": - optional: true - "@esbuild/linux-ppc64": - optional: true - "@esbuild/linux-riscv64": - optional: true - "@esbuild/linux-s390x": - optional: true - "@esbuild/linux-x64": - optional: true - "@esbuild/netbsd-x64": - optional: true - "@esbuild/openbsd-x64": - optional: true - "@esbuild/sunos-x64": - optional: true - "@esbuild/win32-arm64": - optional: true - "@esbuild/win32-ia32": - optional: true - "@esbuild/win32-x64": - optional: true - bin: - esbuild: bin/esbuild - checksum: 10c0/66398f9fb2c65e456a3e649747b39af8a001e47963b25e86d9c09d2a48d61aa641b27da0ce5cad63df95ad246105e1d83e7fee0e1e22a0663def73b1c5101112 - languageName: node - linkType: hard - "esbuild@npm:^0.21.3": version: 0.21.5 resolution: "esbuild@npm:0.21.5" @@ -45722,19 +45456,27 @@ __metadata: languageName: node linkType: hard -"storybook-dark-mode@npm:^4.0.1": - version: 4.0.2 - resolution: "storybook-dark-mode@npm:4.0.2" +"storybook-dark-mode@npm:^3.0.3": + version: 3.0.3 + resolution: "storybook-dark-mode@npm:3.0.3" dependencies: - "@storybook/components": "npm:^8.0.0" - "@storybook/core-events": "npm:^8.0.0" + "@storybook/addons": "npm:^7.0.0" + "@storybook/components": "npm:^7.0.0" + "@storybook/core-events": "npm:^7.0.0" "@storybook/global": "npm:^5.0.0" - "@storybook/icons": "npm:^1.2.5" - "@storybook/manager-api": "npm:^8.0.0" - "@storybook/theming": "npm:^8.0.0" + "@storybook/manager-api": "npm:^7.0.0" + "@storybook/theming": "npm:^7.0.0" fast-deep-equal: "npm:^3.1.3" memoizerific: "npm:^1.11.3" - checksum: 10c0/d4fc652ff080f6cc9f0effab0c989b66ead3372b267c2c328eef608f27c9822bf47aaa177405e42768b2de22f8a3e9a0280af50430efd0cf78bd6ed1f12c8b29 + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + checksum: 10c0/a69f4f6107587e68ac6421ac6c598fc086266683880fd53c4443e31225f0a1fc99c41de03340d1d177b4d2bc7399205fb5c14a45727a72d418c13aad3eb963fc languageName: node linkType: hard @@ -47435,7 +47177,7 @@ __metadata: languageName: node linkType: hard -"tsup@npm:^8.0.1": +"tsup@npm:^8.2.4": version: 8.2.4 resolution: "tsup@npm:8.2.4" dependencies: @@ -47487,7 +47229,7 @@ __metadata: languageName: node linkType: hard -"tsx@npm:^4.7.2": +"tsx@npm:^4.17.0": version: 4.17.0 resolution: "tsx@npm:4.17.0" dependencies: @@ -47834,7 +47576,7 @@ __metadata: dotenv-cli: "npm:^7.2.1" drizzle-kit: "npm:^0.20.14" drizzle-orm: "npm:^0.29.3" - esbuild: "npm:^0.20.2" + esbuild: "npm:^0.23.0" esbuild-plugin-svgr: "npm:^2.1.0" eslint: "npm:^8.53.0" eslint-config-next: "npm:14.0.4" @@ -47958,7 +47700,7 @@ __metadata: storybook: "npm:^7.6.3" storybook-addon-cookie: "npm:^3.2.0" storybook-addon-pseudo-states: "npm:^2.1.2" - storybook-dark-mode: "npm:^4.0.1" + storybook-dark-mode: "npm:^3.0.3" stripe: "npm:^14.17.0" supertest: "npm:^6.1.3" ts-jest: "npm:^29.1.1" @@ -47967,15 +47709,15 @@ __metadata: ts-node: "npm:10.9.1" tsconfig-paths: "npm:^4.2.0" tslib: "npm:^2.3.0" - tsup: "npm:^8.0.1" - tsx: "npm:^4.7.2" + tsup: "npm:^8.2.4" + tsx: "npm:^4.17.0" type-fest: "npm:4.10.1" typeorm: "patch:typeorm@0.3.20#./packages/twenty-server/patches/typeorm+0.3.20.patch" typescript: "npm:5.3.3" use-context-selector: "npm:^2.0.0" use-debounce: "npm:^10.0.0" uuid: "npm:^9.0.0" - vite: "npm:^5.0.0" + vite: "npm:^5.4.0" vite-plugin-checker: "npm:^0.6.2" vite-plugin-dts: "npm:3.8.1" vite-plugin-svgr: "npm:^4.2.0" @@ -49695,7 +49437,7 @@ __metadata: languageName: node linkType: hard -"vite@npm:^5.0.0": +"vite@npm:^5.0.0, vite@npm:^5.4.0": version: 5.4.0 resolution: "vite@npm:5.4.0" dependencies: