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

@ -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

@ -0,0 +1,76 @@
import { useEffect } from 'react';
import { useNavigate, 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';
import { IconBuildingSkyscraper } from '@/ui/display/icon';
import { PageAddButton } from '@/ui/layout/page/PageAddButton';
import { PageBody } from '@/ui/layout/page/PageBody';
import { PageContainer } from '@/ui/layout/page/PageContainer';
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%;
`;
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 (
<PageContainer>
<PageHeader title="Objects" Icon={IconBuildingSkyscraper}>
<PageHotkeysEffect onAddButtonClick={handleAddButtonClick} />
<PageAddButton onClick={handleAddButtonClick} />
</PageHeader>
<PageBody>
<RecoilScope
scopeId={objectNamePlural}
CustomRecoilScopeContext={TableRecoilScopeContext}
>
<StyledTableContainer>
<MetadataObjectScope metadataObjectNamePlural={objectNamePlural}>
<ObjectTable objectNamePlural={objectNamePlural} />
</MetadataObjectScope>
</StyledTableContainer>
<DataTableActionBar />
<DataTableContextMenu />
</RecoilScope>
</PageBody>
</PageContainer>
);
};