Fix Activity Picker part 1 (#2678)

* Fix Activity Picker part 1

* Fix
This commit is contained in:
Charles Bochet 2023-11-23 16:25:13 +01:00 committed by GitHub
parent 033c3bc8b2
commit 72421a39ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 174 additions and 102 deletions

View File

@ -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 = ({
</AssigneeFieldContextProvider>
</>
)}
{/* <ActivityRelationEditableField activity={activity} /> */}
<ActivityTargetsInlineCell
activity={activity as unknown as GraphQLActivity}
/>
</PropertyBox>
</StyledTopContainer>
<ActivityBodyEditor

View File

@ -1,49 +0,0 @@
import { ActivityTargetChips } from '@/activities/components/ActivityTargetChips';
import { Activity } from '@/activities/types/Activity';
import { ActivityTarget } from '@/activities/types/ActivityTarget';
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';
import { ActivityRelationEditableFieldEditMode } from './ActivityRelationEditableFieldEditMode';
type ActivityRelationEditableFieldProps = {
activity?: Pick<Activity, 'id'> & {
activityTargets?: Array<
Pick<ActivityTarget, 'id' | 'personId' | 'companyId'> & {
person?: Pick<Person, 'id' | 'name' | 'avatarUrl'> | null;
company?: Pick<Company, 'id' | 'domainName' | 'name'> | null;
}
> | null;
};
};
export const ActivityRelationEditableField = ({
activity,
}: ActivityRelationEditableFieldProps) => {
return (
<RecoilScope CustomRecoilScopeContext={FieldRecoilScopeContext}>
<RecoilScope>
<RecordInlineCellContainer
buttonIcon={IconPencil}
customEditHotkeyScope={{
scope: RelationPickerHotkeyScope.RelationPicker,
}}
IconLabel={IconArrowUpRight}
editModeContent={
<ActivityRelationEditableFieldEditMode activity={activity} />
}
label="Relations"
displayModeContent={
<ActivityTargetChips targets={activity?.activityTargets} />
}
isDisplayModeContentEmpty={activity?.activityTargets?.length === 0}
/>
</RecoilScope>
</RecoilScope>
);
};

View File

@ -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<Activity, 'id'> & {
activityTargets?: Array<
Pick<ActivityTarget, 'id' | 'personId' | 'companyId'>
> | null;
activity?: Pick<GraphQLActivity, 'id'> & {
activityTargets?: {
edges: Array<{
node: Pick<ActivityTarget, 'id' | 'personId' | 'companyId'>;
}> | null;
};
};
}) => {
const { createOneObject } = useCreateOneObjectRecord<ActivityTarget>({
@ -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) {

View File

@ -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<Activity, 'id'> & {
activityTargets?: Array<
Pick<ActivityTarget, 'id' | 'personId' | 'companyId'>
> | null;
type ActivityTargetInlineCellEditModeProps = {
activity?: Pick<GraphQLActivity, 'id'> & {
activityTargets?: {
edges: Array<{
node: Pick<ActivityTarget, 'id' | 'personId' | 'companyId'>;
}> | 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],
);

View File

@ -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<GraphQLActivity, 'id'> & {
activityTargets?: {
edges: Array<{
node: Pick<ActivityTarget, 'id' | 'personId' | 'companyId'> & {
person?: Pick<Person, 'id' | 'name' | 'avatarUrl'> | null;
company?: Pick<Company, 'id' | 'domainName' | 'name'> | null;
};
}> | null;
};
};
};
export const ActivityTargetsInlineCell = ({
activity,
}: ActivityTargetsInlineCellProps) => {
return (
<RecoilScope CustomRecoilScopeContext={FieldRecoilScopeContext}>
<RecordInlineCellContainer
buttonIcon={IconPencil}
customEditHotkeyScope={{
scope: RelationPickerHotkeyScope.RelationPicker,
}}
IconLabel={IconArrowUpRight}
editModeContent={
<ActivityTargetInlineCellEditMode activity={activity} />
}
label="Relations"
displayModeContent={
<ActivityTargetChips targets={activity?.activityTargets?.edges} />
}
isDisplayModeContentEmpty={
activity?.activityTargets?.edges?.length === 0
}
/>
</RecoilScope>
);
};

View File

@ -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 = ({
<StyledCardContent>{body}</StyledCardContent>
</StyledCardDetailsContainer>
<StyledFooter>
<ActivityRelationEditableField activity={note} />
<ActivityTargetsInlineCell
activity={note as unknown as GraphQLActivity}
/>
{note.comments && note.comments.length > 0 && (
<StyledCommentIcon>
<IconComment size={theme.icon.size.md} />

View File

@ -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<WorkspaceMember, 'id' | 'name' | 'avatarUrl'>;
authorId: string;
assignee: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'> | null;
assigneeId: string | null;
comments: CommentNode[];
};

View File

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

View File

@ -9,19 +9,24 @@ export const useFindOneObjectRecord = <
objectNameSingular,
objectRecordId,
onCompleted,
depth,
skip,
}: Pick<ObjectMetadataItemIdentifier, 'objectNameSingular'> & {
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 },

View File

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

View File

@ -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')}
}
}

View File

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

View File

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

View File

@ -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,
},
],
};