545 replace objects icons and names with records avatars and labelidentifiers in command menu context chips (#10787)
Closes https://github.com/twentyhq/core-team-issues/issues/545 This PR: - Introduces `commandMenuNavigationMorphItemsState` which stores the information about the `recordId` and the `objectMetadataItemId` for each page - Creates `CommandMenuContextChipEffect`, which queries the records from the previous pages in case a record has been updated during the navigation, to keep up to date information and stores it inside `commandMenuNavigationRecordsState` - `useCommandMenuContextChips` returns the context chips information - Style updates (icons background and color) - Updates `useCommandMenu` to set and reset these new states https://github.com/user-attachments/assets/8886848a-721d-4709-9330-8e84ebc0d51e
This commit is contained in:
@ -3,9 +3,9 @@ import { useQuery } from '@apollo/client';
|
||||
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
|
||||
import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery';
|
||||
import { RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature';
|
||||
import { useCombinedFindManyRecordsQueryVariables } from '@/object-record/multiple-objects/hooks/useCombinedFindManyRecordsQueryVariables';
|
||||
import { useGenerateCombinedFindManyRecordsQuery } from '@/object-record/multiple-objects/hooks/useGenerateCombinedFindManyRecordsQuery';
|
||||
import { CombinedFindManyRecordsQueryResult } from '@/object-record/multiple-objects/types/CombinedFindManyRecordsQueryResult';
|
||||
import { generateCombinedFindManyRecordsQueryVariables } from '@/object-record/multiple-objects/utils/generateCombinedFindManyRecordsQueryVariables';
|
||||
|
||||
export const useCombinedFindManyRecords = ({
|
||||
operationSignatures,
|
||||
@ -18,7 +18,7 @@ export const useCombinedFindManyRecords = ({
|
||||
operationSignatures,
|
||||
});
|
||||
|
||||
const queryVariables = useCombinedFindManyRecordsQueryVariables({
|
||||
const queryVariables = generateCombinedFindManyRecordsQueryVariables({
|
||||
operationSignatures,
|
||||
});
|
||||
|
||||
|
||||
@ -0,0 +1,153 @@
|
||||
import { ApolloClient, gql, useApolloClient } from '@apollo/client';
|
||||
import { isUndefined } from '@sniptt/guards';
|
||||
import { capitalize } from 'twenty-shared';
|
||||
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { mapObjectMetadataToGraphQLQuery } from '@/object-metadata/utils/mapObjectMetadataToGraphQLQuery';
|
||||
import { getRecordsFromRecordConnection } from '@/object-record/cache/utils/getRecordsFromRecordConnection';
|
||||
import { EMPTY_QUERY } from '@/object-record/constants/EmptyQuery';
|
||||
import { RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature';
|
||||
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields';
|
||||
import { CombinedFindManyRecordsQueryResult } from '@/object-record/multiple-objects/types/CombinedFindManyRecordsQueryResult';
|
||||
import { generateCombinedFindManyRecordsQueryVariables } from '@/object-record/multiple-objects/utils/generateCombinedFindManyRecordsQueryVariables';
|
||||
import { getCombinedFindManyRecordsQueryFilteringPart } from '@/object-record/multiple-objects/utils/getCombinedFindManyRecordsQueryFilteringPart';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export const usePerformCombinedFindManyRecords = () => {
|
||||
const client = useApolloClient();
|
||||
const objectMetadataItems = useRecoilValue(objectMetadataItemsState);
|
||||
|
||||
const generateCombinedFindManyRecordsQuery = (
|
||||
operationSignatures: RecordGqlOperationSignature[],
|
||||
objectMetadataItemsValue: ObjectMetadataItem[],
|
||||
) => {
|
||||
const filterPerMetadataItemArray = operationSignatures
|
||||
.map(
|
||||
({ objectNameSingular }) =>
|
||||
`$filter${capitalize(objectNameSingular)}: ${capitalize(
|
||||
objectNameSingular,
|
||||
)}FilterInput`,
|
||||
)
|
||||
.join(', ');
|
||||
|
||||
const orderByPerMetadataItemArray = operationSignatures
|
||||
.map(
|
||||
({ objectNameSingular }) =>
|
||||
`$orderBy${capitalize(objectNameSingular)}: [${capitalize(
|
||||
objectNameSingular,
|
||||
)}OrderByInput]`,
|
||||
)
|
||||
.join(', ');
|
||||
|
||||
const cursorFilteringPerMetadataItemArray = operationSignatures
|
||||
.map(
|
||||
({ objectNameSingular }) =>
|
||||
`$after${capitalize(objectNameSingular)}: String, $before${capitalize(objectNameSingular)}: String, $first${capitalize(objectNameSingular)}: Int, $last${capitalize(objectNameSingular)}: Int`,
|
||||
)
|
||||
.join(', ');
|
||||
|
||||
const limitPerMetadataItemArray = operationSignatures
|
||||
.map(
|
||||
({ objectNameSingular }) =>
|
||||
`$limit${capitalize(objectNameSingular)}: Int`,
|
||||
)
|
||||
.join(', ');
|
||||
|
||||
const queryOperationSignatureWithObjectMetadataItemArray =
|
||||
operationSignatures.map((operationSignature) => {
|
||||
const objectMetadataItem = objectMetadataItemsValue.find(
|
||||
(objectMetadataItem) =>
|
||||
objectMetadataItem.nameSingular ===
|
||||
operationSignature.objectNameSingular,
|
||||
);
|
||||
|
||||
if (isUndefined(objectMetadataItem)) {
|
||||
throw new Error(
|
||||
`Object metadata item not found for object name singular: ${operationSignature.objectNameSingular}`,
|
||||
);
|
||||
}
|
||||
|
||||
return { operationSignature, objectMetadataItem };
|
||||
});
|
||||
|
||||
return gql`
|
||||
query CombinedFindManyRecords(
|
||||
${filterPerMetadataItemArray},
|
||||
${orderByPerMetadataItemArray},
|
||||
${cursorFilteringPerMetadataItemArray},
|
||||
${limitPerMetadataItemArray}
|
||||
) {
|
||||
${queryOperationSignatureWithObjectMetadataItemArray
|
||||
.map(
|
||||
({ objectMetadataItem, operationSignature }) =>
|
||||
`${getCombinedFindManyRecordsQueryFilteringPart(
|
||||
objectMetadataItem,
|
||||
)} {
|
||||
edges {
|
||||
node ${mapObjectMetadataToGraphQLQuery({
|
||||
objectMetadataItems: objectMetadataItemsValue,
|
||||
objectMetadataItem,
|
||||
recordGqlFields:
|
||||
operationSignature.fields ??
|
||||
generateDepthOneRecordGqlFields({
|
||||
objectMetadataItem,
|
||||
}),
|
||||
})}
|
||||
cursor
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
totalCount
|
||||
}`,
|
||||
)
|
||||
.join('\n')}
|
||||
}
|
||||
`;
|
||||
};
|
||||
|
||||
const performCombinedFindManyRecords = async ({
|
||||
operationSignatures,
|
||||
client: customClient,
|
||||
}: {
|
||||
operationSignatures: RecordGqlOperationSignature[];
|
||||
client?: ApolloClient<object>;
|
||||
}) => {
|
||||
const apolloClient = customClient || client;
|
||||
|
||||
const findManyQuery = generateCombinedFindManyRecordsQuery(
|
||||
operationSignatures,
|
||||
objectMetadataItems,
|
||||
);
|
||||
|
||||
const queryVariables = generateCombinedFindManyRecordsQueryVariables({
|
||||
operationSignatures,
|
||||
});
|
||||
|
||||
const { data, loading } =
|
||||
await apolloClient.query<CombinedFindManyRecordsQueryResult>({
|
||||
query: findManyQuery ?? EMPTY_QUERY,
|
||||
variables: queryVariables,
|
||||
});
|
||||
|
||||
const resultWithoutConnection = Object.fromEntries(
|
||||
Object.entries(data ?? {}).map(([namePlural, objectRecordConnection]) => [
|
||||
namePlural,
|
||||
getRecordsFromRecordConnection({
|
||||
recordConnection: objectRecordConnection,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
return {
|
||||
result: resultWithoutConnection,
|
||||
loading,
|
||||
};
|
||||
};
|
||||
|
||||
return { performCombinedFindManyRecords };
|
||||
};
|
||||
@ -0,0 +1,4 @@
|
||||
export type MorphItem = {
|
||||
recordId: string;
|
||||
objectMetadataId: string;
|
||||
};
|
||||
@ -1,6 +1,6 @@
|
||||
import { RecordGqlFields } from '@/object-record/graphql/types/RecordGqlFields';
|
||||
import { RecordGqlOperationSignature } from '@/object-record/graphql/types/RecordGqlOperationSignature';
|
||||
import { useCombinedFindManyRecordsQueryVariables } from '@/object-record/multiple-objects/hooks/useCombinedFindManyRecordsQueryVariables';
|
||||
import { generateCombinedFindManyRecordsQueryVariables } from '@/object-record/multiple-objects/utils/generateCombinedFindManyRecordsQueryVariables';
|
||||
|
||||
describe('useCombinedFindManyRecordsQueryVariables', () => {
|
||||
it('should generate variables with after cursor and first limit', () => {
|
||||
@ -26,7 +26,7 @@ describe('useCombinedFindManyRecordsQueryVariables', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const result = useCombinedFindManyRecordsQueryVariables({
|
||||
const result = generateCombinedFindManyRecordsQueryVariables({
|
||||
operationSignatures,
|
||||
});
|
||||
|
||||
@ -58,7 +58,7 @@ describe('useCombinedFindManyRecordsQueryVariables', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const result = useCombinedFindManyRecordsQueryVariables({
|
||||
const result = generateCombinedFindManyRecordsQueryVariables({
|
||||
operationSignatures,
|
||||
});
|
||||
|
||||
@ -86,7 +86,7 @@ describe('useCombinedFindManyRecordsQueryVariables', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const result = useCombinedFindManyRecordsQueryVariables({
|
||||
const result = generateCombinedFindManyRecordsQueryVariables({
|
||||
operationSignatures,
|
||||
});
|
||||
|
||||
@ -125,7 +125,7 @@ describe('useCombinedFindManyRecordsQueryVariables', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const result = useCombinedFindManyRecordsQueryVariables({
|
||||
const result = generateCombinedFindManyRecordsQueryVariables({
|
||||
operationSignatures,
|
||||
});
|
||||
|
||||
@ -139,7 +139,7 @@ describe('useCombinedFindManyRecordsQueryVariables', () => {
|
||||
});
|
||||
|
||||
it('should handle empty operation signatures', () => {
|
||||
const result = useCombinedFindManyRecordsQueryVariables({
|
||||
const result = generateCombinedFindManyRecordsQueryVariables({
|
||||
operationSignatures: [],
|
||||
});
|
||||
|
||||
@ -157,7 +157,7 @@ describe('useCombinedFindManyRecordsQueryVariables', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const result = useCombinedFindManyRecordsQueryVariables({
|
||||
const result = generateCombinedFindManyRecordsQueryVariables({
|
||||
operationSignatures,
|
||||
});
|
||||
|
||||
@ -180,7 +180,7 @@ describe('useCombinedFindManyRecordsQueryVariables', () => {
|
||||
},
|
||||
];
|
||||
|
||||
const result = useCombinedFindManyRecordsQueryVariables({
|
||||
const result = generateCombinedFindManyRecordsQueryVariables({
|
||||
operationSignatures,
|
||||
});
|
||||
|
||||
@ -3,7 +3,7 @@ import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { capitalize, isDefined } from 'twenty-shared';
|
||||
import { isNonEmptyArray } from '~/utils/isNonEmptyArray';
|
||||
|
||||
export const useCombinedFindManyRecordsQueryVariables = ({
|
||||
export const generateCombinedFindManyRecordsQueryVariables = ({
|
||||
operationSignatures,
|
||||
}: {
|
||||
operationSignatures: RecordGqlOperationSignature[];
|
||||
@ -0,0 +1,13 @@
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { capitalize } from 'twenty-shared';
|
||||
|
||||
export const getLimitPerMetadataItem = (
|
||||
objectMetadataItems: Pick<ObjectMetadataItem, 'nameSingular'>[],
|
||||
limit: number,
|
||||
) => {
|
||||
return Object.fromEntries(
|
||||
objectMetadataItems.map(({ nameSingular }) => {
|
||||
return [`limit${capitalize(nameSingular)}`, limit];
|
||||
}),
|
||||
);
|
||||
};
|
||||
@ -1,6 +1,7 @@
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { CombinedFindManyRecordsQueryResult } from '@/object-record/multiple-objects/types/CombinedFindManyRecordsQueryResult';
|
||||
import { generateCombinedSearchRecordsQuery } from '@/object-record/multiple-objects/utils/generateCombinedSearchRecordsQuery';
|
||||
import { getLimitPerMetadataItem } from '@/object-record/multiple-objects/utils/getLimitPerMetadataItem';
|
||||
import { multipleRecordPickerPickableMorphItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerPickableMorphItemsComponentState';
|
||||
import { multipleRecordPickerSearchFilterComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchFilterComponentState';
|
||||
import { multipleRecordPickerSearchableObjectMetadataItemsComponentState } from '@/object-record/record-picker/multiple-record-picker/states/multipleRecordPickerSearchableObjectMetadataItemsComponentState';
|
||||
@ -222,12 +223,9 @@ const performSearchForPickedRecords = async ({
|
||||
),
|
||||
});
|
||||
|
||||
const limitPerMetadataItem = Object.fromEntries(
|
||||
searchableObjectMetadataItems
|
||||
.map(({ nameSingular }) => {
|
||||
return [`limit${capitalize(nameSingular)}`, 10];
|
||||
})
|
||||
.filter(isDefined),
|
||||
const limitPerMetadataItem = getLimitPerMetadataItem(
|
||||
searchableObjectMetadataItemsFilteredOnPickedRecordId,
|
||||
10,
|
||||
);
|
||||
|
||||
const { data: combinedSearchRecordFilteredOnPickedRecordsQueryResult } =
|
||||
@ -309,12 +307,9 @@ const performSearchExcludingPickedRecords = async ({
|
||||
),
|
||||
});
|
||||
|
||||
const limitPerMetadataItem = Object.fromEntries(
|
||||
searchableObjectMetadataItems
|
||||
.map(({ nameSingular }) => {
|
||||
return [`limit${capitalize(nameSingular)}`, 10];
|
||||
})
|
||||
.filter(isDefined),
|
||||
const limitPerMetadataItem = getLimitPerMetadataItem(
|
||||
searchableObjectMetadataItems,
|
||||
10,
|
||||
);
|
||||
|
||||
const { data: combinedSearchRecordExcludingPickedRecordsQueryResult } =
|
||||
|
||||
Reference in New Issue
Block a user