diff --git a/front/src/App.tsx b/front/src/App.tsx index b89be02e3..1e184234d 100644 --- a/front/src/App.tsx +++ b/front/src/App.tsx @@ -59,15 +59,7 @@ export const App = () => { } /> } /> - - } - /> + } /> { Icon={IconTargetArrow} active={currentPath === '/opportunities'} /> + ) : ( diff --git a/front/src/modules/metadata/components/MetadataObjectNavItems.tsx b/front/src/modules/metadata/components/MetadataObjectNavItems.tsx new file mode 100644 index 000000000..18dcb4c61 --- /dev/null +++ b/front/src/modules/metadata/components/MetadataObjectNavItems.tsx @@ -0,0 +1,29 @@ +import { IconBuildingSkyscraper } from '@/ui/display/icon'; +import NavItem from '@/ui/navigation/navbar/components/NavItem'; +import { useGetClientConfigQuery } from '~/generated/graphql'; +import { capitalize } from '~/utils/string/capitalize'; + +import { useFindManyMetadataObjects } from '../hooks/useFindManyMetadataObjects'; + +export const MetadataObjectNavItems = () => { + const { data } = useGetClientConfigQuery(); + + const { metadataObjects } = useFindManyMetadataObjects(); + + const isFlexibleBackendEnabled = data?.clientConfig?.flexibleBackendEnabled; + + if (!isFlexibleBackendEnabled) return <>; + + return ( + <> + {metadataObjects.map((metadataObject) => ( + + ))} + + ); +}; diff --git a/front/src/modules/metadata/components/ObjectDataTableEffect.tsx b/front/src/modules/metadata/components/ObjectDataTableEffect.tsx index 524f10f9c..fd51fd0dd 100644 --- a/front/src/modules/metadata/components/ObjectDataTableEffect.tsx +++ b/front/src/modules/metadata/components/ObjectDataTableEffect.tsx @@ -12,25 +12,24 @@ import { useRecoilScopeId } from '@/ui/utilities/recoil-scope/hooks/useRecoilSco import { useFindManyObjects } from '../hooks/useFindManyObjects'; import { useSetObjectDataTableData } from '../hooks/useSetDataTableData'; +import { MetadataObjectIdentifier } from '../types/MetadataObjectIdentifier'; + +export type ObjectDataTableEffectProps = MetadataObjectIdentifier; export const ObjectDataTableEffect = ({ - objectNameSingular, objectNamePlural, -}: { - objectNamePlural: string; - objectNameSingular: string; -}) => { +}: ObjectDataTableEffectProps) => { const setDataTableData = useSetObjectDataTableData(); const { objects } = useFindManyObjects({ - objectNamePlural: objectNamePlural, + objectNamePlural, }); useEffect(() => { const entities = objects ?? []; setDataTableData(entities); - }, [objects, objectNameSingular, setDataTableData]); + }, [objects, setDataTableData]); const [searchParams] = useSearchParams(); const tableRecoilScopeId = useRecoilScopeId(TableRecoilScopeContext); diff --git a/front/src/modules/metadata/components/ObjectTable.tsx b/front/src/modules/metadata/components/ObjectTable.tsx index 0be4b1a71..c0525104e 100644 --- a/front/src/modules/metadata/components/ObjectTable.tsx +++ b/front/src/modules/metadata/components/ObjectTable.tsx @@ -1,27 +1,43 @@ import { suppliersAvailableColumnDefinitions } from '@/companies/constants/companiesAvailableColumnDefinitions'; -import { useSpreadsheetCompanyImport } from '@/companies/hooks/useSpreadsheetCompanyImport'; 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 { useUpdateOneObject } from '../hooks/useUpdateOneObject'; +import { MetadataObjectIdentifier } from '../types/MetadataObjectIdentifier'; + import { ObjectDataTableEffect } from './ObjectDataTableEffect'; -export const ObjectTable = ({ - objectNamePlural, - objectNameSingular, -}: { - objectNameSingular: string; - objectNamePlural: string; -}) => { +export type ObjectTableProps = MetadataObjectIdentifier; + +export const ObjectTable = ({ objectNamePlural }: ObjectTableProps) => { const { createView, deleteView, submitCurrentView, updateView } = useTableViews({ objectId: 'company', columnDefinitions: suppliersAvailableColumnDefinitions, }); - const { openCompanySpreadsheetImport } = useSpreadsheetCompanyImport(); + const { updateOneObject } = useUpdateOneObject({ + objectNamePlural, + }); + + const updateEntity = ({ + variables, + }: { + variables: { + where: { id: string }; + data: { + [fieldName: string]: any; + }; + }; + }) => { + updateOneObject?.({ + idToUpdate: variables.where.id, + input: variables.data, + }); + }; return ( - + - { - // - }} - /> + ); diff --git a/front/src/modules/metadata/hooks/useCreateOneObject.ts b/front/src/modules/metadata/hooks/useCreateOneObject.ts index ef7bee16c..e7bbe8e91 100644 --- a/front/src/modules/metadata/hooks/useCreateOneObject.ts +++ b/front/src/modules/metadata/hooks/useCreateOneObject.ts @@ -1,19 +1,17 @@ import { gql, useMutation } from '@apollo/client'; +import { MetadataObjectIdentifier } from '../types/MetadataObjectIdentifier'; import { generateCreateOneObjectMutation } from '../utils/generateCreateOneObjectMutation'; -import { useFindManyMetadataObjects } from './useFindManyMetadataObjects'; +import { useFindOneMetadataObject } from './useFindOneMetadataObject'; export const useCreateOneObject = ({ objectNamePlural, -}: { - objectNamePlural: string; -}) => { - const { metadataObjects } = useFindManyMetadataObjects(); - - const foundMetadataObject = metadataObjects.find( - (object) => object.namePlural === objectNamePlural, - ); +}: MetadataObjectIdentifier) => { + const { foundMetadataObject, objectNotFoundInMetadata } = + useFindOneMetadataObject({ + objectNamePlural, + }); const generatedMutation = foundMetadataObject ? generateCreateOneObjectMutation({ @@ -25,6 +23,7 @@ export const useCreateOneObject = ({ } `; + // TODO: type this with a minimal type at least with Record const [mutate] = useMutation(generatedMutation); const createOneObject = foundMetadataObject @@ -39,9 +38,6 @@ export const useCreateOneObject = ({ } : undefined; - const objectNotFoundInMetadata = - metadataObjects.length > 0 && !foundMetadataObject; - return { createOneObject, objectNotFoundInMetadata, diff --git a/front/src/modules/metadata/hooks/useFindManyObjects.ts b/front/src/modules/metadata/hooks/useFindManyObjects.ts index cb88e2112..3b0678452 100644 --- a/front/src/modules/metadata/hooks/useFindManyObjects.ts +++ b/front/src/modules/metadata/hooks/useFindManyObjects.ts @@ -1,11 +1,12 @@ import { useMemo } from 'react'; import { gql, 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 { useFindManyMetadataObjects } from './useFindManyMetadataObjects'; +import { useFindOneMetadataObject } from './useFindOneMetadataObject'; // TODO: test with a wrong name // TODO: add zod to validate that we have at least id on each object @@ -13,14 +14,11 @@ export const useFindManyObjects = < ObjectType extends { id: string } & Record, >({ objectNamePlural, -}: { - objectNamePlural: string; -}) => { - const { metadataObjects } = useFindManyMetadataObjects(); - - const foundMetadataObject = metadataObjects.find( - (object) => object.namePlural === objectNamePlural, - ); +}: MetadataObjectIdentifier) => { + const { foundMetadataObject, objectNotFoundInMetadata } = + useFindOneMetadataObject({ + objectNamePlural, + }); const generatedQuery = foundMetadataObject ? generateFindManyCustomObjectsQuery({ @@ -48,9 +46,6 @@ export const useFindManyObjects = < [data, objectNamePlural], ); - const objectNotFoundInMetadata = - metadataObjects.length > 0 && !foundMetadataObject; - return { objects, loading, diff --git a/front/src/modules/metadata/hooks/useFindOneMetadataObject.ts b/front/src/modules/metadata/hooks/useFindOneMetadataObject.ts new file mode 100644 index 000000000..e4c584c27 --- /dev/null +++ b/front/src/modules/metadata/hooks/useFindOneMetadataObject.ts @@ -0,0 +1,21 @@ +import { MetadataObjectIdentifier } from '../types/MetadataObjectIdentifier'; + +import { useFindManyMetadataObjects } from './useFindManyMetadataObjects'; + +export const useFindOneMetadataObject = ({ + objectNamePlural, +}: MetadataObjectIdentifier) => { + const { metadataObjects } = useFindManyMetadataObjects(); + + const foundMetadataObject = metadataObjects.find( + (object) => object.namePlural === objectNamePlural, + ); + + const objectNotFoundInMetadata = + metadataObjects.length > 0 && !foundMetadataObject; + + return { + foundMetadataObject, + objectNotFoundInMetadata, + }; +}; diff --git a/front/src/modules/metadata/hooks/useUpdateOneMetadataObject.ts b/front/src/modules/metadata/hooks/useUpdateOneMetadataObject.ts index 1faa6650a..5198f9afb 100644 --- a/front/src/modules/metadata/hooks/useUpdateOneMetadataObject.ts +++ b/front/src/modules/metadata/hooks/useUpdateOneMetadataObject.ts @@ -16,8 +16,7 @@ import { useFindManyMetadataObjects } from './useFindManyMetadataObjects'; export const useUpdateOneMetadataObject = () => { const apolloClientMetadata = useApolloMetadataClient(); - const { getMetadataObjectsFromCache: queryMetadataObjects } = - useFindManyMetadataObjects(); + const { getMetadataObjectsFromCache } = useFindManyMetadataObjects(); const [mutate] = useMutation< UpdateOneMetadataObjectMutation, @@ -38,7 +37,7 @@ export const useUpdateOneMetadataObject = () => { > >; }) => { - const metadataObjects = queryMetadataObjects(); + const metadataObjects = getMetadataObjectsFromCache(); const foundMetadataObject = metadataObjects.find( (metadataObject) => metadataObject.id === idToUpdate, diff --git a/front/src/modules/metadata/hooks/useUpdateOneObject.ts b/front/src/modules/metadata/hooks/useUpdateOneObject.ts new file mode 100644 index 000000000..579a3e31b --- /dev/null +++ b/front/src/modules/metadata/hooks/useUpdateOneObject.ts @@ -0,0 +1,52 @@ +import { gql, useMutation } from '@apollo/client'; + +import { MetadataObjectIdentifier } from '../types/MetadataObjectIdentifier'; +import { generateUpdateOneObjectMutation } from '../utils/generateUpdateOneObjectMutation'; + +import { useFindOneMetadataObject } from './useFindOneMetadataObject'; + +export const useUpdateOneObject = ({ + objectNamePlural, +}: MetadataObjectIdentifier) => { + const { foundMetadataObject, objectNotFoundInMetadata } = + useFindOneMetadataObject({ + objectNamePlural, + }); + + const generatedMutation = foundMetadataObject + ? generateUpdateOneObjectMutation({ + metadataObject: foundMetadataObject, + }) + : gql` + mutation EmptyMutation { + empty + } + `; + + // TODO: type this with a minimal type at least with Record + const [mutate] = useMutation(generatedMutation); + + const updateOneObject = foundMetadataObject + ? ({ + idToUpdate, + input, + }: { + idToUpdate: string; + input: Record; + }) => { + return mutate({ + variables: { + idToUpdate: idToUpdate, + input: { + ...input, + }, + }, + }); + } + : undefined; + + return { + updateOneObject, + objectNotFoundInMetadata, + }; +}; diff --git a/front/src/modules/metadata/types/MetadataObjectIdentifier.ts b/front/src/modules/metadata/types/MetadataObjectIdentifier.ts new file mode 100644 index 000000000..05c3f1be0 --- /dev/null +++ b/front/src/modules/metadata/types/MetadataObjectIdentifier.ts @@ -0,0 +1,3 @@ +export type MetadataObjectIdentifier = { + objectNamePlural: string; +}; diff --git a/front/src/modules/metadata/utils/generateUpdateOneObjectMutation.ts b/front/src/modules/metadata/utils/generateUpdateOneObjectMutation.ts new file mode 100644 index 000000000..47b3014dc --- /dev/null +++ b/front/src/modules/metadata/utils/generateUpdateOneObjectMutation.ts @@ -0,0 +1,21 @@ +import { gql } from '@apollo/client'; + +import { capitalize } from '~/utils/string/capitalize'; + +import { MetadataObject } from '../types/MetadataObject'; + +export const generateUpdateOneObjectMutation = ({ + metadataObject, +}: { + metadataObject: MetadataObject; +}) => { + const capitalizedObjectName = capitalize(metadataObject.nameSingular); + + return gql` + mutation UpdateOne${capitalizedObjectName}($idToUpdate: ID!, $input: ${capitalizedObjectName}UpdateInput!) { + updateOne${capitalizedObjectName}(id: $idToUpdate, data: $input) { + id + } + } + `; +}; diff --git a/front/src/modules/types/AppPath.ts b/front/src/modules/types/AppPath.ts index 51e7af629..24ea00f44 100644 --- a/front/src/modules/types/AppPath.ts +++ b/front/src/modules/types/AppPath.ts @@ -17,7 +17,7 @@ export enum AppPath { PersonShowPage = '/person/:personId', TasksPage = '/tasks', OpportunitiesPage = '/opportunities', - ObjectTablePage = '/:objectName', + ObjectTablePage = '/objects/:objectNamePlural', SettingsCatchAll = `/settings/*`, diff --git a/front/src/pages/companies/ObjectsTable.tsx b/front/src/pages/companies/ObjectsTable.tsx index ec99da921..20c17e571 100644 --- a/front/src/pages/companies/ObjectsTable.tsx +++ b/front/src/pages/companies/ObjectsTable.tsx @@ -1,6 +1,8 @@ +import { useParams } from 'react-router-dom'; import styled from '@emotion/styled'; import { ObjectTable } from '@/metadata/components/ObjectTable'; +import { MetadataObjectIdentifier } from '@/metadata/types/MetadataObjectIdentifier'; import { DataTableActionBar } from '@/ui/data/data-table/action-bar/components/DataTableActionBar'; import { DataTableContextMenu } from '@/ui/data/data-table/context-menu/components/DataTableContextMenu'; import { TableRecoilScopeContext } from '@/ui/data/data-table/states/recoil-scope-contexts/TableRecoilScopeContext'; @@ -17,13 +19,11 @@ const StyledTableContainer = styled.div` width: 100%; `; -export const ObjectTablePage = ({ - objectNamePlural, - objectNameSingular, -}: { - objectNameSingular: string; - objectNamePlural: string; -}) => { +export type ObjectTablePageProps = MetadataObjectIdentifier; + +export const ObjectTablePage = () => { + const objectNamePlural = useParams().objectNamePlural ?? ''; + const handleAddButtonClick = async () => { // }; @@ -40,10 +40,7 @@ export const ObjectTablePage = ({ CustomRecoilScopeContext={TableRecoilScopeContext} > - +