Feat/error boundaries (#2779)
* - Changed to objectNameSingular always defined - Added ErrorCatchAll * - Added mock mode for companies logged out - Added a proper ErrorBoundary component * Removed react-error-boundary * Implemented proper ErrorBoundary * Fixes * Change strategy about mocks --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -37,6 +37,10 @@ export const RecordShowPage = () => {
|
||||
objectRecordId: string;
|
||||
}>();
|
||||
|
||||
if (!objectNameSingular) {
|
||||
throw new Error(`Object name is not defined`);
|
||||
}
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
@ -44,7 +48,7 @@ export const RecordShowPage = () => {
|
||||
const { identifiersMapper } = useRelationPicker();
|
||||
|
||||
const { favorites, createFavorite, deleteFavorite } = useFavorites({
|
||||
objectNamePlural: objectMetadataItem?.namePlural,
|
||||
objectNamePlural: objectMetadataItem.namePlural,
|
||||
});
|
||||
|
||||
const [, setEntityFields] = useRecoilState(
|
||||
|
||||
@ -2,6 +2,7 @@ import styled from '@emotion/styled';
|
||||
|
||||
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { RecordTable } from '@/ui/object/record-table/components/RecordTable';
|
||||
import { TableOptionsDropdownId } from '@/ui/object/record-table/constants/TableOptionsDropdownId';
|
||||
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
|
||||
@ -29,17 +30,19 @@ export const RecordTableContainer = ({
|
||||
objectNamePlural: string;
|
||||
createRecord: () => void;
|
||||
}) => {
|
||||
const { objectMetadataItem: foundObjectMetadataItem } = useObjectMetadataItem(
|
||||
{
|
||||
objectNamePlural,
|
||||
},
|
||||
);
|
||||
const { columnDefinitions } = useColumnDefinitionsFromFieldMetadata(
|
||||
foundObjectMetadataItem,
|
||||
);
|
||||
const { objectNameSingular } = useObjectNameSingularFromPlural({
|
||||
objectNamePlural,
|
||||
});
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { columnDefinitions } =
|
||||
useColumnDefinitionsFromFieldMetadata(objectMetadataItem);
|
||||
|
||||
const { updateOneRecord } = useUpdateOneRecord({
|
||||
objectNameSingular: foundObjectMetadataItem?.nameSingular,
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const recordTableId = objectNamePlural ?? '';
|
||||
|
||||
@ -2,6 +2,7 @@ import { useEffect } from 'react';
|
||||
|
||||
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { useRecordTableContextMenuEntries } from '@/object-record/hooks/useRecordTableContextMenuEntries';
|
||||
import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns';
|
||||
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
|
||||
@ -16,18 +17,23 @@ export const RecordTableEffect = ({
|
||||
viewBarId: string;
|
||||
}) => {
|
||||
const {
|
||||
// Todo: do not infer objectNamePlural from recordTableId
|
||||
scopeId: objectNamePlural,
|
||||
setAvailableTableColumns,
|
||||
setOnEntityCountChange,
|
||||
setObjectMetadataConfig,
|
||||
} = useRecordTable({ recordTableScopeId: recordTableId });
|
||||
|
||||
const { objectNameSingular } = useObjectNameSingularFromPlural({
|
||||
objectNamePlural,
|
||||
});
|
||||
|
||||
const {
|
||||
objectMetadataItem,
|
||||
basePathToShowPage,
|
||||
labelIdentifierFieldMetadataId,
|
||||
} = useObjectMetadataItem({
|
||||
objectNamePlural,
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { columnDefinitions, filterDefinitions, sortDefinitions } =
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import styled from '@emotion/styled';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
|
||||
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
|
||||
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { IconBuildingSkyscraper } from '@/ui/display/icon';
|
||||
import { PageAddButton } from '@/ui/layout/page/PageAddButton';
|
||||
import { PageBody } from '@/ui/layout/page/PageBody';
|
||||
@ -25,18 +25,12 @@ const StyledTableContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export type RecordTablePageProps = Pick<
|
||||
ObjectMetadataItemIdentifier,
|
||||
'objectNamePlural'
|
||||
>;
|
||||
|
||||
export const RecordTablePage = () => {
|
||||
const objectNamePlural = useParams().objectNamePlural ?? '';
|
||||
|
||||
const { objectMetadataItemNotFound, objectMetadataItem } =
|
||||
useObjectMetadataItem({
|
||||
objectNamePlural,
|
||||
});
|
||||
const { objectNameSingular } = useObjectNameSingularFromPlural({
|
||||
objectNamePlural,
|
||||
});
|
||||
|
||||
const onboardingStatus = useOnboardingStatus();
|
||||
|
||||
@ -44,15 +38,15 @@ export const RecordTablePage = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
objectMetadataItemNotFound &&
|
||||
!isNonEmptyString(objectNamePlural) &&
|
||||
onboardingStatus === OnboardingStatus.Completed
|
||||
) {
|
||||
navigate('/');
|
||||
}
|
||||
}, [objectMetadataItemNotFound, navigate, onboardingStatus]);
|
||||
}, [objectNamePlural, navigate, onboardingStatus]);
|
||||
|
||||
const { createOneRecord: createOneObject } = useCreateOneRecord({
|
||||
objectNameSingular: objectMetadataItem?.nameSingular,
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const handleAddButtonClick = async () => {
|
||||
|
||||
@ -8,42 +8,39 @@ import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const useCreateOneRecord = <T>({
|
||||
objectNameSingular,
|
||||
}: Pick<ObjectMetadataItemIdentifier, 'objectNameSingular'>) => {
|
||||
}: ObjectMetadataItemIdentifier) => {
|
||||
const { triggerOptimisticEffects } = useOptimisticEffect({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const {
|
||||
objectMetadataItem,
|
||||
objectMetadataItemNotFound,
|
||||
createOneRecordMutation,
|
||||
} = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
const { objectMetadataItem, createOneRecordMutation } = useObjectMetadataItem(
|
||||
{
|
||||
objectNameSingular,
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: type this with a minimal type at least with Record<string, any>
|
||||
const [mutate] = useMutation(createOneRecordMutation);
|
||||
|
||||
const createOneRecord = async (input: Record<string, any>) => {
|
||||
if (!objectMetadataItem || !objectNameSingular) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const createdRecord = await mutate({
|
||||
const createdObject = await mutate({
|
||||
variables: {
|
||||
input: { ...input, id: v4() },
|
||||
},
|
||||
});
|
||||
|
||||
triggerOptimisticEffects(
|
||||
`${capitalize(objectNameSingular)}Edge`,
|
||||
createdRecord.data[`create${capitalize(objectNameSingular)}`],
|
||||
`${capitalize(objectMetadataItem.nameSingular)}Edge`,
|
||||
createdObject.data[
|
||||
`create${capitalize(objectMetadataItem.nameSingular)}`
|
||||
],
|
||||
);
|
||||
return createdRecord.data[`create${capitalize(objectNameSingular)}`] as T;
|
||||
return createdObject.data[
|
||||
`create${capitalize(objectMetadataItem.nameSingular)}`
|
||||
] as T;
|
||||
};
|
||||
|
||||
return {
|
||||
createOneRecord,
|
||||
objectMetadataItemNotFound,
|
||||
};
|
||||
};
|
||||
|
||||
@ -8,41 +8,40 @@ import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const useDeleteOneRecord = <T>({
|
||||
objectNameSingular,
|
||||
}: Pick<ObjectMetadataItemIdentifier, 'objectNameSingular'>) => {
|
||||
}: ObjectMetadataItemIdentifier) => {
|
||||
const { performOptimisticEvict } = useOptimisticEvict();
|
||||
|
||||
const {
|
||||
objectMetadataItem,
|
||||
objectMetadataItemNotFound,
|
||||
deleteOneRecordMutation,
|
||||
} = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
const { objectMetadataItem, deleteOneRecordMutation } = useObjectMetadataItem(
|
||||
{
|
||||
objectNameSingular,
|
||||
},
|
||||
);
|
||||
|
||||
// TODO: type this with a minimal type at least with Record<string, any>
|
||||
const [mutate] = useMutation(deleteOneRecordMutation);
|
||||
|
||||
const deleteOneRecord = useCallback(
|
||||
async (idToDelete: string) => {
|
||||
if (!objectMetadataItem || !objectNameSingular) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const deletedRecord = await mutate({
|
||||
variables: {
|
||||
idToDelete,
|
||||
},
|
||||
});
|
||||
|
||||
performOptimisticEvict(capitalize(objectNameSingular), 'id', idToDelete);
|
||||
performOptimisticEvict(
|
||||
capitalize(objectMetadataItem.nameSingular),
|
||||
'id',
|
||||
idToDelete,
|
||||
);
|
||||
|
||||
return deletedRecord.data[`create${capitalize(objectNameSingular)}`] as T;
|
||||
return deletedRecord.data[
|
||||
`create${capitalize(objectMetadataItem.nameSingular)}`
|
||||
] as T;
|
||||
},
|
||||
[performOptimisticEvict, objectMetadataItem, mutate, objectNameSingular],
|
||||
[performOptimisticEvict, objectMetadataItem, mutate],
|
||||
);
|
||||
|
||||
return {
|
||||
deleteOneRecord,
|
||||
objectMetadataItemNotFound,
|
||||
};
|
||||
};
|
||||
|
||||
@ -2,9 +2,10 @@ import { useCallback, useMemo } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { isNonEmptyArray } from '@apollo/client/utilities';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { ObjectMetadataItemIdentifier } from '@/object-metadata/types/ObjectMetadataItemIdentifier';
|
||||
import { getRecordOptimisticEffectDefinition } from '@/object-record/graphql/optimistic-effect-definition/getRecordOptimisticEffectDefinition';
|
||||
@ -27,13 +28,13 @@ import { mapPaginatedRecordsToRecords } from '../utils/mapPaginatedRecordsToReco
|
||||
export const useFindManyRecords = <
|
||||
RecordType extends { id: string } & Record<string, any>,
|
||||
>({
|
||||
objectNamePlural,
|
||||
objectNameSingular,
|
||||
filter,
|
||||
orderBy,
|
||||
limit = DEFAULT_SEARCH_REQUEST_LIMIT,
|
||||
onCompleted,
|
||||
skip,
|
||||
}: Pick<ObjectMetadataItemIdentifier, 'objectNamePlural'> & {
|
||||
}: ObjectMetadataItemIdentifier & {
|
||||
filter?: any;
|
||||
orderBy?: any;
|
||||
limit?: number;
|
||||
@ -41,7 +42,10 @@ export const useFindManyRecords = <
|
||||
skip?: boolean;
|
||||
}) => {
|
||||
const findManyQueryStateIdentifier =
|
||||
objectNamePlural + JSON.stringify(filter) + JSON.stringify(orderBy) + limit;
|
||||
objectNameSingular +
|
||||
JSON.stringify(filter) +
|
||||
JSON.stringify(orderBy) +
|
||||
limit;
|
||||
|
||||
const [lastCursor, setLastCursor] = useRecoilState(
|
||||
cursorFamilyState(findManyQueryStateIdentifier),
|
||||
@ -55,24 +59,21 @@ export const useFindManyRecords = <
|
||||
isFetchingMoreRecordsFamilyState(findManyQueryStateIdentifier),
|
||||
);
|
||||
|
||||
const {
|
||||
objectMetadataItem,
|
||||
objectMetadataItemNotFound,
|
||||
findManyRecordsQuery,
|
||||
} = useObjectMetadataItem({
|
||||
objectNamePlural,
|
||||
const { objectMetadataItem, findManyRecordsQuery } = useObjectMetadataItem({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { registerOptimisticEffect } = useOptimisticEffect({
|
||||
objectNameSingular: objectMetadataItem?.nameSingular,
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
|
||||
const { data, loading, error, fetchMore } = useQuery<
|
||||
PaginatedRecordType<RecordType>
|
||||
>(findManyRecordsQuery, {
|
||||
skip: skip || !objectMetadataItem || !objectNamePlural,
|
||||
skip: skip || !objectMetadataItem || !currentWorkspace,
|
||||
variables: {
|
||||
filter: filter ?? {},
|
||||
limit: limit,
|
||||
@ -92,21 +93,24 @@ export const useFindManyRecords = <
|
||||
});
|
||||
}
|
||||
|
||||
if (objectNamePlural) {
|
||||
onCompleted?.(data[objectNamePlural]);
|
||||
onCompleted?.(data[objectMetadataItem.namePlural]);
|
||||
|
||||
if (objectNamePlural && data?.[objectNamePlural]) {
|
||||
setLastCursor(data?.[objectNamePlural]?.pageInfo.endCursor);
|
||||
setHasNextPage(data?.[objectNamePlural]?.pageInfo.hasNextPage);
|
||||
}
|
||||
if (data?.[objectMetadataItem.namePlural]) {
|
||||
setLastCursor(
|
||||
data?.[objectMetadataItem.namePlural]?.pageInfo.endCursor,
|
||||
);
|
||||
setHasNextPage(
|
||||
data?.[objectMetadataItem.namePlural]?.pageInfo.hasNextPage,
|
||||
);
|
||||
}
|
||||
},
|
||||
onError: (error) => {
|
||||
logError(
|
||||
`useFindManyObjectRecords for "${objectNamePlural}" error : ` + error,
|
||||
`useFindManyRecords for "${objectMetadataItem.namePlural}" error : ` +
|
||||
error,
|
||||
);
|
||||
enqueueSnackBar(
|
||||
`Error during useFindManyObjectRecords for "${objectNamePlural}", ${error.message}`,
|
||||
`Error during useFindManyRecords for "${objectMetadataItem.namePlural}", ${error.message}`,
|
||||
{
|
||||
variant: 'error',
|
||||
},
|
||||
@ -115,7 +119,7 @@ export const useFindManyRecords = <
|
||||
});
|
||||
|
||||
const fetchMoreRecords = useCallback(async () => {
|
||||
if (objectNamePlural && hasNextPage) {
|
||||
if (hasNextPage) {
|
||||
setIsFetchingMoreObjects(true);
|
||||
|
||||
try {
|
||||
@ -126,33 +130,38 @@ export const useFindManyRecords = <
|
||||
lastCursor: isNonEmptyString(lastCursor) ? lastCursor : undefined,
|
||||
},
|
||||
updateQuery: (prev, { fetchMoreResult }) => {
|
||||
const previousEdges = prev?.[objectNamePlural]?.edges;
|
||||
const nextEdges = fetchMoreResult?.[objectNamePlural]?.edges;
|
||||
const previousEdges = prev?.[objectMetadataItem.namePlural]?.edges;
|
||||
const nextEdges =
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural]?.edges;
|
||||
|
||||
let newEdges: PaginatedRecordTypeEdge<RecordType>[] = [];
|
||||
|
||||
if (isNonEmptyArray(previousEdges) && isNonEmptyArray(nextEdges)) {
|
||||
newEdges = filterUniqueRecordEdgesByCursor([
|
||||
...prev?.[objectNamePlural]?.edges,
|
||||
...fetchMoreResult?.[objectNamePlural]?.edges,
|
||||
...prev?.[objectMetadataItem.namePlural]?.edges,
|
||||
...fetchMoreResult?.[objectMetadataItem.namePlural]?.edges,
|
||||
]);
|
||||
}
|
||||
|
||||
return Object.assign({}, prev, {
|
||||
[objectNamePlural]: {
|
||||
[objectMetadataItem.namePlural]: {
|
||||
__typename: `${capitalize(
|
||||
objectMetadataItem?.nameSingular ?? '',
|
||||
objectMetadataItem.nameSingular,
|
||||
)}Connection`,
|
||||
edges: newEdges,
|
||||
pageInfo: fetchMoreResult?.[objectNamePlural].pageInfo,
|
||||
pageInfo:
|
||||
fetchMoreResult?.[objectMetadataItem.namePlural].pageInfo,
|
||||
},
|
||||
} as PaginatedRecordType<RecordType>);
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logError(`fetchMoreObjects for "${objectNamePlural}" error : ` + error);
|
||||
logError(
|
||||
`fetchMoreObjects for "${objectMetadataItem.namePlural}" error : ` +
|
||||
error,
|
||||
);
|
||||
enqueueSnackBar(
|
||||
`Error during fetchMoreObjects for "${objectNamePlural}", ${error}`,
|
||||
`Error during fetchMoreObjects for "${objectMetadataItem.namePlural}", ${error}`,
|
||||
{
|
||||
variant: 'error',
|
||||
},
|
||||
@ -162,7 +171,6 @@ export const useFindManyRecords = <
|
||||
}
|
||||
}
|
||||
}, [
|
||||
objectNamePlural,
|
||||
lastCursor,
|
||||
fetchMore,
|
||||
filter,
|
||||
@ -175,13 +183,11 @@ export const useFindManyRecords = <
|
||||
|
||||
const records = useMemo(
|
||||
() =>
|
||||
objectNamePlural
|
||||
? mapPaginatedRecordsToRecords({
|
||||
pagedRecords: data,
|
||||
objectNamePlural,
|
||||
})
|
||||
: [],
|
||||
[data, objectNamePlural],
|
||||
mapPaginatedRecordsToRecords({
|
||||
pagedRecords: data,
|
||||
objectNamePlural: objectMetadataItem.namePlural,
|
||||
}),
|
||||
[data, objectMetadataItem],
|
||||
);
|
||||
|
||||
return {
|
||||
@ -189,7 +195,6 @@ export const useFindManyRecords = <
|
||||
records,
|
||||
loading,
|
||||
error,
|
||||
objectMetadataItemNotFound,
|
||||
fetchMoreRecords,
|
||||
};
|
||||
};
|
||||
|
||||
@ -11,19 +11,18 @@ export const useFindOneRecord = <
|
||||
onCompleted,
|
||||
depth,
|
||||
skip,
|
||||
}: Pick<ObjectMetadataItemIdentifier, 'objectNameSingular'> & {
|
||||
}: ObjectMetadataItemIdentifier & {
|
||||
objectRecordId: string | undefined;
|
||||
onCompleted?: (data: ObjectType) => void;
|
||||
skip?: boolean;
|
||||
depth?: number;
|
||||
}) => {
|
||||
const { objectMetadataItem, objectMetadataItemNotFound, findOneRecordQuery } =
|
||||
useObjectMetadataItem(
|
||||
{
|
||||
objectNameSingular,
|
||||
},
|
||||
depth,
|
||||
);
|
||||
const { objectMetadataItem, findOneRecordQuery } = useObjectMetadataItem(
|
||||
{
|
||||
objectNameSingular,
|
||||
},
|
||||
depth,
|
||||
);
|
||||
|
||||
const { data, loading, error } = useQuery<
|
||||
{ [nameSingular: string]: ObjectType },
|
||||
@ -34,19 +33,17 @@ export const useFindOneRecord = <
|
||||
objectRecordId: objectRecordId ?? '',
|
||||
},
|
||||
onCompleted: (data) => {
|
||||
if (onCompleted && objectNameSingular) {
|
||||
if (onCompleted) {
|
||||
onCompleted(data[objectNameSingular]);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const record =
|
||||
objectNameSingular && data ? data[objectNameSingular] : undefined;
|
||||
const record = data ? data[objectNameSingular] : undefined;
|
||||
|
||||
return {
|
||||
record,
|
||||
loading,
|
||||
error,
|
||||
objectMetadataItemNotFound,
|
||||
};
|
||||
};
|
||||
|
||||
@ -8,7 +8,7 @@ import { capitalize } from '~/utils/string/capitalize';
|
||||
export const useGenerateCreateOneRecordMutation = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem | undefined | null;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ export const useGenerateFindManyRecordsQuery = ({
|
||||
objectMetadataItem,
|
||||
depth,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem | undefined | null;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
depth?: number;
|
||||
}) => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
|
||||
@ -8,7 +8,7 @@ export const useGenerateFindOneRecordQuery = ({
|
||||
objectMetadataItem,
|
||||
depth,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem | null | undefined;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
depth?: number;
|
||||
}) => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
|
||||
@ -16,7 +16,7 @@ export const getUpdateOneRecordMutationGraphQLField = ({
|
||||
export const useGenerateUpdateOneRecordMutation = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem | undefined | null;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import { capitalize } from '~/utils/string/capitalize';
|
||||
export const useGetRecordFromCache = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem | undefined | null;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery();
|
||||
const apolloClient = useApolloClient();
|
||||
@ -31,7 +31,7 @@ export const useGetRecordFromCache = ({
|
||||
|
||||
const cache = apolloClient.cache;
|
||||
const cachedRecordId = cache.identify({
|
||||
__typename: capitalize(objectMetadataItem?.nameSingular ?? ''),
|
||||
__typename: capitalize(objectMetadataItem.nameSingular),
|
||||
id: recordId,
|
||||
});
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import { capitalize } from '~/utils/string/capitalize';
|
||||
export const useModifyRecordFromCache = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem | undefined | null;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
const apolloClient = useApolloClient();
|
||||
|
||||
@ -19,7 +19,7 @@ export const useModifyRecordFromCache = ({
|
||||
|
||||
const cache = apolloClient.cache;
|
||||
const cachedRecordId = cache.identify({
|
||||
__typename: capitalize(objectMetadataItem?.nameSingular ?? ''),
|
||||
__typename: capitalize(objectMetadataItem.nameSingular),
|
||||
id: recordId,
|
||||
});
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useOptimisticEffect } from '@/apollo/optimistic-effect/hooks/useOptimisticEffect';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { turnFiltersIntoWhereClause } from '@/ui/object/object-filter-dropdown/utils/turnFiltersIntoWhereClause';
|
||||
import { turnSortsIntoOrderBy } from '@/ui/object/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||
import { useRecordTableScopedStates } from '@/ui/object/record-table/hooks/internal/useRecordTableScopedStates';
|
||||
@ -14,14 +15,18 @@ import { useFindManyRecords } from './useFindManyRecords';
|
||||
export const useObjectRecordTable = () => {
|
||||
const { scopeId: objectNamePlural, setRecordTableData } = useRecordTable();
|
||||
|
||||
const { objectNameSingular } = useObjectNameSingularFromPlural({
|
||||
objectNamePlural,
|
||||
});
|
||||
|
||||
const { objectMetadataItem: foundObjectMetadataItem } = useObjectMetadataItem(
|
||||
{
|
||||
objectNamePlural,
|
||||
objectNameSingular,
|
||||
},
|
||||
);
|
||||
|
||||
const { registerOptimisticEffect } = useOptimisticEffect({
|
||||
objectNameSingular: foundObjectMetadataItem?.nameSingular,
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { tableFiltersState, tableSortsState } = useRecordTableScopedStates();
|
||||
@ -39,7 +44,7 @@ export const useObjectRecordTable = () => {
|
||||
);
|
||||
|
||||
const { records, loading, fetchMoreRecords } = useFindManyRecords({
|
||||
objectNamePlural,
|
||||
objectNameSingular,
|
||||
filter,
|
||||
orderBy,
|
||||
onCompleted: (data) => {
|
||||
|
||||
@ -3,7 +3,7 @@ import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
|
||||
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
|
||||
import { IconHeart, IconHeartOff, IconTrash } from '@/ui/display/icon';
|
||||
import { actionBarEntriesState } from '@/ui/navigation/action-bar/states/actionBarEntriesState';
|
||||
@ -38,7 +38,7 @@ export const useRecordTableContextMenuEntries = (
|
||||
recordTableScopeId: scopeId,
|
||||
});
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
const { objectNameSingular } = useObjectNameSingularFromPlural({
|
||||
objectNamePlural,
|
||||
});
|
||||
|
||||
@ -67,7 +67,7 @@ export const useRecordTableContextMenuEntries = (
|
||||
});
|
||||
|
||||
const { deleteOneRecord } = useDeleteOneRecord({
|
||||
objectNameSingular: objectMetadataItem?.nameSingular,
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const handleDeleteClick = useRecoilCallback(({ snapshot }) => async () => {
|
||||
|
||||
@ -7,10 +7,9 @@ import { capitalize } from '~/utils/string/capitalize';
|
||||
|
||||
export const useUpdateOneRecord = <T>({
|
||||
objectNameSingular,
|
||||
}: Pick<ObjectMetadataItemIdentifier, 'objectNameSingular'>) => {
|
||||
}: ObjectMetadataItemIdentifier) => {
|
||||
const {
|
||||
objectMetadataItem,
|
||||
objectMetadataItemNotFound,
|
||||
updateOneRecordMutation,
|
||||
getRecordFromCache,
|
||||
findManyRecordsQuery,
|
||||
@ -29,10 +28,6 @@ export const useUpdateOneRecord = <T>({
|
||||
input: Record<string, any>;
|
||||
forceRefetch?: boolean;
|
||||
}) => {
|
||||
if (!objectMetadataItem || !objectNameSingular) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const cachedRecord = getRecordFromCache(idToUpdate);
|
||||
|
||||
const updatedRecord = await mutateUpdateOneRecord({
|
||||
@ -43,7 +38,7 @@ export const useUpdateOneRecord = <T>({
|
||||
},
|
||||
},
|
||||
optimisticResponse: {
|
||||
[`update${capitalize(objectNameSingular)}`]: {
|
||||
[`update${capitalize(objectMetadataItem.nameSingular)}`]: {
|
||||
...(cachedRecord ?? {}),
|
||||
...input,
|
||||
},
|
||||
@ -54,11 +49,12 @@ export const useUpdateOneRecord = <T>({
|
||||
awaitRefetchQueries: forceRefetch,
|
||||
});
|
||||
|
||||
return updatedRecord.data[`update${capitalize(objectNameSingular)}`] as T;
|
||||
return updatedRecord.data[
|
||||
`update${capitalize(objectMetadataItem.nameSingular)}`
|
||||
] as T;
|
||||
};
|
||||
|
||||
return {
|
||||
updateOneRecord,
|
||||
objectMetadataItemNotFound,
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
export type PaginatedRecordTypeEdge<RecordType extends { id: string }> = {
|
||||
export type PaginatedRecordTypeEdge<
|
||||
RecordType extends { id: string } & Record<string, any>,
|
||||
> = {
|
||||
node: RecordType;
|
||||
cursor: string;
|
||||
};
|
||||
|
||||
export type PaginatedRecordTypeResults<RecordType extends { id: string }> = {
|
||||
export type PaginatedRecordTypeResults<
|
||||
RecordType extends { id: string } & Record<string, any>,
|
||||
> = {
|
||||
__typename?: string;
|
||||
edges: PaginatedRecordTypeEdge<RecordType>[];
|
||||
pageInfo: {
|
||||
|
||||
@ -7,13 +7,13 @@ import { capitalize } from '~/utils/string/capitalize';
|
||||
export const generateDeleteOneRecordMutation = ({
|
||||
objectMetadataItem,
|
||||
}: {
|
||||
objectMetadataItem: ObjectMetadataItem | undefined | null;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}) => {
|
||||
if (!objectMetadataItem) {
|
||||
return EMPTY_MUTATION;
|
||||
}
|
||||
|
||||
const capitalizedObjectName = capitalize(objectMetadataItem?.nameSingular);
|
||||
const capitalizedObjectName = capitalize(objectMetadataItem.nameSingular);
|
||||
|
||||
return gql`
|
||||
mutation DeleteOne${capitalizedObjectName}($idToDelete: ID!) {
|
||||
|
||||
Reference in New Issue
Block a user