Skip to content

Commit

Permalink
Merge branch 'main' into fix/oidc-behind-proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
AMoreaux authored Jan 20, 2025
2 parents 95cf1da + 056cb7c commit 2de750f
Show file tree
Hide file tree
Showing 246 changed files with 9,241 additions and 2,195 deletions.
Binary file not shown.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ Below are a few features we have implemented to date:
- [NestJS](https://nestjs.com/), with [BullMQ](https://bullmq.io/), [PostgreSQL](https://www.postgresql.org/), [Redis](https://redis.io/)
- [React](https://reactjs.org/), with [Recoil](https://recoiljs.org/) and [Emotion](https://emotion.sh/)
- [Greptile](https://greptile.com) for code reviews.
- [TranslationIO](https://translation.io/) for translations.


# Join the Community
Expand Down
2 changes: 2 additions & 0 deletions packages/twenty-front/.env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
REACT_APP_SERVER_BASE_URL=http://localhost:3000
GENERATE_SOURCEMAP=false
TRANSLATION_IO_API_KEY=xxx


# ———————— Optional ————————
# REACT_APP_PORT=3001
Expand Down
16 changes: 15 additions & 1 deletion packages/twenty-front/lingui.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@ import { defineConfig } from '@lingui/cli';

export default defineConfig({
sourceLocale: 'en',
locales: ['en', 'fr', 'pt', 'de', 'it', 'es', 'zh', 'pseudo-en'],
locales: [
'en',
'fr',
'pt',
'de',
'it',
'es',
'zh-Hans',
'zh-Hant',
'pseudo-en',
],
pseudoLocale: 'pseudo-en',
fallbackLocales: {
'pseudo-en': 'en',
Expand All @@ -15,4 +25,8 @@ export default defineConfig({
],
catalogsMergePath: '<rootDir>/src/locales/generated/{locale}',
compileNamespace: 'ts',
service: {
name: 'TranslationIO',
apiKey: process.env.TRANSLATION_IO_API_KEY ?? '',
},
});
4 changes: 2 additions & 2 deletions packages/twenty-front/nyc.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ const globalCoverage = {

const modulesCoverage = {
branches: 25,
statements: 49,
lines: 50,
statements: 44,
lines: 45,
functions: 38,
include: ['src/modules/**/*'],
exclude: ['src/**/*.ts'],
Expand Down
2 changes: 1 addition & 1 deletion packages/twenty-front/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@
"executor": "nx:run-commands",
"options": {
"cwd": "{projectRoot}",
"command": "lingui extract"
"command": "lingui extract --overwrite"
}
},
"lingui:compile": {
Expand Down
4 changes: 2 additions & 2 deletions packages/twenty-front/src/generated-metadata/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const documents = {
"\n mutation DeleteOneFieldMetadataItem($idToDelete: UUID!) {\n deleteOneField(input: { id: $idToDelete }) {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n settings\n }\n }\n": types.DeleteOneFieldMetadataItemDocument,
"\n mutation DeleteOneRelationMetadataItem($idToDelete: UUID!) {\n deleteOneRelation(input: { id: $idToDelete }) {\n id\n }\n }\n": types.DeleteOneRelationMetadataItemDocument,
"\n query ObjectMetadataItems(\n $objectFilter: objectFilter\n $fieldFilter: fieldFilter\n ) {\n objects(paging: { first: 1000 }, filter: $objectFilter) {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isRemote\n isActive\n isSystem\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n shortcut\n isLabelSyncedWithName\n indexMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n name\n indexWhereClause\n indexType\n isUnique\n indexFieldMetadatas(paging: { first: 100 }) {\n edges {\n node {\n id\n createdAt\n updatedAt\n order\n fieldMetadataId\n }\n }\n }\n }\n }\n }\n fields(paging: { first: 1000 }, filter: $fieldFilter) {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isSystem\n isNullable\n isUnique\n createdAt\n updatedAt\n defaultValue\n options\n settings\n isLabelSyncedWithName\n relationDefinition {\n relationId\n direction\n sourceObjectMetadata {\n id\n nameSingular\n namePlural\n }\n sourceFieldMetadata {\n id\n name\n }\n targetObjectMetadata {\n id\n nameSingular\n namePlural\n }\n targetFieldMetadata {\n id\n name\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n }\n }\n": types.ObjectMetadataItemsDocument,
"\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n latestVersionInputSchema\n publishedVersions\n createdAt\n updatedAt\n }\n": types.ServerlessFunctionFieldsFragmentDoc,
"\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n timeoutSeconds\n syncStatus\n latestVersion\n latestVersionInputSchema\n publishedVersions\n createdAt\n updatedAt\n }\n": types.ServerlessFunctionFieldsFragmentDoc,
"\n \n mutation CreateOneServerlessFunctionItem(\n $input: CreateServerlessFunctionInput!\n ) {\n createOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.CreateOneServerlessFunctionItemDocument,
"\n \n mutation DeleteOneServerlessFunction($input: ServerlessFunctionIdInput!) {\n deleteOneServerlessFunction(input: $input) {\n ...ServerlessFunctionFields\n }\n }\n": types.DeleteOneServerlessFunctionDocument,
"\n mutation ExecuteOneServerlessFunction(\n $input: ExecuteServerlessFunctionInput!\n ) {\n executeOneServerlessFunction(input: $input) {\n data\n duration\n status\n error\n }\n }\n": types.ExecuteOneServerlessFunctionDocument,
Expand Down Expand Up @@ -142,7 +142,7 @@ export function graphql(source: "\n query ObjectMetadataItems(\n $objectFilt
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n latestVersionInputSchema\n publishedVersions\n createdAt\n updatedAt\n }\n"): (typeof documents)["\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n syncStatus\n latestVersion\n latestVersionInputSchema\n publishedVersions\n createdAt\n updatedAt\n }\n"];
export function graphql(source: "\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n timeoutSeconds\n syncStatus\n latestVersion\n latestVersionInputSchema\n publishedVersions\n createdAt\n updatedAt\n }\n"): (typeof documents)["\n fragment ServerlessFunctionFields on ServerlessFunction {\n id\n name\n description\n runtime\n timeoutSeconds\n syncStatus\n latestVersion\n latestVersionInputSchema\n publishedVersions\n createdAt\n updatedAt\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down
3 changes: 1 addition & 2 deletions packages/twenty-front/src/generated-metadata/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,6 @@ export enum FeatureFlagKey {
IsAnalyticsV2Enabled = 'IsAnalyticsV2Enabled',
IsCommandMenuV2Enabled = 'IsCommandMenuV2Enabled',
IsCopilotEnabled = 'IsCopilotEnabled',
IsCrmMigrationEnabled = 'IsCrmMigrationEnabled',
IsEventObjectEnabled = 'IsEventObjectEnabled',
IsFreeAccessEnabled = 'IsFreeAccessEnabled',
IsFunctionSettingsEnabled = 'IsFunctionSettingsEnabled',
Expand Down Expand Up @@ -2314,4 +2313,4 @@ export const UpdateOneServerlessFunctionDocument = {"kind":"Document","definitio
export const FindManyAvailablePackagesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindManyAvailablePackages"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunctionIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getAvailablePackages"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<FindManyAvailablePackagesQuery, FindManyAvailablePackagesQueryVariables>;
export const GetManyServerlessFunctionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetManyServerlessFunctions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findManyServerlessFunctions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode<GetManyServerlessFunctionsQuery, GetManyServerlessFunctionsQueryVariables>;
export const GetOneServerlessFunctionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOneServerlessFunction"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunctionIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findOneServerlessFunction"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerlessFunctionFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServerlessFunctionFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerlessFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"runtime"}},{"kind":"Field","name":{"kind":"Name","value":"syncStatus"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersion"}},{"kind":"Field","name":{"kind":"Name","value":"latestVersionInputSchema"}},{"kind":"Field","name":{"kind":"Name","value":"publishedVersions"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode<GetOneServerlessFunctionQuery, GetOneServerlessFunctionQueryVariables>;
export const FindOneServerlessFunctionSourceCodeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindOneServerlessFunctionSourceCode"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GetServerlessFunctionSourceCodeInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getServerlessFunctionSourceCode"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<FindOneServerlessFunctionSourceCodeQuery, FindOneServerlessFunctionSourceCodeQueryVariables>;
export const FindOneServerlessFunctionSourceCodeDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"FindOneServerlessFunctionSourceCode"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"GetServerlessFunctionSourceCodeInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"getServerlessFunctionSourceCode"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]} as unknown as DocumentNode<FindOneServerlessFunctionSourceCodeQuery, FindOneServerlessFunctionSourceCodeQueryVariables>;
12 changes: 7 additions & 5 deletions packages/twenty-front/src/generated/graphql.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
import { gql } from '@apollo/client';
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
Expand Down Expand Up @@ -223,6 +223,7 @@ export type CreateOneFieldMetadataInput = {
export type CreateServerlessFunctionInput = {
description?: InputMaybe<Scalars['String']>;
name: Scalars['String'];
timeoutSeconds?: InputMaybe<Scalars['Float']>;
};

export type CreateWorkflowVersionStepInput = {
Expand Down Expand Up @@ -320,18 +321,17 @@ export enum FeatureFlagKey {
IsAnalyticsV2Enabled = 'IsAnalyticsV2Enabled',
IsCommandMenuV2Enabled = 'IsCommandMenuV2Enabled',
IsCopilotEnabled = 'IsCopilotEnabled',
IsCrmMigrationEnabled = 'IsCrmMigrationEnabled',
IsEventObjectEnabled = 'IsEventObjectEnabled',
IsFreeAccessEnabled = 'IsFreeAccessEnabled',
IsFunctionSettingsEnabled = 'IsFunctionSettingsEnabled',
IsGmailSendEmailScopeEnabled = 'IsGmailSendEmailScopeEnabled',
IsJsonFilterEnabled = 'IsJsonFilterEnabled',
IsLocalizationEnabled = 'IsLocalizationEnabled',
IsMicrosoftSyncEnabled = 'IsMicrosoftSyncEnabled',
IsPostgreSqlIntegrationEnabled = 'IsPostgreSQLIntegrationEnabled',
IsStripeIntegrationEnabled = 'IsStripeIntegrationEnabled',
IsUniqueIndexesEnabled = 'IsUniqueIndexesEnabled',
IsWorkflowEnabled = 'IsWorkflowEnabled',
IsLocalizationEnabled = 'IsLocalizationEnabled'
IsWorkflowEnabled = 'IsWorkflowEnabled'
}

export type FieldConnection = {
Expand Down Expand Up @@ -1139,6 +1139,7 @@ export type ServerlessFunction = {
publishedVersions: Array<Scalars['String']>;
runtime: Scalars['String'];
syncStatus: ServerlessFunctionSyncStatus;
timeoutSeconds: Scalars['Float'];
updatedAt: Scalars['DateTime'];
};

Expand Down Expand Up @@ -1390,6 +1391,7 @@ export type UpdateServerlessFunctionInput = {
/** Id of the serverless function to execute */
id: Scalars['UUID'];
name: Scalars['String'];
timeoutSeconds?: InputMaybe<Scalars['Float']>;
};

export type UpdateWorkflowVersionStepInput = {
Expand Down Expand Up @@ -4705,4 +4707,4 @@ export function useGetWorkspaceFromInviteHashLazyQuery(baseOptions?: Apollo.Lazy
}
export type GetWorkspaceFromInviteHashQueryHookResult = ReturnType<typeof useGetWorkspaceFromInviteHashQuery>;
export type GetWorkspaceFromInviteHashLazyQueryHookResult = ReturnType<typeof useGetWorkspaceFromInviteHashLazyQuery>;
export type GetWorkspaceFromInviteHashQueryResult = Apollo.QueryResult<GetWorkspaceFromInviteHashQuery, GetWorkspaceFromInviteHashQueryVariables>;
export type GetWorkspaceFromInviteHashQueryResult = Apollo.QueryResult<GetWorkspaceFromInviteHashQuery, GetWorkspaceFromInviteHashQueryVariables>;
72 changes: 72 additions & 0 deletions packages/twenty-front/src/hooks/__tests__/useNavigateApp.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { renderHook } from '@testing-library/react';
import { MemoryRouter, useNavigate } from 'react-router-dom';

import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { AppPath } from '@/types/AppPath';
import { useNavigateApp } from '~/hooks/useNavigateApp';

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useNavigate: jest.fn(),
}));

const Wrapper = ({ children }: { children: React.ReactNode }) => (
<MemoryRouter>{children}</MemoryRouter>
);

describe('useNavigateApp', () => {
const mockNavigate = jest.fn();

beforeEach(() => {
jest.clearAllMocks();
(useNavigate as jest.Mock).mockReturnValue(mockNavigate);
});

it('should navigate to the correct path without params', () => {
const { result } = renderHook(() => useNavigateApp(), {
wrapper: Wrapper,
});

result.current(AppPath.Index);

expect(mockNavigate).toHaveBeenCalledWith('/', undefined);
});

it('should navigate to the correct path with params', () => {
const { result } = renderHook(() => useNavigateApp(), {
wrapper: Wrapper,
});

result.current(AppPath.RecordShowPage, {
objectNameSingular: CoreObjectNameSingular.Company,
objectRecordId: '123',
});

expect(mockNavigate).toHaveBeenCalledWith('/object/company/123', undefined);
});

it('should navigate with query params', () => {
const { result } = renderHook(() => useNavigateApp(), {
wrapper: Wrapper,
});

const queryParams = { viewId: '123', filter: 'test' };
result.current(AppPath.Index, undefined, queryParams);

expect(mockNavigate).toHaveBeenCalledWith(
'/?viewId=123&filter=test',
undefined,
);
});

it('should navigate with options', () => {
const { result } = renderHook(() => useNavigateApp(), {
wrapper: Wrapper,
});

const options = { replace: true, state: { test: true } };
result.current(AppPath.Index, undefined, undefined, options);

expect(mockNavigate).toHaveBeenCalledWith('/', options);
});
});
Loading

0 comments on commit 2de750f

Please sign in to comment.