diff --git a/front/src/modules/activities/components/ActivityEditor.tsx b/front/src/modules/activities/components/ActivityEditor.tsx index a3b76ab54..945f43b2a 100644 --- a/front/src/modules/activities/components/ActivityEditor.tsx +++ b/front/src/modules/activities/components/ActivityEditor.tsx @@ -4,9 +4,11 @@ import styled from '@emotion/styled'; import { ActivityBodyEditor } from '@/activities/components/ActivityBodyEditor'; import { ActivityComments } from '@/activities/components/ActivityComments'; import { ActivityTypeDropdown } from '@/activities/components/ActivityTypeDropdown'; +import { ActivityTargetsInlineCell } from '@/activities/inline-cell/components/ActivityTargetsInlineCell'; import { Activity } from '@/activities/types/Activity'; import { ActivityTarget } from '@/activities/types/ActivityTarget'; import { Comment } from '@/activities/types/Comment'; +import { GraphQLActivity } from '@/activities/types/GraphQLActivity'; import { useFieldContext } from '@/object-record/hooks/useFieldContext'; import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord'; import { RecordInlineCell } from '@/ui/object/record-inline-cell/components/RecordInlineCell'; @@ -166,7 +168,9 @@ export const ActivityEditor = ({ )} - {/* */} + & { - activityTargets?: Array< - Pick & { - person?: Pick | null; - company?: Pick | null; - } - > | null; - }; -}; - -export const ActivityRelationEditableField = ({ - activity, -}: ActivityRelationEditableFieldProps) => { - return ( - - - - } - label="Relations" - displayModeContent={ - - } - isDisplayModeContentEmpty={activity?.activityTargets?.length === 0} - /> - - - ); -}; diff --git a/front/src/modules/activities/hooks/useHandleCheckableActivityTargetChange.ts b/front/src/modules/activities/hooks/useHandleCheckableActivityTargetChange.ts index 9a9636acd..3e8881fec 100644 --- a/front/src/modules/activities/hooks/useHandleCheckableActivityTargetChange.ts +++ b/front/src/modules/activities/hooks/useHandleCheckableActivityTargetChange.ts @@ -1,7 +1,7 @@ import { v4 } from 'uuid'; -import { Activity } from '@/activities/types/Activity'; import { ActivityTarget } from '@/activities/types/ActivityTarget'; +import { GraphQLActivity } from '@/activities/types/GraphQLActivity'; import { useCreateOneObjectRecord } from '@/object-record/hooks/useCreateOneObjectRecord'; import { useDeleteOneObjectRecord } from '@/object-record/hooks/useDeleteOneObjectRecord'; @@ -10,10 +10,12 @@ import { ActivityTargetableEntityForSelect } from '../types/ActivityTargetableEn export const useHandleCheckableActivityTargetChange = ({ activity, }: { - activity?: Pick & { - activityTargets?: Array< - Pick - > | null; + activity?: Pick & { + activityTargets?: { + edges: Array<{ + node: Pick; + }> | null; + }; }; }) => { const { createOneObject } = useCreateOneObjectRecord({ @@ -31,9 +33,9 @@ export const useHandleCheckableActivityTargetChange = ({ return; } - const currentEntityIds = activity.activityTargets - ? activity.activityTargets.map( - ({ personId, companyId }) => personId ?? companyId, + const currentEntityIds = activity.activityTargets?.edges + ? activity.activityTargets.edges.map( + ({ node }) => node.personId ?? node.companyId, ) : []; @@ -55,14 +57,14 @@ export const useHandleCheckableActivityTargetChange = ({ }); } - const activityTargetIdsToDelete = activity.activityTargets - ? activity.activityTargets + const activityTargetIdsToDelete = activity.activityTargets?.edges + ? activity.activityTargets.edges .filter( - ({ personId, companyId }) => - (personId ?? companyId) && - !entityValues[personId ?? companyId ?? ''], + ({ node }) => + (node.personId ?? node.companyId) && + !entityValues[node.personId ?? node.companyId ?? ''], ) - .map(({ id }) => id) + .map(({ node }) => node.id) : []; if (activityTargetIdsToDelete.length) { diff --git a/front/src/modules/activities/editable-fields/components/ActivityRelationEditableFieldEditMode.tsx b/front/src/modules/activities/inline-cell/components/ActivityTargetInlineCellEditMode.tsx similarity index 81% rename from front/src/modules/activities/editable-fields/components/ActivityRelationEditableFieldEditMode.tsx rename to front/src/modules/activities/inline-cell/components/ActivityTargetInlineCellEditMode.tsx index b0318d218..5c4d0d7d5 100644 --- a/front/src/modules/activities/editable-fields/components/ActivityRelationEditableFieldEditMode.tsx +++ b/front/src/modules/activities/inline-cell/components/ActivityTargetInlineCellEditMode.tsx @@ -2,16 +2,18 @@ import { useCallback, useMemo, useState } from 'react'; import styled from '@emotion/styled'; import { useHandleCheckableActivityTargetChange } from '@/activities/hooks/useHandleCheckableActivityTargetChange'; -import { Activity } from '@/activities/types/Activity'; import { ActivityTarget } from '@/activities/types/ActivityTarget'; +import { GraphQLActivity } from '@/activities/types/GraphQLActivity'; import { useInlineCell } from '@/ui/object/record-inline-cell/hooks/useInlineCell'; import { assertNotNull } from '~/utils/assert'; -type ActivityRelationEditableFieldEditModeProps = { - activity?: Pick & { - activityTargets?: Array< - Pick - > | null; +type ActivityTargetInlineCellEditModeProps = { + activity?: Pick & { + activityTargets?: { + edges: Array<{ + node: Pick; + }> | null; + }; }; }; @@ -21,25 +23,25 @@ const StyledSelectContainer = styled.div` top: -8px; `; -export const ActivityRelationEditableFieldEditMode = ({ +export const ActivityTargetInlineCellEditMode = ({ activity, -}: ActivityRelationEditableFieldEditModeProps) => { +}: ActivityTargetInlineCellEditModeProps) => { const [searchFilter, setSearchFilter] = useState(''); const initialPeopleIds = useMemo( () => - activity?.activityTargets - ?.filter((relation) => relation.personId !== null) - .map((relation) => relation.personId) + activity?.activityTargets?.edges + ?.filter(({ node }) => node.personId !== null) + .map(({ node }) => node.personId) .filter(assertNotNull) ?? [], [activity?.activityTargets], ); const initialCompanyIds = useMemo( () => - activity?.activityTargets - ?.filter((relation) => relation.companyId !== null) - .map((relation) => relation.companyId) + activity?.activityTargets?.edges + ?.filter(({ node }) => node.companyId !== null) + .map(({ node }) => node.companyId) .filter(assertNotNull) ?? [], [activity?.activityTargets], ); diff --git a/front/src/modules/activities/inline-cell/components/ActivityTargetsInlineCell.tsx b/front/src/modules/activities/inline-cell/components/ActivityTargetsInlineCell.tsx new file mode 100644 index 000000000..c749fad8c --- /dev/null +++ b/front/src/modules/activities/inline-cell/components/ActivityTargetsInlineCell.tsx @@ -0,0 +1,50 @@ +import { ActivityTargetChips } from '@/activities/components/ActivityTargetChips'; +import { ActivityTargetInlineCellEditMode } from '@/activities/inline-cell/components/ActivityTargetInlineCellEditMode'; +import { ActivityTarget } from '@/activities/types/ActivityTarget'; +import { GraphQLActivity } from '@/activities/types/GraphQLActivity'; +import { Company } from '@/companies/types/Company'; +import { Person } from '@/people/types/Person'; +import { IconArrowUpRight, IconPencil } from '@/ui/display/icon'; +import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope'; +import { RecordInlineCellContainer } from '@/ui/object/record-inline-cell/components/RecordInlineCellContainer'; +import { FieldRecoilScopeContext } from '@/ui/object/record-inline-cell/states/recoil-scope-contexts/FieldRecoilScopeContext'; +import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; + +type ActivityTargetsInlineCellProps = { + activity?: Pick & { + activityTargets?: { + edges: Array<{ + node: Pick & { + person?: Pick | null; + company?: Pick | null; + }; + }> | null; + }; + }; +}; + +export const ActivityTargetsInlineCell = ({ + activity, +}: ActivityTargetsInlineCellProps) => { + return ( + + + } + label="Relations" + displayModeContent={ + + } + isDisplayModeContentEmpty={ + activity?.activityTargets?.edges?.length === 0 + } + /> + + ); +}; diff --git a/front/src/modules/activities/notes/components/NoteCard.tsx b/front/src/modules/activities/notes/components/NoteCard.tsx index 5408f3087..fd8916e6a 100644 --- a/front/src/modules/activities/notes/components/NoteCard.tsx +++ b/front/src/modules/activities/notes/components/NoteCard.tsx @@ -2,8 +2,9 @@ import { useMemo } from 'react'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { ActivityRelationEditableField } from '@/activities/editable-fields/components/ActivityRelationEditableField'; import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer'; +import { ActivityTargetsInlineCell } from '@/activities/inline-cell/components/ActivityTargetsInlineCell'; +import { GraphQLActivity } from '@/activities/types/GraphQLActivity'; import { Note } from '@/activities/types/Note'; import { IconComment } from '@/ui/display/icon'; import { @@ -100,7 +101,9 @@ export const NoteCard = ({ {body} - + {note.comments && note.comments.length > 0 && ( diff --git a/front/src/modules/activities/types/GraphQLActivity.ts b/front/src/modules/activities/types/GraphQLActivity.ts new file mode 100644 index 000000000..84aa1425f --- /dev/null +++ b/front/src/modules/activities/types/GraphQLActivity.ts @@ -0,0 +1,33 @@ +import { ActivityTarget } from '@/activities/types/ActivityTarget'; +import { Comment } from '@/activities/types/Comment'; +import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; + +export type ActivityType = 'Task' | 'Note'; + +type ActivityTargetNode = { + node: ActivityTarget; +}; + +type CommentNode = { + node: Comment; +}; + +export type GraphQLActivity = { + __typename: 'Activity'; + id: string; + createdAt: string; + updatedAt: string; + completedAt: string | null; + dueAt: string | null; + activityTargets: { + edges: ActivityTargetNode[]; + }; + type: ActivityType; + title: string; + body: string; + author: Pick; + authorId: string; + assignee: Pick | null; + assigneeId: string | null; + comments: CommentNode[]; +}; diff --git a/front/src/modules/object-metadata/hooks/useObjectMetadataItem.ts b/front/src/modules/object-metadata/hooks/useObjectMetadataItem.ts index 2d9951861..d75a13bfb 100644 --- a/front/src/modules/object-metadata/hooks/useObjectMetadataItem.ts +++ b/front/src/modules/object-metadata/hooks/useObjectMetadataItem.ts @@ -24,10 +24,10 @@ export const EMPTY_MUTATION = gql` } `; -export const useObjectMetadataItem = ({ - objectNamePlural, - objectNameSingular, -}: ObjectMetadataItemIdentifier) => { +export const useObjectMetadataItem = ( + { objectNamePlural, objectNameSingular }: ObjectMetadataItemIdentifier, + depth?: number, +) => { const objectMetadataItem = useRecoilValue( objectMetadataItemFamilySelector({ objectNamePlural, @@ -43,10 +43,12 @@ export const useObjectMetadataItem = ({ const findManyQuery = useGenerateFindManyCustomObjectsQuery({ objectMetadataItem, + depth, }); const findOneQuery = useGenerateFindOneCustomObjectQuery({ objectMetadataItem, + depth, }); const createOneMutation = useGenerateCreateOneObjectMutation({ diff --git a/front/src/modules/object-record/hooks/useFindOneObjectRecord.ts b/front/src/modules/object-record/hooks/useFindOneObjectRecord.ts index 6d7471857..969c6ddaa 100644 --- a/front/src/modules/object-record/hooks/useFindOneObjectRecord.ts +++ b/front/src/modules/object-record/hooks/useFindOneObjectRecord.ts @@ -9,19 +9,24 @@ export const useFindOneObjectRecord = < objectNameSingular, objectRecordId, onCompleted, + depth, skip, }: Pick & { objectRecordId: string | undefined; onCompleted?: (data: ObjectType) => void; skip?: boolean; + depth?: number; }) => { const { objectMetadataItem: foundObjectMetadataItem, objectNotFoundInMetadata, findOneQuery, - } = useObjectMetadataItem({ - objectNameSingular, - }); + } = useObjectMetadataItem( + { + objectNameSingular, + }, + depth, + ); const { data, loading, error } = useQuery< { [nameSingular: string]: ObjectType }, diff --git a/front/src/modules/object-record/utils/useGenerateFindManyCustomObjectsQuery.ts b/front/src/modules/object-record/utils/useGenerateFindManyCustomObjectsQuery.ts index 33de2e519..6c8c228d5 100644 --- a/front/src/modules/object-record/utils/useGenerateFindManyCustomObjectsQuery.ts +++ b/front/src/modules/object-record/utils/useGenerateFindManyCustomObjectsQuery.ts @@ -7,8 +7,10 @@ import { capitalize } from '~/utils/string/capitalize'; export const useGenerateFindManyCustomObjectsQuery = ({ objectMetadataItem, + depth, }: { objectMetadataItem: ObjectMetadataItem | undefined | null; + depth?: number; }) => { const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery(); @@ -31,7 +33,7 @@ export const useGenerateFindManyCustomObjectsQuery = ({ node { id ${objectMetadataItem.fields - .map((field) => mapFieldMetadataToGraphQLQuery(field)) + .map((field) => mapFieldMetadataToGraphQLQuery(field, depth)) .join('\n')} } cursor diff --git a/front/src/modules/object-record/utils/useGenerateFindOneCustomObjectQuery.ts b/front/src/modules/object-record/utils/useGenerateFindOneCustomObjectQuery.ts index 5d463ac73..a8948c63f 100644 --- a/front/src/modules/object-record/utils/useGenerateFindOneCustomObjectQuery.ts +++ b/front/src/modules/object-record/utils/useGenerateFindOneCustomObjectQuery.ts @@ -6,8 +6,10 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; export const useGenerateFindOneCustomObjectQuery = ({ objectMetadataItem, + depth, }: { objectMetadataItem: ObjectMetadataItem | null | undefined; + depth?: number; }) => { const mapFieldMetadataToGraphQLQuery = useMapFieldMetadataToGraphQLQuery(); @@ -24,7 +26,7 @@ export const useGenerateFindOneCustomObjectQuery = ({ }){ id ${objectMetadataItem.fields - .map((field) => mapFieldMetadataToGraphQLQuery(field)) + .map((field) => mapFieldMetadataToGraphQLQuery(field, depth)) .join('\n')} } } diff --git a/front/src/modules/ui/display/tag/components/Tag.tsx b/front/src/modules/ui/display/tag/components/Tag.tsx index 4a6adf579..b198052e8 100644 --- a/front/src/modules/ui/display/tag/components/Tag.tsx +++ b/front/src/modules/ui/display/tag/components/Tag.tsx @@ -27,7 +27,7 @@ const StyledTag = styled.h3<{ background: ${({ color, theme }) => theme.tag.background[color]}; border-radius: ${({ theme }) => theme.border.radius.sm}; color: ${({ color, theme }) => theme.tag.text[color]}; - display: inline-block; + display: flex; flex-direction: row; font-size: ${({ theme }) => theme.font.size.md}; font-style: normal; diff --git a/server/src/workspace/workspace-manager/standard-objects/activity-target.ts b/server/src/workspace/workspace-manager/standard-objects/activity-target.ts index 71b5af94b..d4075b4db 100644 --- a/server/src/workspace/workspace-manager/standard-objects/activity-target.ts +++ b/server/src/workspace/workspace-manager/standard-objects/activity-target.ts @@ -29,9 +29,7 @@ const activityTargetMetadata = { type: FieldMetadataType.RELATION, name: 'person', label: 'Person', - targetColumnMap: { - value: 'personId', - }, + targetColumnMap: {}, description: 'ActivityTarget person', icon: 'IconUser', isNullable: true, @@ -42,9 +40,7 @@ const activityTargetMetadata = { type: FieldMetadataType.RELATION, name: 'company', label: 'Company', - targetColumnMap: { - value: 'companyId', - }, + targetColumnMap: {}, description: 'ActivityTarget company', icon: 'IconBuildingSkyscraper', isNullable: true, diff --git a/server/src/workspace/workspace-manager/standard-objects/comment.ts b/server/src/workspace/workspace-manager/standard-objects/comment.ts index cf1d2b734..fd25f947b 100644 --- a/server/src/workspace/workspace-manager/standard-objects/comment.ts +++ b/server/src/workspace/workspace-manager/standard-objects/comment.ts @@ -32,26 +32,46 @@ const commentMetadata = { type: FieldMetadataType.RELATION, name: 'author', label: 'Author', - targetColumnMap: { - value: 'authorId', - }, + targetColumnMap: {}, description: 'Comment author', icon: 'IconCircleUser', isNullable: true, }, + { + isCustom: false, + isActive: true, + type: FieldMetadataType.RELATION, + name: 'authorId', + label: 'Author', + targetColumnMap: {}, + description: 'Comment author', + icon: 'IconCircleUser', + isNullable: true, + isSystem: true, + }, { isCustom: false, isActive: true, type: FieldMetadataType.RELATION, name: 'activity', label: 'Activity', - targetColumnMap: { - value: 'activityId', - }, + targetColumnMap: {}, description: 'Comment activity', icon: 'IconNotes', isNullable: true, }, + { + isCustom: false, + isActive: true, + type: FieldMetadataType.UUID, + name: 'activityId', + label: 'Activity', + targetColumnMap: {}, + description: 'Comment activity', + icon: 'IconNotes', + isNullable: true, + isSystem: true, + }, ], };