Feat/activities custom objects (#3213)
* WIP * WIP - MultiObjectSearch * WIP * WIP * Finished working version * Fix * Fixed and cleaned * Fix * Disabled files and emails for custom objects * Cleaned console.log * Fixed attachment * Fixed * fix lint --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,22 +1,15 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import styled from '@emotion/styled';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useHandleCheckableActivityTargetChange } from '@/activities/hooks/useHandleCheckableActivityTargetChange';
|
||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||
import { flatMapAndSortEntityForSelectArrayOfArrayByName } from '@/activities/utils/flatMapAndSortEntityForSelectArrayByName';
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { ActivityTargetObjectRecord } from '@/activities/types/ActivityTargetObject';
|
||||
import { getActivityTargetObjectFieldIdName } from '@/activities/utils/getTargetObjectFilterFieldName';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useCreateManyRecords } from '@/object-record/hooks/useCreateManyRecords';
|
||||
import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords';
|
||||
import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell';
|
||||
import { MultipleEntitySelect } from '@/object-record/relation-picker/components/MultipleEntitySelect';
|
||||
import { useRelationPicker } from '@/object-record/relation-picker/hooks/useRelationPicker';
|
||||
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
|
||||
import { assertNotNull } from '~/utils/assert';
|
||||
|
||||
type ActivityTargetInlineCellEditModeProps = {
|
||||
activityId: string;
|
||||
activityTargets: Array<Pick<ActivityTarget, 'id' | 'personId' | 'companyId'>>;
|
||||
};
|
||||
import { MultipleObjectRecordSelect } from '@/object-record/relation-picker/components/MultipleObjectRecordSelect';
|
||||
import { ObjectRecordForSelect } from '@/object-record/relation-picker/hooks/useMultiObjectSearch';
|
||||
|
||||
const StyledSelectContainer = styled.div`
|
||||
left: 0px;
|
||||
@ -24,125 +17,77 @@ const StyledSelectContainer = styled.div`
|
||||
top: -8px;
|
||||
`;
|
||||
|
||||
type ActivityTargetInlineCellEditModeProps = {
|
||||
activityId: string;
|
||||
activityTargetObjectRecords: ActivityTargetObjectRecord[];
|
||||
};
|
||||
|
||||
export const ActivityTargetInlineCellEditMode = ({
|
||||
activityId,
|
||||
activityTargets,
|
||||
activityTargetObjectRecords,
|
||||
}: ActivityTargetInlineCellEditModeProps) => {
|
||||
const [searchFilter, setSearchFilter] = useState('');
|
||||
|
||||
const initialPeopleIds = useMemo(
|
||||
() =>
|
||||
activityTargets
|
||||
?.filter(({ personId }) => personId !== null)
|
||||
.map(({ personId }) => personId)
|
||||
.filter(assertNotNull) ?? [],
|
||||
[activityTargets],
|
||||
const selectedObjectRecordIds = activityTargetObjectRecords.map(
|
||||
(activityTarget) => ({
|
||||
objectNameSingular: activityTarget.targetObjectNameSingular,
|
||||
id: activityTarget.targetObjectRecord.id,
|
||||
}),
|
||||
);
|
||||
|
||||
const initialCompanyIds = useMemo(
|
||||
() =>
|
||||
activityTargets
|
||||
?.filter(({ companyId }) => companyId !== null)
|
||||
.map(({ companyId }) => companyId)
|
||||
.filter(assertNotNull) ?? [],
|
||||
[activityTargets],
|
||||
);
|
||||
|
||||
const initialSelectedEntityIds = useMemo(
|
||||
() =>
|
||||
[...initialPeopleIds, ...initialCompanyIds].reduce<
|
||||
Record<string, boolean>
|
||||
>((result, entityId) => ({ ...result, [entityId]: true }), {}),
|
||||
[initialPeopleIds, initialCompanyIds],
|
||||
);
|
||||
|
||||
const { findManyRecordsQuery: findManyPeopleQuery } = useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.Person,
|
||||
});
|
||||
|
||||
const { findManyRecordsQuery: findManyCompaniesQuery } =
|
||||
useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.Company,
|
||||
const { createManyRecords: createManyActivityTargets } =
|
||||
useCreateManyRecords<ActivityTarget>({
|
||||
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||
});
|
||||
|
||||
const useFindManyPeopleQuery = (options: any) =>
|
||||
useQuery(findManyPeopleQuery, options);
|
||||
const { deleteManyRecords: deleteManyActivityTargets } = useDeleteManyRecords(
|
||||
{
|
||||
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||
},
|
||||
);
|
||||
|
||||
const useFindManyCompaniesQuery = (options: any) =>
|
||||
useQuery(findManyCompaniesQuery, options);
|
||||
|
||||
const [selectedEntityIds, setSelectedEntityIds] = useState<
|
||||
Record<string, boolean>
|
||||
>(initialSelectedEntityIds);
|
||||
|
||||
const { identifiersMapper, searchQuery } = useRelationPicker();
|
||||
|
||||
const people = useFilteredSearchEntityQuery({
|
||||
queryHook: useFindManyPeopleQuery,
|
||||
filters: [
|
||||
{
|
||||
fieldNames: searchQuery?.computeFilterFields?.('person') ?? [],
|
||||
filter: searchFilter,
|
||||
},
|
||||
],
|
||||
orderByField: 'createdAt',
|
||||
mappingFunction: (record: any) => identifiersMapper?.(record, 'person'),
|
||||
selectedIds: initialPeopleIds,
|
||||
objectNameSingular: CoreObjectNameSingular.Person,
|
||||
limit: 3,
|
||||
});
|
||||
|
||||
const companies = useFilteredSearchEntityQuery({
|
||||
queryHook: useFindManyCompaniesQuery,
|
||||
filters: [
|
||||
{
|
||||
fieldNames: searchQuery?.computeFilterFields?.('company') ?? [],
|
||||
filter: searchFilter,
|
||||
},
|
||||
],
|
||||
orderByField: 'createdAt',
|
||||
mappingFunction: (record: any) => identifiersMapper?.(record, 'company'),
|
||||
selectedIds: initialCompanyIds,
|
||||
objectNameSingular: CoreObjectNameSingular.Company,
|
||||
limit: 3,
|
||||
});
|
||||
|
||||
const selectedEntities = flatMapAndSortEntityForSelectArrayOfArrayByName([
|
||||
people.selectedEntities,
|
||||
companies.selectedEntities,
|
||||
]);
|
||||
|
||||
const filteredSelectedEntities =
|
||||
flatMapAndSortEntityForSelectArrayOfArrayByName([
|
||||
people.filteredSelectedEntities,
|
||||
companies.filteredSelectedEntities,
|
||||
]);
|
||||
|
||||
const entitiesToSelect = flatMapAndSortEntityForSelectArrayOfArrayByName([
|
||||
people.entitiesToSelect,
|
||||
companies.entitiesToSelect,
|
||||
]);
|
||||
|
||||
const handleCheckItemsChange = useHandleCheckableActivityTargetChange({
|
||||
activityId,
|
||||
currentActivityTargets: activityTargets,
|
||||
});
|
||||
const { closeInlineCell: closeEditableField } = useInlineCell();
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
handleCheckItemsChange(
|
||||
selectedEntityIds,
|
||||
entitiesToSelect,
|
||||
selectedEntities,
|
||||
);
|
||||
const handleSubmit = async (selectedRecords: ObjectRecordForSelect[]) => {
|
||||
closeEditableField();
|
||||
}, [
|
||||
closeEditableField,
|
||||
entitiesToSelect,
|
||||
handleCheckItemsChange,
|
||||
selectedEntities,
|
||||
selectedEntityIds,
|
||||
]);
|
||||
|
||||
const activityTargetRecordsToDelete = activityTargetObjectRecords.filter(
|
||||
(activityTargetObjectRecord) =>
|
||||
!selectedRecords.some(
|
||||
(selectedRecord) =>
|
||||
selectedRecord.recordIdentifier.id ===
|
||||
activityTargetObjectRecord.targetObjectRecord.id,
|
||||
),
|
||||
);
|
||||
|
||||
const activityTargetRecordsToCreate = selectedRecords.filter(
|
||||
(selectedRecord) =>
|
||||
!activityTargetObjectRecords.some(
|
||||
(activityTargetObjectRecord) =>
|
||||
activityTargetObjectRecord.targetObjectRecord.id ===
|
||||
selectedRecord.recordIdentifier.id,
|
||||
),
|
||||
);
|
||||
|
||||
if (activityTargetRecordsToCreate.length > 0) {
|
||||
await createManyActivityTargets(
|
||||
activityTargetRecordsToCreate.map((selectedRecord) => ({
|
||||
id: v4(),
|
||||
activityId,
|
||||
[getActivityTargetObjectFieldIdName({
|
||||
nameSingular: selectedRecord.objectMetadataItem.nameSingular,
|
||||
})]: selectedRecord.recordIdentifier.id,
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
if (activityTargetRecordsToDelete.length > 0) {
|
||||
await deleteManyActivityTargets(
|
||||
activityTargetRecordsToDelete.map(
|
||||
(activityTargetObjectRecord) =>
|
||||
activityTargetObjectRecord.activityTargetRecord.id,
|
||||
),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
closeEditableField();
|
||||
@ -150,17 +95,8 @@ export const ActivityTargetInlineCellEditMode = ({
|
||||
|
||||
return (
|
||||
<StyledSelectContainer>
|
||||
<MultipleEntitySelect
|
||||
entities={{
|
||||
entitiesToSelect,
|
||||
filteredSelectedEntities,
|
||||
selectedEntities,
|
||||
loading: false,
|
||||
}}
|
||||
onChange={setSelectedEntityIds}
|
||||
onSearchFilterChange={setSearchFilter}
|
||||
searchFilter={searchFilter}
|
||||
value={selectedEntityIds}
|
||||
<MultipleObjectRecordSelect
|
||||
selectedObjectRecordIds={selectedObjectRecordIds}
|
||||
onCancel={handleCancel}
|
||||
onSubmit={handleSubmit}
|
||||
/>
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { ActivityTargetChips } from '@/activities/components/ActivityTargetChips';
|
||||
import { useActivityTargetObjectRecords } from '@/activities/hooks/useActivityTargetObjectRecords';
|
||||
import { ActivityTargetInlineCellEditMode } from '@/activities/inline-cell/components/ActivityTargetInlineCellEditMode';
|
||||
import { ActivityTarget } from '@/activities/types/ActivityTarget';
|
||||
import { GraphQLActivity } from '@/activities/types/GraphQLActivity';
|
||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
||||
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
|
||||
import { RecordInlineCellContainer } from '@/object-record/record-inline-cell/components/RecordInlineCellContainer';
|
||||
import { FieldRecoilScopeContext } from '@/object-record/record-inline-cell/states/recoil-scope-contexts/FieldRecoilScopeContext';
|
||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||
@ -23,14 +22,8 @@ type ActivityTargetsInlineCellProps = {
|
||||
export const ActivityTargetsInlineCell = ({
|
||||
activity,
|
||||
}: ActivityTargetsInlineCellProps) => {
|
||||
const activityTargetIds =
|
||||
activity?.activityTargets?.edges?.map(
|
||||
(activityTarget) => activityTarget.node.id,
|
||||
) ?? [];
|
||||
|
||||
const { records: activityTargets } = useFindManyRecords<ActivityTarget>({
|
||||
objectNameSingular: CoreObjectNameSingular.ActivityTarget,
|
||||
filter: { id: { in: activityTargetIds } },
|
||||
const { activityTargetObjectRecords } = useActivityTargetObjectRecords({
|
||||
activityId: activity?.id ?? '',
|
||||
});
|
||||
|
||||
return (
|
||||
@ -44,11 +37,15 @@ export const ActivityTargetsInlineCell = ({
|
||||
editModeContent={
|
||||
<ActivityTargetInlineCellEditMode
|
||||
activityId={activity?.id ?? ''}
|
||||
activityTargets={activityTargets as any}
|
||||
activityTargetObjectRecords={activityTargetObjectRecords as any}
|
||||
/>
|
||||
}
|
||||
label="Relations"
|
||||
displayModeContent={<ActivityTargetChips targets={activityTargets} />}
|
||||
displayModeContent={
|
||||
<ActivityTargetChips
|
||||
activityTargetObjectRecords={activityTargetObjectRecords}
|
||||
/>
|
||||
}
|
||||
isDisplayModeContentEmpty={
|
||||
activity?.activityTargets?.edges?.length === 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user