Feat/metadata datatable types (#2175)

* Handled new url v2 type

* Fixed refetch queries

* wip

* Ok delete but views bug

* Fix lint

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Lucas Bordeau
2023-10-21 14:07:18 +02:00
committed by GitHub
parent 598fda8f45
commit f1670f0cf4
50 changed files with 1125 additions and 350 deletions

View File

@@ -28,7 +28,7 @@ import { SettingsWorkspaceMembers } from '~/pages/settings/SettingsWorkspaceMemb
import { Tasks } from '~/pages/tasks/Tasks';
import { getPageTitleFromPath } from '~/utils/title-utils';
import { ObjectTablePage } from './pages/companies/ObjectsTable';
import { ObjectTablePage } from './modules/metadata/components/ObjectTablePage';
import { SettingsObjectNewFieldStep1 } from './pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldStep1';
import { SettingsObjectNewFieldStep2 } from './pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldStep2';
import { SettingsApis } from './pages/settings/SettingsApis';

View File

@@ -19,6 +19,7 @@ import MainNavbar from '@/ui/navigation/navbar/components/MainNavbar';
import NavItem from '@/ui/navigation/navbar/components/NavItem';
import NavTitle from '@/ui/navigation/navbar/components/NavTitle';
import { useGetClientConfigQuery } from './generated/graphql';
import { measureTotalFrameLoad } from './utils/measureTotalFrameLoad';
export const AppNavbar = () => {
@@ -30,6 +31,10 @@ export const AppNavbar = () => {
const isInSubMenu = useIsSubMenuNavbarDisplayed();
const { currentUserDueTaskCount } = useCurrentUserTaskCount();
const { data } = useGetClientConfigQuery();
const isFlexibleBackendEnabled = data?.clientConfig?.flexibleBackendEnabled;
return (
<>
{!isInSubMenu ? (
@@ -90,7 +95,7 @@ export const AppNavbar = () => {
Icon={IconTargetArrow}
active={currentPath === '/opportunities'}
/>
<MetadataObjectNavItems />
{isFlexibleBackendEnabled && <MetadataObjectNavItems />}
</MainNavbar>
) : (
<SettingsNavbar />

View File

@@ -13,10 +13,12 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/
* Therefore it is highly recommended to use the babel or swc plugin for production.
*/
const documents = {
"\n mutation CreateOneMetadataObject($input: CreateOneObjectInput!) {\n createOneObject(input: $input) {\n id\n }\n }\n": types.CreateOneMetadataObjectDocument,
"\n mutation CreateOneMetadataObject($input: CreateOneObjectInput!) {\n createOneObject(input: $input) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n }\n }\n": types.CreateOneMetadataObjectDocument,
"\n mutation CreateOneMetadataField($input: CreateOneFieldInput!) {\n createOneField(input: $input) {\n id\n type\n name\n label\n description\n icon\n placeholder\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n }\n }\n": types.CreateOneMetadataFieldDocument,
"\n mutation UpdateOneMetadataField(\n $idToUpdate: ID!\n $updatePayload: UpdateFieldInput!\n ) {\n updateOneField(input: { id: $idToUpdate, update: $updatePayload }) {\n id\n }\n }\n": types.UpdateOneMetadataFieldDocument,
"\n mutation UpdateOneMetadataObject(\n $idToUpdate: ID!\n $updatePayload: UpdateObjectInput!\n ) {\n updateOneObject(input: { id: $idToUpdate, update: $updatePayload }) {\n id\n }\n }\n": types.UpdateOneMetadataObjectDocument,
"\n mutation UpdateOneMetadataField(\n $idToUpdate: ID!\n $updatePayload: UpdateFieldInput!\n ) {\n updateOneField(input: { id: $idToUpdate, update: $updatePayload }) {\n id\n type\n name\n label\n description\n icon\n placeholder\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n }\n }\n": types.UpdateOneMetadataFieldDocument,
"\n mutation UpdateOneMetadataObject(\n $idToUpdate: ID!\n $updatePayload: UpdateObjectInput!\n ) {\n updateOneObject(input: { id: $idToUpdate, update: $updatePayload }) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n }\n }\n": types.UpdateOneMetadataObjectDocument,
"\n mutation DeleteOneMetadataObject($idToDelete: ID!) {\n deleteOneObject(input: { id: $idToDelete }) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n }\n }\n": types.DeleteOneMetadataObjectDocument,
"\n mutation DeleteOneMetadataField($idToDelete: ID!) {\n deleteOneField(input: { id: $idToDelete }) {\n id\n type\n name\n label\n description\n icon\n placeholder\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n }\n }\n": types.DeleteOneMetadataFieldDocument,
"\n query MetadataObjects {\n objects {\n edges {\n node {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n fields {\n edges {\n node {\n id\n type\n name\n label\n description\n icon\n placeholder\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n }\n pageInfo {\n hasNextPage\n hasPreviousPage\n startCursor\n endCursor\n }\n totalCount\n }\n }\n": types.MetadataObjectsDocument,
};
@@ -37,7 +39,7 @@ export function graphql(source: string): unknown;
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n mutation CreateOneMetadataObject($input: CreateOneObjectInput!) {\n createOneObject(input: $input) {\n id\n }\n }\n"): (typeof documents)["\n mutation CreateOneMetadataObject($input: CreateOneObjectInput!) {\n createOneObject(input: $input) {\n id\n }\n }\n"];
export function graphql(source: "\n mutation CreateOneMetadataObject($input: CreateOneObjectInput!) {\n createOneObject(input: $input) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n }\n }\n"): (typeof documents)["\n mutation CreateOneMetadataObject($input: CreateOneObjectInput!) {\n createOneObject(input: $input) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -45,11 +47,19 @@ export function graphql(source: "\n mutation CreateOneMetadataField($input: Cre
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n mutation UpdateOneMetadataField(\n $idToUpdate: ID!\n $updatePayload: UpdateFieldInput!\n ) {\n updateOneField(input: { id: $idToUpdate, update: $updatePayload }) {\n id\n }\n }\n"): (typeof documents)["\n mutation UpdateOneMetadataField(\n $idToUpdate: ID!\n $updatePayload: UpdateFieldInput!\n ) {\n updateOneField(input: { id: $idToUpdate, update: $updatePayload }) {\n id\n }\n }\n"];
export function graphql(source: "\n mutation UpdateOneMetadataField(\n $idToUpdate: ID!\n $updatePayload: UpdateFieldInput!\n ) {\n updateOneField(input: { id: $idToUpdate, update: $updatePayload }) {\n id\n type\n name\n label\n description\n icon\n placeholder\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n }\n }\n"): (typeof documents)["\n mutation UpdateOneMetadataField(\n $idToUpdate: ID!\n $updatePayload: UpdateFieldInput!\n ) {\n updateOneField(input: { id: $idToUpdate, update: $updatePayload }) {\n id\n type\n name\n label\n description\n icon\n placeholder\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n mutation UpdateOneMetadataObject(\n $idToUpdate: ID!\n $updatePayload: UpdateObjectInput!\n ) {\n updateOneObject(input: { id: $idToUpdate, update: $updatePayload }) {\n id\n }\n }\n"): (typeof documents)["\n mutation UpdateOneMetadataObject(\n $idToUpdate: ID!\n $updatePayload: UpdateObjectInput!\n ) {\n updateOneObject(input: { id: $idToUpdate, update: $updatePayload }) {\n id\n }\n }\n"];
export function graphql(source: "\n mutation UpdateOneMetadataObject(\n $idToUpdate: ID!\n $updatePayload: UpdateObjectInput!\n ) {\n updateOneObject(input: { id: $idToUpdate, update: $updatePayload }) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n }\n }\n"): (typeof documents)["\n mutation UpdateOneMetadataObject(\n $idToUpdate: ID!\n $updatePayload: UpdateObjectInput!\n ) {\n updateOneObject(input: { id: $idToUpdate, update: $updatePayload }) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n mutation DeleteOneMetadataObject($idToDelete: ID!) {\n deleteOneObject(input: { id: $idToDelete }) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n }\n }\n"): (typeof documents)["\n mutation DeleteOneMetadataObject($idToDelete: ID!) {\n deleteOneObject(input: { id: $idToDelete }) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n mutation DeleteOneMetadataField($idToDelete: ID!) {\n deleteOneField(input: { id: $idToDelete }) {\n id\n type\n name\n label\n description\n icon\n placeholder\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n }\n }\n"): (typeof documents)["\n mutation DeleteOneMetadataField($idToDelete: ID!) {\n deleteOneField(input: { id: $idToDelete }) {\n id\n type\n name\n label\n description\n icon\n placeholder\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/

View File

@@ -635,18 +635,15 @@ export type UpdateFieldInput = {
description?: InputMaybe<Scalars['String']['input']>;
icon?: InputMaybe<Scalars['String']['input']>;
isActive?: InputMaybe<Scalars['Boolean']['input']>;
label: Scalars['String']['input'];
name: Scalars['String']['input'];
label?: InputMaybe<Scalars['String']['input']>;
};
export type UpdateObjectInput = {
description?: InputMaybe<Scalars['String']['input']>;
icon?: InputMaybe<Scalars['String']['input']>;
isActive?: InputMaybe<Scalars['Boolean']['input']>;
labelPlural: Scalars['String']['input'];
labelSingular: Scalars['String']['input'];
namePlural: Scalars['String']['input'];
nameSingular: Scalars['String']['input'];
labelPlural?: InputMaybe<Scalars['String']['input']>;
labelSingular?: InputMaybe<Scalars['String']['input']>;
};
export type UpdateOneFieldInput = {
@@ -868,7 +865,7 @@ export type CreateOneMetadataObjectMutationVariables = Exact<{
}>;
export type CreateOneMetadataObjectMutation = { __typename?: 'Mutation', createOneObject: { __typename?: 'object', id: string } };
export type CreateOneMetadataObjectMutation = { __typename?: 'Mutation', createOneObject: { __typename?: 'object', id: string, dataSourceId: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isActive: boolean, createdAt: any, updatedAt: any } };
export type CreateOneMetadataFieldMutationVariables = Exact<{
input: CreateOneFieldInput;
@@ -883,7 +880,7 @@ export type UpdateOneMetadataFieldMutationVariables = Exact<{
}>;
export type UpdateOneMetadataFieldMutation = { __typename?: 'Mutation', updateOneField: { __typename?: 'field', id: string } };
export type UpdateOneMetadataFieldMutation = { __typename?: 'Mutation', updateOneField: { __typename?: 'field', id: string, type: string, name: string, label: string, description?: string | null, icon?: string | null, placeholder?: string | null, isCustom: boolean, isActive: boolean, isNullable: boolean, createdAt: any, updatedAt: any } };
export type UpdateOneMetadataObjectMutationVariables = Exact<{
idToUpdate: Scalars['ID']['input'];
@@ -891,7 +888,21 @@ export type UpdateOneMetadataObjectMutationVariables = Exact<{
}>;
export type UpdateOneMetadataObjectMutation = { __typename?: 'Mutation', updateOneObject: { __typename?: 'object', id: string } };
export type UpdateOneMetadataObjectMutation = { __typename?: 'Mutation', updateOneObject: { __typename?: 'object', id: string, dataSourceId: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isActive: boolean, createdAt: any, updatedAt: any } };
export type DeleteOneMetadataObjectMutationVariables = Exact<{
idToDelete: Scalars['ID']['input'];
}>;
export type DeleteOneMetadataObjectMutation = { __typename?: 'Mutation', deleteOneObject: { __typename?: 'ObjectDeleteResponse', id?: string | null, dataSourceId?: string | null, nameSingular?: string | null, namePlural?: string | null, labelSingular?: string | null, labelPlural?: string | null, description?: string | null, icon?: string | null, isCustom?: boolean | null, isActive?: boolean | null, createdAt?: any | null, updatedAt?: any | null } };
export type DeleteOneMetadataFieldMutationVariables = Exact<{
idToDelete: Scalars['ID']['input'];
}>;
export type DeleteOneMetadataFieldMutation = { __typename?: 'Mutation', deleteOneField: { __typename?: 'FieldDeleteResponse', id?: string | null, type?: string | null, name?: string | null, label?: string | null, description?: string | null, icon?: string | null, placeholder?: string | null, isCustom?: boolean | null, isActive?: boolean | null, isNullable?: boolean | null, createdAt?: any | null, updatedAt?: any | null } };
export type MetadataObjectsQueryVariables = Exact<{ [key: string]: never; }>;
@@ -899,8 +910,10 @@ export type MetadataObjectsQueryVariables = Exact<{ [key: string]: never; }>;
export type MetadataObjectsQuery = { __typename?: 'Query', objects: { __typename?: 'ObjectConnection', totalCount: number, edges: Array<{ __typename?: 'objectEdge', node: { __typename?: 'object', id: string, dataSourceId: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isActive: boolean, createdAt: any, updatedAt: any, fields: { __typename?: 'ObjectFieldsConnection', totalCount: number, edges: Array<{ __typename?: 'fieldEdge', node: { __typename?: 'field', id: string, type: string, name: string, label: string, description?: string | null, icon?: string | null, placeholder?: string | null, isCustom: boolean, isActive: boolean, isNullable: boolean, createdAt: any, updatedAt: any } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } };
export const CreateOneMetadataObjectDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOneMetadataObject"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateOneObjectInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneObject"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode<CreateOneMetadataObjectMutation, CreateOneMetadataObjectMutationVariables>;
export const CreateOneMetadataObjectDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOneMetadataObject"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateOneObjectInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneObject"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode<CreateOneMetadataObjectMutation, CreateOneMetadataObjectMutationVariables>;
export const CreateOneMetadataFieldDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOneMetadataField"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateOneFieldInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneField"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"placeholder"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode<CreateOneMetadataFieldMutation, CreateOneMetadataFieldMutationVariables>;
export const UpdateOneMetadataFieldDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateOneMetadataField"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"idToUpdate"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"updatePayload"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateFieldInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOneField"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"idToUpdate"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"update"},"value":{"kind":"Variable","name":{"kind":"Name","value":"updatePayload"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode<UpdateOneMetadataFieldMutation, UpdateOneMetadataFieldMutationVariables>;
export const UpdateOneMetadataObjectDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateOneMetadataObject"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"idToUpdate"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"updatePayload"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateObjectInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOneObject"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"idToUpdate"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"update"},"value":{"kind":"Variable","name":{"kind":"Name","value":"updatePayload"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode<UpdateOneMetadataObjectMutation, UpdateOneMetadataObjectMutationVariables>;
export const UpdateOneMetadataFieldDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateOneMetadataField"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"idToUpdate"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"updatePayload"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateFieldInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOneField"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"idToUpdate"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"update"},"value":{"kind":"Variable","name":{"kind":"Name","value":"updatePayload"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"placeholder"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode<UpdateOneMetadataFieldMutation, UpdateOneMetadataFieldMutationVariables>;
export const UpdateOneMetadataObjectDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateOneMetadataObject"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"idToUpdate"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"updatePayload"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateObjectInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOneObject"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"idToUpdate"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"update"},"value":{"kind":"Variable","name":{"kind":"Name","value":"updatePayload"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode<UpdateOneMetadataObjectMutation, UpdateOneMetadataObjectMutationVariables>;
export const DeleteOneMetadataObjectDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneMetadataObject"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneObject"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode<DeleteOneMetadataObjectMutation, DeleteOneMetadataObjectMutationVariables>;
export const DeleteOneMetadataFieldDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteOneMetadataField"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneField"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"idToDelete"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"placeholder"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}}]} as unknown as DocumentNode<DeleteOneMetadataFieldMutation, DeleteOneMetadataFieldMutationVariables>;
export const MetadataObjectsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MetadataObjects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"objects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"fields"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"placeholder"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"hasPreviousPage"}},{"kind":"Field","name":{"kind":"Name","value":"startCursor"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]} as unknown as DocumentNode<MetadataObjectsQuery, MetadataObjectsQueryVariables>;

View File

@@ -17,7 +17,6 @@ import '@emotion/react';
import { PageChangeEffect } from './effect-components/PageChangeEffect';
import { ApolloMetadataClientProvider } from './modules/metadata/components/ApolloMetadataClientProvider';
import { FetchMetadataEffect } from './modules/metadata/components/FetchMetadataEffect';
import { App } from './App';
import './index.css';
@@ -36,7 +35,6 @@ root.render(
<ClientConfigProvider>
<UserProvider>
<ApolloMetadataClientProvider>
<FetchMetadataEffect />
<PageChangeEffect />
<AppThemeProvider>
<SnackBarProvider>

View File

@@ -1,110 +0,0 @@
import { useEffect, useState } from 'react';
import { useRecoilState } from 'recoil';
import { isFlexibleBackendEnabledState } from '@/client-config/states/isFlexibleBackendEnabledState';
import { MetadataObjectsQuery } from '~/generated-metadata/graphql';
import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
import { useApolloMetadataClient } from '../hooks/useApolloMetadataClient';
import { useCreateOneObject } from '../hooks/useCreateOneObject';
import { useFindManyObjects } from '../hooks/useFindManyObjects';
import { useSeedCustomObjectsTemp } from '../hooks/useSeedCustomObjectsTemp';
import { metadataObjectsState } from '../states/metadataObjectsState';
import { MetadataObject } from '../types/MetadataObject';
export const FetchMetadataEffect = () => {
const [metadataObjects, setMetadataObjects] =
useRecoilState(metadataObjectsState);
const [isFlexibleBackendEnabled] = useRecoilState(
isFlexibleBackendEnabledState,
);
const apolloMetadataClient = useApolloMetadataClient();
const seedCustomObjectsTemp = useSeedCustomObjectsTemp();
const { createOneObject } = useCreateOneObject({
objectNamePlural: 'suppliers',
});
const { objects: suppliers, loading } = useFindManyObjects({
objectNamePlural: 'suppliers',
});
const [created, setCreated] = useState(false);
useEffect(() => {
if (!created && !loading && suppliers.length === 0 && createOneObject) {
createOneObject({
name: 'Supplier 1',
city: 'City 1',
});
createOneObject({
name: 'Supplier 2',
city: 'City 2',
});
createOneObject({
name: 'Supplier 3',
city: 'City 3',
});
setCreated(true);
}
}, [suppliers, createOneObject, loading, created]);
useEffect(() => {
if (!isFlexibleBackendEnabled) return;
(async () => {
if (apolloMetadataClient && metadataObjects.length === 0) {
const objects = await apolloMetadataClient.query<MetadataObjectsQuery>({
query: FIND_MANY_METADATA_OBJECTS,
});
if (
objects.data.objects.edges.length > 0 &&
metadataObjects.length === 0
) {
const formattedObjects: MetadataObject[] =
objects.data.objects.edges.map((object) => ({
...object.node,
fields: object.node.fields.edges.map((field) => field.node),
}));
setMetadataObjects(formattedObjects);
} else if (
objects.data.objects.edges.length === 0 &&
metadataObjects.length === 0
) {
try {
await seedCustomObjectsTemp();
const objects =
await apolloMetadataClient.query<MetadataObjectsQuery>({
query: FIND_MANY_METADATA_OBJECTS,
});
const formattedObjects: MetadataObject[] =
objects.data.objects.edges.map((object) => ({
...object.node,
fields: object.node.fields.edges.map((field) => field.node),
}));
setMetadataObjects(formattedObjects);
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
}
}
}
})();
}, [
isFlexibleBackendEnabled,
metadataObjects,
setMetadataObjects,
apolloMetadataClient,
seedCustomObjectsTemp,
]);
return <></>;
};

View File

@@ -1,29 +1,58 @@
import { useNavigate } from 'react-router-dom';
import { IconArchive } from '@/ui/display/icon';
import { IconBuildingSkyscraper } from '@/ui/display/icon';
import { Button } from '@/ui/input/button/components/Button';
import { IconButton } from '@/ui/input/button/components/IconButton';
import NavItem from '@/ui/navigation/navbar/components/NavItem';
import { useGetClientConfigQuery } from '~/generated/graphql';
import { capitalize } from '~/utils/string/capitalize';
import { useCreateNewTempsCustomObject } from '../hooks/useCreateNewTempCustomObject';
import { useDeleteOneMetadataObject } from '../hooks/useDeleteOneMetadataObject';
import { useFindManyMetadataObjects } from '../hooks/useFindManyMetadataObjects';
export const MetadataObjectNavItems = () => {
const { data } = useGetClientConfigQuery();
const { metadataObjects } = useFindManyMetadataObjects();
const isFlexibleBackendEnabled = data?.clientConfig?.flexibleBackendEnabled;
// eslint-disable-next-line no-console
console.log({
metadataObjects,
});
if (!isFlexibleBackendEnabled) return <></>;
const createNewTempCustomObject = useCreateNewTempsCustomObject();
const { deleteOneMetadataObject } = useDeleteOneMetadataObject();
const navigate = useNavigate();
return (
<>
{metadataObjects.map((metadataObject) => (
<NavItem
key={metadataObject.id}
label={capitalize(metadataObject.namePlural)}
to={`/objects/${metadataObject.namePlural}`}
Icon={IconBuildingSkyscraper}
/>
))}
<Button
title="+ Create new object"
variant="secondary"
onClick={createNewTempCustomObject}
/>
{metadataObjects
.filter((metadataObject) => !!metadataObject.isActive)
.map((metadataObject) => (
<div style={{ display: 'flex', flexDirection: 'row', width: '60%' }}>
<IconButton
Icon={IconArchive}
onClick={() => {
deleteOneMetadataObject(metadataObject.id);
}}
/>
<NavItem
key={metadataObject.id}
label={capitalize(metadataObject.namePlural)}
to={`/objects/${metadataObject.namePlural}`}
Icon={IconBuildingSkyscraper}
onClick={() => {
navigate(`/objects/${metadataObject.namePlural}`);
}}
/>
</div>
))}
</>
);
};

View File

@@ -21,15 +21,17 @@ export const ObjectDataTableEffect = ({
}: ObjectDataTableEffectProps) => {
const setDataTableData = useSetObjectDataTableData();
const { objects } = useFindManyObjects({
const { objects, loading } = useFindManyObjects({
objectNamePlural,
});
useEffect(() => {
const entities = objects ?? [];
if (!loading) {
const entities = objects ?? [];
setDataTableData(entities);
}, [objects, setDataTableData]);
setDataTableData(entities);
}
}, [objects, setDataTableData, loading]);
const [searchParams] = useSearchParams();
const tableRecoilScopeId = useRecoilScopeId(TableRecoilScopeContext);
@@ -61,8 +63,10 @@ export const ObjectDataTableEffect = ({
const viewId = searchParams.get('view');
if (viewId) {
handleViewSelect(viewId);
} else {
handleViewSelect(objectNamePlural);
}
}, [handleViewSelect, searchParams]);
}, [handleViewSelect, searchParams, objectNamePlural]);
return <></>;
};

View File

@@ -1,10 +1,9 @@
import { suppliersAvailableColumnDefinitions } from '@/companies/constants/companiesAvailableColumnDefinitions';
import { DataTable } from '@/ui/data/data-table/components/DataTable';
import { TableContext } from '@/ui/data/data-table/contexts/TableContext';
import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
import { ViewBarContext } from '@/ui/data/view-bar/contexts/ViewBarContext';
import { useTableViews } from '@/views/hooks/useTableViews';
import { useMetadataTableViews } from '../hooks/useMetadataTableViews';
import { useUpdateOneObject } from '../hooks/useUpdateOneObject';
import { MetadataObjectIdentifier } from '../types/MetadataObjectIdentifier';
@@ -14,10 +13,7 @@ export type ObjectTableProps = MetadataObjectIdentifier;
export const ObjectTable = ({ objectNamePlural }: ObjectTableProps) => {
const { createView, deleteView, submitCurrentView, updateView } =
useTableViews({
objectId: 'company',
columnDefinitions: suppliersAvailableColumnDefinitions,
});
useMetadataTableViews();
const { updateOneObject } = useUpdateOneObject({
objectNamePlural,

View File

@@ -1,4 +1,5 @@
import { useParams } from 'react-router-dom';
import { useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import styled from '@emotion/styled';
import { ObjectTable } from '@/metadata/components/ObjectTable';
@@ -14,6 +15,10 @@ import { PageHeader } from '@/ui/layout/page/PageHeader';
import { PageHotkeysEffect } from '@/ui/layout/page/PageHotkeysEffect';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { useCreateOneObject } from '../hooks/useCreateOneObject';
import { useFindOneMetadataObject } from '../hooks/useFindOneMetadataObject';
import { MetadataObjectScope } from '../scopes/MetadataObjectScope';
const StyledTableContainer = styled.div`
display: flex;
width: 100%;
@@ -24,8 +29,26 @@ export type ObjectTablePageProps = MetadataObjectIdentifier;
export const ObjectTablePage = () => {
const objectNamePlural = useParams().objectNamePlural ?? '';
const { objectNotFoundInMetadata, loading } = useFindOneMetadataObject({
objectNamePlural,
});
const navigate = useNavigate();
useEffect(() => {
if (!loading && objectNotFoundInMetadata) {
navigate('/');
}
}, [objectNotFoundInMetadata, loading, navigate]);
const { createOneObject } = useCreateOneObject({
objectNamePlural,
});
const handleAddButtonClick = async () => {
//
createOneObject?.({
name: 'Test',
});
};
return (
@@ -36,11 +59,13 @@ export const ObjectTablePage = () => {
</PageHeader>
<PageBody>
<RecoilScope
scopeId="objects"
scopeId={objectNamePlural}
CustomRecoilScopeContext={TableRecoilScopeContext}
>
<StyledTableContainer>
<ObjectTable objectNamePlural={objectNamePlural} />
<MetadataObjectScope metadataObjectNamePlural={objectNamePlural}>
<ObjectTable objectNamePlural={objectNamePlural} />
</MetadataObjectScope>
</StyledTableContainer>
<DataTableActionBar />
<DataTableContextMenu />

View File

@@ -4,6 +4,17 @@ export const CREATE_ONE_METADATA_OBJECT = gql`
mutation CreateOneMetadataObject($input: CreateOneObjectInput!) {
createOneObject(input: $input) {
id
dataSourceId
nameSingular
namePlural
labelSingular
labelPlural
description
icon
isCustom
isActive
createdAt
updatedAt
}
}
`;
@@ -34,6 +45,17 @@ export const UPDATE_ONE_METADATA_FIELD = gql`
) {
updateOneField(input: { id: $idToUpdate, update: $updatePayload }) {
id
type
name
label
description
icon
placeholder
isCustom
isActive
isNullable
createdAt
updatedAt
}
}
`;
@@ -45,6 +67,55 @@ export const UPDATE_ONE_METADATA_OBJECT = gql`
) {
updateOneObject(input: { id: $idToUpdate, update: $updatePayload }) {
id
dataSourceId
nameSingular
namePlural
labelSingular
labelPlural
description
icon
isCustom
isActive
createdAt
updatedAt
}
}
`;
export const DELETE_ONE_METADATA_OBJECT = gql`
mutation DeleteOneMetadataObject($idToDelete: ID!) {
deleteOneObject(input: { id: $idToDelete }) {
id
dataSourceId
nameSingular
namePlural
labelSingular
labelPlural
description
icon
isCustom
isActive
createdAt
updatedAt
}
}
`;
export const DELETE_ONE_METADATA_FIELD = gql`
mutation DeleteOneMetadataField($idToDelete: ID!) {
deleteOneField(input: { id: $idToDelete }) {
id
type
name
label
description
icon
placeholder
isCustom
isActive
isNullable
createdAt
updatedAt
}
}
`;

View File

@@ -0,0 +1,160 @@
import { getOperationName } from '@apollo/client/utilities';
import { ColumnDefinition } from '@/ui/data/data-table/types/ColumnDefinition';
import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata';
import { FieldType } from '@/ui/data/field/types/FieldType';
import { IconBrandLinkedin } from '@/ui/display/icon';
import { GET_VIEW_FIELDS } from '@/views/graphql/queries/getViewFields';
import { GET_VIEWS } from '@/views/graphql/queries/getViews';
import { toViewFieldInput } from '@/views/hooks/useTableViewFields';
import {
useCreateViewFieldsMutation,
useCreateViewMutation,
ViewType,
} from '~/generated/graphql';
import { useCreateOneMetadataField } from './useCreateOneMetadataField';
import { useCreateOneMetadataObject } from './useCreateOneMetadataObject';
import { useUpdateOneMetadataField } from './useUpdateOneMetadataField';
import { useUpdateOneMetadataObject } from './useUpdateOneMetadataObject';
export const useCreateNewTempsCustomObject = () => {
const { createOneMetadataObject } = useCreateOneMetadataObject();
const { createOneMetadataField } = useCreateOneMetadataField();
const { updateOneMetadataObject } = useUpdateOneMetadataObject();
const { updateOneMetadataField } = useUpdateOneMetadataField();
const [createViewMutation] = useCreateViewMutation();
const [createViewFieldsMutation] = useCreateViewFieldsMutation();
return async () => {
const date = new Date().toISOString().replace(/[\/:\.\-\_]/g, '');
const { data: createdMetadataObject } = await createOneMetadataObject({
labelPlural: 'Suppliers' + date,
labelSingular: 'Supplier' + date,
nameSingular: 'supplier' + date,
namePlural: 'suppliers' + date,
description: 'Suppliers' + date,
icon: 'IconBuilding',
});
const supplierObjectId = createdMetadataObject?.createOneObject?.id ?? '';
if (!createdMetadataObject) {
throw new Error('Could not create metadata object');
}
await updateOneMetadataObject({
idToUpdate: supplierObjectId,
updatePayload: {
isActive: true,
},
});
const { data: nameFieldData } = await createOneMetadataField({
objectId: supplierObjectId,
name: 'name',
type: 'text',
description: 'Name',
label: 'Name',
icon: 'IconBuilding',
});
if (!nameFieldData || !nameFieldData.createOneField.name) {
throw new Error('Could not create metadata field');
}
await updateOneMetadataField({
fieldIdToUpdate: nameFieldData?.createOneField?.id ?? '',
updatePayload: {
isActive: true,
},
});
const { data: cityFieldData } = await createOneMetadataField({
objectId: supplierObjectId,
label: 'City',
name: 'city',
type: 'text',
description: 'City',
icon: 'IconMap',
});
if (!cityFieldData || !cityFieldData.createOneField.name) {
throw new Error('Could not create metadata field');
}
await updateOneMetadataField({
fieldIdToUpdate: cityFieldData?.createOneField?.id ?? '',
updatePayload: {
isActive: true,
},
});
const { data: emailFieldData } = await createOneMetadataField({
objectId: supplierObjectId,
label: 'Email',
name: 'email',
type: 'url',
description: 'Email',
icon: 'IconMap',
});
if (!emailFieldData || !emailFieldData.createOneField.name) {
throw new Error('Could not create metadata field');
}
await updateOneMetadataField({
fieldIdToUpdate: emailFieldData?.createOneField?.id ?? '',
updatePayload: {
isActive: true,
},
});
const objectId = 'suppliers' + date;
const { data: newView } = await createViewMutation({
variables: {
data: {
name: 'Default',
objectId: objectId,
type: ViewType.Table,
},
},
refetchQueries: [getOperationName(GET_VIEWS) ?? ''],
});
const createdFields = [
emailFieldData.createOneField,
nameFieldData.createOneField,
cityFieldData.createOneField,
];
const tempColumnDefinitions: ColumnDefinition<FieldMetadata>[] =
createdFields.map((field, index) => ({
index,
key: field.name,
name: field.label,
size: 100,
type: field.type as FieldType,
metadata: {
fieldName: field.name,
placeHolder: field.label,
},
Icon: IconBrandLinkedin,
isVisible: true,
})) ?? [];
await createViewFieldsMutation({
variables: {
data: tempColumnDefinitions.map((column) => ({
...toViewFieldInput(objectId, column),
viewId: newView?.view.id ?? '',
})),
},
refetchQueries: [getOperationName(GET_VIEW_FIELDS) ?? ''],
});
};
};

View File

@@ -1,6 +1,7 @@
import { ApolloClient, useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import { FieldType } from '@/ui/data/field/types/FieldType';
import {
CreateOneMetadataFieldMutation,
CreateOneMetadataFieldMutationVariables,
@@ -11,6 +12,11 @@ import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
import { useApolloMetadataClient } from './useApolloMetadataClient';
type CreateOneMetadataFieldArgs =
CreateOneMetadataFieldMutationVariables['input']['field'] & {
type: FieldType;
};
export const useCreateOneMetadataField = () => {
const apolloMetadataClient = useApolloMetadataClient();
@@ -21,10 +27,8 @@ export const useCreateOneMetadataField = () => {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
});
const createOneMetadataField = (
input: CreateOneMetadataFieldMutationVariables['input']['field'],
) =>
mutate({
const createOneMetadataField = async (input: CreateOneMetadataFieldArgs) => {
return await mutate({
variables: {
input: {
field: {
@@ -32,8 +36,10 @@ export const useCreateOneMetadataField = () => {
},
},
},
awaitRefetchQueries: true,
refetchQueries: [getOperationName(FIND_MANY_METADATA_OBJECTS) ?? ''],
});
};
return {
createOneMetadataField,

View File

@@ -21,10 +21,10 @@ export const useCreateOneMetadataObject = () => {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
});
const createOneMetadataObject = (
const createOneMetadataObject = async (
input: CreateOneMetadataObjectMutationVariables['input']['object'],
) =>
mutate({
) => {
return await mutate({
variables: {
input: {
object: {
@@ -32,8 +32,10 @@ export const useCreateOneMetadataObject = () => {
},
},
},
awaitRefetchQueries: true,
refetchQueries: [getOperationName(FIND_MANY_METADATA_OBJECTS) ?? ''],
});
};
return {
createOneMetadataObject,

View File

@@ -1,30 +1,24 @@
import { gql, useMutation } from '@apollo/client';
import { useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import { MetadataObjectIdentifier } from '../types/MetadataObjectIdentifier';
import { generateCreateOneObjectMutation } from '../utils/generateCreateOneObjectMutation';
import { useFindOneMetadataObject } from './useFindOneMetadataObject';
export const useCreateOneObject = ({
objectNamePlural,
}: MetadataObjectIdentifier) => {
const { foundMetadataObject, objectNotFoundInMetadata } =
useFindOneMetadataObject({
objectNamePlural,
});
const generatedMutation = foundMetadataObject
? generateCreateOneObjectMutation({
metadataObject: foundMetadataObject,
})
: gql`
mutation EmptyMutation {
empty
}
`;
const {
foundMetadataObject,
objectNotFoundInMetadata,
findManyQuery,
createOneMutation,
} = useFindOneMetadataObject({
objectNamePlural,
});
// TODO: type this with a minimal type at least with Record<string, any>
const [mutate] = useMutation(generatedMutation);
const [mutate] = useMutation(createOneMutation);
const createOneObject = foundMetadataObject
? (input: Record<string, any>) => {
@@ -34,6 +28,7 @@ export const useCreateOneObject = ({
...input,
},
},
refetchQueries: [getOperationName(findManyQuery) ?? ''],
});
}
: undefined;

View File

@@ -0,0 +1,39 @@
import { ApolloClient, useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import {
DeleteOneMetadataFieldMutation,
DeleteOneMetadataFieldMutationVariables,
} from '~/generated-metadata/graphql';
import { DELETE_ONE_METADATA_FIELD } from '../graphql/mutations';
import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
import { useApolloMetadataClient } from './useApolloMetadataClient';
export const useDeleteOneMetadataField = () => {
const apolloMetadataClient = useApolloMetadataClient();
const [mutate] = useMutation<
DeleteOneMetadataFieldMutation,
DeleteOneMetadataFieldMutationVariables
>(DELETE_ONE_METADATA_FIELD, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
});
const deleteOneMetadataField = async (
idToDelete: DeleteOneMetadataFieldMutationVariables['idToDelete'],
) => {
return await mutate({
variables: {
idToDelete,
},
awaitRefetchQueries: true,
refetchQueries: [getOperationName(FIND_MANY_METADATA_OBJECTS) ?? ''],
});
};
return {
deleteOneMetadataField,
};
};

View File

@@ -0,0 +1,39 @@
import { ApolloClient, useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import {
DeleteOneMetadataObjectMutation,
DeleteOneMetadataObjectMutationVariables,
} from '~/generated-metadata/graphql';
import { DELETE_ONE_METADATA_OBJECT } from '../graphql/mutations';
import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
import { useApolloMetadataClient } from './useApolloMetadataClient';
export const useDeleteOneMetadataObject = () => {
const apolloMetadataClient = useApolloMetadataClient();
const [mutate] = useMutation<
DeleteOneMetadataObjectMutation,
DeleteOneMetadataObjectMutationVariables
>(DELETE_ONE_METADATA_OBJECT, {
client: apolloMetadataClient ?? ({} as ApolloClient<any>),
});
const deleteOneMetadataObject = async (
idToDelete: DeleteOneMetadataObjectMutationVariables['idToDelete'],
) => {
return await mutate({
variables: {
idToDelete,
},
awaitRefetchQueries: true,
refetchQueries: [getOperationName(FIND_MANY_METADATA_OBJECTS) ?? ''],
});
};
return {
deleteOneMetadataObject,
};
};

View File

@@ -0,0 +1,40 @@
import { useMutation } from '@apollo/client';
import { getOperationName } from '@apollo/client/utilities';
import { MetadataObjectIdentifier } from '../types/MetadataObjectIdentifier';
import { useFindOneMetadataObject } from './useFindOneMetadataObject';
export const useDeleteOneObject = ({
objectNamePlural,
}: MetadataObjectIdentifier) => {
const {
foundMetadataObject,
objectNotFoundInMetadata,
findManyQuery,
deleteOneMutation,
} = useFindOneMetadataObject({
objectNamePlural,
});
// TODO: type this with a minimal type at least with Record<string, any>
const [mutate] = useMutation(deleteOneMutation);
const deleteOneObject = foundMetadataObject
? (input: Record<string, any>) => {
return mutate({
variables: {
input: {
...input,
},
},
refetchQueries: [getOperationName(findManyQuery) ?? ''],
});
}
: undefined;
return {
deleteOneObject,
objectNotFoundInMetadata,
};
};

View File

@@ -15,13 +15,17 @@ import { useApolloMetadataClient } from './useApolloMetadataClient';
export const useFindManyMetadataObjects = () => {
const apolloMetadataClient = useApolloMetadataClient();
const { data, fetchMore: fetchMoreInternal } = useQuery<
MetadataObjectsQuery,
MetadataObjectsQueryVariables
>(FIND_MANY_METADATA_OBJECTS, {
client: apolloMetadataClient ?? undefined,
skip: !apolloMetadataClient,
});
const {
data,
fetchMore: fetchMoreInternal,
loading,
} = useQuery<MetadataObjectsQuery, MetadataObjectsQueryVariables>(
FIND_MANY_METADATA_OBJECTS,
{
client: apolloMetadataClient ?? undefined,
skip: !apolloMetadataClient,
},
);
const hasMore = data?.objects?.pageInfo?.hasNextPage;
@@ -38,23 +42,10 @@ export const useFindManyMetadataObjects = () => {
});
}, [data]);
const getMetadataObjectsFromCache = () => {
const queryResult = apolloMetadataClient?.readQuery<
MetadataObjectsQuery,
MetadataObjectsQueryVariables
>({
query: FIND_MANY_METADATA_OBJECTS,
});
return formatPagedMetadataObjectsToMetadataObjects({
pagedMetadataObjects: queryResult ?? undefined,
});
};
return {
metadataObjects,
hasMore,
fetchMore,
getMetadataObjectsFromCache,
loading,
};
};

View File

@@ -1,10 +1,9 @@
import { useMemo } from 'react';
import { gql, useQuery } from '@apollo/client';
import { useQuery } from '@apollo/client';
import { MetadataObjectIdentifier } from '../types/MetadataObjectIdentifier';
import { PaginatedObjectType } from '../types/PaginatedObjectType';
import { formatPagedObjectsToObjects } from '../utils/formatPagedObjectsToObjects';
import { generateFindManyCustomObjectsQuery } from '../utils/generateFindManyCustomObjectsQuery';
import { useFindOneMetadataObject } from './useFindOneMetadataObject';
@@ -15,23 +14,13 @@ export const useFindManyObjects = <
>({
objectNamePlural,
}: MetadataObjectIdentifier) => {
const { foundMetadataObject, objectNotFoundInMetadata } =
const { foundMetadataObject, objectNotFoundInMetadata, findManyQuery } =
useFindOneMetadataObject({
objectNamePlural,
});
const generatedQuery = foundMetadataObject
? generateFindManyCustomObjectsQuery({
metadataObject: foundMetadataObject,
})
: gql`
query EmptyQuery {
empty
}
`;
const { data, loading, error } = useQuery<PaginatedObjectType<ObjectType>>(
generatedQuery,
findManyQuery,
{
skip: !foundMetadataObject,
},

View File

@@ -1,21 +1,80 @@
import { gql } from '@apollo/client';
import { ColumnDefinition } from '@/ui/data/data-table/types/ColumnDefinition';
import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata';
import { MetadataObjectIdentifier } from '../types/MetadataObjectIdentifier';
import { formatMetadataFieldAsColumnDefinition } from '../utils/formatMetadataFieldAsColumnDefinition';
import { generateCreateOneObjectMutation } from '../utils/generateCreateOneObjectMutation';
import { generateFindManyCustomObjectsQuery } from '../utils/generateFindManyCustomObjectsQuery';
import { useFindManyMetadataObjects } from './useFindManyMetadataObjects';
export const useFindOneMetadataObject = ({
objectNamePlural,
}: MetadataObjectIdentifier) => {
const { metadataObjects } = useFindManyMetadataObjects();
const { metadataObjects, loading } = useFindManyMetadataObjects();
const foundMetadataObject = metadataObjects.find(
(object) => object.namePlural === objectNamePlural,
);
const objectNotFoundInMetadata =
metadataObjects.length > 0 && !foundMetadataObject;
metadataObjects.length === 0 ||
(metadataObjects.length > 0 && !foundMetadataObject);
const columnDefinitions: ColumnDefinition<FieldMetadata>[] =
foundMetadataObject?.fields.map((field, index) =>
formatMetadataFieldAsColumnDefinition({
index,
field,
}),
) ?? [];
// eslint-disable-next-line no-console
console.log({
foundMetadataObject,
columnDefinitions,
});
const findManyQuery = foundMetadataObject
? generateFindManyCustomObjectsQuery({
metadataObject: foundMetadataObject,
})
: gql`
query EmptyQuery {
empty
}
`;
const createOneMutation = foundMetadataObject
? generateCreateOneObjectMutation({
metadataObject: foundMetadataObject,
})
: gql`
mutation EmptyMutation {
empty
}
`;
// TODO: implement backend delete
const deleteOneMutation = foundMetadataObject
? generateCreateOneObjectMutation({
metadataObject: foundMetadataObject,
})
: gql`
mutation EmptyMutation {
empty
}
`;
return {
foundMetadataObject,
objectNotFoundInMetadata,
columnDefinitions,
findManyQuery,
createOneMutation,
deleteOneMutation,
loading,
};
};

View File

@@ -0,0 +1,18 @@
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { MetadataObjectScopeInternalContext } from '../scopes/scope-internal-context/MetadataObjectScopeInternalContext';
type UseMetadataObjectProps = {
metadataObjectNamePlural?: string;
};
export const useMetadataObject = (props?: UseMetadataObjectProps) => {
const scopeId = useAvailableScopeIdOrThrow(
MetadataObjectScopeInternalContext,
props?.metadataObjectNamePlural,
);
return {
scopeId,
};
};

View File

@@ -0,0 +1,27 @@
import { useContext } from 'react';
import { MetadataObjectScopeInternalContext } from '../scopes/scope-internal-context/MetadataObjectScopeInternalContext';
import { useFindOneMetadataObject } from './useFindOneMetadataObject';
export const useMetadataObjectInContext = () => {
const context = useContext(MetadataObjectScopeInternalContext);
if (!context) {
throw new Error(
'Could not find MetadataObjectScopeInternalContext while in useMetadataObjectInContext',
);
}
const { foundMetadataObject, loading, columnDefinitions } =
useFindOneMetadataObject({
objectNamePlural: context.objectNamePlural,
});
return {
...context,
foundMetadataObject,
loading,
columnDefinitions,
};
};

View File

@@ -0,0 +1,81 @@
import { useSearchParams } from 'react-router-dom';
import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scope-contexts/TableRecoilScopeContext';
import { tableColumnsScopedState } from '@/ui/data/data-table/states/tableColumnsScopedState';
import { filtersScopedState } from '@/ui/data/view-bar/states/filtersScopedState';
import { sortsScopedState } from '@/ui/data/view-bar/states/sortsScopedState';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { useTableViewFields } from '@/views/hooks/useTableViewFields';
import { useViewFilters } from '@/views/hooks/useViewFilters';
import { useViews } from '@/views/hooks/useViews';
import { useViewSorts } from '@/views/hooks/useViewSorts';
import { ViewType } from '~/generated/graphql';
import { useMetadataObjectInContext } from './useMetadataObjectInContext';
export const useMetadataTableViews = () => {
const { objectNamePlural, columnDefinitions } = useMetadataObjectInContext();
const tableColumns = useRecoilScopedValue(
tableColumnsScopedState,
TableRecoilScopeContext,
);
const filters = useRecoilScopedValue(
filtersScopedState,
TableRecoilScopeContext,
);
const sorts = useRecoilScopedValue(sortsScopedState, TableRecoilScopeContext);
const [_, setSearchParams] = useSearchParams();
const handleViewCreate = async (viewId: string) => {
await createViewFields(tableColumns, viewId);
await createViewFilters(filters, viewId);
await createViewSorts(sorts, viewId);
setSearchParams({ view: viewId });
};
const objectId = objectNamePlural;
const { createView, deleteView, isFetchingViews, updateView } = useViews({
objectId,
onViewCreate: handleViewCreate,
type: ViewType.Table,
RecoilScopeContext: TableRecoilScopeContext,
});
const { createViewFields, persistColumns } = useTableViewFields({
objectId,
columnDefinitions,
skipFetch: isFetchingViews,
});
const createDefaultViewFields = async () => {
await createViewFields(tableColumns);
};
const { createViewFilters, persistFilters } = useViewFilters({
RecoilScopeContext: TableRecoilScopeContext,
skipFetch: isFetchingViews,
});
const { createViewSorts, persistSorts } = useViewSorts({
RecoilScopeContext: TableRecoilScopeContext,
skipFetch: isFetchingViews,
});
const submitCurrentView = async () => {
await persistFilters();
await persistSorts();
};
return {
createView,
deleteView,
persistColumns,
submitCurrentView,
updateView,
createDefaultViewFields,
isFetchingViews,
};
};

View File

@@ -2,16 +2,11 @@ import { isNonEmptyArray } from '~/utils/isNonEmptyArray';
import { useCreateOneMetadataField } from './useCreateOneMetadataField';
import { useCreateOneMetadataObject } from './useCreateOneMetadataObject';
import { useUpdateOneMetadataField } from './useUpdateOneMetadataField';
import { useUpdateOneMetadataObject } from './useUpdateOneMetadataObject';
export const useSeedCustomObjectsTemp = () => {
const { createOneMetadataObject } = useCreateOneMetadataObject();
const { createOneMetadataField } = useCreateOneMetadataField();
const { updateOneMetadataObject } = useUpdateOneMetadataObject();
const { updateOneMetadataField } = useUpdateOneMetadataField();
return async () => {
const { data: createdMetadataObject, errors } =
await createOneMetadataObject({
@@ -26,7 +21,7 @@ export const useSeedCustomObjectsTemp = () => {
if (!isNonEmptyArray(errors)) {
const supplierObjectId = createdMetadataObject?.createOneObject?.id ?? '';
const { data: createNameFieldData } = await createOneMetadataField({
await createOneMetadataField({
objectId: supplierObjectId,
name: 'name',
type: 'text',
@@ -35,9 +30,7 @@ export const useSeedCustomObjectsTemp = () => {
icon: 'IconBuilding',
});
const nameFieldId = createNameFieldData?.createOneField.id ?? '';
const { data: createCityFieldData } = await createOneMetadataField({
await createOneMetadataField({
objectId: supplierObjectId,
label: 'City',
name: 'city',
@@ -45,31 +38,6 @@ export const useSeedCustomObjectsTemp = () => {
description: 'City',
icon: 'IconMap',
});
const cityFieldId = createCityFieldData?.createOneField.id ?? '';
await updateOneMetadataObject({
idToUpdate: supplierObjectId,
updatePayload: {
labelPlural: 'Suppliers 2',
},
});
await updateOneMetadataField({
objectIdToUpdate: supplierObjectId,
fieldIdToUpdate: cityFieldId,
updatePayload: {
label: 'City 2',
},
});
await updateOneMetadataField({
objectIdToUpdate: supplierObjectId,
fieldIdToUpdate: nameFieldId,
updatePayload: {
label: 'Name 2',
},
});
}
};
};

View File

@@ -10,13 +10,10 @@ import { UPDATE_ONE_METADATA_FIELD } from '../graphql/mutations';
import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
import { useApolloMetadataClient } from './useApolloMetadataClient';
import { useFindManyMetadataObjects } from './useFindManyMetadataObjects';
export const useUpdateOneMetadataField = () => {
const apolloMetadataClient = useApolloMetadataClient();
const { getMetadataObjectsFromCache } = useFindManyMetadataObjects();
const [mutate] = useMutation<
UpdateOneMetadataFieldMutation,
UpdateOneMetadataFieldMutationVariables
@@ -24,48 +21,24 @@ export const useUpdateOneMetadataField = () => {
client: apolloMetadataClient ?? undefined,
});
const updateOneMetadataField = ({
objectIdToUpdate,
const updateOneMetadataField = async ({
fieldIdToUpdate,
updatePayload,
}: {
objectIdToUpdate: string;
fieldIdToUpdate: UpdateOneMetadataFieldMutationVariables['idToUpdate'];
updatePayload: Partial<
Pick<
UpdateOneMetadataFieldMutationVariables['updatePayload'],
'description' | 'icon' | 'isActive' | 'label'
>
updatePayload: Pick<
UpdateOneMetadataFieldMutationVariables['updatePayload'],
'description' | 'icon' | 'isActive' | 'label'
>;
}) => {
const metadataObjects = getMetadataObjectsFromCache();
const foundMetadataObject = metadataObjects.find(
(metadataObject) => metadataObject.id === objectIdToUpdate,
);
if (!foundMetadataObject)
throw new Error(`Metadata object with id ${objectIdToUpdate} not found`);
const foundMetadataField = foundMetadataObject.fields.find(
(metadataField) => metadataField.id === fieldIdToUpdate,
);
if (!foundMetadataField)
throw new Error(`Metadata field with id ${fieldIdToUpdate} not found`);
return mutate({
return await mutate({
variables: {
idToUpdate: fieldIdToUpdate,
updatePayload: {
name: foundMetadataField.name,
description: foundMetadataField.description,
icon: foundMetadataField.icon,
isActive: foundMetadataField.isActive,
label: foundMetadataField.label,
...updatePayload,
label: updatePayload.label ?? undefined,
},
},
awaitRefetchQueries: true,
refetchQueries: [getOperationName(FIND_MANY_METADATA_OBJECTS) ?? ''],
});
};

View File

@@ -10,14 +10,11 @@ import { UPDATE_ONE_METADATA_OBJECT } from '../graphql/mutations';
import { FIND_MANY_METADATA_OBJECTS } from '../graphql/queries';
import { useApolloMetadataClient } from './useApolloMetadataClient';
import { useFindManyMetadataObjects } from './useFindManyMetadataObjects';
// TODO: Slice the Apollo store synchronously in the update function instead of subscribing, so we can use update after read in the same function call
export const useUpdateOneMetadataObject = () => {
const apolloClientMetadata = useApolloMetadataClient();
const { getMetadataObjectsFromCache } = useFindManyMetadataObjects();
const [mutate] = useMutation<
UpdateOneMetadataObjectMutation,
UpdateOneMetadataObjectMutationVariables
@@ -25,41 +22,22 @@ export const useUpdateOneMetadataObject = () => {
client: apolloClientMetadata ?? undefined,
});
const updateOneMetadataObject = ({
const updateOneMetadataObject = async ({
idToUpdate,
updatePayload,
}: {
idToUpdate: UpdateOneMetadataObjectMutationVariables['idToUpdate'];
updatePayload: Partial<
Pick<
UpdateOneMetadataObjectMutationVariables['updatePayload'],
'description' | 'icon' | 'isActive' | 'labelPlural' | 'labelSingular'
>
updatePayload: Pick<
UpdateOneMetadataObjectMutationVariables['updatePayload'],
'description' | 'icon' | 'isActive' | 'labelPlural' | 'labelSingular'
>;
}) => {
const metadataObjects = getMetadataObjectsFromCache();
const foundMetadataObject = metadataObjects.find(
(metadataObject) => metadataObject.id === idToUpdate,
);
if (!foundMetadataObject)
throw new Error(`Metadata object with id ${idToUpdate} not found`);
return mutate({
return await mutate({
variables: {
idToUpdate,
updatePayload: {
namePlural: foundMetadataObject.namePlural,
nameSingular: foundMetadataObject.nameSingular,
description: foundMetadataObject.description,
icon: foundMetadataObject.icon,
isActive: foundMetadataObject.isActive,
labelPlural: foundMetadataObject.labelPlural,
labelSingular: foundMetadataObject.labelSingular,
...updatePayload,
},
updatePayload,
},
awaitRefetchQueries: true,
refetchQueries: [getOperationName(FIND_MANY_METADATA_OBJECTS) ?? ''],
});
};

View File

@@ -0,0 +1,24 @@
import { ReactNode } from 'react';
import { MetadataObjectScopeInternalContext } from './scope-internal-context/MetadataObjectScopeInternalContext';
type MetadataObjectScopeProps = {
children: ReactNode;
metadataObjectNamePlural: string;
};
export const MetadataObjectScope = ({
children,
metadataObjectNamePlural,
}: MetadataObjectScopeProps) => {
return (
<MetadataObjectScopeInternalContext.Provider
value={{
scopeId: metadataObjectNamePlural,
objectNamePlural: metadataObjectNamePlural,
}}
>
{children}
</MetadataObjectScopeInternalContext.Provider>
);
};

View File

@@ -0,0 +1,9 @@
import { ScopedStateKey } from '@/ui/utilities/recoil-scope/scopes-internal/types/ScopedStateKey';
import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
type MetadataObjectScopeInternalContextProps = ScopedStateKey & {
objectNamePlural: string;
};
export const MetadataObjectScopeInternalContext =
createScopeInternalContext<MetadataObjectScopeInternalContextProps>();

View File

@@ -2,6 +2,9 @@ import { atom } from 'recoil';
import { MetadataObject } from '../types/MetadataObject';
/**
* @deprecated Use `useFindManyMetadataObjects` instead.
*/
export const metadataObjectsState = atom<MetadataObject[]>({
key: 'metadataObjectsState',
default: [],

View File

@@ -0,0 +1,34 @@
import { ColumnDefinition } from '@/ui/data/data-table/types/ColumnDefinition';
import { FieldMetadata } from '@/ui/data/field/types/FieldMetadata';
import { FieldType } from '@/ui/data/field/types/FieldType';
import { IconBrandLinkedin } from '@/ui/display/icon';
import { MetadataObject } from '../types/MetadataObject';
const parseFieldType = (fieldType: string): FieldType => {
if (fieldType === 'url') {
return 'urlV2';
}
return fieldType as FieldType;
};
export const formatMetadataFieldAsColumnDefinition = ({
index,
field,
}: {
index: number;
field: MetadataObject['fields'][0];
}): ColumnDefinition<FieldMetadata> => ({
index,
key: field.name,
name: field.label,
size: 100,
type: parseFieldType(field.type),
metadata: {
fieldName: field.name,
placeHolder: field.label,
},
Icon: IconBrandLinkedin,
isVisible: true,
});

View File

@@ -0,0 +1,22 @@
import { gql } from '@apollo/client';
import { capitalize } from '~/utils/string/capitalize';
import { MetadataObject } from '../types/MetadataObject';
// TODO: implement
export const generateDeleteOneObjectMutation = ({
metadataObject,
}: {
metadataObject: MetadataObject;
}) => {
const capitalizedObjectName = capitalize(metadataObject.nameSingular);
return gql`
mutation DeleteOne${capitalizedObjectName}($input: ${capitalizedObjectName}DeleteInput!) {
createOne${capitalizedObjectName}(data: $input) {
id
}
}
`;
};

View File

@@ -1,5 +1,7 @@
import { gql } from '@apollo/client';
import { FieldType } from '@/ui/data/field/types/FieldType';
import { MetadataObject } from '../types/MetadataObject';
export const generateFindManyCustomObjectsQuery = ({
@@ -15,7 +17,24 @@ export const generateFindManyCustomObjectsQuery = ({
edges {
node {
id
${metadataObject.fields.map((field) => field.name).join('\n')}
${metadataObject.fields
.map((field) => {
// TODO: parse
const fieldType = field.type as FieldType;
if (fieldType === 'text') {
return field.name;
} else if (fieldType === 'url') {
return `
${field.name}
{
text
link
}
`;
}
})
.join('\n')}
}
cursor
}

View File

@@ -12,6 +12,7 @@ import { PhoneFieldDisplay } from '../meta-types/display/components/PhoneFieldDi
import { RelationFieldDisplay } from '../meta-types/display/components/RelationFieldDisplay';
import { TextFieldDisplay } from '../meta-types/display/components/TextFieldDisplay';
import { URLFieldDisplay } from '../meta-types/display/components/URLFieldDisplay';
import { URLV2FieldDisplay } from '../meta-types/display/components/URLV2FieldDisplay';
import { isFieldChip } from '../types/guards/isFieldChip';
import { isFieldDate } from '../types/guards/isFieldDate';
import { isFieldDoubleText } from '../types/guards/isFieldDoubleText';
@@ -23,6 +24,7 @@ import { isFieldPhone } from '../types/guards/isFieldPhone';
import { isFieldRelation } from '../types/guards/isFieldRelation';
import { isFieldText } from '../types/guards/isFieldText';
import { isFieldURL } from '../types/guards/isFieldURL';
import { isFieldURLV2 } from '../types/guards/isFieldURLV2';
export const FieldDisplay = () => {
const { fieldDefinition } = useContext(FieldContext);
@@ -43,6 +45,8 @@ export const FieldDisplay = () => {
<MoneyFieldDisplay />
) : isFieldURL(fieldDefinition) ? (
<URLFieldDisplay />
) : isFieldURLV2(fieldDefinition) ? (
<URLV2FieldDisplay />
) : isFieldPhone(fieldDefinition) ? (
<PhoneFieldDisplay />
) : isFieldChip(fieldDefinition) ? (

View File

@@ -16,6 +16,7 @@ import { ProbabilityFieldInput } from '../meta-types/input/components/Probabilit
import { RelationFieldInput } from '../meta-types/input/components/RelationFieldInput';
import { TextFieldInput } from '../meta-types/input/components/TextFieldInput';
import { URLFieldInput } from '../meta-types/input/components/URLFieldInput';
import { URLV2FieldInput } from '../meta-types/input/components/URLV2FieldInput';
import { FieldInputEvent } from '../types/FieldInputEvent';
import { isFieldBoolean } from '../types/guards/isFieldBoolean';
import { isFieldChip } from '../types/guards/isFieldChip';
@@ -30,6 +31,7 @@ import { isFieldProbability } from '../types/guards/isFieldProbability';
import { isFieldRelation } from '../types/guards/isFieldRelation';
import { isFieldText } from '../types/guards/isFieldText';
import { isFieldURL } from '../types/guards/isFieldURL';
import { isFieldURLV2 } from '../types/guards/isFieldURLV2';
type FieldInputProps = {
onSubmit?: FieldInputEvent;
@@ -98,6 +100,14 @@ export const FieldInput = ({
onTab={onTab}
onShiftTab={onShiftTab}
/>
) : isFieldURLV2(fieldDefinition) ? (
<URLV2FieldInput
onEnter={onEnter}
onEscape={onEscape}
onClickOutside={onClickOutside}
onTab={onTab}
onShiftTab={onShiftTab}
/>
) : isFieldPhone(fieldDefinition) ? (
<PhoneFieldInput
onEnter={onEnter}

View File

@@ -28,6 +28,8 @@ import { isFieldRelationValue } from '../types/guards/isFieldRelationValue';
import { isFieldText } from '../types/guards/isFieldText';
import { isFieldTextValue } from '../types/guards/isFieldTextValue';
import { isFieldURL } from '../types/guards/isFieldURL';
import { isFieldURLV2 } from '../types/guards/isFieldURLV2';
import { isFieldURLV2Value } from '../types/guards/isFieldURLV2Value';
import { isFieldURLValue } from '../types/guards/isFieldURLValue';
export const usePersistField = () => {
@@ -66,6 +68,9 @@ export const usePersistField = () => {
const fieldIsURL =
isFieldURL(fieldDefinition) && isFieldURLValue(valueToPersist);
const fieldIsURLV2 =
isFieldURLV2(fieldDefinition) && isFieldURLV2Value(valueToPersist);
const fieldIsBoolean =
isFieldBoolean(fieldDefinition) &&
isFieldBooleanValue(valueToPersist);
@@ -154,7 +159,8 @@ export const usePersistField = () => {
fieldIsNumber ||
fieldIsMoney ||
fieldIsDate ||
fieldIsPhone
fieldIsPhone ||
fieldIsURLV2
) {
const fieldName = fieldDefinition.metadata.fieldName;
@@ -173,7 +179,11 @@ export const usePersistField = () => {
});
} else {
throw new Error(
`Invalid value to persist: ${valueToPersist} for type : ${fieldDefinition.type}, type may not be implemented in usePersistField.`,
`Invalid value to persist: ${JSON.stringify(
valueToPersist,
)} for type : ${
fieldDefinition.type
}, type may not be implemented in usePersistField.`,
);
}
},

View File

@@ -0,0 +1,8 @@
import { useURLV2Field } from '../../hooks/useURLV2Field';
import { URLV2Display } from '../content-display/components/URLDisplayV2';
export const URLV2FieldDisplay = () => {
const { fieldValue } = useURLV2Field();
return <URLV2Display value={fieldValue} />;
};

View File

@@ -0,0 +1,73 @@
import { MouseEvent } from 'react';
import styled from '@emotion/styled';
import { FieldURLV2Value } from '@/ui/data/field/types/FieldMetadata';
import { RoundedLink } from '@/ui/navigation/link/components/RoundedLink';
import {
LinkType,
SocialLink,
} from '@/ui/navigation/link/components/SocialLink';
import { EllipsisDisplay } from './EllipsisDisplay';
const StyledRawLink = styled(RoundedLink)`
overflow: hidden;
a {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
`;
type URLV2DisplayProps = {
value?: FieldURLV2Value;
};
const checkUrlType = (url: string) => {
if (
/^(http|https):\/\/(?:www\.)?linkedin.com(\w+:{0,1}\w*@)?(\S+)(:([0-9])+)?(\/|\/([\w#!:.?+=&%@!\-/]))?/.test(
url,
)
) {
return LinkType.LinkedIn;
}
if (url.match(/^((http|https):\/\/)?(?:www\.)?twitter\.com\/(\w+)?/i)) {
return LinkType.Twitter;
}
return LinkType.Url;
};
export const URLV2Display = ({ value }: URLV2DisplayProps) => {
const handleClick = (event: MouseEvent<HTMLElement>) => {
event.stopPropagation();
};
const absoluteUrl = value?.link
? value.link.startsWith('http')
? value.link
: 'https://' + value.link
: '';
const displayedValue = value?.text ?? '';
const type = checkUrlType(absoluteUrl);
if (type === LinkType.LinkedIn || type === LinkType.Twitter) {
return (
<EllipsisDisplay>
<SocialLink href={absoluteUrl} onClick={handleClick} type={type}>
{displayedValue}
</SocialLink>
</EllipsisDisplay>
);
}
return (
<EllipsisDisplay>
<StyledRawLink href={absoluteUrl} onClick={handleClick}>
{displayedValue}
</StyledRawLink>
</EllipsisDisplay>
);
};

View File

@@ -0,0 +1,43 @@
import { useContext } from 'react';
import { useRecoilState } from 'recoil';
import { FieldContext } from '../../contexts/FieldContext';
import { usePersistField } from '../../hooks/usePersistField';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
import { FieldURLV2Value } from '../../types/FieldMetadata';
import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata';
import { isFieldURLV2 } from '../../types/guards/isFieldURLV2';
import { isFieldURLV2Value } from '../../types/guards/isFieldURLV2Value';
export const useURLV2Field = () => {
const { entityId, fieldDefinition, hotkeyScope } = useContext(FieldContext);
assertFieldMetadata('urlV2', isFieldURLV2, fieldDefinition);
const fieldName = fieldDefinition.metadata.fieldName;
const [fieldValue, setFieldValue] = useRecoilState<FieldURLV2Value>(
entityFieldsFamilySelector({
entityId: entityId,
fieldName: fieldName,
}),
);
const persistField = usePersistField();
const persistURLField = (newValue: FieldURLV2Value) => {
if (!isFieldURLV2Value(newValue)) {
return;
}
persistField(newValue);
};
return {
fieldDefinition,
fieldValue,
setFieldValue,
hotkeyScope,
persistURLField,
};
};

View File

@@ -0,0 +1,89 @@
import { FieldDoubleText } from '../../../types/FieldDoubleText';
import { useURLV2Field } from '../../hooks/useURLV2Field';
import { DoubleTextInput } from './internal/DoubleTextInput';
import { FieldInputOverlay } from './internal/FieldInputOverlay';
import { FieldInputEvent } from './DateFieldInput';
export type URLV2FieldInputProps = {
onClickOutside?: FieldInputEvent;
onEnter?: FieldInputEvent;
onEscape?: FieldInputEvent;
onTab?: FieldInputEvent;
onShiftTab?: FieldInputEvent;
};
export const URLV2FieldInput = ({
onEnter,
onEscape,
onClickOutside,
onTab,
onShiftTab,
}: URLV2FieldInputProps) => {
const { fieldValue, hotkeyScope, persistURLField } = useURLV2Field();
const handleEnter = (newURL: FieldDoubleText) => {
onEnter?.(() =>
persistURLField({
link: newURL.firstValue,
text: newURL.secondValue,
}),
);
};
const handleEscape = (newURL: FieldDoubleText) => {
onEscape?.(() =>
persistURLField({
link: newURL.firstValue,
text: newURL.secondValue,
}),
);
};
const handleClickOutside = (
event: MouseEvent | TouchEvent,
newURL: FieldDoubleText,
) => {
onClickOutside?.(() =>
persistURLField({
link: newURL.firstValue,
text: newURL.secondValue,
}),
);
};
const handleTab = (newURL: FieldDoubleText) => {
onTab?.(() =>
persistURLField({
link: newURL.firstValue,
text: newURL.secondValue,
}),
);
};
const handleShiftTab = (newURL: FieldDoubleText) => {
onShiftTab?.(() =>
persistURLField({
link: newURL.firstValue,
text: newURL.secondValue,
}),
);
};
return (
<FieldInputOverlay>
<DoubleTextInput
firstValue={fieldValue.link}
secondValue={fieldValue.text}
firstValuePlaceholder={'Link'}
secondValuePlaceholder={'Label'}
hotkeyScope={hotkeyScope}
onClickOutside={handleClickOutside}
onEnter={handleEnter}
onEscape={handleEscape}
onTab={handleTab}
onShiftTab={handleShiftTab}
/>
</FieldInputOverlay>
);
};

View File

@@ -16,6 +16,11 @@ export type FieldURLMetadata = {
fieldName: string;
};
export type FieldURLV2Metadata = {
placeHolder: string;
fieldName: string;
};
export type FieldDateMetadata = {
fieldName: string;
};
@@ -82,6 +87,7 @@ export type FieldMetadata =
| FieldDoubleTextMetadata
| FieldPhoneMetadata
| FieldURLMetadata
| FieldURLV2Metadata
| FieldNumberMetadata
| FieldMoneyMetadata
| FieldEmailMetadata
@@ -95,6 +101,7 @@ export type FieldChipValue = string;
export type FieldDateValue = string | null;
export type FieldPhoneValue = string;
export type FieldURLValue = string;
export type FieldURLV2Value = { link: string; text: string };
export type FieldNumberValue = number | null;
export type FieldMoneyValue = number | null;
export type FieldEmailValue = string;

View File

@@ -10,5 +10,6 @@ export type FieldType =
| 'date'
| 'phone'
| 'url'
| 'urlV2'
| 'probability'
| 'moneyAmount';

View File

@@ -14,6 +14,7 @@ import {
FieldRelationMetadata,
FieldTextMetadata,
FieldURLMetadata,
FieldURLV2Metadata,
} from '../FieldMetadata';
import { FieldType } from '../FieldType';
@@ -41,6 +42,8 @@ type AssertFieldMetadataFunction = <
? FieldPhoneMetadata
: E extends 'url'
? FieldURLMetadata
: E extends 'urlV2'
? FieldURLV2Metadata
: E extends 'probability'
? FieldProbabilityMetadata
: E extends 'moneyAmount'

View File

@@ -0,0 +1,6 @@
import { FieldDefinition } from '../FieldDefinition';
import { FieldMetadata, FieldURLV2Metadata } from '../FieldMetadata';
export const isFieldURLV2 = (
field: FieldDefinition<FieldMetadata>,
): field is FieldDefinition<FieldURLV2Metadata> => field.type === 'urlV2';

View File

@@ -0,0 +1,13 @@
import { z } from 'zod';
import { FieldURLV2Value } from '../FieldMetadata';
const urlV2Schema = z.object({
link: z.string(),
text: z.string(),
});
// TODO: add yup
export const isFieldURLV2Value = (
fieldValue: unknown,
): fieldValue is FieldURLV2Value => urlV2Schema.safeParse(fieldValue).success;

View File

@@ -23,8 +23,8 @@ import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { GET_VIEW_FIELDS } from '../graphql/queries/getViewFields';
const toViewFieldInput = (
objectId: 'company' | 'person',
export const toViewFieldInput = (
objectId: string,
fieldDefinition: ColumnDefinition<FieldMetadata>,
) => ({
key: fieldDefinition.key,
@@ -40,7 +40,7 @@ export const useTableViewFields = ({
columnDefinitions,
skipFetch,
}: {
objectId: 'company' | 'person';
objectId: string;
columnDefinitions: ColumnDefinition<FieldMetadata>[];
skipFetch?: boolean;
}) => {
@@ -110,7 +110,7 @@ export const useTableViewFields = ({
);
useGetViewFieldsQuery({
skip: !currentViewId || skipFetch,
skip: !currentViewId || skipFetch || columnDefinitions.length === 0,
variables: {
orderBy: { index: SortOrder.Asc },
where: {

View File

@@ -18,7 +18,7 @@ export const useTableViews = ({
objectId,
columnDefinitions,
}: {
objectId: 'company' | 'person';
objectId: string;
columnDefinitions: ColumnDefinition<FieldMetadata>[];
}) => {
const tableColumns = useRecoilScopedValue(
@@ -52,6 +52,10 @@ export const useTableViews = ({
skipFetch: isFetchingViews,
});
const createDefaultViewFields = async () => {
await createViewFields(tableColumns);
};
const { createViewFilters, persistFilters } = useViewFilters({
RecoilScopeContext: TableRecoilScopeContext,
skipFetch: isFetchingViews,
@@ -73,5 +77,7 @@ export const useTableViews = ({
persistColumns,
submitCurrentView,
updateView,
createDefaultViewFields,
isFetchingViews,
};
};

View File

@@ -22,7 +22,7 @@ export const useViews = ({
RecoilScopeContext,
type,
}: {
objectId: 'company' | 'person';
objectId: string;
onViewCreate?: (viewId: string) => Promise<void>;
RecoilScopeContext: RecoilScopeContext;
type: ViewType;

View File

@@ -1,18 +1,13 @@
import { Field, InputType } from '@nestjs/graphql';
import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator';
import { IsBoolean, IsOptional, IsString } from 'class-validator';
@InputType()
export class UpdateFieldInput {
@IsString()
@IsNotEmpty()
@Field()
name: string;
@IsString()
@IsNotEmpty()
@Field()
label: string;
@IsOptional()
@Field({ nullable: true })
label?: string;
@IsString()
@IsOptional()

View File

@@ -6,23 +6,13 @@ import { IsBoolean, IsOptional, IsString } from 'class-validator';
export class UpdateObjectInput {
@IsString()
@IsOptional()
@Field()
nameSingular: string;
@Field({ nullable: true })
labelSingular?: string;
@IsString()
@IsOptional()
@Field()
namePlural: string;
@IsString()
@IsOptional()
@Field()
labelSingular: string;
@IsString()
@IsOptional()
@Field()
labelPlural: string;
@Field({ nullable: true })
labelPlural?: string;
@IsString()
@IsOptional()