mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-26 21:53:48 +03:00
parent
033c3bc8b2
commit
72421a39ea
@ -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
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
@ -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) {
|
||||
|
@ -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],
|
||||
);
|
@ -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>
|
||||
);
|
||||
};
|
@ -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} />
|
||||
|
33
front/src/modules/activities/types/GraphQLActivity.ts
Normal file
33
front/src/modules/activities/types/GraphQLActivity.ts
Normal 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[];
|
||||
};
|
@ -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({
|
||||
|
@ -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 },
|
||||
|
@ -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
|
||||
|
@ -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')}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user