feat: rename comment thread into activity (#939)
* feat: rename commentThread into activity server * feat: rename commentThread into activity front * feat: migration only create tables feat: migration only create tables * Update activities * fix: rebase partial fix * fix: all rebase problems and drop activity target alter * fix: lint * Update migration * Update migration * Fix conflicts * Fix conflicts --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
|
||||
import { CommentThreadActionBar } from '../../right-drawer/components/CommentThreadActionBar';
|
||||
import { ActivityActionBar } from '../../right-drawer/components/ActivityActionBar';
|
||||
import { Comment } from '../Comment';
|
||||
|
||||
import { mockComment, mockCommentWithLongValues } from './mock-comment';
|
||||
@ -15,7 +15,7 @@ const meta: Meta<typeof Comment> = {
|
||||
actionBar: {
|
||||
type: 'boolean',
|
||||
mapping: {
|
||||
true: <CommentThreadActionBar commentThreadId="test-id" />,
|
||||
true: <ActivityActionBar activityId="test-id" />,
|
||||
false: undefined,
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { CommentThreadActionBar } from '@/activities/right-drawer/components/CommentThreadActionBar';
|
||||
import { ActivityActionBar } from '@/activities/right-drawer/components/ActivityActionBar';
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
import { avatarUrl } from '~/testing/mock-data/users';
|
||||
|
||||
@ -17,7 +17,7 @@ const meta: Meta<typeof CommentHeader> = {
|
||||
actionBar: {
|
||||
type: 'boolean',
|
||||
mapping: {
|
||||
true: <CommentThreadActionBar commentThreadId="test-id" />,
|
||||
true: <ActivityActionBar activityId="test-id" />,
|
||||
false: undefined,
|
||||
},
|
||||
},
|
||||
|
||||
@ -6,24 +6,21 @@ import styled from '@emotion/styled';
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
import { BlockEditor } from '@/ui/editor/components/BlockEditor';
|
||||
import {
|
||||
CommentThread,
|
||||
useUpdateCommentThreadMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { Activity, useUpdateActivityMutation } from '~/generated/graphql';
|
||||
|
||||
import { GET_COMMENT_THREADS_BY_TARGETS } from '../queries/select';
|
||||
import { GET_ACTIVITIES_BY_TARGETS } from '../queries/select';
|
||||
|
||||
const BlockNoteStyledContainer = styled.div`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
type OwnProps = {
|
||||
commentThread: Pick<CommentThread, 'id' | 'body'>;
|
||||
onChange?: (commentThreadBody: string) => void;
|
||||
activity: Pick<Activity, 'id' | 'body'>;
|
||||
onChange?: (activityBody: string) => void;
|
||||
};
|
||||
|
||||
export function CommentThreadBodyEditor({ commentThread, onChange }: OwnProps) {
|
||||
const [updateCommentThreadMutation] = useUpdateCommentThreadMutation();
|
||||
export function ActivityBodyEditor({ activity, onChange }: OwnProps) {
|
||||
const [updateActivityMutation] = useUpdateActivityMutation();
|
||||
|
||||
const [body, setBody] = useState<string | null>(null);
|
||||
|
||||
@ -34,26 +31,22 @@ export function CommentThreadBodyEditor({ commentThread, onChange }: OwnProps) {
|
||||
}, [body, onChange]);
|
||||
|
||||
const debounceOnChange = useMemo(() => {
|
||||
function onInternalChange(commentThreadBody: string) {
|
||||
setBody(commentThreadBody);
|
||||
updateCommentThreadMutation({
|
||||
function onInternalChange(activityBody: string) {
|
||||
setBody(activityBody);
|
||||
updateActivityMutation({
|
||||
variables: {
|
||||
id: commentThread.id,
|
||||
body: commentThreadBody,
|
||||
id: activity.id,
|
||||
body: activityBody,
|
||||
},
|
||||
refetchQueries: [
|
||||
getOperationName(GET_COMMENT_THREADS_BY_TARGETS) ?? '',
|
||||
],
|
||||
refetchQueries: [getOperationName(GET_ACTIVITIES_BY_TARGETS) ?? ''],
|
||||
});
|
||||
}
|
||||
|
||||
return debounce(onInternalChange, 200);
|
||||
}, [commentThread, updateCommentThreadMutation, setBody]);
|
||||
}, [activity, updateActivityMutation, setBody]);
|
||||
|
||||
const editor: BlockNoteEditor | null = useBlockNote({
|
||||
initialContent: commentThread.body
|
||||
? JSON.parse(commentThread.body)
|
||||
: undefined,
|
||||
initialContent: activity.body ? JSON.parse(activity.body) : undefined,
|
||||
editorDOMAttributes: { class: 'editor' },
|
||||
onEditorContentChange: (editor) => {
|
||||
debounceOnChange(JSON.stringify(editor.topLevelBlocks) ?? '');
|
||||
@ -6,15 +6,15 @@ import { v4 } from 'uuid';
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
import { useIsMobile } from '@/ui/hooks/useIsMobile';
|
||||
import { AutosizeTextInput } from '@/ui/input/components/AutosizeTextInput';
|
||||
import { CommentThread, useCreateCommentMutation } from '~/generated/graphql';
|
||||
import { Activity, useCreateCommentMutation } from '~/generated/graphql';
|
||||
import { isNonEmptyString } from '~/utils/isNonEmptyString';
|
||||
|
||||
import { Comment } from '../comment/Comment';
|
||||
import { GET_COMMENT_THREAD } from '../queries';
|
||||
import { GET_ACTIVITY } from '../queries';
|
||||
import { CommentForDrawer } from '../types/CommentForDrawer';
|
||||
|
||||
type OwnProps = {
|
||||
commentThread: Pick<CommentThread, 'id'> & {
|
||||
activity: Pick<Activity, 'id'> & {
|
||||
comments: Array<CommentForDrawer>;
|
||||
};
|
||||
};
|
||||
@ -52,7 +52,7 @@ const StyledThreadCommentTitle = styled.div`
|
||||
text-transform: uppercase;
|
||||
`;
|
||||
|
||||
export function CommentThreadComments({ commentThread }: OwnProps) {
|
||||
export function ActivityComments({ activity }: OwnProps) {
|
||||
const [createCommentMutation] = useCreateCommentMutation();
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
|
||||
@ -69,21 +69,21 @@ export function CommentThreadComments({ commentThread }: OwnProps) {
|
||||
variables: {
|
||||
commentId: v4(),
|
||||
authorId: currentUser?.id ?? '',
|
||||
commentThreadId: commentThread?.id ?? '',
|
||||
activityId: activity?.id ?? '',
|
||||
commentText: commentText,
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
refetchQueries: [getOperationName(GET_COMMENT_THREAD) ?? ''],
|
||||
refetchQueries: [getOperationName(GET_ACTIVITY) ?? ''],
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{commentThread?.comments.length > 0 && (
|
||||
{activity?.comments.length > 0 && (
|
||||
<>
|
||||
<StyledThreadItemListContainer>
|
||||
<StyledThreadCommentTitle>Comments</StyledThreadCommentTitle>
|
||||
{commentThread?.comments?.map((comment) => (
|
||||
{activity?.comments?.map((comment) => (
|
||||
<Comment key={comment.id} comment={comment} />
|
||||
))}
|
||||
</StyledThreadItemListContainer>
|
||||
@ -4,17 +4,17 @@ import { Button, ButtonVariant } from '@/ui/button/components/Button';
|
||||
import { ButtonGroup } from '@/ui/button/components/ButtonGroup';
|
||||
import { IconCheckbox, IconNotes, IconTimelineEvent } from '@/ui/icon/index';
|
||||
|
||||
type CommentThreadCreateButtonProps = {
|
||||
type ActivityCreateButtonProps = {
|
||||
onNoteClick?: () => void;
|
||||
onTaskClick?: () => void;
|
||||
onActivityClick?: () => void;
|
||||
};
|
||||
|
||||
export function CommentThreadCreateButton({
|
||||
export function ActivityCreateButton({
|
||||
onNoteClick,
|
||||
onTaskClick,
|
||||
onActivityClick,
|
||||
}: CommentThreadCreateButtonProps) {
|
||||
}: ActivityCreateButtonProps) {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<ButtonGroup variant={ButtonVariant.Secondary}>
|
||||
@ -2,26 +2,26 @@ import React, { useCallback, useState } from 'react';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { CommentThreadBodyEditor } from '@/activities/components/CommentThreadBodyEditor';
|
||||
import { CommentThreadComments } from '@/activities/components/CommentThreadComments';
|
||||
import { CommentThreadRelationPicker } from '@/activities/components/CommentThreadRelationPicker';
|
||||
import { CommentThreadTypeDropdown } from '@/activities/components/CommentThreadTypeDropdown';
|
||||
import { GET_COMMENT_THREADS_BY_TARGETS } from '@/activities/queries';
|
||||
import { ActivityBodyEditor } from '@/activities/components/ActivityBodyEditor';
|
||||
import { ActivityComments } from '@/activities/components/ActivityComments';
|
||||
import { ActivityRelationPicker } from '@/activities/components/ActivityRelationPicker';
|
||||
import { ActivityTypeDropdown } from '@/activities/components/ActivityTypeDropdown';
|
||||
import { GET_ACTIVITIES_BY_TARGETS } from '@/activities/queries';
|
||||
import { PropertyBox } from '@/ui/editable-field/property-box/components/PropertyBox';
|
||||
import { PropertyBoxItem } from '@/ui/editable-field/property-box/components/PropertyBoxItem';
|
||||
import { useIsMobile } from '@/ui/hooks/useIsMobile';
|
||||
import { IconArrowUpRight } from '@/ui/icon/index';
|
||||
import {
|
||||
CommentThread,
|
||||
CommentThreadTarget,
|
||||
useUpdateCommentThreadMutation,
|
||||
Activity,
|
||||
ActivityTarget,
|
||||
useUpdateActivityMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { debounce } from '~/utils/debounce';
|
||||
|
||||
import { CommentThreadActionBar } from '../right-drawer/components/CommentThreadActionBar';
|
||||
import { ActivityActionBar } from '../right-drawer/components/ActivityActionBar';
|
||||
import { CommentForDrawer } from '../types/CommentForDrawer';
|
||||
|
||||
import { CommentThreadTitle } from './CommentThreadTitle';
|
||||
import { ActivityTitle } from './ActivityTitle';
|
||||
|
||||
import '@blocknote/core/style.css';
|
||||
|
||||
@ -65,64 +65,57 @@ const StyledTopActionsContainer = styled.div`
|
||||
`;
|
||||
|
||||
type OwnProps = {
|
||||
commentThread: Pick<
|
||||
CommentThread,
|
||||
'id' | 'title' | 'body' | 'type' | 'completedAt'
|
||||
> & {
|
||||
activity: Pick<Activity, 'id' | 'title' | 'body' | 'type' | 'completedAt'> & {
|
||||
comments?: Array<CommentForDrawer> | null;
|
||||
} & {
|
||||
commentThreadTargets?: Array<
|
||||
Pick<CommentThreadTarget, 'id' | 'commentableId' | 'commentableType'>
|
||||
activityTargets?: Array<
|
||||
Pick<ActivityTarget, 'id' | 'commentableId' | 'commentableType'>
|
||||
> | null;
|
||||
};
|
||||
showComment?: boolean;
|
||||
autoFillTitle?: boolean;
|
||||
};
|
||||
|
||||
export function CommentThreadEditor({
|
||||
commentThread,
|
||||
export function ActivityEditor({
|
||||
activity,
|
||||
showComment = true,
|
||||
autoFillTitle = false,
|
||||
}: OwnProps) {
|
||||
const [hasUserManuallySetTitle, setHasUserManuallySetTitle] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [title, setTitle] = useState<string | null>(commentThread.title ?? '');
|
||||
const [title, setTitle] = useState<string | null>(activity.title ?? '');
|
||||
const [completedAt, setCompletedAt] = useState<string | null>(
|
||||
commentThread.completedAt ?? '',
|
||||
activity.completedAt ?? '',
|
||||
);
|
||||
|
||||
const [updateCommentThreadMutation] = useUpdateCommentThreadMutation();
|
||||
const [updateActivityMutation] = useUpdateActivityMutation();
|
||||
|
||||
const updateTitle = useCallback(
|
||||
(newTitle: string) => {
|
||||
updateCommentThreadMutation({
|
||||
updateActivityMutation({
|
||||
variables: {
|
||||
id: commentThread.id,
|
||||
id: activity.id,
|
||||
title: newTitle ?? '',
|
||||
},
|
||||
refetchQueries: [
|
||||
getOperationName(GET_COMMENT_THREADS_BY_TARGETS) ?? '',
|
||||
],
|
||||
refetchQueries: [getOperationName(GET_ACTIVITIES_BY_TARGETS) ?? ''],
|
||||
});
|
||||
},
|
||||
[commentThread, updateCommentThreadMutation],
|
||||
[activity, updateActivityMutation],
|
||||
);
|
||||
|
||||
const handleActivityCompletionChange = useCallback(
|
||||
(value: boolean) => {
|
||||
updateCommentThreadMutation({
|
||||
updateActivityMutation({
|
||||
variables: {
|
||||
id: commentThread.id,
|
||||
id: activity.id,
|
||||
completedAt: value ? new Date().toISOString() : null,
|
||||
},
|
||||
refetchQueries: [
|
||||
getOperationName(GET_COMMENT_THREADS_BY_TARGETS) ?? '',
|
||||
],
|
||||
refetchQueries: [getOperationName(GET_ACTIVITIES_BY_TARGETS) ?? ''],
|
||||
});
|
||||
setCompletedAt(value ? new Date().toISOString() : null);
|
||||
},
|
||||
[commentThread, updateCommentThreadMutation],
|
||||
[activity, updateActivityMutation],
|
||||
);
|
||||
|
||||
const debouncedUpdateTitle = debounce(updateTitle, 200);
|
||||
@ -135,7 +128,7 @@ export function CommentThreadEditor({
|
||||
}
|
||||
}
|
||||
|
||||
if (!commentThread) {
|
||||
if (!activity) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
@ -144,13 +137,13 @@ export function CommentThreadEditor({
|
||||
<StyledUpperPartContainer>
|
||||
<StyledTopContainer>
|
||||
<StyledTopActionsContainer>
|
||||
<CommentThreadTypeDropdown commentThread={commentThread} />
|
||||
<CommentThreadActionBar commentThreadId={commentThread?.id ?? ''} />
|
||||
<ActivityTypeDropdown activity={activity} />
|
||||
<ActivityActionBar activityId={activity?.id ?? ''} />
|
||||
</StyledTopActionsContainer>
|
||||
<CommentThreadTitle
|
||||
<ActivityTitle
|
||||
title={title ?? ''}
|
||||
completed={!!completedAt}
|
||||
type={commentThread.type}
|
||||
type={activity.type}
|
||||
onTitleChange={(newTitle) => {
|
||||
setTitle(newTitle);
|
||||
setHasUserManuallySetTitle(true);
|
||||
@ -162,11 +155,10 @@ export function CommentThreadEditor({
|
||||
<PropertyBoxItem
|
||||
icon={<IconArrowUpRight />}
|
||||
value={
|
||||
<CommentThreadRelationPicker
|
||||
commentThread={{
|
||||
id: commentThread.id,
|
||||
commentThreadTargets:
|
||||
commentThread.commentThreadTargets ?? [],
|
||||
<ActivityRelationPicker
|
||||
activity={{
|
||||
id: activity.id,
|
||||
activityTargets: activity.activityTargets ?? [],
|
||||
}}
|
||||
/>
|
||||
}
|
||||
@ -174,16 +166,16 @@ export function CommentThreadEditor({
|
||||
/>
|
||||
</PropertyBox>
|
||||
</StyledTopContainer>
|
||||
<CommentThreadBodyEditor
|
||||
commentThread={commentThread}
|
||||
<ActivityBodyEditor
|
||||
activity={activity}
|
||||
onChange={updateTitleFromBody}
|
||||
/>
|
||||
</StyledUpperPartContainer>
|
||||
{showComment && (
|
||||
<CommentThreadComments
|
||||
commentThread={{
|
||||
id: commentThread.id,
|
||||
comments: commentThread.comments ?? [],
|
||||
<ActivityComments
|
||||
activity={{
|
||||
id: activity.id,
|
||||
comments: activity.comments ?? [],
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@ -18,19 +18,15 @@ import { useScopedHotkeys } from '@/ui/hotkey/hooks/useScopedHotkeys';
|
||||
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
|
||||
import { MultipleEntitySelect } from '@/ui/relation-picker/components/MultipleEntitySelect';
|
||||
import { RelationPickerHotkeyScope } from '@/ui/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import {
|
||||
CommentableType,
|
||||
CommentThread,
|
||||
CommentThreadTarget,
|
||||
} from '~/generated/graphql';
|
||||
import { Activity, ActivityTarget, CommentableType } from '~/generated/graphql';
|
||||
|
||||
import { useHandleCheckableCommentThreadTargetChange } from '../hooks/useHandleCheckableCommentThreadTargetChange';
|
||||
import { useHandleCheckableActivityTargetChange } from '../hooks/useHandleCheckableActivityTargetChange';
|
||||
import { flatMapAndSortEntityForSelectArrayOfArrayByName } from '../utils/flatMapAndSortEntityForSelectArrayByName';
|
||||
|
||||
type OwnProps = {
|
||||
commentThread?: Pick<CommentThread, 'id'> & {
|
||||
commentThreadTargets: Array<
|
||||
Pick<CommentThreadTarget, 'id' | 'commentableId' | 'commentableType'>
|
||||
activity?: Pick<Activity, 'id'> & {
|
||||
activityTargets: Array<
|
||||
Pick<ActivityTarget, 'id' | 'commentableId' | 'commentableType'>
|
||||
>;
|
||||
};
|
||||
};
|
||||
@ -75,7 +71,7 @@ const StyledMenuWrapper = styled.div`
|
||||
z-index: ${({ theme }) => theme.lastLayerZIndex};
|
||||
`;
|
||||
|
||||
export function CommentThreadRelationPicker({ commentThread }: OwnProps) {
|
||||
export function ActivityRelationPicker({ activity }: OwnProps) {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const [searchFilter, setSearchFilter] = useState('');
|
||||
const [selectedEntityIds, setSelectedEntityIds] = useState<
|
||||
@ -88,17 +84,18 @@ export function CommentThreadRelationPicker({ commentThread }: OwnProps) {
|
||||
|
||||
const initialPeopleIds = useMemo(
|
||||
() =>
|
||||
commentThread?.commentThreadTargets
|
||||
activity?.activityTargets
|
||||
?.filter((relation) => relation.commentableType === 'Person')
|
||||
.map((relation) => relation.commentableId) ?? [],
|
||||
[commentThread?.commentThreadTargets],
|
||||
[activity?.activityTargets],
|
||||
);
|
||||
|
||||
const initialCompanyIds = useMemo(
|
||||
() =>
|
||||
commentThread?.commentThreadTargets
|
||||
activity?.activityTargets
|
||||
?.filter((relation) => relation.commentableType === 'Company')
|
||||
.map((relation) => relation.commentableId) ?? [],
|
||||
[commentThread?.commentThreadTargets],
|
||||
[activity?.activityTargets],
|
||||
);
|
||||
|
||||
const initialSelectedEntityIds = useMemo(
|
||||
@ -135,8 +132,8 @@ export function CommentThreadRelationPicker({ commentThread }: OwnProps) {
|
||||
companiesForMultiSelect.entitiesToSelect,
|
||||
]);
|
||||
|
||||
const handleCheckItemsChange = useHandleCheckableCommentThreadTargetChange({
|
||||
commentThread,
|
||||
const handleCheckItemsChange = useHandleCheckableActivityTargetChange({
|
||||
activity,
|
||||
});
|
||||
|
||||
const exitEditMode = useCallback(() => {
|
||||
@ -51,7 +51,7 @@ type OwnProps = {
|
||||
onCompletionChange: (value: boolean) => void;
|
||||
};
|
||||
|
||||
export function CommentThreadTitle({
|
||||
export function ActivityTitle({
|
||||
title,
|
||||
completed,
|
||||
type,
|
||||
@ -7,17 +7,17 @@ import {
|
||||
ChipVariant,
|
||||
} from '@/ui/chip/components/Chip';
|
||||
import { IconPhone } from '@/ui/icon';
|
||||
import { CommentThread } from '~/generated/graphql';
|
||||
import { Activity } from '~/generated/graphql';
|
||||
|
||||
type OwnProps = {
|
||||
commentThread: Pick<CommentThread, 'type'>;
|
||||
activity: Pick<Activity, 'type'>;
|
||||
};
|
||||
|
||||
export function CommentThreadTypeDropdown({ commentThread }: OwnProps) {
|
||||
export function ActivityTypeDropdown({ activity }: OwnProps) {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Chip
|
||||
label={commentThread.type}
|
||||
label={activity.type}
|
||||
leftComponent={<IconPhone size={theme.icon.size.md} />}
|
||||
size={ChipSize.Large}
|
||||
accent={ChipAccent.TextSecondary}
|
||||
@ -4,17 +4,17 @@ import type { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { mockedCommentThreads } from '~/testing/mock-data/comment-threads';
|
||||
import { mockedActivities } from '~/testing/mock-data/activities';
|
||||
|
||||
import { CommentThreadRelationPicker } from '../CommentThreadRelationPicker';
|
||||
import { ActivityRelationPicker } from '../ActivityRelationPicker';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
width: 400px;
|
||||
`;
|
||||
|
||||
const meta: Meta<typeof CommentThreadRelationPicker> = {
|
||||
title: 'Modules/Comments/CommentThreadRelationPicker',
|
||||
component: CommentThreadRelationPicker,
|
||||
const meta: Meta<typeof ActivityRelationPicker> = {
|
||||
title: 'Modules/Comments/ActivityRelationPicker',
|
||||
component: ActivityRelationPicker,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<MemoryRouter>
|
||||
@ -25,13 +25,13 @@ const meta: Meta<typeof CommentThreadRelationPicker> = {
|
||||
),
|
||||
ComponentDecorator,
|
||||
],
|
||||
args: { commentThread: mockedCommentThreads[0] },
|
||||
args: { activity: mockedActivities[0] },
|
||||
parameters: {
|
||||
msw: graphqlMocks,
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof CommentThreadRelationPicker>;
|
||||
type Story = StoryObj<typeof ActivityRelationPicker>;
|
||||
|
||||
export const Default: Story = {};
|
||||
@ -0,0 +1,84 @@
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { GET_COMPANIES } from '@/companies/queries';
|
||||
import { GET_PEOPLE } from '@/people/queries';
|
||||
import {
|
||||
Activity,
|
||||
ActivityTarget,
|
||||
useAddActivityTargetsOnActivityMutation,
|
||||
useRemoveActivityTargetsOnActivityMutation,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import { GET_ACTIVITIES_BY_TARGETS } from '../queries';
|
||||
import { CommentableEntityForSelect } from '../types/CommentableEntityForSelect';
|
||||
|
||||
export function useHandleCheckableActivityTargetChange({
|
||||
activity,
|
||||
}: {
|
||||
activity?: Pick<Activity, 'id'> & {
|
||||
activityTargets: Array<
|
||||
Pick<ActivityTarget, 'id' | 'commentableId' | 'commentableType'>
|
||||
>;
|
||||
};
|
||||
}) {
|
||||
const [addActivityTargetsOnActivity] =
|
||||
useAddActivityTargetsOnActivityMutation({
|
||||
refetchQueries: [
|
||||
getOperationName(GET_COMPANIES) ?? '',
|
||||
getOperationName(GET_PEOPLE) ?? '',
|
||||
getOperationName(GET_ACTIVITIES_BY_TARGETS) ?? '',
|
||||
],
|
||||
});
|
||||
|
||||
const [removeActivityTargetsOnActivity] =
|
||||
useRemoveActivityTargetsOnActivityMutation({
|
||||
refetchQueries: [
|
||||
getOperationName(GET_COMPANIES) ?? '',
|
||||
getOperationName(GET_PEOPLE) ?? '',
|
||||
getOperationName(GET_ACTIVITIES_BY_TARGETS) ?? '',
|
||||
],
|
||||
});
|
||||
|
||||
return async function handleCheckItemsChange(
|
||||
entityValues: Record<string, boolean>,
|
||||
entities: CommentableEntityForSelect[],
|
||||
) {
|
||||
if (!activity) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentEntityIds = activity.activityTargets.map(
|
||||
({ commentableId }) => commentableId,
|
||||
);
|
||||
|
||||
const entitiesToAdd = entities.filter(
|
||||
({ id }) => entityValues[id] && !currentEntityIds.includes(id),
|
||||
);
|
||||
|
||||
if (entitiesToAdd.length)
|
||||
await addActivityTargetsOnActivity({
|
||||
variables: {
|
||||
activityId: activity.id,
|
||||
activityTargetInputs: entitiesToAdd.map((entity) => ({
|
||||
id: v4(),
|
||||
createdAt: new Date().toISOString(),
|
||||
commentableType: entity.entityType,
|
||||
commentableId: entity.id,
|
||||
})),
|
||||
},
|
||||
});
|
||||
|
||||
const activityTargetIdsToDelete = activity.activityTargets
|
||||
.filter(({ commentableId }) => !entityValues[commentableId])
|
||||
.map(({ id }) => id);
|
||||
|
||||
if (activityTargetIdsToDelete.length)
|
||||
await removeActivityTargetsOnActivity({
|
||||
variables: {
|
||||
activityId: activity.id,
|
||||
activityTargetIds: activityTargetIdsToDelete,
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
@ -1,84 +0,0 @@
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { GET_COMPANIES } from '@/companies/queries';
|
||||
import { GET_PEOPLE } from '@/people/queries';
|
||||
import {
|
||||
CommentThread,
|
||||
CommentThreadTarget,
|
||||
useAddCommentThreadTargetsOnCommentThreadMutation,
|
||||
useRemoveCommentThreadTargetsOnCommentThreadMutation,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import { GET_COMMENT_THREADS_BY_TARGETS } from '../queries';
|
||||
import { CommentableEntityForSelect } from '../types/CommentableEntityForSelect';
|
||||
|
||||
export function useHandleCheckableCommentThreadTargetChange({
|
||||
commentThread,
|
||||
}: {
|
||||
commentThread?: Pick<CommentThread, 'id'> & {
|
||||
commentThreadTargets: Array<
|
||||
Pick<CommentThreadTarget, 'id' | 'commentableId'>
|
||||
>;
|
||||
};
|
||||
}) {
|
||||
const [addCommentThreadTargetsOnCommentThread] =
|
||||
useAddCommentThreadTargetsOnCommentThreadMutation({
|
||||
refetchQueries: [
|
||||
getOperationName(GET_COMPANIES) ?? '',
|
||||
getOperationName(GET_PEOPLE) ?? '',
|
||||
getOperationName(GET_COMMENT_THREADS_BY_TARGETS) ?? '',
|
||||
],
|
||||
});
|
||||
|
||||
const [removeCommentThreadTargetsOnCommentThread] =
|
||||
useRemoveCommentThreadTargetsOnCommentThreadMutation({
|
||||
refetchQueries: [
|
||||
getOperationName(GET_COMPANIES) ?? '',
|
||||
getOperationName(GET_PEOPLE) ?? '',
|
||||
getOperationName(GET_COMMENT_THREADS_BY_TARGETS) ?? '',
|
||||
],
|
||||
});
|
||||
|
||||
return async function handleCheckItemsChange(
|
||||
entityValues: Record<string, boolean>,
|
||||
entities: CommentableEntityForSelect[],
|
||||
) {
|
||||
if (!commentThread) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentEntityIds = commentThread.commentThreadTargets.map(
|
||||
({ commentableId }) => commentableId,
|
||||
);
|
||||
|
||||
const entitiesToAdd = entities.filter(
|
||||
({ id }) => entityValues[id] && !currentEntityIds.includes(id),
|
||||
);
|
||||
|
||||
if (entitiesToAdd.length)
|
||||
await addCommentThreadTargetsOnCommentThread({
|
||||
variables: {
|
||||
commentThreadId: commentThread.id,
|
||||
commentThreadTargetInputs: entitiesToAdd.map((entity) => ({
|
||||
id: v4(),
|
||||
createdAt: new Date().toISOString(),
|
||||
commentableType: entity.entityType,
|
||||
commentableId: entity.id,
|
||||
})),
|
||||
},
|
||||
});
|
||||
|
||||
const commentThreadTargetIdsToDelete = commentThread.commentThreadTargets
|
||||
.filter(({ commentableId }) => !entityValues[commentableId])
|
||||
.map(({ id }) => id);
|
||||
|
||||
if (commentThreadTargetIdsToDelete.length)
|
||||
await removeCommentThreadTargetsOnCommentThread({
|
||||
variables: {
|
||||
commentThreadId: commentThread.id,
|
||||
commentThreadTargetIds: commentThreadTargetIdsToDelete,
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
@ -5,18 +5,16 @@ import { useRightDrawer } from '@/ui/right-drawer/hooks/useRightDrawer';
|
||||
import { RightDrawerHotkeyScope } from '@/ui/right-drawer/types/RightDrawerHotkeyScope';
|
||||
import { RightDrawerPages } from '@/ui/right-drawer/types/RightDrawerPages';
|
||||
|
||||
import { viewableCommentThreadIdState } from '../states/viewableCommentThreadIdState';
|
||||
import { viewableActivityIdState } from '../states/viewableActivityIdState';
|
||||
|
||||
export function useOpenCommentThreadRightDrawer() {
|
||||
export function useOpenActivityRightDrawer() {
|
||||
const { openRightDrawer } = useRightDrawer();
|
||||
const [, setViewableCommentThreadId] = useRecoilState(
|
||||
viewableCommentThreadIdState,
|
||||
);
|
||||
const [, setViewableActivityId] = useRecoilState(viewableActivityIdState);
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
|
||||
return function openCommentThreadRightDrawer(commentThreadId: string) {
|
||||
return function openActivityRightDrawer(activityId: string) {
|
||||
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
||||
setViewableCommentThreadId(commentThreadId);
|
||||
openRightDrawer(RightDrawerPages.EditCommentThread);
|
||||
setViewableActivityId(activityId);
|
||||
openRightDrawer(RightDrawerPages.EditActivity);
|
||||
};
|
||||
}
|
||||
@ -9,40 +9,35 @@ import { useSetHotkeyScope } from '@/ui/hotkey/hooks/useSetHotkeyScope';
|
||||
import { useRightDrawer } from '@/ui/right-drawer/hooks/useRightDrawer';
|
||||
import { RightDrawerHotkeyScope } from '@/ui/right-drawer/types/RightDrawerHotkeyScope';
|
||||
import { RightDrawerPages } from '@/ui/right-drawer/types/RightDrawerPages';
|
||||
import {
|
||||
ActivityType,
|
||||
useCreateCommentThreadMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { ActivityType, useCreateActivityMutation } from '~/generated/graphql';
|
||||
|
||||
import { GET_COMMENT_THREAD, GET_COMMENT_THREADS_BY_TARGETS } from '../queries';
|
||||
import { GET_ACTIVITIES_BY_TARGETS, GET_ACTIVITY } from '../queries';
|
||||
import { commentableEntityArrayState } from '../states/commentableEntityArrayState';
|
||||
import { viewableCommentThreadIdState } from '../states/viewableCommentThreadIdState';
|
||||
import { viewableActivityIdState } from '../states/viewableActivityIdState';
|
||||
import { CommentableEntity } from '../types/CommentableEntity';
|
||||
|
||||
export function useOpenCreateCommentThreadDrawer() {
|
||||
export function useOpenCreateActivityDrawer() {
|
||||
const { openRightDrawer } = useRightDrawer();
|
||||
const [createCommentThreadMutation] = useCreateCommentThreadMutation();
|
||||
const [createActivityMutation] = useCreateActivityMutation();
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
|
||||
const [, setCommentableEntityArray] = useRecoilState(
|
||||
commentableEntityArrayState,
|
||||
);
|
||||
const [, setViewableCommentThreadId] = useRecoilState(
|
||||
viewableCommentThreadIdState,
|
||||
);
|
||||
const [, setViewableActivityId] = useRecoilState(viewableActivityIdState);
|
||||
|
||||
return function openCreateCommentThreadDrawer(
|
||||
return function openCreateActivityDrawer(
|
||||
entity: CommentableEntity,
|
||||
type: ActivityType,
|
||||
) {
|
||||
createCommentThreadMutation({
|
||||
createActivityMutation({
|
||||
variables: {
|
||||
authorId: currentUser?.id ?? '',
|
||||
commentThreadId: v4(),
|
||||
activityId: v4(),
|
||||
createdAt: new Date().toISOString(),
|
||||
type: type,
|
||||
commentThreadTargetArray: [
|
||||
activityTargetArray: [
|
||||
{
|
||||
commentableId: entity.id,
|
||||
commentableType: entity.type,
|
||||
@ -54,14 +49,14 @@ export function useOpenCreateCommentThreadDrawer() {
|
||||
refetchQueries: [
|
||||
getOperationName(GET_COMPANIES) ?? '',
|
||||
getOperationName(GET_PEOPLE) ?? '',
|
||||
getOperationName(GET_COMMENT_THREAD) ?? '',
|
||||
getOperationName(GET_COMMENT_THREADS_BY_TARGETS) ?? '',
|
||||
getOperationName(GET_ACTIVITY) ?? '',
|
||||
getOperationName(GET_ACTIVITIES_BY_TARGETS) ?? '',
|
||||
],
|
||||
onCompleted(data) {
|
||||
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
||||
setViewableCommentThreadId(data.createOneCommentThread.id);
|
||||
setViewableActivityId(data.createOneActivity.id);
|
||||
setCommentableEntityArray([entity]);
|
||||
openRightDrawer(RightDrawerPages.CreateCommentThread);
|
||||
openRightDrawer(RightDrawerPages.CreateActivity);
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -13,21 +13,19 @@ import { selectedRowIdsSelector } from '@/ui/table/states/selectedRowIdsSelector
|
||||
import {
|
||||
ActivityType,
|
||||
CommentableType,
|
||||
useCreateCommentThreadMutation,
|
||||
useCreateActivityMutation,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import { GET_COMMENT_THREAD, GET_COMMENT_THREADS_BY_TARGETS } from '../queries';
|
||||
import { GET_ACTIVITIES_BY_TARGETS, GET_ACTIVITY } from '../queries';
|
||||
import { commentableEntityArrayState } from '../states/commentableEntityArrayState';
|
||||
import { viewableCommentThreadIdState } from '../states/viewableCommentThreadIdState';
|
||||
import { viewableActivityIdState } from '../states/viewableActivityIdState';
|
||||
import { CommentableEntity } from '../types/CommentableEntity';
|
||||
|
||||
export function useOpenCreateCommentThreadDrawerForSelectedRowIds() {
|
||||
export function useOpenCreateActivityDrawerForSelectedRowIds() {
|
||||
const { openRightDrawer } = useRightDrawer();
|
||||
const [createCommentThreadMutation] = useCreateCommentThreadMutation();
|
||||
const [createActivityMutation] = useCreateActivityMutation();
|
||||
const currentUser = useRecoilValue(currentUserState);
|
||||
const [, setViewableCommentThreadId] = useRecoilState(
|
||||
viewableCommentThreadIdState,
|
||||
);
|
||||
const [, setViewableActivityId] = useRecoilState(viewableActivityIdState);
|
||||
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
|
||||
@ -47,13 +45,13 @@ export function useOpenCreateCommentThreadDrawerForSelectedRowIds() {
|
||||
}),
|
||||
);
|
||||
|
||||
createCommentThreadMutation({
|
||||
createActivityMutation({
|
||||
variables: {
|
||||
authorId: currentUser?.id ?? '',
|
||||
commentThreadId: v4(),
|
||||
activityId: v4(),
|
||||
createdAt: new Date().toISOString(),
|
||||
type: ActivityType.Note,
|
||||
commentThreadTargetArray: commentableEntityArray.map((entity) => ({
|
||||
activityTargetArray: commentableEntityArray.map((entity) => ({
|
||||
commentableId: entity.id,
|
||||
commentableType: entity.type,
|
||||
id: v4(),
|
||||
@ -63,14 +61,14 @@ export function useOpenCreateCommentThreadDrawerForSelectedRowIds() {
|
||||
refetchQueries: [
|
||||
getOperationName(GET_COMPANIES) ?? '',
|
||||
getOperationName(GET_PEOPLE) ?? '',
|
||||
getOperationName(GET_COMMENT_THREAD) ?? '',
|
||||
getOperationName(GET_COMMENT_THREADS_BY_TARGETS) ?? '',
|
||||
getOperationName(GET_ACTIVITY) ?? '',
|
||||
getOperationName(GET_ACTIVITIES_BY_TARGETS) ?? '',
|
||||
],
|
||||
onCompleted(data) {
|
||||
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
||||
setViewableCommentThreadId(data.createOneCommentThread.id);
|
||||
setViewableActivityId(data.createOneActivity.id);
|
||||
setCommentableEntityArray(commentableEntityArray);
|
||||
openRightDrawer(RightDrawerPages.CreateCommentThread);
|
||||
openRightDrawer(RightDrawerPages.CreateActivity);
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -5,7 +5,7 @@ export const CREATE_COMMENT = gql`
|
||||
$commentId: String!
|
||||
$commentText: String!
|
||||
$authorId: String!
|
||||
$commentThreadId: String!
|
||||
$activityId: String!
|
||||
$createdAt: DateTime!
|
||||
) {
|
||||
createOneComment(
|
||||
@ -14,7 +14,7 @@ export const CREATE_COMMENT = gql`
|
||||
createdAt: $createdAt
|
||||
body: $commentText
|
||||
author: { connect: { id: $authorId } }
|
||||
commentThread: { connect: { id: $commentThreadId } }
|
||||
activity: { connect: { id: $activityId } }
|
||||
}
|
||||
) {
|
||||
id
|
||||
@ -27,32 +27,32 @@ export const CREATE_COMMENT = gql`
|
||||
lastName
|
||||
avatarUrl
|
||||
}
|
||||
commentThreadId
|
||||
activityId
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const CREATE_COMMENT_THREAD_WITH_COMMENT = gql`
|
||||
mutation CreateCommentThread(
|
||||
$commentThreadId: String!
|
||||
export const CREATE_ACTIVITY_WITH_COMMENT = gql`
|
||||
mutation CreateActivity(
|
||||
$activityId: String!
|
||||
$body: String
|
||||
$title: String
|
||||
$type: ActivityType!
|
||||
$authorId: String!
|
||||
$createdAt: DateTime!
|
||||
$commentThreadTargetArray: [CommentThreadTargetCreateManyCommentThreadInput!]!
|
||||
$activityTargetArray: [ActivityTargetCreateManyActivityInput!]!
|
||||
) {
|
||||
createOneCommentThread(
|
||||
createOneActivity(
|
||||
data: {
|
||||
id: $commentThreadId
|
||||
id: $activityId
|
||||
createdAt: $createdAt
|
||||
updatedAt: $createdAt
|
||||
author: { connect: { id: $authorId } }
|
||||
body: $body
|
||||
title: $title
|
||||
type: $type
|
||||
commentThreadTargets: {
|
||||
createMany: { data: $commentThreadTargetArray, skipDuplicates: true }
|
||||
activityTargets: {
|
||||
createMany: { data: $activityTargetArray, skipDuplicates: true }
|
||||
}
|
||||
}
|
||||
) {
|
||||
@ -61,11 +61,11 @@ export const CREATE_COMMENT_THREAD_WITH_COMMENT = gql`
|
||||
updatedAt
|
||||
authorId
|
||||
type
|
||||
commentThreadTargets {
|
||||
activityTargets {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
commentThreadId
|
||||
activityId
|
||||
commentableType
|
||||
commentableId
|
||||
}
|
||||
|
||||
@ -1,16 +1,14 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_COMMENT_THREADS_BY_TARGETS = gql`
|
||||
query GetCommentThreadsByTargets(
|
||||
$commentThreadTargetIds: [String!]!
|
||||
$orderBy: [CommentThreadOrderByWithRelationInput!]
|
||||
export const GET_ACTIVITIES_BY_TARGETS = gql`
|
||||
query GetActivitiesByTargets(
|
||||
$activityTargetIds: [String!]!
|
||||
$orderBy: [ActivityOrderByWithRelationInput!]
|
||||
) {
|
||||
findManyCommentThreads(
|
||||
findManyActivities(
|
||||
orderBy: $orderBy
|
||||
where: {
|
||||
commentThreadTargets: {
|
||||
some: { commentableId: { in: $commentThreadTargetIds } }
|
||||
}
|
||||
activityTargets: { some: { commentableId: { in: $activityTargetIds } } }
|
||||
}
|
||||
) {
|
||||
id
|
||||
@ -38,18 +36,18 @@ export const GET_COMMENT_THREADS_BY_TARGETS = gql`
|
||||
avatarUrl
|
||||
}
|
||||
}
|
||||
commentThreadTargets {
|
||||
activityTargets {
|
||||
id
|
||||
commentableId
|
||||
commentableType
|
||||
commentableId
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const GET_COMMENT_THREAD = gql`
|
||||
query GetCommentThread($commentThreadId: String!) {
|
||||
findManyCommentThreads(where: { id: { equals: $commentThreadId } }) {
|
||||
export const GET_ACTIVITY = gql`
|
||||
query GetActivity($activityId: String!) {
|
||||
findManyActivities(where: { id: { equals: $activityId } }) {
|
||||
id
|
||||
createdAt
|
||||
body
|
||||
@ -75,10 +73,10 @@ export const GET_COMMENT_THREAD = gql`
|
||||
avatarUrl
|
||||
}
|
||||
}
|
||||
commentThreadTargets {
|
||||
activityTargets {
|
||||
id
|
||||
commentableId
|
||||
commentableType
|
||||
commentableId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,22 +1,18 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const ADD_COMMENT_THREAD_TARGETS = gql`
|
||||
mutation AddCommentThreadTargetsOnCommentThread(
|
||||
$commentThreadId: String!
|
||||
$commentThreadTargetInputs: [CommentThreadTargetCreateManyCommentThreadInput!]!
|
||||
export const ADD_ACTIVITY_TARGETS = gql`
|
||||
mutation AddActivityTargetsOnActivity(
|
||||
$activityId: String!
|
||||
$activityTargetInputs: [ActivityTargetCreateManyActivityInput!]!
|
||||
) {
|
||||
updateOneCommentThread(
|
||||
where: { id: $commentThreadId }
|
||||
data: {
|
||||
commentThreadTargets: {
|
||||
createMany: { data: $commentThreadTargetInputs }
|
||||
}
|
||||
}
|
||||
updateOneActivity(
|
||||
where: { id: $activityId }
|
||||
data: { activityTargets: { createMany: { data: $activityTargetInputs } } }
|
||||
) {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
commentThreadTargets {
|
||||
activityTargets {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
@ -27,23 +23,21 @@ export const ADD_COMMENT_THREAD_TARGETS = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const REMOVE_COMMENT_THREAD_TARGETS = gql`
|
||||
mutation RemoveCommentThreadTargetsOnCommentThread(
|
||||
$commentThreadId: String!
|
||||
$commentThreadTargetIds: [String!]!
|
||||
export const REMOVE_ACTIVITY_TARGETS = gql`
|
||||
mutation RemoveActivityTargetsOnActivity(
|
||||
$activityId: String!
|
||||
$activityTargetIds: [String!]!
|
||||
) {
|
||||
updateOneCommentThread(
|
||||
where: { id: $commentThreadId }
|
||||
updateOneActivity(
|
||||
where: { id: $activityId }
|
||||
data: {
|
||||
commentThreadTargets: {
|
||||
deleteMany: { id: { in: $commentThreadTargetIds } }
|
||||
}
|
||||
activityTargets: { deleteMany: { id: { in: $activityTargetIds } } }
|
||||
}
|
||||
) {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
commentThreadTargets {
|
||||
activityTargets {
|
||||
id
|
||||
createdAt
|
||||
updatedAt
|
||||
@ -54,23 +48,23 @@ export const REMOVE_COMMENT_THREAD_TARGETS = gql`
|
||||
}
|
||||
`;
|
||||
|
||||
export const DELETE_COMMENT_THREAD = gql`
|
||||
mutation DeleteCommentThread($commentThreadId: String!) {
|
||||
deleteManyCommentThreads(where: { id: { equals: $commentThreadId } }) {
|
||||
export const DELETE_ACTIVITY = gql`
|
||||
mutation DeleteActivity($activityId: String!) {
|
||||
deleteManyActivities(where: { id: { equals: $activityId } }) {
|
||||
count
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const UPDATE_COMMENT_THREAD = gql`
|
||||
mutation UpdateCommentThread(
|
||||
export const UPDATE_ACTIVITY = gql`
|
||||
mutation UpdateActivity(
|
||||
$id: String!
|
||||
$body: String
|
||||
$title: String
|
||||
$type: ActivityType
|
||||
$completedAt: DateTime
|
||||
) {
|
||||
updateOneCommentThread(
|
||||
updateOneActivity(
|
||||
where: { id: $id }
|
||||
data: {
|
||||
body: $body
|
||||
|
||||
@ -0,0 +1,201 @@
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { ActivityBodyEditor } from '@/activities/components/ActivityBodyEditor';
|
||||
import { ActivityComments } from '@/activities/components/ActivityComments';
|
||||
import { ActivityRelationPicker } from '@/activities/components/ActivityRelationPicker';
|
||||
import { ActivityTypeDropdown } from '@/activities/components/ActivityTypeDropdown';
|
||||
import { GET_ACTIVITY } from '@/activities/queries';
|
||||
import { PropertyBox } from '@/ui/editable-field/property-box/components/PropertyBox';
|
||||
import { PropertyBoxItem } from '@/ui/editable-field/property-box/components/PropertyBoxItem';
|
||||
import { useIsMobile } from '@/ui/hooks/useIsMobile';
|
||||
import { IconArrowUpRight } from '@/ui/icon/index';
|
||||
import {
|
||||
useGetActivityQuery,
|
||||
useUpdateActivityMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { debounce } from '~/utils/debounce';
|
||||
|
||||
import { ActivityActionBar } from './ActivityActionBar';
|
||||
|
||||
import '@blocknote/core/style.css';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
justify-content: space-between;
|
||||
overflow-y: auto;
|
||||
`;
|
||||
|
||||
const StyledUpperPartContainer = styled.div`
|
||||
align-items: flex-start;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
gap: ${({ theme }) => theme.spacing(4)};
|
||||
justify-content: flex-start;
|
||||
`;
|
||||
|
||||
const StyledTopContainer = styled.div`
|
||||
align-items: flex-start;
|
||||
align-self: stretch;
|
||||
background: ${({ theme }) => theme.background.secondary};
|
||||
border-bottom: ${({ theme }) =>
|
||||
useIsMobile() ? 'none' : `1px solid ${theme.border.color.medium}`};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
padding: 24px 24px 24px 48px;
|
||||
`;
|
||||
|
||||
const StyledEditableTitleInput = styled.input`
|
||||
background: transparent;
|
||||
|
||||
border: none;
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
display: flex;
|
||||
flex: 1 0 0;
|
||||
|
||||
flex-direction: column;
|
||||
font-family: Inter;
|
||||
font-size: ${({ theme }) => theme.font.size.xl};
|
||||
font-style: normal;
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
justify-content: center;
|
||||
|
||||
line-height: ${({ theme }) => theme.text.lineHeight.md};
|
||||
outline: none;
|
||||
width: calc(100% - ${({ theme }) => theme.spacing(2)});
|
||||
&::placeholder {
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledTopActionsContainer = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
type OwnProps = {
|
||||
activityId: string;
|
||||
showComment?: boolean;
|
||||
autoFillTitle?: boolean;
|
||||
};
|
||||
|
||||
export function Activity({
|
||||
activityId,
|
||||
showComment = true,
|
||||
autoFillTitle = false,
|
||||
}: OwnProps) {
|
||||
const { data } = useGetActivityQuery({
|
||||
variables: {
|
||||
activityId: activityId ?? '',
|
||||
},
|
||||
skip: !activityId,
|
||||
});
|
||||
const activity = data?.findManyActivities[0];
|
||||
const [hasUserManuallySetTitle, setHasUserManuallySetTitle] =
|
||||
useState<boolean>(false);
|
||||
|
||||
const [title, setTitle] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasUserManuallySetTitle) {
|
||||
setTitle(activity?.title ?? '');
|
||||
}
|
||||
}, [setTitle, activity?.title, hasUserManuallySetTitle]);
|
||||
|
||||
const [updateActivityMutation] = useUpdateActivityMutation();
|
||||
|
||||
const debounceUpdateTitle = useMemo(() => {
|
||||
function updateTitle(title: string) {
|
||||
if (activity) {
|
||||
updateActivityMutation({
|
||||
variables: {
|
||||
id: activityId,
|
||||
title: title ?? '',
|
||||
},
|
||||
refetchQueries: [getOperationName(GET_ACTIVITY) ?? ''],
|
||||
optimisticResponse: {
|
||||
__typename: 'Mutation',
|
||||
updateOneActivity: {
|
||||
__typename: 'Activity',
|
||||
id: activityId,
|
||||
title: title,
|
||||
type: activity.type,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
return debounce(updateTitle, 200);
|
||||
}, [activityId, updateActivityMutation, activity]);
|
||||
|
||||
function updateTitleFromBody(body: string) {
|
||||
const parsedTitle = JSON.parse(body)[0]?.content[0]?.text;
|
||||
if (!hasUserManuallySetTitle && autoFillTitle) {
|
||||
setTitle(parsedTitle);
|
||||
debounceUpdateTitle(parsedTitle);
|
||||
}
|
||||
}
|
||||
|
||||
if (!activity) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<StyledUpperPartContainer>
|
||||
<StyledTopContainer>
|
||||
<StyledTopActionsContainer>
|
||||
<ActivityTypeDropdown activity={activity} />
|
||||
<ActivityActionBar activityId={activity?.id ?? ''} />
|
||||
</StyledTopActionsContainer>
|
||||
<StyledEditableTitleInput
|
||||
autoFocus
|
||||
placeholder={`${activity.type} title (optional)`}
|
||||
onChange={(event) => {
|
||||
setHasUserManuallySetTitle(true);
|
||||
setTitle(event.target.value);
|
||||
debounceUpdateTitle(event.target.value);
|
||||
}}
|
||||
value={title ?? ''}
|
||||
/>
|
||||
<PropertyBox>
|
||||
<PropertyBoxItem
|
||||
icon={<IconArrowUpRight />}
|
||||
value={
|
||||
<ActivityRelationPicker
|
||||
activity={{
|
||||
id: activity.id,
|
||||
activityTargets: activity.activityTargets ?? [],
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label="Relations"
|
||||
/>
|
||||
</PropertyBox>
|
||||
</StyledTopContainer>
|
||||
<ActivityBodyEditor
|
||||
activity={activity}
|
||||
onChange={updateTitleFromBody}
|
||||
/>
|
||||
</StyledUpperPartContainer>
|
||||
{showComment && (
|
||||
<ActivityComments
|
||||
activity={{
|
||||
id: activity.id,
|
||||
comments: activity.comments ?? [],
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@ -3,13 +3,13 @@ import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { GET_COMMENT_THREADS_BY_TARGETS } from '@/activities/queries';
|
||||
import { GET_ACTIVITIES_BY_TARGETS } from '@/activities/queries';
|
||||
import { GET_COMPANIES } from '@/companies/queries';
|
||||
import { GET_PEOPLE } from '@/people/queries';
|
||||
import { Button, ButtonVariant } from '@/ui/button/components/Button';
|
||||
import { IconTrash } from '@/ui/icon';
|
||||
import { isRightDrawerOpenState } from '@/ui/right-drawer/states/isRightDrawerOpenState';
|
||||
import { useDeleteCommentThreadMutation } from '~/generated/graphql';
|
||||
import { useDeleteActivityMutation } from '~/generated/graphql';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
@ -17,21 +17,21 @@ const StyledContainer = styled.div`
|
||||
`;
|
||||
|
||||
type OwnProps = {
|
||||
commentThreadId: string;
|
||||
activityId: string;
|
||||
};
|
||||
|
||||
export function CommentThreadActionBar({ commentThreadId }: OwnProps) {
|
||||
export function ActivityActionBar({ activityId }: OwnProps) {
|
||||
const theme = useTheme();
|
||||
const [createCommentMutation] = useDeleteCommentThreadMutation();
|
||||
const [createCommentMutation] = useDeleteActivityMutation();
|
||||
const [, setIsRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
|
||||
|
||||
function deleteCommentThread() {
|
||||
function deleteActivity() {
|
||||
createCommentMutation({
|
||||
variables: { commentThreadId },
|
||||
variables: { activityId },
|
||||
refetchQueries: [
|
||||
getOperationName(GET_COMPANIES) ?? '',
|
||||
getOperationName(GET_PEOPLE) ?? '',
|
||||
getOperationName(GET_COMMENT_THREADS_BY_TARGETS) ?? '',
|
||||
getOperationName(GET_ACTIVITIES_BY_TARGETS) ?? '',
|
||||
],
|
||||
});
|
||||
setIsRightDrawerOpen(false);
|
||||
@ -43,7 +43,7 @@ export function CommentThreadActionBar({ commentThreadId }: OwnProps) {
|
||||
icon={
|
||||
<IconTrash size={theme.icon.size.sm} stroke={theme.icon.stroke.md} />
|
||||
}
|
||||
onClick={deleteCommentThread}
|
||||
onClick={deleteActivity}
|
||||
variant={ButtonVariant.Tertiary}
|
||||
/>
|
||||
</StyledContainer>
|
||||
@ -1,34 +0,0 @@
|
||||
import { CommentThreadEditor } from '@/activities/components/CommentThreadEditor';
|
||||
import { useGetCommentThreadQuery } from '~/generated/graphql';
|
||||
|
||||
import '@blocknote/core/style.css';
|
||||
|
||||
type OwnProps = {
|
||||
commentThreadId: string;
|
||||
showComment?: boolean;
|
||||
autoFillTitle?: boolean;
|
||||
};
|
||||
|
||||
export function CommentThread({
|
||||
commentThreadId,
|
||||
showComment = true,
|
||||
autoFillTitle = false,
|
||||
}: OwnProps) {
|
||||
const { data } = useGetCommentThreadQuery({
|
||||
variables: {
|
||||
commentThreadId: commentThreadId ?? '',
|
||||
},
|
||||
skip: !commentThreadId,
|
||||
});
|
||||
const commentThread = data?.findManyCommentThreads[0];
|
||||
|
||||
return commentThread ? (
|
||||
<CommentThreadEditor
|
||||
commentThread={commentThread}
|
||||
showComment={showComment}
|
||||
autoFillTitle={autoFillTitle}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
}
|
||||
@ -1,22 +1,22 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { viewableCommentThreadIdState } from '@/activities/states/viewableCommentThreadIdState';
|
||||
import { viewableActivityIdState } from '@/activities/states/viewableActivityIdState';
|
||||
import { RightDrawerBody } from '@/ui/right-drawer/components/RightDrawerBody';
|
||||
import { RightDrawerPage } from '@/ui/right-drawer/components/RightDrawerPage';
|
||||
import { RightDrawerTopBar } from '@/ui/right-drawer/components/RightDrawerTopBar';
|
||||
|
||||
import { CommentThread } from '../CommentThread';
|
||||
import { Activity } from '../Activity';
|
||||
|
||||
export function RightDrawerCreateCommentThread() {
|
||||
const commentThreadId = useRecoilValue(viewableCommentThreadIdState);
|
||||
export function RightDrawerCreateActivity() {
|
||||
const activityId = useRecoilValue(viewableActivityIdState);
|
||||
|
||||
return (
|
||||
<RightDrawerPage>
|
||||
<RightDrawerTopBar />
|
||||
<RightDrawerBody>
|
||||
{commentThreadId && (
|
||||
<CommentThread
|
||||
commentThreadId={commentThreadId}
|
||||
{activityId && (
|
||||
<Activity
|
||||
activityId={activityId}
|
||||
showComment={false}
|
||||
autoFillTitle={true}
|
||||
/>
|
||||
@ -1,20 +1,20 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { viewableCommentThreadIdState } from '@/activities/states/viewableCommentThreadIdState';
|
||||
import { viewableActivityIdState } from '@/activities/states/viewableActivityIdState';
|
||||
import { RightDrawerBody } from '@/ui/right-drawer/components/RightDrawerBody';
|
||||
import { RightDrawerPage } from '@/ui/right-drawer/components/RightDrawerPage';
|
||||
import { RightDrawerTopBar } from '@/ui/right-drawer/components/RightDrawerTopBar';
|
||||
|
||||
import { CommentThread } from '../CommentThread';
|
||||
import { Activity } from '../Activity';
|
||||
|
||||
export function RightDrawerEditCommentThread() {
|
||||
const commentThreadId = useRecoilValue(viewableCommentThreadIdState);
|
||||
export function RightDrawerEditActivity() {
|
||||
const activityId = useRecoilValue(viewableActivityIdState);
|
||||
|
||||
return (
|
||||
<RightDrawerPage>
|
||||
<RightDrawerTopBar />
|
||||
<RightDrawerBody>
|
||||
{commentThreadId && <CommentThread commentThreadId={commentThreadId} />}
|
||||
{activityId && <Activity activityId={activityId} />}
|
||||
</RightDrawerBody>
|
||||
</RightDrawerPage>
|
||||
);
|
||||
@ -3,6 +3,6 @@ import { atom } from 'recoil';
|
||||
import { CommentableEntity } from '../types/CommentableEntity';
|
||||
|
||||
export const commentableEntityArrayState = atom<CommentableEntity[]>({
|
||||
key: 'comments/commentable-entity-array',
|
||||
key: 'activities/commentable-entity-array',
|
||||
default: [],
|
||||
});
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const viewableActivityIdState = atom<string | null>({
|
||||
key: 'activities/viewable-activity-id',
|
||||
default: null,
|
||||
});
|
||||
@ -1,6 +0,0 @@
|
||||
import { atom } from 'recoil';
|
||||
|
||||
export const viewableCommentThreadIdState = atom<string | null>({
|
||||
key: 'comments/viewable-comment-thread-id',
|
||||
default: null,
|
||||
});
|
||||
@ -2,16 +2,16 @@ import React from 'react';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { CommentThreadCreateButton } from '@/activities/components/CommentThreadCreateButton';
|
||||
import { useOpenCreateCommentThreadDrawer } from '@/activities/hooks/useOpenCreateCommentThreadDrawer';
|
||||
import { ActivityCreateButton } from '@/activities/components/ActivityCreateButton';
|
||||
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
|
||||
import { ActivityForDrawer } from '@/activities/types/ActivityForDrawer';
|
||||
import { CommentableEntity } from '@/activities/types/CommentableEntity';
|
||||
import { CommentThreadForDrawer } from '@/activities/types/CommentThreadForDrawer';
|
||||
import { useIsMobile } from '@/ui/hooks/useIsMobile';
|
||||
import { IconCircleDot } from '@/ui/icon';
|
||||
import {
|
||||
ActivityType,
|
||||
SortOrder,
|
||||
useGetCommentThreadsByTargetsQuery,
|
||||
useGetActivitiesByTargetsQuery,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import { TimelineActivity } from './TimelineActivity';
|
||||
@ -96,9 +96,9 @@ const StyledStartIcon = styled.div`
|
||||
export function Timeline({ entity }: { entity: CommentableEntity }) {
|
||||
const theme = useTheme();
|
||||
|
||||
const { data: queryResult, loading } = useGetCommentThreadsByTargetsQuery({
|
||||
const { data: queryResult, loading } = useGetActivitiesByTargetsQuery({
|
||||
variables: {
|
||||
commentThreadTargetIds: [entity.id],
|
||||
activityTargetIds: [entity.id],
|
||||
orderBy: [
|
||||
{
|
||||
createdAt: SortOrder.Desc,
|
||||
@ -107,21 +107,20 @@ export function Timeline({ entity }: { entity: CommentableEntity }) {
|
||||
},
|
||||
});
|
||||
|
||||
const openCreateCommandThread = useOpenCreateCommentThreadDrawer();
|
||||
const openCreateCommandThread = useOpenCreateActivityDrawer();
|
||||
|
||||
const commentThreads: CommentThreadForDrawer[] =
|
||||
queryResult?.findManyCommentThreads ?? [];
|
||||
const activities: ActivityForDrawer[] = queryResult?.findManyActivities ?? [];
|
||||
|
||||
if (loading) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
if (!commentThreads.length) {
|
||||
if (!activities.length) {
|
||||
return (
|
||||
<StyledTimelineEmptyContainer>
|
||||
<StyledEmptyTimelineTitle>No activity yet</StyledEmptyTimelineTitle>
|
||||
<StyledEmptyTimelineSubTitle>Create one:</StyledEmptyTimelineSubTitle>
|
||||
<CommentThreadCreateButton
|
||||
<ActivityCreateButton
|
||||
onNoteClick={() => openCreateCommandThread(entity, ActivityType.Note)}
|
||||
onTaskClick={() => openCreateCommandThread(entity, ActivityType.Task)}
|
||||
/>
|
||||
@ -132,17 +131,14 @@ export function Timeline({ entity }: { entity: CommentableEntity }) {
|
||||
return (
|
||||
<StyledMainContainer>
|
||||
<StyledTopActionBar>
|
||||
<CommentThreadCreateButton
|
||||
<ActivityCreateButton
|
||||
onNoteClick={() => openCreateCommandThread(entity, ActivityType.Note)}
|
||||
onTaskClick={() => openCreateCommandThread(entity, ActivityType.Task)}
|
||||
/>
|
||||
</StyledTopActionBar>
|
||||
<StyledTimelineContainer>
|
||||
{commentThreads.map((commentThread) => (
|
||||
<TimelineActivity
|
||||
key={commentThread.id}
|
||||
commentThread={commentThread}
|
||||
/>
|
||||
{activities.map((activity) => (
|
||||
<TimelineActivity key={activity.id} activity={activity} />
|
||||
))}
|
||||
<StyledStartIcon>
|
||||
<IconCircleDot size={theme.icon.size.lg} />
|
||||
|
||||
@ -3,14 +3,11 @@ import { Tooltip } from 'react-tooltip';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { useOpenCommentThreadRightDrawer } from '@/activities/hooks/useOpenCommentThreadRightDrawer';
|
||||
import { GET_COMMENT_THREADS_BY_TARGETS } from '@/activities/queries';
|
||||
import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
|
||||
import { GET_ACTIVITIES_BY_TARGETS } from '@/activities/queries';
|
||||
import { IconNotes } from '@/ui/icon';
|
||||
import { OverflowingTextWithTooltip } from '@/ui/tooltip/OverflowingTextWithTooltip';
|
||||
import {
|
||||
CommentThread,
|
||||
useUpdateCommentThreadMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { Activity, useUpdateActivityMutation } from '~/generated/graphql';
|
||||
import {
|
||||
beautifyExactDate,
|
||||
beautifyPastDateRelativeToNow,
|
||||
@ -115,35 +112,31 @@ const StyledTimelineItemContainer = styled.div`
|
||||
`;
|
||||
|
||||
type OwnProps = {
|
||||
commentThread: Pick<
|
||||
CommentThread,
|
||||
activity: Pick<
|
||||
Activity,
|
||||
'id' | 'title' | 'body' | 'createdAt' | 'completedAt' | 'type'
|
||||
> & { author: Pick<CommentThread['author'], 'displayName'> };
|
||||
> & { author: Pick<Activity['author'], 'displayName'> };
|
||||
};
|
||||
|
||||
export function TimelineActivity({ commentThread }: OwnProps) {
|
||||
const beautifiedCreatedAt = beautifyPastDateRelativeToNow(
|
||||
commentThread.createdAt,
|
||||
);
|
||||
const exactCreatedAt = beautifyExactDate(commentThread.createdAt);
|
||||
const body = JSON.parse(commentThread.body ?? '{}')[0]?.content[0]?.text;
|
||||
export function TimelineActivity({ activity }: OwnProps) {
|
||||
const beautifiedCreatedAt = beautifyPastDateRelativeToNow(activity.createdAt);
|
||||
const exactCreatedAt = beautifyExactDate(activity.createdAt);
|
||||
const body = JSON.parse(activity.body ?? '{}')[0]?.content[0]?.text;
|
||||
|
||||
const openCommentThreadRightDrawer = useOpenCommentThreadRightDrawer();
|
||||
const [updateCommentThreadMutation] = useUpdateCommentThreadMutation();
|
||||
const openActivityRightDrawer = useOpenActivityRightDrawer();
|
||||
const [updateActivityMutation] = useUpdateActivityMutation();
|
||||
|
||||
const handleActivityCompletionChange = useCallback(
|
||||
(value: boolean) => {
|
||||
updateCommentThreadMutation({
|
||||
updateActivityMutation({
|
||||
variables: {
|
||||
id: commentThread.id,
|
||||
id: activity.id,
|
||||
completedAt: value ? new Date().toISOString() : null,
|
||||
},
|
||||
refetchQueries: [
|
||||
getOperationName(GET_COMMENT_THREADS_BY_TARGETS) ?? '',
|
||||
],
|
||||
refetchQueries: [getOperationName(GET_ACTIVITIES_BY_TARGETS) ?? ''],
|
||||
});
|
||||
},
|
||||
[commentThread, updateCommentThreadMutation],
|
||||
[activity, updateActivityMutation],
|
||||
);
|
||||
|
||||
return (
|
||||
@ -153,14 +146,14 @@ export function TimelineActivity({ commentThread }: OwnProps) {
|
||||
<IconNotes />
|
||||
</StyledIconContainer>
|
||||
<StyledItemTitleContainer>
|
||||
<span>{commentThread.author.displayName}</span>
|
||||
created a {commentThread.type.toLowerCase()}
|
||||
<span>{activity.author.displayName}</span>
|
||||
created a note created a {activity.type.toLowerCase()}
|
||||
</StyledItemTitleContainer>
|
||||
<StyledItemTitleDate id={`id-${commentThread.id}`}>
|
||||
<StyledItemTitleDate id={`id-${activity.id}`}>
|
||||
{beautifiedCreatedAt} ago
|
||||
</StyledItemTitleDate>
|
||||
<StyledTooltip
|
||||
anchorSelect={`#id-${commentThread.id}`}
|
||||
anchorSelect={`#id-${activity.id}`}
|
||||
content={exactCreatedAt}
|
||||
clickable
|
||||
noArrow
|
||||
@ -171,13 +164,11 @@ export function TimelineActivity({ commentThread }: OwnProps) {
|
||||
<StyledVerticalLine></StyledVerticalLine>
|
||||
</StyledVerticalLineContainer>
|
||||
<StyledCardContainer>
|
||||
<StyledCard
|
||||
onClick={() => openCommentThreadRightDrawer(commentThread.id)}
|
||||
>
|
||||
<StyledCard onClick={() => openActivityRightDrawer(activity.id)}>
|
||||
<TimelineActivityTitle
|
||||
title={commentThread.title ?? ''}
|
||||
completed={!!commentThread.completedAt}
|
||||
type={commentThread.type}
|
||||
title={activity.title ?? ''}
|
||||
completed={!!activity.completedAt}
|
||||
type={activity.type}
|
||||
onCompletionChange={handleActivityCompletionChange}
|
||||
/>
|
||||
<StyledCardContent>
|
||||
|
||||
4
front/src/modules/activities/types/ActivityForDrawer.ts
Normal file
4
front/src/modules/activities/types/ActivityForDrawer.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { GetActivitiesByTargetsQuery } from '~/generated/graphql';
|
||||
|
||||
export type ActivityForDrawer =
|
||||
GetActivitiesByTargetsQuery['findManyActivities'][0];
|
||||
@ -1,5 +1,3 @@
|
||||
import { CommentThreadForDrawer } from './CommentThreadForDrawer';
|
||||
import { ActivityForDrawer } from './ActivityForDrawer';
|
||||
|
||||
export type CommentForDrawer = NonNullable<
|
||||
CommentThreadForDrawer['comments']
|
||||
>[0];
|
||||
export type CommentForDrawer = NonNullable<ActivityForDrawer['comments']>[0];
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
import { GetCommentThreadsByTargetsQuery } from '~/generated/graphql';
|
||||
|
||||
export type CommentThreadForDrawer =
|
||||
GetCommentThreadsByTargetsQuery['findManyCommentThreads'][0];
|
||||
@ -6,7 +6,7 @@ import { useRecoilState } from 'recoil';
|
||||
import { tokenPairState } from '@/auth/states/tokenPairState';
|
||||
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
|
||||
import { AppPath } from '@/types/AppPath';
|
||||
import { CommentThreadTarget } from '~/generated/graphql';
|
||||
import { ActivityTarget } from '~/generated/graphql';
|
||||
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
|
||||
import { useUpdateEffect } from '~/hooks/useUpdateEffect';
|
||||
|
||||
@ -25,12 +25,12 @@ export function useApolloFactory() {
|
||||
uri: `${process.env.REACT_APP_API_URL}`,
|
||||
cache: new InMemoryCache({
|
||||
typePolicies: {
|
||||
CommentThread: {
|
||||
Activity: {
|
||||
fields: {
|
||||
commentThreadTargets: {
|
||||
activityTargets: {
|
||||
merge(
|
||||
_existing: CommentThreadTarget[] = [],
|
||||
incoming: CommentThreadTarget[],
|
||||
_existing: ActivityTarget[] = [],
|
||||
incoming: ActivityTarget[],
|
||||
) {
|
||||
return [...incoming];
|
||||
},
|
||||
|
||||
@ -14,7 +14,7 @@ import { CompanyChip } from './CompanyChip';
|
||||
type OwnProps = {
|
||||
company: Pick<
|
||||
GetCompaniesQuery['companies'][0],
|
||||
'id' | 'name' | 'domainName' | '_commentThreadCount'
|
||||
'id' | 'name' | 'domainName' | '_activityCount'
|
||||
>;
|
||||
};
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ export const GET_COMPANIES = gql`
|
||||
address
|
||||
linkedinUrl
|
||||
employees
|
||||
_commentThreadCount
|
||||
_activityCount
|
||||
accountOwner {
|
||||
id
|
||||
email
|
||||
|
||||
@ -12,7 +12,7 @@ export const GET_COMPANY = gql`
|
||||
address
|
||||
linkedinUrl
|
||||
employees
|
||||
_commentThreadCount
|
||||
_activityCount
|
||||
accountOwner {
|
||||
id
|
||||
email
|
||||
|
||||
@ -25,7 +25,7 @@ export function EditableCompanyNameCell() {
|
||||
id: currentRowEntityId ?? '',
|
||||
name: name ?? '',
|
||||
domainName: domainName ?? '',
|
||||
_commentThreadCount: commentCount ?? 0,
|
||||
_activityCount: commentCount ?? 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds';
|
||||
import { TableActionBarButtonToggleComments } from '@/ui/table/action-bar/components/TableActionBarButtonOpenComments';
|
||||
import { CommentableType } from '~/generated/graphql';
|
||||
|
||||
export function TableActionBarButtonCreateActivityCompany() {
|
||||
const openCreateActivityRightDrawer =
|
||||
useOpenCreateActivityDrawerForSelectedRowIds();
|
||||
|
||||
async function handleButtonClick() {
|
||||
openCreateActivityRightDrawer(CommentableType.Company);
|
||||
}
|
||||
|
||||
return <TableActionBarButtonToggleComments onClick={handleButtonClick} />;
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
import { useOpenCreateCommentThreadDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateCommentDrawerForSelectedRowIds';
|
||||
import { TableActionBarButtonToggleComments } from '@/ui/table/action-bar/components/TableActionBarButtonOpenComments';
|
||||
import { CommentableType } from '~/generated/graphql';
|
||||
|
||||
export function TableActionBarButtonCreateCommentThreadCompany() {
|
||||
const openCreateCommentThreadRightDrawer =
|
||||
useOpenCreateCommentThreadDrawerForSelectedRowIds();
|
||||
|
||||
async function handleButtonClick() {
|
||||
openCreateCommentThreadRightDrawer(CommentableType.Company);
|
||||
}
|
||||
|
||||
return <TableActionBarButtonToggleComments onClick={handleButtonClick} />;
|
||||
}
|
||||
@ -10,7 +10,7 @@ type MockedCompany = Pick<
|
||||
| 'address'
|
||||
| 'employees'
|
||||
| 'linkedinUrl'
|
||||
| '_commentThreadCount'
|
||||
| '_activityCount'
|
||||
> & {
|
||||
accountOwner: Pick<
|
||||
User,
|
||||
@ -33,7 +33,7 @@ export const mockedCompaniesData: Array<MockedCompany> = [
|
||||
createdAt: '2023-04-26T10:08:54.724515+00:00',
|
||||
address: 'San Francisco, CA',
|
||||
employees: 5000,
|
||||
_commentThreadCount: 0,
|
||||
_activityCount: 0,
|
||||
accountOwner: {
|
||||
email: 'charles@test.com',
|
||||
displayName: 'Charles Test',
|
||||
@ -53,7 +53,7 @@ export const mockedCompaniesData: Array<MockedCompany> = [
|
||||
createdAt: '2023-04-26T10:12:42.33625+00:00',
|
||||
address: 'Paris, France',
|
||||
employees: 800,
|
||||
_commentThreadCount: 0,
|
||||
_activityCount: 0,
|
||||
accountOwner: null,
|
||||
__typename: 'Company',
|
||||
},
|
||||
@ -65,7 +65,7 @@ export const mockedCompaniesData: Array<MockedCompany> = [
|
||||
createdAt: '2023-04-26T10:10:32.530184+00:00',
|
||||
address: 'San Francisco, CA',
|
||||
employees: 8000,
|
||||
_commentThreadCount: 0,
|
||||
_activityCount: 0,
|
||||
accountOwner: null,
|
||||
__typename: 'Company',
|
||||
},
|
||||
@ -77,7 +77,7 @@ export const mockedCompaniesData: Array<MockedCompany> = [
|
||||
createdAt: '2023-03-21T06:30:25.39474+00:00',
|
||||
address: 'San Francisco, CA',
|
||||
employees: 800,
|
||||
_commentThreadCount: 0,
|
||||
_activityCount: 0,
|
||||
accountOwner: null,
|
||||
__typename: 'Company',
|
||||
},
|
||||
@ -89,7 +89,7 @@ export const mockedCompaniesData: Array<MockedCompany> = [
|
||||
createdAt: '2023-04-26T10:13:29.712485+00:00',
|
||||
address: 'San Francisco, CA',
|
||||
employees: 400,
|
||||
_commentThreadCount: 0,
|
||||
_activityCount: 0,
|
||||
accountOwner: null,
|
||||
__typename: 'Company',
|
||||
},
|
||||
|
||||
@ -83,10 +83,10 @@ export function useSetCompanyEntityTable() {
|
||||
.getLoadable(companyCommentCountFamilyState(company.id))
|
||||
.valueOrThrow();
|
||||
|
||||
if (currentCommentCount !== company._commentThreadCount) {
|
||||
if (currentCommentCount !== company._activityCount) {
|
||||
set(
|
||||
companyCommentCountFamilyState(company.id),
|
||||
company._commentThreadCount,
|
||||
company._activityCount,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -10,11 +10,7 @@ type OwnProps = {
|
||||
| Partial<
|
||||
Pick<
|
||||
Person,
|
||||
| 'id'
|
||||
| 'firstName'
|
||||
| 'lastName'
|
||||
| 'displayName'
|
||||
| '_commentThreadCount'
|
||||
'id' | 'firstName' | 'lastName' | 'displayName' | '_activityCount'
|
||||
>
|
||||
>
|
||||
| null
|
||||
|
||||
@ -101,12 +101,12 @@ export function useSetPeopleEntityTable() {
|
||||
if (
|
||||
currentNameCell.firstName !== person.firstName ||
|
||||
currentNameCell.lastName !== person.lastName ||
|
||||
currentNameCell.commentCount !== person._commentThreadCount
|
||||
currentNameCell.commentCount !== person._activityCount
|
||||
) {
|
||||
set(peopleNameCellFamilyState(person.id), {
|
||||
firstName: person.firstName ?? null,
|
||||
lastName: person.lastName ?? null,
|
||||
commentCount: person._commentThreadCount,
|
||||
commentCount: person._activityCount,
|
||||
displayName: person.displayName ?? null,
|
||||
});
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ export const GET_PEOPLE = gql`
|
||||
jobTitle
|
||||
linkedinUrl
|
||||
createdAt
|
||||
_commentThreadCount
|
||||
_activityCount
|
||||
company {
|
||||
id
|
||||
name
|
||||
@ -107,7 +107,7 @@ export const GET_PERSON_NAMES_AND_COMMENT_COUNT = gql`
|
||||
firstName
|
||||
lastName
|
||||
displayName
|
||||
_commentThreadCount
|
||||
_activityCount
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -129,7 +129,7 @@ export const GET_PERSON_COMMENT_COUNT = gql`
|
||||
query GetPersonCommentCountById($id: String!) {
|
||||
person: findUniquePerson(id: $id) {
|
||||
id
|
||||
_commentThreadCount
|
||||
_activityCount
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -15,7 +15,7 @@ export const GET_PERSON = gql`
|
||||
jobTitle
|
||||
linkedinUrl
|
||||
phone
|
||||
_commentThreadCount
|
||||
_activityCount
|
||||
company {
|
||||
id
|
||||
name
|
||||
|
||||
@ -21,7 +21,7 @@ export function EditablePeopleFullNameCell() {
|
||||
<EditablePeopleFullName
|
||||
person={{
|
||||
id: currentRowEntityId ?? undefined,
|
||||
_commentThreadCount: commentCount ?? undefined,
|
||||
_activityCount: commentCount ?? undefined,
|
||||
firstName,
|
||||
lastName,
|
||||
displayName: displayName ?? undefined,
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds';
|
||||
import { TableActionBarButtonToggleComments } from '@/ui/table/action-bar/components/TableActionBarButtonOpenComments';
|
||||
import { CommentableType } from '~/generated/graphql';
|
||||
|
||||
export function TableActionBarButtonCreateActivityPeople() {
|
||||
const openCreateActivityRightDrawer =
|
||||
useOpenCreateActivityDrawerForSelectedRowIds();
|
||||
|
||||
async function handleButtonClick() {
|
||||
openCreateActivityRightDrawer(CommentableType.Person);
|
||||
}
|
||||
|
||||
return <TableActionBarButtonToggleComments onClick={handleButtonClick} />;
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
import { useOpenCreateCommentThreadDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateCommentDrawerForSelectedRowIds';
|
||||
import { TableActionBarButtonToggleComments } from '@/ui/table/action-bar/components/TableActionBarButtonOpenComments';
|
||||
import { CommentableType } from '~/generated/graphql';
|
||||
|
||||
export function TableActionBarButtonCreateCommentThreadPeople() {
|
||||
const openCreateCommentThreadRightDrawer =
|
||||
useOpenCreateCommentThreadDrawerForSelectedRowIds();
|
||||
|
||||
async function handleButtonClick() {
|
||||
openCreateCommentThreadRightDrawer(CommentableType.Person);
|
||||
}
|
||||
|
||||
return <TableActionBarButtonToggleComments onClick={handleButtonClick} />;
|
||||
}
|
||||
144
front/src/modules/ui/button/components/DropdownButton.tsx
Normal file
144
front/src/modules/ui/button/components/DropdownButton.tsx
Normal file
@ -0,0 +1,144 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { IconChevronDown } from '@/ui/icon/index';
|
||||
|
||||
type ButtonProps = React.ComponentProps<'button'>;
|
||||
|
||||
export type DropdownOptionType = {
|
||||
key: string;
|
||||
label: string;
|
||||
icon: React.ReactNode;
|
||||
};
|
||||
|
||||
type OwnProps = {
|
||||
options: DropdownOptionType[];
|
||||
selectedOptionKey?: string;
|
||||
onSelection: (value: DropdownOptionType) => void;
|
||||
} & ButtonProps;
|
||||
|
||||
const StyledButton = styled.button<ButtonProps & { isOpen: boolean }>`
|
||||
align-items: center;
|
||||
background: ${({ theme }) => theme.background.tertiary};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
border-bottom-left-radius: ${({ isOpen, theme }) =>
|
||||
isOpen ? 0 : theme.border.radius.sm};
|
||||
border-bottom-right-radius: ${({ isOpen, theme }) =>
|
||||
isOpen ? 0 : theme.border.radius.sm};
|
||||
border-top-left-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
border-top-right-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding: ${({ theme }) => theme.spacing(1)} ${({ theme }) => theme.spacing(2)};
|
||||
|
||||
svg {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 14px;
|
||||
justify-content: center;
|
||||
width: 14px;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledDropdownItem = styled.button<ButtonProps>`
|
||||
align-items: center;
|
||||
background: ${({ theme }) => theme.background.tertiary};
|
||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
border-top: none;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
color: ${({ theme }) => theme.font.color.secondary};
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding: ${({ theme }) => theme.spacing(1)} ${({ theme }) => theme.spacing(2)};
|
||||
|
||||
svg {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 14px;
|
||||
justify-content: center;
|
||||
width: 14px;
|
||||
}
|
||||
`;
|
||||
|
||||
const DropdownContainer = styled.div`
|
||||
position: relative;
|
||||
`;
|
||||
|
||||
const DropdownMenu = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export function DropdownButton({
|
||||
options,
|
||||
selectedOptionKey,
|
||||
onSelection,
|
||||
...buttonProps
|
||||
}: OwnProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [selectedOption, setSelectedOption] = useState<
|
||||
DropdownOptionType | undefined
|
||||
>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedOptionKey) {
|
||||
const option = options.find((option) => option.key === selectedOptionKey);
|
||||
setSelectedOption(option);
|
||||
} else {
|
||||
setSelectedOption(options[0]);
|
||||
}
|
||||
}, [selectedOptionKey, options]);
|
||||
|
||||
if (!options.length) {
|
||||
throw new Error('You must provide at least one option.');
|
||||
}
|
||||
|
||||
const handleSelect =
|
||||
(option: DropdownOptionType) =>
|
||||
(event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault();
|
||||
onSelection(option);
|
||||
setSelectedOption(option);
|
||||
setIsOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{selectedOption && (
|
||||
<DropdownContainer>
|
||||
<StyledButton
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
{...buttonProps}
|
||||
isOpen={isOpen}
|
||||
>
|
||||
{selectedOption.icon}
|
||||
{selectedOption.label}
|
||||
{options.length > 1 && <IconChevronDown />}
|
||||
</StyledButton>
|
||||
{isOpen && (
|
||||
<DropdownMenu>
|
||||
{options
|
||||
.filter((option) => option.label !== selectedOption.label)
|
||||
.map((option, index) => (
|
||||
<StyledDropdownItem
|
||||
key={index}
|
||||
onClick={handleSelect(option)}
|
||||
>
|
||||
{option.icon}
|
||||
{option.label}
|
||||
</StyledDropdownItem>
|
||||
))}
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</DropdownContainer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { RightDrawerCreateCommentThread } from '@/activities/right-drawer/components/create/RightDrawerCreateCommentThread';
|
||||
import { RightDrawerEditCommentThread } from '@/activities/right-drawer/components/edit/RightDrawerEditCommentThread';
|
||||
import { RightDrawerCreateActivity } from '@/activities/right-drawer/components/create/RightDrawerCreateActivity';
|
||||
import { RightDrawerEditActivity } from '@/activities/right-drawer/components/edit/RightDrawerEditActivity';
|
||||
import { RightDrawerTimeline } from '@/activities/right-drawer/components/RightDrawerTimeline';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
@ -18,10 +18,10 @@ export function RightDrawerRouter() {
|
||||
switch (rightDrawerPage) {
|
||||
case RightDrawerPages.Timeline:
|
||||
return <RightDrawerTimeline />;
|
||||
case RightDrawerPages.CreateCommentThread:
|
||||
return <RightDrawerCreateCommentThread />;
|
||||
case RightDrawerPages.EditCommentThread:
|
||||
return <RightDrawerEditCommentThread />;
|
||||
case RightDrawerPages.CreateActivity:
|
||||
return <RightDrawerCreateActivity />;
|
||||
case RightDrawerPages.EditActivity:
|
||||
return <RightDrawerEditActivity />;
|
||||
default:
|
||||
return <></>;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export enum RightDrawerPages {
|
||||
Timeline = 'timeline',
|
||||
CreateCommentThread = 'create-comment-thread',
|
||||
EditCommentThread = 'edit-comment-thread',
|
||||
CreateActivity = 'create-activity',
|
||||
EditActivity = 'edit-activity',
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ export type EditableChipProps = {
|
||||
value: string;
|
||||
editModeHorizontalAlign?: 'left' | 'right';
|
||||
ChipComponent: React.ReactNode;
|
||||
commentThreadCount?: number;
|
||||
activityCount?: number;
|
||||
onCommentClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
|
||||
rightEndContents?: ReactNode[];
|
||||
onSubmit?: (newValue: string) => void;
|
||||
|
||||
@ -4,7 +4,7 @@ import styled from '@emotion/styled';
|
||||
|
||||
import { GET_COMPANIES } from '@/companies/queries';
|
||||
import { CompanyTable } from '@/companies/table/components/CompanyTable';
|
||||
import { TableActionBarButtonCreateCommentThreadCompany } from '@/companies/table/components/TableActionBarButtonCreateCommentThreadCompany';
|
||||
import { TableActionBarButtonCreateActivityCompany } from '@/companies/table/components/TableActionBarButtonCreateActivityCompany';
|
||||
import { TableActionBarButtonDeleteCompanies } from '@/companies/table/components/TableActionBarButtonDeleteCompanies';
|
||||
import { IconBuildingSkyscraper } from '@/ui/icon';
|
||||
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
|
||||
@ -53,7 +53,7 @@ export function Companies() {
|
||||
<CompanyTable />
|
||||
</StyledTableContainer>
|
||||
<EntityTableActionBar>
|
||||
<TableActionBarButtonCreateCommentThreadCompany />
|
||||
<TableActionBarButtonCreateActivityCompany />
|
||||
<TableActionBarButtonDeleteCompanies />
|
||||
</EntityTableActionBar>
|
||||
</RecoilScope>
|
||||
|
||||
@ -2,7 +2,7 @@ import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { CompanyTableMockMode } from '@/companies/table/components/CompanyTableMockMode';
|
||||
import { TableActionBarButtonCreateCommentThreadCompany } from '@/companies/table/components/TableActionBarButtonCreateCommentThreadCompany';
|
||||
import { TableActionBarButtonCreateActivityCompany } from '@/companies/table/components/TableActionBarButtonCreateActivityCompany';
|
||||
import { TableActionBarButtonDeleteCompanies } from '@/companies/table/components/TableActionBarButtonDeleteCompanies';
|
||||
import { IconBuildingSkyscraper } from '@/ui/icon';
|
||||
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
|
||||
@ -29,7 +29,7 @@ export function CompaniesMockMode() {
|
||||
<CompanyTableMockMode />
|
||||
</StyledTableContainer>
|
||||
<EntityTableActionBar>
|
||||
<TableActionBarButtonCreateCommentThreadCompany />
|
||||
<TableActionBarButtonCreateActivityCompany />
|
||||
<TableActionBarButtonDeleteCompanies />
|
||||
</EntityTableActionBar>
|
||||
</RecoilScope>
|
||||
|
||||
@ -4,18 +4,15 @@ import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { fireEvent, within } from '@storybook/testing-library';
|
||||
import { graphql } from 'msw';
|
||||
|
||||
import {
|
||||
GET_COMMENT_THREAD,
|
||||
GET_COMMENT_THREADS_BY_TARGETS,
|
||||
} from '@/activities/queries';
|
||||
import { CREATE_COMMENT_THREAD_WITH_COMMENT } from '@/activities/queries/create';
|
||||
import { GET_ACTIVITIES_BY_TARGETS, GET_ACTIVITY } from '@/activities/queries';
|
||||
import { CREATE_ACTIVITY_WITH_COMMENT } from '@/activities/queries/create';
|
||||
import { GET_COMPANY } from '@/companies/queries';
|
||||
import {
|
||||
PageDecorator,
|
||||
type PageDecoratorArgs,
|
||||
} from '~/testing/decorators/PageDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { mockedCommentThreads } from '~/testing/mock-data/comment-threads';
|
||||
import { mockedActivities } from '~/testing/mock-data/activities';
|
||||
import { mockedCompaniesData } from '~/testing/mock-data/companies';
|
||||
|
||||
import { CompanyShow } from '../CompanyShow';
|
||||
@ -30,11 +27,11 @@ const meta: Meta<PageDecoratorArgs> = {
|
||||
msw: [
|
||||
...graphqlMocks,
|
||||
graphql.query(
|
||||
getOperationName(GET_COMMENT_THREADS_BY_TARGETS) ?? '',
|
||||
getOperationName(GET_ACTIVITIES_BY_TARGETS) ?? '',
|
||||
(req, res, ctx) => {
|
||||
return res(
|
||||
ctx.data({
|
||||
findManyCommentThreads: mockedCommentThreads,
|
||||
findManyActivities: mockedActivities,
|
||||
}),
|
||||
);
|
||||
},
|
||||
@ -82,25 +79,22 @@ export const EditNote: Story = {
|
||||
msw: [
|
||||
...meta.parameters?.msw,
|
||||
graphql.mutation(
|
||||
getOperationName(CREATE_COMMENT_THREAD_WITH_COMMENT) ?? '',
|
||||
getOperationName(CREATE_ACTIVITY_WITH_COMMENT) ?? '',
|
||||
(req, res, ctx) => {
|
||||
return res(
|
||||
ctx.data({
|
||||
createOneCommentThread: mockedCommentThreads[0],
|
||||
}),
|
||||
);
|
||||
},
|
||||
),
|
||||
graphql.query(
|
||||
getOperationName(GET_COMMENT_THREAD) ?? '',
|
||||
(req, res, ctx) => {
|
||||
return res(
|
||||
ctx.data({
|
||||
findManyCommentThreads: [mockedCommentThreads[0]],
|
||||
createOneActivity: mockedActivities[0],
|
||||
}),
|
||||
);
|
||||
},
|
||||
),
|
||||
graphql.query(getOperationName(GET_ACTIVITY) ?? '', (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.data({
|
||||
findManyActivitys: [mockedActivities[0]],
|
||||
}),
|
||||
);
|
||||
}),
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@ -4,7 +4,7 @@ import styled from '@emotion/styled';
|
||||
|
||||
import { GET_PEOPLE } from '@/people/queries';
|
||||
import { PeopleTable } from '@/people/table/components/PeopleTable';
|
||||
import { TableActionBarButtonCreateCommentThreadPeople } from '@/people/table/components/TableActionBarButtonCreateCommentThreadPeople';
|
||||
import { TableActionBarButtonCreateActivityPeople } from '@/people/table/components/TableActionBarButtonCreateActivityPeople';
|
||||
import { TableActionBarButtonDeletePeople } from '@/people/table/components/TableActionBarButtonDeletePeople';
|
||||
import { IconUser } from '@/ui/icon';
|
||||
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
|
||||
@ -46,7 +46,7 @@ export function People() {
|
||||
<PeopleTable />
|
||||
</StyledTableContainer>
|
||||
<EntityTableActionBar>
|
||||
<TableActionBarButtonCreateCommentThreadPeople />
|
||||
<TableActionBarButtonCreateActivityPeople />
|
||||
<TableActionBarButtonDeletePeople />
|
||||
</EntityTableActionBar>
|
||||
</WithTopBarContainer>
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import {
|
||||
Activity,
|
||||
ActivityTarget,
|
||||
ActivityType,
|
||||
Comment,
|
||||
CommentableType,
|
||||
CommentThread,
|
||||
CommentThreadTarget,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
type MockedCommentThread = Pick<
|
||||
CommentThread,
|
||||
type MockedActivity = Pick<
|
||||
Activity,
|
||||
| 'id'
|
||||
| 'createdAt'
|
||||
| 'updatedAt'
|
||||
@ -25,21 +25,21 @@ type MockedCommentThread = Pick<
|
||||
displayName: string;
|
||||
};
|
||||
comments: Array<Pick<Comment, 'body'>>;
|
||||
commentThreadTargets: Array<
|
||||
activityTargets: Array<
|
||||
Pick<
|
||||
CommentThreadTarget,
|
||||
ActivityTarget,
|
||||
| 'id'
|
||||
| '__typename'
|
||||
| 'createdAt'
|
||||
| 'updatedAt'
|
||||
| 'commentableType'
|
||||
| 'activityId'
|
||||
| 'commentableId'
|
||||
| 'commentThreadId'
|
||||
> & { commentThread: Pick<CommentThread, 'id' | 'createdAt' | 'updatedAt'> }
|
||||
| 'commentableType'
|
||||
> & { activity: Pick<Activity, 'id' | 'createdAt' | 'updatedAt'> }
|
||||
>;
|
||||
};
|
||||
|
||||
export const mockedCommentThreads: Array<MockedCommentThread> = [
|
||||
export const mockedActivities: Array<MockedActivity> = [
|
||||
{
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb230',
|
||||
createdAt: '2023-04-26T10:12:42.33625+00:00',
|
||||
@ -55,20 +55,20 @@ export const mockedCommentThreads: Array<MockedCommentThread> = [
|
||||
},
|
||||
authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
|
||||
comments: [],
|
||||
commentThreadTargets: [
|
||||
activityTargets: [
|
||||
{
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb300',
|
||||
createdAt: '2023-04-26T10:12:42.33625+00:00',
|
||||
updatedAt: '2023-04-26T10:23:42.33625+00:00',
|
||||
commentableType: CommentableType.Company,
|
||||
commentableId: '89bb825c-171e-4bcc-9cf7-43448d6fb278', // airbnb
|
||||
commentThreadId: '89bb825c-171e-4bcc-9cf7-43448d6fb230',
|
||||
commentThread: {
|
||||
activityId: '89bb825c-171e-4bcc-9cf7-43448d6fb230',
|
||||
activity: {
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb230',
|
||||
createdAt: '2023-04-26T10:12:42.33625+00:00',
|
||||
updatedAt: '2023-04-26T10:23:42.33625+00:00',
|
||||
},
|
||||
__typename: 'CommentThreadTarget',
|
||||
__typename: 'ActivityTarget',
|
||||
},
|
||||
{
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb301',
|
||||
@ -76,16 +76,16 @@ export const mockedCommentThreads: Array<MockedCommentThread> = [
|
||||
updatedAt: new Date().toISOString(),
|
||||
commentableType: CommentableType.Company,
|
||||
commentableId: 'b396e6b9-dc5c-4643-bcff-61b6cf7523ae', // aircall
|
||||
commentThreadId: '89bb825c-171e-4bcc-9cf7-43448d6fb231',
|
||||
commentThread: {
|
||||
activityId: '89bb825c-171e-4bcc-9cf7-43448d6fb231',
|
||||
activity: {
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb231',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
__typename: 'CommentThreadTarget',
|
||||
__typename: 'ActivityTarget',
|
||||
},
|
||||
],
|
||||
__typename: 'CommentThread',
|
||||
__typename: 'Activity',
|
||||
},
|
||||
{
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
|
||||
@ -102,20 +102,20 @@ export const mockedCommentThreads: Array<MockedCommentThread> = [
|
||||
},
|
||||
authorId: '374fe3a5-df1e-4119-afe0-2a62a2ba481e',
|
||||
comments: [],
|
||||
commentThreadTargets: [
|
||||
activityTargets: [
|
||||
{
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
|
||||
createdAt: '2023-04-26T10:12:42.33625+00:00',
|
||||
updatedAt: '2023-04-26T10:23:42.33625+00:00',
|
||||
commentableType: CommentableType.Person,
|
||||
commentableId: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b', // Alexandre
|
||||
commentThreadId: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
|
||||
commentThread: {
|
||||
activityId: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
|
||||
activity: {
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
|
||||
createdAt: '2023-04-26T10:12:42.33625+00:00',
|
||||
updatedAt: '2023-04-26T10:23:42.33625+00:00',
|
||||
},
|
||||
__typename: 'CommentThreadTarget',
|
||||
__typename: 'ActivityTarget',
|
||||
},
|
||||
{
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
|
||||
@ -123,15 +123,15 @@ export const mockedCommentThreads: Array<MockedCommentThread> = [
|
||||
updatedAt: new Date().toISOString(),
|
||||
commentableType: CommentableType.Person,
|
||||
commentableId: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6d', // Jean d'Eau
|
||||
commentThreadId: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
|
||||
commentThread: {
|
||||
activityId: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
|
||||
activity: {
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
__typename: 'CommentThreadTarget',
|
||||
__typename: 'ActivityTarget',
|
||||
},
|
||||
],
|
||||
__typename: 'CommentThread',
|
||||
__typename: 'Activity',
|
||||
},
|
||||
];
|
||||
@ -10,7 +10,7 @@ type MockedCompany = Pick<
|
||||
| 'address'
|
||||
| 'employees'
|
||||
| 'linkedinUrl'
|
||||
| '_commentThreadCount'
|
||||
| '_activityCount'
|
||||
> & {
|
||||
accountOwner: Pick<
|
||||
User,
|
||||
@ -33,7 +33,7 @@ export const mockedCompaniesData: Array<MockedCompany> = [
|
||||
address: '17 rue de clignancourt',
|
||||
employees: 12,
|
||||
linkedinUrl: 'https://www.linkedin.com/company/airbnb/',
|
||||
_commentThreadCount: 1,
|
||||
_activityCount: 1,
|
||||
accountOwner: {
|
||||
email: 'charles@test.com',
|
||||
displayName: 'Charles Test',
|
||||
@ -53,7 +53,7 @@ export const mockedCompaniesData: Array<MockedCompany> = [
|
||||
address: '',
|
||||
employees: 1,
|
||||
linkedinUrl: 'https://www.linkedin.com/company/aircall/',
|
||||
_commentThreadCount: 1,
|
||||
_activityCount: 1,
|
||||
accountOwner: null,
|
||||
__typename: 'Company',
|
||||
},
|
||||
@ -65,7 +65,7 @@ export const mockedCompaniesData: Array<MockedCompany> = [
|
||||
address: '',
|
||||
employees: 1,
|
||||
linkedinUrl: 'https://www.linkedin.com/company/algolia/',
|
||||
_commentThreadCount: 1,
|
||||
_activityCount: 1,
|
||||
accountOwner: null,
|
||||
__typename: 'Company',
|
||||
},
|
||||
@ -77,7 +77,7 @@ export const mockedCompaniesData: Array<MockedCompany> = [
|
||||
address: '',
|
||||
employees: 10,
|
||||
linkedinUrl: 'https://www.linkedin.com/company/apple/',
|
||||
_commentThreadCount: 0,
|
||||
_activityCount: 0,
|
||||
accountOwner: null,
|
||||
__typename: 'Company',
|
||||
},
|
||||
@ -89,7 +89,7 @@ export const mockedCompaniesData: Array<MockedCompany> = [
|
||||
address: '10 rue de la Paix',
|
||||
employees: 1,
|
||||
linkedinUrl: 'https://www.linkedin.com/company/qonto/',
|
||||
_commentThreadCount: 2,
|
||||
_activityCount: 2,
|
||||
accountOwner: null,
|
||||
__typename: 'Company',
|
||||
},
|
||||
@ -101,7 +101,7 @@ export const mockedCompaniesData: Array<MockedCompany> = [
|
||||
address: '',
|
||||
employees: 1,
|
||||
linkedinUrl: 'https://www.linkedin.com/company/facebook/',
|
||||
_commentThreadCount: 13,
|
||||
_activityCount: 13,
|
||||
accountOwner: null,
|
||||
__typename: 'Company',
|
||||
},
|
||||
@ -113,7 +113,7 @@ export const mockedCompaniesData: Array<MockedCompany> = [
|
||||
address: '',
|
||||
employees: 1,
|
||||
linkedinUrl: 'https://www.linkedin.com/company/sequoia/',
|
||||
_commentThreadCount: 1,
|
||||
_activityCount: 1,
|
||||
accountOwner: null,
|
||||
__typename: 'Company',
|
||||
},
|
||||
|
||||
@ -17,7 +17,7 @@ type MockedPerson = RequiredAndNotNull<
|
||||
| '__typename'
|
||||
| 'phone'
|
||||
| 'city'
|
||||
| '_commentThreadCount'
|
||||
| '_activityCount'
|
||||
| 'createdAt'
|
||||
> & {
|
||||
company: Pick<Company, 'id' | 'name' | 'domainName' | '__typename'>;
|
||||
@ -41,7 +41,7 @@ export const mockedPeopleData: MockedPerson[] = [
|
||||
__typename: 'Company',
|
||||
},
|
||||
phone: '06 12 34 56 78',
|
||||
_commentThreadCount: 1,
|
||||
_activityCount: 1,
|
||||
createdAt: '2023-04-20T13:20:09.158312+00:00',
|
||||
|
||||
city: 'Paris',
|
||||
@ -62,7 +62,7 @@ export const mockedPeopleData: MockedPerson[] = [
|
||||
__typename: 'Company',
|
||||
},
|
||||
phone: '06 12 34 56 78',
|
||||
_commentThreadCount: 1,
|
||||
_activityCount: 1,
|
||||
createdAt: '2023-04-20T13:20:09.158312+00:00',
|
||||
|
||||
city: 'Paris',
|
||||
@ -83,7 +83,7 @@ export const mockedPeopleData: MockedPerson[] = [
|
||||
__typename: 'Company',
|
||||
},
|
||||
phone: '06 12 34 56 78',
|
||||
_commentThreadCount: 1,
|
||||
_activityCount: 1,
|
||||
createdAt: '2023-04-20T13:20:09.158312+00:00',
|
||||
|
||||
city: 'Paris',
|
||||
@ -104,7 +104,7 @@ export const mockedPeopleData: MockedPerson[] = [
|
||||
__typename: 'Company',
|
||||
},
|
||||
phone: '06 12 34 56 78',
|
||||
_commentThreadCount: 2,
|
||||
_activityCount: 2,
|
||||
createdAt: '2023-04-20T13:20:09.158312+00:00',
|
||||
|
||||
city: 'Paris',
|
||||
|
||||
@ -3,7 +3,8 @@ import { Injectable } from '@nestjs/common';
|
||||
import { PureAbility, AbilityBuilder } from '@casl/ability';
|
||||
import { createPrismaAbility, PrismaQuery, Subjects } from '@casl/prisma';
|
||||
import {
|
||||
CommentThread,
|
||||
Attachment,
|
||||
Activity,
|
||||
Company,
|
||||
Comment,
|
||||
Person,
|
||||
@ -11,11 +12,10 @@ import {
|
||||
User,
|
||||
Workspace,
|
||||
WorkspaceMember,
|
||||
CommentThreadTarget,
|
||||
ActivityTarget,
|
||||
Pipeline,
|
||||
PipelineStage,
|
||||
PipelineProgress,
|
||||
Attachment,
|
||||
UserSettings,
|
||||
ViewField,
|
||||
} from '@prisma/client';
|
||||
@ -29,9 +29,9 @@ type SubjectsAbility = Subjects<{
|
||||
Company: Company;
|
||||
Person: Person;
|
||||
RefreshToken: RefreshToken;
|
||||
CommentThread: CommentThread;
|
||||
Activity: Activity;
|
||||
Comment: Comment;
|
||||
CommentThreadTarget: CommentThreadTarget;
|
||||
ActivityTarget: ActivityTarget;
|
||||
Pipeline: Pipeline;
|
||||
PipelineStage: PipelineStage;
|
||||
PipelineProgress: PipelineProgress;
|
||||
@ -86,11 +86,11 @@ export class AbilityFactory {
|
||||
// RefreshToken
|
||||
cannot(AbilityAction.Manage, 'RefreshToken');
|
||||
|
||||
// CommentThread
|
||||
can(AbilityAction.Read, 'CommentThread', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Create, 'CommentThread');
|
||||
can(AbilityAction.Update, 'CommentThread', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Delete, 'CommentThread', { workspaceId: workspace.id });
|
||||
// Activity
|
||||
can(AbilityAction.Read, 'Activity', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Create, 'Activity');
|
||||
can(AbilityAction.Update, 'Activity', { workspaceId: workspace.id });
|
||||
can(AbilityAction.Delete, 'Activity', { workspaceId: workspace.id });
|
||||
|
||||
// Comment
|
||||
can(AbilityAction.Read, 'Comment', { workspaceId: workspace.id });
|
||||
@ -104,9 +104,9 @@ export class AbilityFactory {
|
||||
authorId: user.id,
|
||||
});
|
||||
|
||||
// CommentThreadTarget
|
||||
can(AbilityAction.Read, 'CommentThreadTarget');
|
||||
can(AbilityAction.Create, 'CommentThreadTarget');
|
||||
// ActivityTarget
|
||||
can(AbilityAction.Read, 'ActivityTarget');
|
||||
can(AbilityAction.Create, 'ActivityTarget');
|
||||
|
||||
// Attachment
|
||||
can(AbilityAction.Read, 'Attachment', { workspaceId: workspace.id });
|
||||
|
||||
@ -46,12 +46,12 @@ import {
|
||||
DeleteRefreshTokenAbilityHandler,
|
||||
} from './handlers/refresh-token.ability-handler';
|
||||
import {
|
||||
ManageCommentThreadAbilityHandler,
|
||||
ReadCommentThreadAbilityHandler,
|
||||
CreateCommentThreadAbilityHandler,
|
||||
UpdateCommentThreadAbilityHandler,
|
||||
DeleteCommentThreadAbilityHandler,
|
||||
} from './handlers/comment-thread.ability-handler';
|
||||
ManageActivityAbilityHandler,
|
||||
ReadActivityAbilityHandler,
|
||||
CreateActivityAbilityHandler,
|
||||
UpdateActivityAbilityHandler,
|
||||
DeleteActivityAbilityHandler,
|
||||
} from './handlers/activity.ability-handler';
|
||||
import {
|
||||
ManageCommentAbilityHandler,
|
||||
ReadCommentAbilityHandler,
|
||||
@ -60,12 +60,12 @@ import {
|
||||
DeleteCommentAbilityHandler,
|
||||
} from './handlers/comment.ability-handler';
|
||||
import {
|
||||
ManageCommentThreadTargetAbilityHandler,
|
||||
ReadCommentThreadTargetAbilityHandler,
|
||||
CreateCommentThreadTargetAbilityHandler,
|
||||
UpdateCommentThreadTargetAbilityHandler,
|
||||
DeleteCommentThreadTargetAbilityHandler,
|
||||
} from './handlers/comment-thread-target.ability-handler';
|
||||
ManageActivityTargetAbilityHandler,
|
||||
ReadActivityTargetAbilityHandler,
|
||||
CreateActivityTargetAbilityHandler,
|
||||
UpdateActivityTargetAbilityHandler,
|
||||
DeleteActivityTargetAbilityHandler,
|
||||
} from './handlers/activity-target.ability-handler';
|
||||
import {
|
||||
ManagePipelineAbilityHandler,
|
||||
ReadPipelineAbilityHandler,
|
||||
@ -140,24 +140,24 @@ import {
|
||||
CreateRefreshTokenAbilityHandler,
|
||||
UpdateRefreshTokenAbilityHandler,
|
||||
DeleteRefreshTokenAbilityHandler,
|
||||
// CommentThread
|
||||
ManageCommentThreadAbilityHandler,
|
||||
ReadCommentThreadAbilityHandler,
|
||||
CreateCommentThreadAbilityHandler,
|
||||
UpdateCommentThreadAbilityHandler,
|
||||
DeleteCommentThreadAbilityHandler,
|
||||
// Activity
|
||||
ManageActivityAbilityHandler,
|
||||
ReadActivityAbilityHandler,
|
||||
CreateActivityAbilityHandler,
|
||||
UpdateActivityAbilityHandler,
|
||||
DeleteActivityAbilityHandler,
|
||||
// Comment
|
||||
ManageCommentAbilityHandler,
|
||||
ReadCommentAbilityHandler,
|
||||
CreateCommentAbilityHandler,
|
||||
UpdateCommentAbilityHandler,
|
||||
DeleteCommentAbilityHandler,
|
||||
// CommentThreadTarget
|
||||
ManageCommentThreadTargetAbilityHandler,
|
||||
ReadCommentThreadTargetAbilityHandler,
|
||||
CreateCommentThreadTargetAbilityHandler,
|
||||
UpdateCommentThreadTargetAbilityHandler,
|
||||
DeleteCommentThreadTargetAbilityHandler,
|
||||
// ActivityTarget
|
||||
ManageActivityTargetAbilityHandler,
|
||||
ReadActivityTargetAbilityHandler,
|
||||
CreateActivityTargetAbilityHandler,
|
||||
UpdateActivityTargetAbilityHandler,
|
||||
DeleteActivityTargetAbilityHandler,
|
||||
//Attachment
|
||||
ManageAttachmentAbilityHandler,
|
||||
ReadAttachmentAbilityHandler,
|
||||
@ -224,24 +224,24 @@ import {
|
||||
CreateRefreshTokenAbilityHandler,
|
||||
UpdateRefreshTokenAbilityHandler,
|
||||
DeleteRefreshTokenAbilityHandler,
|
||||
// CommentThread
|
||||
ManageCommentThreadAbilityHandler,
|
||||
ReadCommentThreadAbilityHandler,
|
||||
CreateCommentThreadAbilityHandler,
|
||||
UpdateCommentThreadAbilityHandler,
|
||||
DeleteCommentThreadAbilityHandler,
|
||||
// Activity
|
||||
ManageActivityAbilityHandler,
|
||||
ReadActivityAbilityHandler,
|
||||
CreateActivityAbilityHandler,
|
||||
UpdateActivityAbilityHandler,
|
||||
DeleteActivityAbilityHandler,
|
||||
// Comment
|
||||
ManageCommentAbilityHandler,
|
||||
ReadCommentAbilityHandler,
|
||||
CreateCommentAbilityHandler,
|
||||
UpdateCommentAbilityHandler,
|
||||
DeleteCommentAbilityHandler,
|
||||
// CommentThreadTarget
|
||||
ManageCommentThreadTargetAbilityHandler,
|
||||
ReadCommentThreadTargetAbilityHandler,
|
||||
CreateCommentThreadTargetAbilityHandler,
|
||||
UpdateCommentThreadTargetAbilityHandler,
|
||||
DeleteCommentThreadTargetAbilityHandler,
|
||||
// ActivityTarget
|
||||
ManageActivityTargetAbilityHandler,
|
||||
ReadActivityTargetAbilityHandler,
|
||||
CreateActivityTargetAbilityHandler,
|
||||
UpdateActivityTargetAbilityHandler,
|
||||
DeleteActivityTargetAbilityHandler,
|
||||
//Attachment
|
||||
ManageAttachmentAbilityHandler,
|
||||
ReadAttachmentAbilityHandler,
|
||||
|
||||
@ -0,0 +1,79 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { ActivityTargetWhereInput } from 'src/core/@generated/activity-target/activity-target-where.input';
|
||||
|
||||
class ActivityTargetArgs {
|
||||
where?: ActivityTargetWhereInput;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManageActivityTargetAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'ActivityTarget');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadActivityTargetAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'ActivityTarget');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateActivityTargetAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Create, 'ActivityTarget');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdateActivityTargetAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<ActivityTargetArgs>();
|
||||
const ActivityTarget = await this.prismaService.client.activityTarget.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(ActivityTarget, '', NotFoundException);
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Update,
|
||||
subject('ActivityTarget', ActivityTarget),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeleteActivityTargetAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<ActivityTargetArgs>();
|
||||
const ActivityTarget = await this.prismaService.client.activityTarget.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(ActivityTarget, '', NotFoundException);
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Delete,
|
||||
subject('ActivityTarget', ActivityTarget),
|
||||
);
|
||||
}
|
||||
}
|
||||
73
server/src/ability/handlers/activity.ability-handler.ts
Normal file
73
server/src/ability/handlers/activity.ability-handler.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { ActivityWhereInput } from 'src/core/@generated/activity/activity-where.input';
|
||||
|
||||
class ActivityArgs {
|
||||
where?: ActivityWhereInput;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManageActivityAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'Activity');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadActivityAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'Activity');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateActivityAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Create, 'Activity');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdateActivityAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<ActivityArgs>();
|
||||
const Activity = await this.prismaService.client.activity.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(Activity, '', NotFoundException);
|
||||
|
||||
return ability.can(AbilityAction.Update, subject('Activity', Activity));
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeleteActivityAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<ActivityArgs>();
|
||||
const Activity = await this.prismaService.client.activity.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(Activity, '', NotFoundException);
|
||||
|
||||
return ability.can(AbilityAction.Delete, subject('Activity', Activity));
|
||||
}
|
||||
}
|
||||
@ -42,16 +42,12 @@ export class CreateAttachmentAbilityHandler implements IAbilityHandler {
|
||||
const args = gqlContext.getArgs<AttachmentArgs>();
|
||||
assert(args.activityId, '', ForbiddenException);
|
||||
|
||||
const activity = await this.prismaService.client.commentThread.findUnique({
|
||||
const activity = await this.prismaService.client.activity.findUnique({
|
||||
where: { id: args.activityId },
|
||||
include: { workspace: true },
|
||||
});
|
||||
assert(activity, '', NotFoundException);
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Update,
|
||||
subject('Workspace', activity.workspace),
|
||||
);
|
||||
return ability.can(AbilityAction.Update, subject('Activity', activity));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,118 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { CommentThreadTargetWhereInput } from 'src/core/@generated/comment-thread-target/comment-thread-target-where.input';
|
||||
import { relationAbilityChecker } from 'src/ability/ability.util';
|
||||
import { assert } from 'src/utils/assert';
|
||||
|
||||
class CommentThreadTargetArgs {
|
||||
where?: CommentThreadTargetWhereInput;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManageCommentThreadTargetAbilityHandler
|
||||
implements IAbilityHandler
|
||||
{
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'CommentThreadTarget');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadCommentThreadTargetAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'CommentThreadTarget');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateCommentThreadTargetAbilityHandler
|
||||
implements IAbilityHandler
|
||||
{
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'CommentThreadTarget',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Create, 'CommentThreadTarget');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdateCommentThreadTargetAbilityHandler
|
||||
implements IAbilityHandler
|
||||
{
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<CommentThreadTargetArgs>();
|
||||
const commentThreadTarget =
|
||||
await this.prismaService.client.commentThreadTarget.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(commentThreadTarget, '', NotFoundException);
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'CommentThreadTarget',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Update,
|
||||
subject('CommentThreadTarget', commentThreadTarget),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeleteCommentThreadTargetAbilityHandler
|
||||
implements IAbilityHandler
|
||||
{
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<CommentThreadTargetArgs>();
|
||||
const commentThreadTarget =
|
||||
await this.prismaService.client.commentThreadTarget.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(commentThreadTarget, '', NotFoundException);
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Delete,
|
||||
subject('CommentThreadTarget', commentThreadTarget),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,110 +0,0 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
|
||||
import { subject } from '@casl/ability';
|
||||
|
||||
import { IAbilityHandler } from 'src/ability/interfaces/ability-handler.interface';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { AbilityAction } from 'src/ability/ability.action';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { CommentThreadWhereInput } from 'src/core/@generated/comment-thread/comment-thread-where.input';
|
||||
import { relationAbilityChecker } from 'src/ability/ability.util';
|
||||
import { assert } from 'src/utils/assert';
|
||||
|
||||
class CommentThreadArgs {
|
||||
where?: CommentThreadWhereInput;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ManageCommentThreadAbilityHandler implements IAbilityHandler {
|
||||
async handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Manage, 'CommentThread');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ReadCommentThreadAbilityHandler implements IAbilityHandler {
|
||||
handle(ability: AppAbility) {
|
||||
return ability.can(AbilityAction.Read, 'CommentThread');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class CreateCommentThreadAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'CommentThread',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(AbilityAction.Create, 'CommentThread');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class UpdateCommentThreadAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<CommentThreadArgs>();
|
||||
const commentThread =
|
||||
await this.prismaService.client.commentThread.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(commentThread, '', NotFoundException);
|
||||
|
||||
const allowed = await relationAbilityChecker(
|
||||
'CommentThread',
|
||||
ability,
|
||||
this.prismaService.client,
|
||||
args,
|
||||
);
|
||||
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Update,
|
||||
subject('CommentThread', commentThread),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DeleteCommentThreadAbilityHandler implements IAbilityHandler {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
async handle(ability: AppAbility, context: ExecutionContext) {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const args = gqlContext.getArgs<CommentThreadArgs>();
|
||||
const commentThread =
|
||||
await this.prismaService.client.commentThread.findFirst({
|
||||
where: args.where,
|
||||
});
|
||||
assert(commentThread, '', NotFoundException);
|
||||
|
||||
return ability.can(
|
||||
AbilityAction.Delete,
|
||||
subject('CommentThread', commentThread),
|
||||
);
|
||||
}
|
||||
}
|
||||
11
server/src/core/activity/activity.module.ts
Normal file
11
server/src/core/activity/activity.module.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { ActivityResolver } from './resolvers/activity.resolver';
|
||||
import { ActivityService } from './services/activity.service';
|
||||
import { ActivityTargetService } from './services/activity-target.service';
|
||||
|
||||
@Module({
|
||||
providers: [ActivityResolver, ActivityService, ActivityTargetService],
|
||||
exports: [ActivityService, ActivityTargetService],
|
||||
})
|
||||
export class ActivityModule {}
|
||||
@ -1,19 +1,19 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { CommentThreadService } from 'src/core/comment/services/comment-thread.service';
|
||||
import { ActivityService } from 'src/core/activity/services/activity.service';
|
||||
import { AbilityFactory } from 'src/ability/ability.factory';
|
||||
|
||||
import { CommentThreadResolver } from './comment-thread.resolver';
|
||||
import { ActivityResolver } from './activity.resolver';
|
||||
|
||||
describe('CommentThreadResolver', () => {
|
||||
let resolver: CommentThreadResolver;
|
||||
describe('ActivityResolver', () => {
|
||||
let resolver: ActivityResolver;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
CommentThreadResolver,
|
||||
ActivityResolver,
|
||||
{
|
||||
provide: CommentThreadService,
|
||||
provide: ActivityService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
@ -23,7 +23,7 @@ describe('CommentThreadResolver', () => {
|
||||
],
|
||||
}).compile();
|
||||
|
||||
resolver = module.get<CommentThreadResolver>(CommentThreadResolver);
|
||||
resolver = module.get<ActivityResolver>(ActivityResolver);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
156
server/src/core/activity/resolvers/activity.resolver.ts
Normal file
156
server/src/core/activity/resolvers/activity.resolver.ts
Normal file
@ -0,0 +1,156 @@
|
||||
import { Resolver, Args, Mutation, Query } from '@nestjs/graphql';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
|
||||
import { accessibleBy } from '@casl/prisma';
|
||||
import { Prisma } from '@prisma/client';
|
||||
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import {
|
||||
PrismaSelector,
|
||||
PrismaSelect,
|
||||
} from 'src/decorators/prisma-select.decorator';
|
||||
import { AbilityGuard } from 'src/guards/ability.guard';
|
||||
import { CheckAbilities } from 'src/decorators/check-abilities.decorator';
|
||||
import {
|
||||
CreateActivityAbilityHandler,
|
||||
DeleteActivityAbilityHandler,
|
||||
ReadActivityAbilityHandler,
|
||||
UpdateActivityAbilityHandler,
|
||||
} from 'src/ability/handlers/activity.ability-handler';
|
||||
import { UserAbility } from 'src/decorators/user-ability.decorator';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { AffectedRows } from 'src/core/@generated/prisma/affected-rows.output';
|
||||
import { Activity } from 'src/core/@generated/activity/activity.model';
|
||||
import { ActivityService } from 'src/core/activity/services/activity.service';
|
||||
import { CreateOneActivityArgs } from 'src/core/@generated/activity/create-one-activity.args';
|
||||
import { Workspace } from 'src/core/@generated/workspace/workspace.model';
|
||||
import { UpdateOneActivityArgs } from 'src/core/@generated/activity/update-one-activity.args';
|
||||
import { FindManyActivityArgs } from 'src/core/@generated/activity/find-many-activity.args';
|
||||
import { DeleteManyActivityArgs } from 'src/core/@generated/activity/delete-many-activity.args';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => Activity)
|
||||
export class ActivityResolver {
|
||||
constructor(private readonly activityService: ActivityService) {}
|
||||
|
||||
@Mutation(() => Activity, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(CreateActivityAbilityHandler)
|
||||
async createOneActivity(
|
||||
@Args() args: CreateOneActivityArgs,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@PrismaSelector({ modelName: 'Activity' })
|
||||
prismaSelect: PrismaSelect<'Activity'>,
|
||||
): Promise<Partial<Activity>> {
|
||||
const createdActivity = await this.activityService.create({
|
||||
data: {
|
||||
...args.data,
|
||||
...{ workspace: { connect: { id: workspace.id } } },
|
||||
activityTargets: args.data?.activityTargets?.createMany
|
||||
? {
|
||||
createMany: {
|
||||
data: args.data.activityTargets.createMany.data.map(
|
||||
(target) => ({ ...target, workspaceId: workspace.id }),
|
||||
),
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.ActivityCreateArgs);
|
||||
|
||||
return createdActivity;
|
||||
}
|
||||
|
||||
@Mutation(() => Activity, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(UpdateActivityAbilityHandler)
|
||||
async updateOneActivity(
|
||||
@Args() args: UpdateOneActivityArgs,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@PrismaSelector({ modelName: 'Activity' })
|
||||
prismaSelect: PrismaSelect<'Activity'>,
|
||||
): Promise<Partial<Activity>> {
|
||||
// TODO: Do a proper check with recursion testing on args in a more generic place
|
||||
for (const key in args.data) {
|
||||
if (args.data[key]) {
|
||||
for (const subKey in args.data[key]) {
|
||||
if (JSON.stringify(args.data[key][subKey]) === '{}') {
|
||||
delete args.data[key][subKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (JSON.stringify(args.data[key]) === '{}') {
|
||||
delete args.data[key];
|
||||
}
|
||||
}
|
||||
const updatedActivity = await this.activityService.update({
|
||||
where: args.where,
|
||||
data: {
|
||||
...args.data,
|
||||
activityTargets: args.data?.activityTargets
|
||||
? {
|
||||
createMany: args.data.activityTargets.createMany
|
||||
? {
|
||||
data: args.data.activityTargets.createMany.data.map(
|
||||
(target) => ({
|
||||
...target,
|
||||
workspaceId: workspace.id,
|
||||
}),
|
||||
),
|
||||
}
|
||||
: undefined,
|
||||
deleteMany: args.data.activityTargets.deleteMany ?? undefined,
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.ActivityUpdateArgs);
|
||||
|
||||
return updatedActivity;
|
||||
}
|
||||
|
||||
@Query(() => [Activity])
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(ReadActivityAbilityHandler)
|
||||
async findManyActivities(
|
||||
@Args() args: FindManyActivityArgs,
|
||||
@UserAbility() ability: AppAbility,
|
||||
@PrismaSelector({ modelName: 'Activity' })
|
||||
prismaSelect: PrismaSelect<'Activity'>,
|
||||
): Promise<Partial<Activity>[]> {
|
||||
const result = await this.activityService.findMany({
|
||||
where: {
|
||||
...args.where,
|
||||
AND: [accessibleBy(ability).Activity],
|
||||
},
|
||||
orderBy: args.orderBy,
|
||||
cursor: args.cursor,
|
||||
take: args.take,
|
||||
skip: args.skip,
|
||||
distinct: args.distinct,
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Mutation(() => AffectedRows, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(DeleteActivityAbilityHandler)
|
||||
async deleteManyActivities(
|
||||
@Args() args: DeleteManyActivityArgs,
|
||||
): Promise<AffectedRows> {
|
||||
return this.activityService.deleteMany({
|
||||
where: args.where,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -3,15 +3,15 @@ import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { prismaMock } from 'src/database/client-mock/jest-prisma-singleton';
|
||||
|
||||
import { CommentThreadTargetService } from './comment-thread-target.service';
|
||||
import { ActivityTargetService } from './activity-target.service';
|
||||
|
||||
describe('CommentThreadTargetService', () => {
|
||||
let service: CommentThreadTargetService;
|
||||
describe('ActivityTargetService', () => {
|
||||
let service: ActivityTargetService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
CommentThreadTargetService,
|
||||
ActivityTargetService,
|
||||
{
|
||||
provide: PrismaService,
|
||||
useValue: prismaMock,
|
||||
@ -19,9 +19,7 @@ describe('CommentThreadTargetService', () => {
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<CommentThreadTargetService>(
|
||||
CommentThreadTargetService,
|
||||
);
|
||||
service = module.get<ActivityTargetService>(ActivityTargetService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
40
server/src/core/activity/services/activity-target.service.ts
Normal file
40
server/src/core/activity/services/activity-target.service.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class ActivityTargetService {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
// Find
|
||||
findFirst = this.prismaService.client.activityTarget.findFirst;
|
||||
findFirstOrThrow = this.prismaService.client.activityTarget.findFirstOrThrow;
|
||||
|
||||
findUnique = this.prismaService.client.activityTarget.findUnique;
|
||||
findUniqueOrThrow =
|
||||
this.prismaService.client.activityTarget.findUniqueOrThrow;
|
||||
|
||||
findMany = this.prismaService.client.activityTarget.findMany;
|
||||
|
||||
// Create
|
||||
create = this.prismaService.client.activityTarget.create;
|
||||
createMany = this.prismaService.client.activityTarget.createMany;
|
||||
|
||||
// Update
|
||||
update = this.prismaService.client.activityTarget.update;
|
||||
upsert = this.prismaService.client.activityTarget.upsert;
|
||||
updateMany = this.prismaService.client.activityTarget.updateMany;
|
||||
|
||||
// Delete
|
||||
delete = this.prismaService.client.activityTarget.delete;
|
||||
deleteMany = this.prismaService.client.activityTarget.deleteMany;
|
||||
|
||||
// Aggregate
|
||||
aggregate = this.prismaService.client.activityTarget.aggregate;
|
||||
|
||||
// Count
|
||||
count = this.prismaService.client.activityTarget.count;
|
||||
|
||||
// GroupBy
|
||||
groupBy = this.prismaService.client.activityTarget.groupBy;
|
||||
}
|
||||
@ -3,15 +3,15 @@ import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { prismaMock } from 'src/database/client-mock/jest-prisma-singleton';
|
||||
|
||||
import { CommentThreadService } from './comment-thread.service';
|
||||
import { ActivityService } from './activity.service';
|
||||
|
||||
describe('CommentThreadService', () => {
|
||||
let service: CommentThreadService;
|
||||
describe('ActivityService', () => {
|
||||
let service: ActivityService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
CommentThreadService,
|
||||
ActivityService,
|
||||
{
|
||||
provide: PrismaService,
|
||||
useValue: prismaMock,
|
||||
@ -19,7 +19,7 @@ describe('CommentThreadService', () => {
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<CommentThreadService>(CommentThreadService);
|
||||
service = module.get<ActivityService>(ActivityService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
39
server/src/core/activity/services/activity.service.ts
Normal file
39
server/src/core/activity/services/activity.service.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class ActivityService {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
// Find
|
||||
findFirst = this.prismaService.client.activity.findFirst;
|
||||
findFirstOrThrow = this.prismaService.client.activity.findFirstOrThrow;
|
||||
|
||||
findUnique = this.prismaService.client.activity.findUnique;
|
||||
findUniqueOrThrow = this.prismaService.client.activity.findUniqueOrThrow;
|
||||
|
||||
findMany = this.prismaService.client.activity.findMany;
|
||||
|
||||
// Create
|
||||
create = this.prismaService.client.activity.create;
|
||||
createMany = this.prismaService.client.activity.createMany;
|
||||
|
||||
// Update
|
||||
update = this.prismaService.client.activity.update;
|
||||
upsert = this.prismaService.client.activity.upsert;
|
||||
updateMany = this.prismaService.client.activity.updateMany;
|
||||
|
||||
// Delete
|
||||
delete = this.prismaService.client.activity.delete;
|
||||
deleteMany = this.prismaService.client.activity.deleteMany;
|
||||
|
||||
// Aggregate
|
||||
aggregate = this.prismaService.client.activity.aggregate;
|
||||
|
||||
// Count
|
||||
count = this.prismaService.client.activity.count;
|
||||
|
||||
// GroupBy
|
||||
groupBy = this.prismaService.client.activity.groupBy;
|
||||
}
|
||||
@ -1,19 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { CommentService } from './services/comment.service';
|
||||
import { CommentResolver } from './resolvers/comment.resolver';
|
||||
import { CommentThreadTargetService } from './services/comment-thread-target.service';
|
||||
import { CommentThreadResolver } from './resolvers/comment-thread.resolver';
|
||||
import { CommentThreadService } from './services/comment-thread.service';
|
||||
import { CommentService } from './comment.service';
|
||||
import { CommentResolver } from './comment.resolver';
|
||||
|
||||
@Module({
|
||||
providers: [
|
||||
CommentService,
|
||||
CommentThreadService,
|
||||
CommentThreadTargetService,
|
||||
CommentResolver,
|
||||
CommentThreadResolver,
|
||||
],
|
||||
exports: [CommentService, CommentThreadService, CommentThreadTargetService],
|
||||
providers: [CommentService, CommentResolver],
|
||||
exports: [CommentService],
|
||||
})
|
||||
export class CommentModule {}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { CommentService } from 'src/core/comment/services/comment.service';
|
||||
import { CommentService } from 'src/core/comment/comment.service';
|
||||
import { AbilityFactory } from 'src/ability/ability.factory';
|
||||
|
||||
import { CommentResolver } from './comment.resolver';
|
||||
@ -8,7 +8,7 @@ import { Workspace } from 'src/core/@generated/workspace/workspace.model';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import { CreateOneCommentArgs } from 'src/core/@generated/comment/create-one-comment.args';
|
||||
import { Comment } from 'src/core/@generated/comment/comment.model';
|
||||
import { CommentService } from 'src/core/comment/services/comment.service';
|
||||
import { CommentService } from 'src/core/comment/comment.service';
|
||||
import {
|
||||
PrismaSelector,
|
||||
PrismaSelect,
|
||||
@ -1,150 +0,0 @@
|
||||
import { Resolver, Args, Mutation, Query } from '@nestjs/graphql';
|
||||
import { UseGuards } from '@nestjs/common';
|
||||
|
||||
import { accessibleBy } from '@casl/prisma';
|
||||
import { Prisma } from '@prisma/client';
|
||||
|
||||
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
|
||||
import { Workspace } from 'src/core/@generated/workspace/workspace.model';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import { CommentThread } from 'src/core/@generated/comment-thread/comment-thread.model';
|
||||
import { CreateOneCommentThreadArgs } from 'src/core/@generated/comment-thread/create-one-comment-thread.args';
|
||||
import { FindManyCommentThreadArgs } from 'src/core/@generated/comment-thread/find-many-comment-thread.args';
|
||||
import { CommentThreadService } from 'src/core/comment/services/comment-thread.service';
|
||||
import { UpdateOneCommentThreadArgs } from 'src/core/@generated/comment-thread/update-one-comment-thread.args';
|
||||
import {
|
||||
PrismaSelector,
|
||||
PrismaSelect,
|
||||
} from 'src/decorators/prisma-select.decorator';
|
||||
import { AbilityGuard } from 'src/guards/ability.guard';
|
||||
import { CheckAbilities } from 'src/decorators/check-abilities.decorator';
|
||||
import {
|
||||
CreateCommentThreadAbilityHandler,
|
||||
DeleteCommentThreadAbilityHandler,
|
||||
ReadCommentThreadAbilityHandler,
|
||||
UpdateCommentThreadAbilityHandler,
|
||||
} from 'src/ability/handlers/comment-thread.ability-handler';
|
||||
import { UserAbility } from 'src/decorators/user-ability.decorator';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { AffectedRows } from 'src/core/@generated/prisma/affected-rows.output';
|
||||
import { DeleteManyCommentThreadArgs } from 'src/core/@generated/comment-thread/delete-many-comment-thread.args';
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Resolver(() => CommentThread)
|
||||
export class CommentThreadResolver {
|
||||
constructor(private readonly commentThreadService: CommentThreadService) {}
|
||||
|
||||
@Mutation(() => CommentThread, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(CreateCommentThreadAbilityHandler)
|
||||
async createOneCommentThread(
|
||||
@Args() args: CreateOneCommentThreadArgs,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@PrismaSelector({ modelName: 'CommentThread' })
|
||||
prismaSelect: PrismaSelect<'CommentThread'>,
|
||||
): Promise<Partial<CommentThread>> {
|
||||
const createdCommentThread = await this.commentThreadService.create({
|
||||
data: {
|
||||
...args.data,
|
||||
...{ workspace: { connect: { id: workspace.id } } },
|
||||
commentThreadTargets: args.data?.commentThreadTargets?.createMany
|
||||
? {
|
||||
createMany: {
|
||||
data: args.data.commentThreadTargets.createMany.data.map(
|
||||
(target) => ({ ...target, workspaceId: workspace.id }),
|
||||
),
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.CommentThreadCreateArgs);
|
||||
|
||||
return createdCommentThread;
|
||||
}
|
||||
|
||||
@Mutation(() => CommentThread, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(UpdateCommentThreadAbilityHandler)
|
||||
async updateOneCommentThread(
|
||||
@Args() args: UpdateOneCommentThreadArgs,
|
||||
@AuthWorkspace() workspace: Workspace,
|
||||
@PrismaSelector({ modelName: 'CommentThread' })
|
||||
prismaSelect: PrismaSelect<'CommentThread'>,
|
||||
): Promise<Partial<CommentThread>> {
|
||||
// TODO: Do a proper check with recursion testing on args in a more generic place
|
||||
for (const key in args.data) {
|
||||
if (args.data[key]) {
|
||||
for (const subKey in args.data[key]) {
|
||||
if (JSON.stringify(args.data[key][subKey]) === '{}') {
|
||||
delete args.data[key][subKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (JSON.stringify(args.data[key]) === '{}') {
|
||||
delete args.data[key];
|
||||
}
|
||||
}
|
||||
const updatedCommentThread = await this.commentThreadService.update({
|
||||
where: args.where,
|
||||
data: {
|
||||
...args.data,
|
||||
commentThreadTargets: args.data?.commentThreadTargets?.createMany
|
||||
? {
|
||||
createMany: {
|
||||
data: args.data.commentThreadTargets.createMany.data.map(
|
||||
(target) => ({ ...target, workspaceId: workspace.id }),
|
||||
),
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
select: prismaSelect.value,
|
||||
} as Prisma.CommentThreadUpdateArgs);
|
||||
|
||||
return updatedCommentThread;
|
||||
}
|
||||
|
||||
@Query(() => [CommentThread])
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(ReadCommentThreadAbilityHandler)
|
||||
async findManyCommentThreads(
|
||||
@Args() args: FindManyCommentThreadArgs,
|
||||
@UserAbility() ability: AppAbility,
|
||||
@PrismaSelector({ modelName: 'CommentThread' })
|
||||
prismaSelect: PrismaSelect<'CommentThread'>,
|
||||
): Promise<Partial<CommentThread>[]> {
|
||||
const result = await this.commentThreadService.findMany({
|
||||
where: {
|
||||
...args.where,
|
||||
AND: [accessibleBy(ability).CommentThread],
|
||||
},
|
||||
orderBy: args.orderBy,
|
||||
cursor: args.cursor,
|
||||
take: args.take,
|
||||
skip: args.skip,
|
||||
distinct: args.distinct,
|
||||
select: prismaSelect.value,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Mutation(() => AffectedRows, {
|
||||
nullable: false,
|
||||
})
|
||||
@UseGuards(AbilityGuard)
|
||||
@CheckAbilities(DeleteCommentThreadAbilityHandler)
|
||||
async deleteManyCommentThreads(
|
||||
@Args() args: DeleteManyCommentThreadArgs,
|
||||
): Promise<AffectedRows> {
|
||||
return this.commentThreadService.deleteMany({
|
||||
where: args.where,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class CommentThreadTargetService {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
// Find
|
||||
findFirst = this.prismaService.client.commentThreadTarget.findFirst;
|
||||
findFirstOrThrow =
|
||||
this.prismaService.client.commentThreadTarget.findFirstOrThrow;
|
||||
|
||||
findUnique = this.prismaService.client.commentThreadTarget.findUnique;
|
||||
findUniqueOrThrow =
|
||||
this.prismaService.client.commentThreadTarget.findUniqueOrThrow;
|
||||
|
||||
findMany = this.prismaService.client.commentThreadTarget.findMany;
|
||||
|
||||
// Create
|
||||
create = this.prismaService.client.commentThreadTarget.create;
|
||||
createMany = this.prismaService.client.commentThreadTarget.createMany;
|
||||
|
||||
// Update
|
||||
update = this.prismaService.client.commentThreadTarget.update;
|
||||
upsert = this.prismaService.client.commentThreadTarget.upsert;
|
||||
updateMany = this.prismaService.client.commentThreadTarget.updateMany;
|
||||
|
||||
// Delete
|
||||
delete = this.prismaService.client.commentThreadTarget.delete;
|
||||
deleteMany = this.prismaService.client.commentThreadTarget.deleteMany;
|
||||
|
||||
// Aggregate
|
||||
aggregate = this.prismaService.client.commentThreadTarget.aggregate;
|
||||
|
||||
// Count
|
||||
count = this.prismaService.client.commentThreadTarget.count;
|
||||
|
||||
// GroupBy
|
||||
groupBy = this.prismaService.client.commentThreadTarget.groupBy;
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class CommentThreadService {
|
||||
constructor(private readonly prismaService: PrismaService) {}
|
||||
|
||||
// Find
|
||||
findFirst = this.prismaService.client.commentThread.findFirst;
|
||||
findFirstOrThrow = this.prismaService.client.commentThread.findFirstOrThrow;
|
||||
|
||||
findUnique = this.prismaService.client.commentThread.findUnique;
|
||||
findUniqueOrThrow = this.prismaService.client.commentThread.findUniqueOrThrow;
|
||||
|
||||
findMany = this.prismaService.client.commentThread.findMany;
|
||||
|
||||
// Create
|
||||
create = this.prismaService.client.commentThread.create;
|
||||
createMany = this.prismaService.client.commentThread.createMany;
|
||||
|
||||
// Update
|
||||
update = this.prismaService.client.commentThread.update;
|
||||
upsert = this.prismaService.client.commentThread.upsert;
|
||||
updateMany = this.prismaService.client.commentThread.updateMany;
|
||||
|
||||
// Delete
|
||||
delete = this.prismaService.client.commentThread.delete;
|
||||
deleteMany = this.prismaService.client.commentThread.deleteMany;
|
||||
|
||||
// Aggregate
|
||||
aggregate = this.prismaService.client.commentThread.aggregate;
|
||||
|
||||
// Count
|
||||
count = this.prismaService.client.commentThread.count;
|
||||
|
||||
// GroupBy
|
||||
groupBy = this.prismaService.client.commentThread.groupBy;
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { CommentThreadService } from 'src/core/comment/services/comment-thread.service';
|
||||
import { CommentService } from 'src/core/comment/services/comment.service';
|
||||
import { CommentService } from 'src/core/comment/comment.service';
|
||||
import { ActivityService } from 'src/core/activity/services/activity.service';
|
||||
|
||||
import { CompanyRelationsResolver } from './company-relations.resolver';
|
||||
import { CompanyService } from './company.service';
|
||||
@ -18,7 +18,7 @@ describe('CompanyRelationsResolver', () => {
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: CommentThreadService,
|
||||
provide: ActivityService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
|
||||
@ -1,36 +1,36 @@
|
||||
import { Resolver, ResolveField, Root, Int } from '@nestjs/graphql';
|
||||
|
||||
import { CommentThread } from 'src/core/@generated/comment-thread/comment-thread.model';
|
||||
import { Comment } from 'src/core/@generated/comment/comment.model';
|
||||
import { Company } from 'src/core/@generated/company/company.model';
|
||||
import { CommentThreadService } from 'src/core/comment/services/comment-thread.service';
|
||||
import { CommentService } from 'src/core/comment/services/comment.service';
|
||||
import { CommentService } from 'src/core/comment/comment.service';
|
||||
import {
|
||||
PrismaSelect,
|
||||
PrismaSelector,
|
||||
} from 'src/decorators/prisma-select.decorator';
|
||||
import { ActivityService } from 'src/core/activity/services/activity.service';
|
||||
import { Activity } from 'src/core/@generated/activity/activity.model';
|
||||
|
||||
@Resolver(() => Company)
|
||||
export class CompanyRelationsResolver {
|
||||
constructor(
|
||||
private readonly commentThreadService: CommentThreadService,
|
||||
private readonly activityService: ActivityService,
|
||||
private readonly commentService: CommentService,
|
||||
) {}
|
||||
|
||||
@ResolveField(() => [CommentThread], {
|
||||
@ResolveField(() => [Activity], {
|
||||
nullable: false,
|
||||
})
|
||||
async commentThreads(
|
||||
async activities(
|
||||
@Root() company: Company,
|
||||
@PrismaSelector({ modelName: 'CommentThread' })
|
||||
prismaSelect: PrismaSelect<'CommentThread'>,
|
||||
): Promise<Partial<CommentThread>[]> {
|
||||
return this.commentThreadService.findMany({
|
||||
@PrismaSelector({ modelName: 'Activity' })
|
||||
prismaSelect: PrismaSelect<'Activity'>,
|
||||
): Promise<Partial<Activity>[]> {
|
||||
return this.activityService.findMany({
|
||||
where: {
|
||||
commentThreadTargets: {
|
||||
activityTargets: {
|
||||
some: {
|
||||
commentableId: company.id,
|
||||
commentableType: 'Company',
|
||||
commentableId: company.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -48,11 +48,11 @@ export class CompanyRelationsResolver {
|
||||
): Promise<Partial<Comment>[]> {
|
||||
return this.commentService.findMany({
|
||||
where: {
|
||||
commentThread: {
|
||||
commentThreadTargets: {
|
||||
activity: {
|
||||
activityTargets: {
|
||||
some: {
|
||||
commentableId: company.id,
|
||||
commentableType: 'Company',
|
||||
commentableId: company.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -64,13 +64,13 @@ export class CompanyRelationsResolver {
|
||||
@ResolveField(() => Int, {
|
||||
nullable: false,
|
||||
})
|
||||
async _commentThreadCount(@Root() company: Company): Promise<number> {
|
||||
return this.commentThreadService.count({
|
||||
async _activityCount(@Root() company: Company): Promise<number> {
|
||||
return this.activityService.count({
|
||||
where: {
|
||||
commentThreadTargets: {
|
||||
activityTargets: {
|
||||
some: {
|
||||
commentableId: company.id,
|
||||
commentableType: 'Company',
|
||||
commentableId: company.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { CommentModule } from 'src/core/comment/comment.module';
|
||||
import { ActivityModule } from 'src/core/activity/activity.module';
|
||||
|
||||
import { CompanyService } from './company.service';
|
||||
import { CompanyResolver } from './company.resolver';
|
||||
import { CompanyRelationsResolver } from './company-relations.resolver';
|
||||
|
||||
@Module({
|
||||
imports: [CommentModule],
|
||||
imports: [CommentModule, ActivityModule],
|
||||
providers: [CompanyService, CompanyResolver, CompanyRelationsResolver],
|
||||
exports: [CompanyService],
|
||||
})
|
||||
|
||||
@ -11,6 +11,7 @@ import { AnalyticsModule } from './analytics/analytics.module';
|
||||
import { FileModule } from './file/file.module';
|
||||
import { ClientConfigModule } from './client-config/client-config.module';
|
||||
import { AttachmentModule } from './attachment/attachment.module';
|
||||
import { ActivityModule } from './activity/activity.module';
|
||||
import { ViewModule } from './view/view.module';
|
||||
|
||||
@Module({
|
||||
@ -26,6 +27,7 @@ import { ViewModule } from './view/view.module';
|
||||
FileModule,
|
||||
ClientConfigModule,
|
||||
AttachmentModule,
|
||||
ActivityModule,
|
||||
ViewModule,
|
||||
],
|
||||
exports: [
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { CommentThreadService } from 'src/core/comment/services/comment-thread.service';
|
||||
import { CommentService } from 'src/core/comment/services/comment.service';
|
||||
import { CommentService } from 'src/core/comment/comment.service';
|
||||
import { ActivityService } from 'src/core/activity/services/activity.service';
|
||||
|
||||
import { PersonRelationsResolver } from './person-relations.resolver';
|
||||
import { PersonService } from './person.service';
|
||||
@ -18,7 +18,7 @@ describe('PersonRelationsResolver', () => {
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: CommentThreadService,
|
||||
provide: ActivityService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
|
||||
@ -1,36 +1,36 @@
|
||||
import { Resolver, Root, ResolveField, Int } from '@nestjs/graphql';
|
||||
|
||||
import { CommentThread } from 'src/core/@generated/comment-thread/comment-thread.model';
|
||||
import { Comment } from 'src/core/@generated/comment/comment.model';
|
||||
import { Person } from 'src/core/@generated/person/person.model';
|
||||
import { CommentThreadService } from 'src/core/comment/services/comment-thread.service';
|
||||
import { CommentService } from 'src/core/comment/services/comment.service';
|
||||
import { CommentService } from 'src/core/comment/comment.service';
|
||||
import {
|
||||
PrismaSelect,
|
||||
PrismaSelector,
|
||||
} from 'src/decorators/prisma-select.decorator';
|
||||
import { Activity } from 'src/core/@generated/activity/activity.model';
|
||||
import { ActivityService } from 'src/core/activity/services/activity.service';
|
||||
|
||||
@Resolver(() => Person)
|
||||
export class PersonRelationsResolver {
|
||||
constructor(
|
||||
private readonly commentThreadService: CommentThreadService,
|
||||
private readonly activityService: ActivityService,
|
||||
private readonly commentService: CommentService,
|
||||
) {}
|
||||
|
||||
@ResolveField(() => [CommentThread], {
|
||||
@ResolveField(() => [Activity], {
|
||||
nullable: false,
|
||||
})
|
||||
async commentThreads(
|
||||
async activities(
|
||||
@Root() person: Person,
|
||||
@PrismaSelector({ modelName: 'CommentThread' })
|
||||
prismaSelect: PrismaSelect<'CommentThread'>,
|
||||
): Promise<Partial<CommentThread>[]> {
|
||||
return await this.commentThreadService.findMany({
|
||||
@PrismaSelector({ modelName: 'Activity' })
|
||||
prismaSelect: PrismaSelect<'Activity'>,
|
||||
): Promise<Partial<Activity>[]> {
|
||||
return await this.activityService.findMany({
|
||||
where: {
|
||||
commentThreadTargets: {
|
||||
activityTargets: {
|
||||
some: {
|
||||
commentableId: person.id,
|
||||
commentableType: 'Person',
|
||||
commentableId: person.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -48,11 +48,11 @@ export class PersonRelationsResolver {
|
||||
): Promise<Partial<Comment>[]> {
|
||||
return this.commentService.findMany({
|
||||
where: {
|
||||
commentThread: {
|
||||
commentThreadTargets: {
|
||||
activity: {
|
||||
activityTargets: {
|
||||
some: {
|
||||
commentableId: person.id,
|
||||
commentableType: 'Person',
|
||||
commentableId: person.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -64,13 +64,13 @@ export class PersonRelationsResolver {
|
||||
@ResolveField(() => Int, {
|
||||
nullable: false,
|
||||
})
|
||||
async _commentThreadCount(@Root() person: Person): Promise<number> {
|
||||
return this.commentThreadService.count({
|
||||
async _activityCount(@Root() person: Person): Promise<number> {
|
||||
return this.activityService.count({
|
||||
where: {
|
||||
commentThreadTargets: {
|
||||
activityTargets: {
|
||||
some: {
|
||||
commentableId: person.id,
|
||||
commentableType: 'Person',
|
||||
commentableId: person.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { CommentModule } from 'src/core/comment/comment.module';
|
||||
import { ActivityModule } from 'src/core/activity/activity.module';
|
||||
|
||||
import { PersonService } from './person.service';
|
||||
import { PersonResolver } from './person.resolver';
|
||||
import { PersonRelationsResolver } from './person-relations.resolver';
|
||||
|
||||
@Module({
|
||||
imports: [CommentModule, CommentModule],
|
||||
imports: [CommentModule, ActivityModule],
|
||||
providers: [PersonService, PersonResolver, PersonRelationsResolver],
|
||||
exports: [PersonService],
|
||||
})
|
||||
|
||||
@ -18,7 +18,6 @@ import { UpdateOnePersonArgs } from 'src/core/@generated/person/update-one-perso
|
||||
import { CreateOnePersonArgs } from 'src/core/@generated/person/create-one-person.args';
|
||||
import { AffectedRows } from 'src/core/@generated/prisma/affected-rows.output';
|
||||
import { DeleteManyPersonArgs } from 'src/core/@generated/person/delete-many-person.args';
|
||||
import { Workspace } from 'src/core/@generated/workspace/workspace.model';
|
||||
import { AuthWorkspace } from 'src/decorators/auth-workspace.decorator';
|
||||
import {
|
||||
PrismaSelect,
|
||||
@ -34,6 +33,7 @@ import {
|
||||
} from 'src/ability/handlers/person.ability-handler';
|
||||
import { UserAbility } from 'src/decorators/user-ability.decorator';
|
||||
import { AppAbility } from 'src/ability/ability.factory';
|
||||
import { Workspace } from 'src/core/@generated/workspace/workspace.model';
|
||||
|
||||
import { PersonService } from './person.service';
|
||||
|
||||
|
||||
@ -109,11 +109,11 @@ export class WorkspaceService {
|
||||
refreshToken,
|
||||
attachment,
|
||||
comment,
|
||||
commentThreadTarget,
|
||||
commentThread,
|
||||
activityTarget,
|
||||
activity,
|
||||
} = this.prismaService.client;
|
||||
|
||||
const commentThreads = await commentThread.findMany({
|
||||
const activitys = await activity.findMany({
|
||||
where: { authorId: userId },
|
||||
});
|
||||
|
||||
@ -142,12 +142,12 @@ export class WorkspaceService {
|
||||
comment.deleteMany({
|
||||
where,
|
||||
}),
|
||||
...commentThreads.map(({ id: commentThreadId }) =>
|
||||
commentThreadTarget.deleteMany({
|
||||
where: { commentThreadId },
|
||||
...activitys.map(({ id: activityId }) =>
|
||||
activityTarget.deleteMany({
|
||||
where: { activityId },
|
||||
}),
|
||||
),
|
||||
commentThread.deleteMany({
|
||||
activity.deleteMany({
|
||||
where,
|
||||
}),
|
||||
refreshToken.deleteMany({
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
-- Create the new tables first, without any foreign key constraints
|
||||
-- Activities Table
|
||||
CREATE TABLE "activities" (
|
||||
"id" TEXT NOT NULL,
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the `comment_thread_targets` table. If the table is not empty, all the data it contains will be lost.
|
||||
- You are about to drop the `comment_threads` table. If the table is not empty, all the data it contains will be lost.
|
||||
|
||||
*/
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "attachments" DROP CONSTRAINT "attachments_activityId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "comment_thread_targets" DROP CONSTRAINT "comment_thread_targets_commentThreadId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "comment_thread_targets" DROP CONSTRAINT "comment_thread_targets_workspaceId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "comment_threads" DROP CONSTRAINT "comment_threads_assigneeId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "comment_threads" DROP CONSTRAINT "comment_threads_authorId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "comment_threads" DROP CONSTRAINT "comment_threads_workspaceId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "comments" DROP CONSTRAINT "comments_commentThreadId_fkey";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "activity_targets" ADD COLUMN "commentableId" TEXT,
|
||||
ADD COLUMN "commentableType" "CommentableType",
|
||||
ALTER COLUMN "personId" DROP NOT NULL,
|
||||
ALTER COLUMN "companyId" DROP NOT NULL;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "comments" ADD COLUMN "activityId" TEXT;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "comment_thread_targets";
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "comment_threads";
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "activities" ADD CONSTRAINT "activities_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "activities" ADD CONSTRAINT "activities_assigneeId_fkey" FOREIGN KEY ("assigneeId") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "activities" ADD CONSTRAINT "activities_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "workspaces"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "comments" ADD CONSTRAINT "comments_activityId_fkey" FOREIGN KEY ("activityId") REFERENCES "activities"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "activity_targets" ADD CONSTRAINT "activity_targets_activityId_fkey" FOREIGN KEY ("activityId") REFERENCES "activities"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "activity_targets" ADD CONSTRAINT "activity_targets_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "workspaces"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "activity_targets" ADD CONSTRAINT "activity_targets_personId_fkey" FOREIGN KEY ("personId") REFERENCES "people"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "activity_targets" ADD CONSTRAINT "activity_targets_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "companies"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "attachments" ADD CONSTRAINT "attachments_activityId_fkey" FOREIGN KEY ("activityId") REFERENCES "activities"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@ -20,20 +20,20 @@ generator nestgraphql {
|
||||
fields_Validator_from = "class-validator"
|
||||
|
||||
// All relations, only allow connect
|
||||
decorate_all_type = "!(CommentThreadTarget*Input|UserSettingsUpdateOneRequiredWithoutUserNestedInput)"
|
||||
decorate_all_type = "!(ActivityTarget*Input|UserSettingsUpdateOneRequiredWithoutUserNestedInput)"
|
||||
decorate_all_field = "*(create|connectOrCreate|update|upsert|delete|createMany|updateMany|deleteMany)"
|
||||
decorate_all_name = "HideField"
|
||||
decorate_all_from = "@nestjs/graphql"
|
||||
decorate_all_arguments = "[]"
|
||||
|
||||
// CommentThread: Only Allow targets createOrConnect / createMany
|
||||
decorate_commentThreadTargets_type = "*CommentThreadTarget*Input"
|
||||
decorate_commentThreadTargets_field = "*(update|upsert|updateMany)"
|
||||
decorate_commentThreadTargets_name = "HideField"
|
||||
decorate_commentThreadTargets_from = "@nestjs/graphql"
|
||||
decorate_commentThreadTargets_arguments = "[]"
|
||||
// Activity: Only Allow targets createOrConnect / createMany
|
||||
decorate_activityTargets_type = "*ActivityTarget*Input"
|
||||
decorate_activityTargets_field = "*(update|upsert|updateMany)"
|
||||
decorate_activityTargets_name = "HideField"
|
||||
decorate_activityTargets_from = "@nestjs/graphql"
|
||||
decorate_activityTargets_arguments = "[]"
|
||||
|
||||
// CommentThread: Only Allow targets createOrConnect / createMany
|
||||
// User Settings: Only Allow targets createOrConnect / createMany
|
||||
decorate_userSettings_type = "*UserSettingsUpdateOneRequiredWithoutUserNestedInput"
|
||||
decorate_userSettings_field = "!(update)"
|
||||
decorate_userSettings_name = "HideField"
|
||||
@ -106,10 +106,10 @@ model User {
|
||||
refreshTokens RefreshToken[]
|
||||
comments Comment[]
|
||||
|
||||
authoredCommentThreads CommentThread[] @relation(name: "authoredCommentThreads")
|
||||
assignedCommentThreads CommentThread[] @relation(name: "assignedCommentThreads")
|
||||
settings UserSettings @relation(fields: [settingsId], references: [id])
|
||||
settingsId String @unique
|
||||
authoredActivities Activity[] @relation(name: "authoredActivities")
|
||||
assignedActivities Activity[] @relation(name: "assignedActivities")
|
||||
settings UserSettings @relation(fields: [settingsId], references: [id])
|
||||
settingsId String @unique
|
||||
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
deletedAt DateTime?
|
||||
@ -164,20 +164,20 @@ model Workspace {
|
||||
workspaceMember WorkspaceMember[]
|
||||
companies Company[]
|
||||
people Person[]
|
||||
commentThreads CommentThread[]
|
||||
activities Activity[]
|
||||
comments Comment[]
|
||||
pipelines Pipeline[]
|
||||
pipelineStages PipelineStage[]
|
||||
pipelineProgresses PipelineProgress[]
|
||||
activityTargets ActivityTarget[]
|
||||
viewFields ViewField[]
|
||||
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
deletedAt DateTime?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
Attachment Attachment[]
|
||||
CommentThreadTarget CommentThreadTarget[]
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
Attachment Attachment[]
|
||||
|
||||
@@map("workspaces")
|
||||
}
|
||||
@ -234,8 +234,9 @@ model Company {
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
deletedAt DateTime?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
ActivityTarget ActivityTarget[]
|
||||
|
||||
@@map("companies")
|
||||
}
|
||||
@ -277,8 +278,9 @@ model Person {
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
deletedAt DateTime?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
ActivityTarget ActivityTarget[]
|
||||
|
||||
@@map("people")
|
||||
}
|
||||
@ -311,30 +313,28 @@ enum ActivityType {
|
||||
Task
|
||||
}
|
||||
|
||||
model CommentThread {
|
||||
model Activity {
|
||||
/// @Validator.IsString()
|
||||
/// @Validator.IsOptional()
|
||||
id String @id @default(uuid())
|
||||
|
||||
commentThreadTargets CommentThreadTarget[]
|
||||
comments Comment[]
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id])
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
workspaceId String
|
||||
|
||||
authorId String
|
||||
author User @relation(fields: [authorId], references: [id], name: "authoredCommentThreads")
|
||||
|
||||
body String?
|
||||
title String?
|
||||
type ActivityType @default(Note)
|
||||
|
||||
id String @id @default(uuid())
|
||||
body String?
|
||||
title String?
|
||||
type ActivityType @default(Note)
|
||||
reminderAt DateTime?
|
||||
dueAt DateTime?
|
||||
completedAt DateTime?
|
||||
assignee User? @relation(fields: [assigneeId], references: [id], name: "assignedCommentThreads")
|
||||
assigneeId String?
|
||||
|
||||
activityTargets ActivityTarget[]
|
||||
comments Comment[]
|
||||
attachments Attachment[]
|
||||
author User @relation(fields: [authorId], references: [id], name: "authoredActivities")
|
||||
authorId String
|
||||
assignee User? @relation(fields: [assigneeId], references: [id], name: "assignedActivities")
|
||||
assigneeId String?
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id])
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
workspaceId String
|
||||
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
deletedAt DateTime?
|
||||
@ -342,9 +342,12 @@ model CommentThread {
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
attachments Attachment[]
|
||||
@@map("activities")
|
||||
}
|
||||
|
||||
@@map("comment_threads")
|
||||
enum CommentableType {
|
||||
Person
|
||||
Company
|
||||
}
|
||||
|
||||
model Comment {
|
||||
@ -354,12 +357,13 @@ model Comment {
|
||||
/// @Validator.IsString()
|
||||
body String
|
||||
|
||||
author User @relation(fields: [authorId], references: [id])
|
||||
author User @relation(fields: [authorId], references: [id])
|
||||
authorId String
|
||||
commentThread CommentThread @relation(fields: [commentThreadId], references: [id], onDelete: Cascade)
|
||||
activity Activity? @relation(fields: [activityId], references: [id], onDelete: Cascade)
|
||||
activityId String?
|
||||
commentThreadId String
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id])
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id])
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
workspaceId String
|
||||
|
||||
@ -372,24 +376,25 @@ model Comment {
|
||||
@@map("comments")
|
||||
}
|
||||
|
||||
enum CommentableType {
|
||||
Person
|
||||
Company
|
||||
}
|
||||
|
||||
model CommentThreadTarget {
|
||||
model ActivityTarget {
|
||||
/// @Validator.IsString()
|
||||
/// @Validator.IsOptional()
|
||||
id String @id @default(uuid())
|
||||
|
||||
commentThread CommentThread @relation(fields: [commentThreadId], references: [id], onDelete: Cascade)
|
||||
commentThreadId String
|
||||
/// @TypeGraphQL.omit(input: true, output: false)
|
||||
workspace Workspace? @relation(fields: [workspaceId], references: [id])
|
||||
activity Activity @relation(fields: [activityId], references: [id], onDelete: Cascade)
|
||||
activityId String
|
||||
commentableType CommentableType?
|
||||
commentableId String?
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
workspaceId String?
|
||||
commentableType CommentableType
|
||||
commentableId String
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id])
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
workspaceId String
|
||||
|
||||
personId String?
|
||||
person Person? @relation(fields: [personId], references: [id])
|
||||
|
||||
companyId String?
|
||||
company Company? @relation(fields: [companyId], references: [id])
|
||||
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
deletedAt DateTime?
|
||||
@ -397,7 +402,7 @@ model CommentThreadTarget {
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@map("comment_thread_targets")
|
||||
@@map("activity_targets")
|
||||
}
|
||||
|
||||
model Pipeline {
|
||||
@ -515,17 +520,16 @@ model Attachment {
|
||||
type AttachmentType
|
||||
name String
|
||||
|
||||
authorId String
|
||||
author User @relation(fields: [authorId], references: [id], name: "authoredAttachments")
|
||||
|
||||
activityId String
|
||||
activity CommentThread @relation(fields: [activityId], references: [id])
|
||||
|
||||
/// @TypeGraphQL.omit(input: true, output: false)
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id])
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
workspaceId String
|
||||
|
||||
author User @relation(fields: [authorId], references: [id], name: "authoredAttachments")
|
||||
authorId String
|
||||
activity Activity @relation(fields: [activityId], references: [id])
|
||||
activityId String
|
||||
|
||||
/// @TypeGraphQL.omit(input: true, output: false)
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id])
|
||||
/// @TypeGraphQL.omit(input: true, output: true)
|
||||
deletedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
export const seedComments = async (prisma: PrismaClient) => {
|
||||
await prisma.commentThread.upsert({
|
||||
await prisma.activity.upsert({
|
||||
where: { id: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfb400' },
|
||||
update: {},
|
||||
create: {
|
||||
@ -13,15 +13,15 @@ export const seedComments = async (prisma: PrismaClient) => {
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.commentThreadTarget.upsert({
|
||||
await prisma.activityTarget.upsert({
|
||||
where: { id: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfb600' },
|
||||
update: {},
|
||||
create: {
|
||||
id: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfb600',
|
||||
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
|
||||
commentableType: 'Company',
|
||||
commentableId: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfa408',
|
||||
commentThreadId: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfb400',
|
||||
activityId: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfb400',
|
||||
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
|
||||
},
|
||||
});
|
||||
|
||||
@ -32,6 +32,7 @@ export const seedComments = async (prisma: PrismaClient) => {
|
||||
id: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfb200',
|
||||
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
|
||||
body: 'Hi Félix ! How do you like your Twenty workspace?',
|
||||
activityId: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfb400',
|
||||
commentThreadId: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfb400',
|
||||
authorId: 'twenty-ge256b39-3ec3-4fe3-8997-b76aa0bfa408',
|
||||
},
|
||||
@ -44,12 +45,13 @@ export const seedComments = async (prisma: PrismaClient) => {
|
||||
id: 'twenty-fe256b40-3ec3-4fe3-8997-b76aa0bfb200',
|
||||
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
|
||||
body: 'I love it!',
|
||||
activityId: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfb400',
|
||||
commentThreadId: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfb400',
|
||||
authorId: 'twenty-gk256b39-3ec3-4fe3-8997-b76aa0bfa408',
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.commentThread.upsert({
|
||||
await prisma.activity.upsert({
|
||||
where: { id: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfc408' },
|
||||
update: {},
|
||||
create: {
|
||||
@ -64,15 +66,15 @@ export const seedComments = async (prisma: PrismaClient) => {
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.commentThreadTarget.upsert({
|
||||
await prisma.activityTarget.upsert({
|
||||
where: { id: 'twenty-fe256b39-3ec3-4fe3-8997-a76aa0bfb600' },
|
||||
update: {},
|
||||
create: {
|
||||
id: 'twenty-fe256b39-3ec3-4fe3-8997-a76aa0bfb600',
|
||||
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
|
||||
commentableType: 'Person',
|
||||
commentableId: 'twenty-755035db-623d-41fe-92e7-dd45b7c568e1',
|
||||
commentThreadId: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfc408',
|
||||
activityId: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfc408',
|
||||
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
|
||||
},
|
||||
});
|
||||
|
||||
@ -83,12 +85,13 @@ export const seedComments = async (prisma: PrismaClient) => {
|
||||
id: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfb100',
|
||||
workspaceId: 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419',
|
||||
body: 'I really like this comment thread feature!',
|
||||
activityId: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfc408',
|
||||
commentThreadId: 'twenty-fe256b39-3ec3-4fe3-8997-b76aa0bfc408',
|
||||
authorId: 'twenty-ge256b39-3ec3-4fe3-8997-b76aa0bfa408',
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.commentThread.upsert({
|
||||
await prisma.activity.upsert({
|
||||
where: { id: 'twenty-dev-fe256b39-3ec3-4fe3-8997-b76aaabfb408' },
|
||||
update: {},
|
||||
create: {
|
||||
@ -100,15 +103,15 @@ export const seedComments = async (prisma: PrismaClient) => {
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.commentThreadTarget.upsert({
|
||||
await prisma.activityTarget.upsert({
|
||||
where: { id: 'twenty-dev-fe256b39-3ec3-4fe3-8997-a76aa0bfba00' },
|
||||
update: {},
|
||||
create: {
|
||||
id: 'twenty-dev-fe256b39-3ec3-4fe3-8997-a76aa0bfba00',
|
||||
workspaceId: 'twenty-dev-7ed9d212-1c25-4d02-bf25-6aeccf7ea420',
|
||||
commentableType: 'Company',
|
||||
commentableId: 'twenty-dev-a674fa6c-1455-4c57-afaf-dd5dc086361e',
|
||||
commentThreadId: 'twenty-dev-fe256b39-3ec3-4fe3-8997-b76aaabfb408',
|
||||
activityId: 'twenty-dev-fe256b39-3ec3-4fe3-8997-b76aaabfb408',
|
||||
workspaceId: 'twenty-dev-7ed9d212-1c25-4d02-bf25-6aeccf7ea420',
|
||||
},
|
||||
});
|
||||
|
||||
@ -119,6 +122,7 @@ export const seedComments = async (prisma: PrismaClient) => {
|
||||
id: 'twenty-dev-fe256b39-3ec3-4fe3-8997-b76aa0bfb000',
|
||||
workspaceId: 'twenty-dev-7ed9d212-1c25-4d02-bf25-6aeccf7ea420',
|
||||
body: 'I really like this comment thread feature!',
|
||||
activityId: 'twenty-dev-fe256b39-3ec3-4fe3-8997-b76aaabfb408',
|
||||
commentThreadId: 'twenty-dev-fe256b39-3ec3-4fe3-8997-b76aaabfb408',
|
||||
authorId: 'twenty-dev-gk256b39-3ec3-4fe3-8997-b76aa0boa408',
|
||||
},
|
||||
|
||||
@ -9,9 +9,9 @@ export type ModelSelectMap = {
|
||||
Company: Prisma.CompanySelect;
|
||||
Person: Prisma.PersonSelect;
|
||||
RefreshToken: Prisma.RefreshTokenSelect;
|
||||
CommentThread: Prisma.CommentThreadSelect;
|
||||
Activity: Prisma.ActivitySelect;
|
||||
Comment: Prisma.CommentSelect;
|
||||
CommentThreadTarget: Prisma.CommentThreadTargetSelect;
|
||||
ActivityTarget: Prisma.ActivityTargetSelect;
|
||||
Pipeline: Prisma.PipelineSelect;
|
||||
PipelineStage: Prisma.PipelineStageSelect;
|
||||
PipelineProgress: Prisma.PipelineProgressSelect;
|
||||
|
||||
Reference in New Issue
Block a user