forked from github/plane
refactor: issue peek overview (#3001)
* refactor: peek overview components * fix: issue reactions * chore: update comment types * fix: access sepcifier value * chore: remove unused vars * fix: build errors * build-error: build error resolved --------- Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com> Co-authored-by: gurusainath <gurusainath007@gmail.com>
This commit is contained in:
parent
5fdd2ac366
commit
196e994519
@ -10,7 +10,7 @@ import { IssueService, IssueCommentService } from "services/issue";
|
|||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IIssueComment } from "types";
|
import { IIssue, IIssueActivity } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
|
import { PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ export const InboxIssueActivity: React.FC<Props> = observer(({ issueDetails }) =
|
|||||||
|
|
||||||
const user = userStore.currentUser;
|
const user = userStore.currentUser;
|
||||||
|
|
||||||
const handleCommentUpdate = async (commentId: string, data: Partial<IIssueComment>) => {
|
const handleCommentUpdate = async (commentId: string, data: Partial<any>) => {
|
||||||
if (!workspaceSlug || !projectId || !issueDetails.id || !user) return;
|
if (!workspaceSlug || !projectId || !issueDetails.id || !user) return;
|
||||||
|
|
||||||
await issueCommentService
|
await issueCommentService
|
||||||
@ -86,7 +86,7 @@ export const InboxIssueActivity: React.FC<Props> = observer(({ issueDetails }) =
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddComment = async (formData: IIssueComment) => {
|
const handleAddComment = async (formData: IIssueActivity) => {
|
||||||
if (!workspaceSlug || !issueDetails || !user) return;
|
if (!workspaceSlug || !issueDetails || !user) return;
|
||||||
|
|
||||||
await issueCommentService
|
await issueCommentService
|
||||||
|
@ -37,13 +37,10 @@ export const InboxMainContent: React.FC = observer(() => {
|
|||||||
const {
|
const {
|
||||||
inboxIssues: inboxIssuesStore,
|
inboxIssues: inboxIssuesStore,
|
||||||
inboxIssueDetails: inboxIssueDetailsStore,
|
inboxIssueDetails: inboxIssueDetailsStore,
|
||||||
user: userStore,
|
user: { currentUser, currentProjectRole },
|
||||||
projectState: { states },
|
projectState: { states },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const user = userStore.currentUser;
|
|
||||||
const userRole = userStore.currentProjectRole;
|
|
||||||
|
|
||||||
const { reset, control, watch } = useForm<IIssue>({
|
const { reset, control, watch } = useForm<IIssue>({
|
||||||
defaultValues,
|
defaultValues,
|
||||||
});
|
});
|
||||||
@ -156,7 +153,7 @@ export const InboxMainContent: React.FC = observer(() => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const isAllowed = !!userRole && userRole >= EUserWorkspaceRoles.MEMBER;
|
const isAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -249,12 +246,17 @@ export const InboxMainContent: React.FC = observer(() => {
|
|||||||
id: issueDetails.id,
|
id: issueDetails.id,
|
||||||
}}
|
}}
|
||||||
handleFormSubmit={submitChanges}
|
handleFormSubmit={submitChanges}
|
||||||
isAllowed={isAllowed || user?.id === issueDetails.created_by}
|
isAllowed={isAllowed || currentUser?.id === issueDetails.created_by}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<IssueReaction projectId={projectId} workspaceSlug={workspaceSlug} issueId={issueDetails.id} />
|
{workspaceSlug && projectId && (
|
||||||
|
<IssueReaction
|
||||||
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
|
projectId={projectId.toString()}
|
||||||
|
issueId={issueDetails.id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<InboxIssueActivity issueDetails={issueDetails} />
|
<InboxIssueActivity issueDetails={issueDetails} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -11,12 +11,12 @@ import { Loader, Tooltip } from "@plane/ui";
|
|||||||
// helpers
|
// helpers
|
||||||
import { render24HourFormatTime, renderLongDateFormat, timeAgo } from "helpers/date-time.helper";
|
import { render24HourFormatTime, renderLongDateFormat, timeAgo } from "helpers/date-time.helper";
|
||||||
// types
|
// types
|
||||||
import { IIssueActivity, IIssueComment } from "types";
|
import { IIssueActivity } from "types";
|
||||||
import { History } from "lucide-react";
|
import { History } from "lucide-react";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
activity: IIssueActivity[] | undefined;
|
activity: IIssueActivity[] | undefined;
|
||||||
handleCommentUpdate: (commentId: string, data: Partial<IIssueComment>) => Promise<void>;
|
handleCommentUpdate: (commentId: string, data: Partial<IIssueActivity>) => Promise<void>;
|
||||||
handleCommentDelete: (commentId: string) => Promise<void>;
|
handleCommentDelete: (commentId: string) => Promise<void>;
|
||||||
showAccessSpecifier?: boolean;
|
showAccessSpecifier?: boolean;
|
||||||
};
|
};
|
||||||
@ -130,7 +130,7 @@ export const IssueActivitySection: React.FC<Props> = ({
|
|||||||
return (
|
return (
|
||||||
<div key={activityItem.id} className="mt-4">
|
<div key={activityItem.id} className="mt-4">
|
||||||
<CommentCard
|
<CommentCard
|
||||||
comment={activityItem as IIssueComment}
|
comment={activityItem as IIssueActivity}
|
||||||
handleCommentDeletion={handleCommentDelete}
|
handleCommentDeletion={handleCommentDelete}
|
||||||
onSubmit={handleCommentUpdate}
|
onSubmit={handleCommentUpdate}
|
||||||
showAccessSpecifier={showAccessSpecifier}
|
showAccessSpecifier={showAccessSpecifier}
|
||||||
|
@ -11,17 +11,17 @@ import { Button } from "@plane/ui";
|
|||||||
import { Globe2, Lock } from "lucide-react";
|
import { Globe2, Lock } from "lucide-react";
|
||||||
|
|
||||||
// types
|
// types
|
||||||
import type { IIssueComment } from "types";
|
import type { IIssueActivity } from "types";
|
||||||
import useEditorSuggestions from "hooks/use-editor-suggestions";
|
import useEditorSuggestions from "hooks/use-editor-suggestions";
|
||||||
|
|
||||||
const defaultValues: Partial<IIssueComment> = {
|
const defaultValues: Partial<IIssueActivity> = {
|
||||||
access: "INTERNAL",
|
access: "INTERNAL",
|
||||||
comment_html: "",
|
comment_html: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
onSubmit: (data: IIssueComment) => Promise<void>;
|
onSubmit: (data: IIssueActivity) => Promise<void>;
|
||||||
showAccessSpecifier?: boolean;
|
showAccessSpecifier?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -59,9 +59,9 @@ export const AddComment: React.FC<Props> = ({ disabled = false, onSubmit, showAc
|
|||||||
formState: { isSubmitting },
|
formState: { isSubmitting },
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
reset,
|
reset,
|
||||||
} = useForm<IIssueComment>({ defaultValues });
|
} = useForm<IIssueActivity>({ defaultValues });
|
||||||
|
|
||||||
const handleAddComment = async (formData: IIssueComment) => {
|
const handleAddComment = async (formData: IIssueActivity) => {
|
||||||
if (!formData.comment_html || isSubmitting) return;
|
if (!formData.comment_html || isSubmitting) return;
|
||||||
|
|
||||||
await onSubmit(formData).then(() => {
|
await onSubmit(formData).then(() => {
|
||||||
@ -96,7 +96,7 @@ export const AddComment: React.FC<Props> = ({ disabled = false, onSubmit, showAc
|
|||||||
onChange={(comment_json: Object, comment_html: string) => onCommentChange(comment_html)}
|
onChange={(comment_json: Object, comment_html: string) => onCommentChange(comment_html)}
|
||||||
commentAccessSpecifier={
|
commentAccessSpecifier={
|
||||||
showAccessSpecifier
|
showAccessSpecifier
|
||||||
? { accessValue, onAccessChange, showAccessSpecifier, commentAccess }
|
? { accessValue: accessValue ?? "INTERNAL", onAccessChange, showAccessSpecifier, commentAccess }
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
mentionSuggestions={editorSuggestions.mentionSuggestions}
|
mentionSuggestions={editorSuggestions.mentionSuggestions}
|
||||||
|
@ -14,16 +14,16 @@ import { LiteTextEditorWithRef, LiteReadOnlyEditorWithRef } from "@plane/lite-te
|
|||||||
// helpers
|
// helpers
|
||||||
import { timeAgo } from "helpers/date-time.helper";
|
import { timeAgo } from "helpers/date-time.helper";
|
||||||
// types
|
// types
|
||||||
import type { IIssueComment } from "types";
|
import type { IIssueActivity } from "types";
|
||||||
import useEditorSuggestions from "hooks/use-editor-suggestions";
|
import useEditorSuggestions from "hooks/use-editor-suggestions";
|
||||||
|
|
||||||
// services
|
// services
|
||||||
const fileService = new FileService();
|
const fileService = new FileService();
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
comment: IIssueComment;
|
comment: IIssueActivity;
|
||||||
handleCommentDeletion: (comment: string) => void;
|
handleCommentDeletion: (comment: string) => void;
|
||||||
onSubmit: (commentId: string, data: Partial<IIssueComment>) => void;
|
onSubmit: (commentId: string, data: Partial<IIssueActivity>) => void;
|
||||||
showAccessSpecifier?: boolean;
|
showAccessSpecifier?: boolean;
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
};
|
};
|
||||||
@ -50,11 +50,11 @@ export const CommentCard: React.FC<Props> = ({
|
|||||||
setFocus,
|
setFocus,
|
||||||
watch,
|
watch,
|
||||||
setValue,
|
setValue,
|
||||||
} = useForm<IIssueComment>({
|
} = useForm<IIssueActivity>({
|
||||||
defaultValues: comment,
|
defaultValues: comment,
|
||||||
});
|
});
|
||||||
|
|
||||||
const onEnter = (formData: Partial<IIssueComment>) => {
|
const onEnter = (formData: Partial<IIssueActivity>) => {
|
||||||
if (isSubmitting) return;
|
if (isSubmitting) return;
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
|
|
||||||
@ -110,13 +110,10 @@ export const CommentCard: React.FC<Props> = ({
|
|||||||
deleteFile={fileService.deleteImage}
|
deleteFile={fileService.deleteImage}
|
||||||
restoreFile={fileService.restoreImage}
|
restoreFile={fileService.restoreImage}
|
||||||
ref={editorRef}
|
ref={editorRef}
|
||||||
value={watch("comment_html")}
|
value={watch("comment_html") ?? ""}
|
||||||
debouncedUpdatesEnabled={false}
|
debouncedUpdatesEnabled={false}
|
||||||
customClassName="min-h-[50px] p-3 shadow-sm"
|
customClassName="min-h-[50px] p-3 shadow-sm"
|
||||||
onChange={(comment_json: Object, comment_html: string) => {
|
onChange={(comment_json: Object, comment_html: string) => setValue("comment_html", comment_html)}
|
||||||
setValue("comment_json", comment_json);
|
|
||||||
setValue("comment_html", comment_html);
|
|
||||||
}}
|
|
||||||
mentionSuggestions={editorSuggestions.mentionSuggestions}
|
mentionSuggestions={editorSuggestions.mentionSuggestions}
|
||||||
mentionHighlights={editorSuggestions.mentionHighlights}
|
mentionHighlights={editorSuggestions.mentionHighlights}
|
||||||
/>
|
/>
|
||||||
@ -147,7 +144,7 @@ export const CommentCard: React.FC<Props> = ({
|
|||||||
)}
|
)}
|
||||||
<LiteReadOnlyEditorWithRef
|
<LiteReadOnlyEditorWithRef
|
||||||
ref={showEditorRef}
|
ref={showEditorRef}
|
||||||
value={comment.comment_html}
|
value={comment.comment_html ?? ""}
|
||||||
customClassName="text-xs border border-custom-border-200 bg-custom-background-100"
|
customClassName="text-xs border border-custom-border-200 bg-custom-background-100"
|
||||||
mentionHighlights={editorSuggestions.mentionHighlights}
|
mentionHighlights={editorSuggestions.mentionHighlights}
|
||||||
/>
|
/>
|
||||||
|
@ -6,7 +6,7 @@ import { Dialog, Transition } from "@headlessui/react";
|
|||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// services
|
// services
|
||||||
import { IssueService, IssueDraftService } from "services/issue";
|
import { IssueService } from "services/issue";
|
||||||
import { ModuleService } from "services/module.service";
|
import { ModuleService } from "services/module.service";
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
|
@ -7,7 +7,7 @@ export * from "./delete-issue-modal";
|
|||||||
export * from "./description-form";
|
export * from "./description-form";
|
||||||
export * from "./form";
|
export * from "./form";
|
||||||
export * from "./issue-layouts";
|
export * from "./issue-layouts";
|
||||||
export * from "./issue-peek-overview";
|
export * from "./peek-overview";
|
||||||
export * from "./main-content";
|
export * from "./main-content";
|
||||||
export * from "./modal";
|
export * from "./modal";
|
||||||
export * from "./parent-issues-list-modal";
|
export * from "./parent-issues-list-modal";
|
||||||
|
@ -31,9 +31,9 @@ interface IBaseCalendarRoot {
|
|||||||
calendarViewStore: IIssueCalendarViewStore;
|
calendarViewStore: IIssueCalendarViewStore;
|
||||||
QuickActions: FC<IQuickActionProps>;
|
QuickActions: FC<IQuickActionProps>;
|
||||||
issueActions: {
|
issueActions: {
|
||||||
[EIssueActions.DELETE]: (issue: IIssue) => void;
|
[EIssueActions.DELETE]: (issue: IIssue) => Promise<void>;
|
||||||
[EIssueActions.UPDATE]?: (issue: IIssue) => void;
|
[EIssueActions.UPDATE]?: (issue: IIssue) => Promise<void>;
|
||||||
[EIssueActions.REMOVE]?: (issue: IIssue) => void;
|
[EIssueActions.REMOVE]?: (issue: IIssue) => Promise<void>;
|
||||||
};
|
};
|
||||||
viewId?: string;
|
viewId?: string;
|
||||||
handleDragDrop: (source: any, destination: any, issues: any, issueWithIds: any) => void;
|
handleDragDrop: (source: any, destination: any, issues: any, issueWithIds: any) => void;
|
||||||
@ -64,9 +64,9 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleIssues = useCallback(
|
const handleIssues = useCallback(
|
||||||
(date: string, issue: IIssue, action: EIssueActions) => {
|
async (date: string, issue: IIssue, action: EIssueActions) => {
|
||||||
if (issueActions[action]) {
|
if (issueActions[action]) {
|
||||||
issueActions[action]!(issue);
|
await issueActions[action]!(issue);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[issueActions]
|
[issueActions]
|
||||||
@ -108,8 +108,8 @@ export const BaseCalendarRoot = observer((props: IBaseCalendarRoot) => {
|
|||||||
workspaceSlug={workspaceSlug.toString()}
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
projectId={peekProjectId.toString()}
|
projectId={peekProjectId.toString()}
|
||||||
issueId={peekIssueId.toString()}
|
issueId={peekIssueId.toString()}
|
||||||
handleIssue={(issueToUpdate) =>
|
handleIssue={async (issueToUpdate) =>
|
||||||
handleIssues(issueToUpdate.target_date ?? "", issueToUpdate as IIssue, EIssueActions.UPDATE)
|
await handleIssues(issueToUpdate.target_date ?? "", issueToUpdate as IIssue, EIssueActions.UPDATE)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -14,46 +14,50 @@ export const CycleCalendarLayout: React.FC = observer(() => {
|
|||||||
cycleIssues: cycleIssueStore,
|
cycleIssues: cycleIssueStore,
|
||||||
cycleIssuesFilter: cycleIssueFilterStore,
|
cycleIssuesFilter: cycleIssueFilterStore,
|
||||||
cycleIssueCalendarView: cycleIssueCalendarViewStore,
|
cycleIssueCalendarView: cycleIssueCalendarViewStore,
|
||||||
calendarHelpers: calendarHelperStore,
|
calendarHelpers: { handleDragDrop: handleCalenderDragDrop },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, cycleId } = router.query as {
|
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||||
workspaceSlug: string;
|
|
||||||
projectId: string;
|
|
||||||
cycleId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const issueActions = {
|
const issueActions = {
|
||||||
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug || !cycleId) return;
|
if (!workspaceSlug || !cycleId) return;
|
||||||
|
|
||||||
cycleIssueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue, cycleId);
|
await cycleIssueStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue, cycleId.toString());
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug || !cycleId) return;
|
if (!workspaceSlug || !cycleId) return;
|
||||||
cycleIssueStore.removeIssue(workspaceSlug, issue.project, issue.id, cycleId);
|
await cycleIssueStore.removeIssue(workspaceSlug.toString(), issue.project, issue.id, cycleId.toString());
|
||||||
},
|
},
|
||||||
[EIssueActions.REMOVE]: async (issue: IIssue) => {
|
[EIssueActions.REMOVE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug || !cycleId || !issue.bridge_id) return;
|
if (!workspaceSlug || !cycleId || !projectId || !issue.bridge_id) return;
|
||||||
cycleIssueStore.removeIssueFromCycle(workspaceSlug, issue.project, cycleId, issue.id, issue.bridge_id);
|
await cycleIssueStore.removeIssueFromCycle(
|
||||||
|
workspaceSlug.toString(),
|
||||||
|
issue.project,
|
||||||
|
cycleId.toString(),
|
||||||
|
issue.id,
|
||||||
|
issue.bridge_id
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDragDrop = (source: any, destination: any, issues: IIssue[], issueWithIds: any) => {
|
const handleDragDrop = (source: any, destination: any, issues: IIssue[], issueWithIds: any) => {
|
||||||
if (calendarHelperStore.handleDragDrop)
|
if (workspaceSlug && projectId && cycleId)
|
||||||
calendarHelperStore.handleDragDrop(
|
handleCalenderDragDrop(
|
||||||
source,
|
source,
|
||||||
destination,
|
destination,
|
||||||
workspaceSlug,
|
workspaceSlug.toString(),
|
||||||
projectId,
|
projectId.toString(),
|
||||||
cycleIssueStore,
|
cycleIssueStore,
|
||||||
issues,
|
issues,
|
||||||
issueWithIds,
|
issueWithIds,
|
||||||
cycleId
|
cycleId.toString()
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!cycleId) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseCalendarRoot
|
<BaseCalendarRoot
|
||||||
issueStore={cycleIssueStore}
|
issueStore={cycleIssueStore}
|
||||||
@ -61,7 +65,7 @@ export const CycleCalendarLayout: React.FC = observer(() => {
|
|||||||
calendarViewStore={cycleIssueCalendarViewStore}
|
calendarViewStore={cycleIssueCalendarViewStore}
|
||||||
QuickActions={CycleIssueQuickActions}
|
QuickActions={CycleIssueQuickActions}
|
||||||
issueActions={issueActions}
|
issueActions={issueActions}
|
||||||
viewId={cycleId}
|
viewId={cycleId.toString()}
|
||||||
handleDragDrop={handleDragDrop}
|
handleDragDrop={handleDragDrop}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -14,7 +14,7 @@ export const ModuleCalendarLayout: React.FC = observer(() => {
|
|||||||
moduleIssues: moduleIssueStore,
|
moduleIssues: moduleIssueStore,
|
||||||
moduleIssuesFilter: moduleIssueFilterStore,
|
moduleIssuesFilter: moduleIssueFilterStore,
|
||||||
moduleIssueCalendarView: moduleIssueCalendarViewStore,
|
moduleIssueCalendarView: moduleIssueCalendarViewStore,
|
||||||
calendarHelpers: calendarHelperStore,
|
calendarHelpers: { handleDragDrop: handleCalenderDragDrop },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -25,32 +25,31 @@ export const ModuleCalendarLayout: React.FC = observer(() => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const issueActions = {
|
const issueActions = {
|
||||||
[EIssueActions.UPDATE]: (issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug || !moduleId) return;
|
if (!workspaceSlug || !moduleId) return;
|
||||||
moduleIssueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue, moduleId);
|
await moduleIssueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue, moduleId);
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: (issue: IIssue) => {
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug || !moduleId) return;
|
if (!workspaceSlug || !moduleId) return;
|
||||||
moduleIssueStore.removeIssue(workspaceSlug, issue.project, issue.id, moduleId);
|
await moduleIssueStore.removeIssue(workspaceSlug, issue.project, issue.id, moduleId);
|
||||||
},
|
},
|
||||||
[EIssueActions.REMOVE]: (issue: IIssue) => {
|
[EIssueActions.REMOVE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug || !moduleId || !issue.bridge_id) return;
|
if (!workspaceSlug || !moduleId || !issue.bridge_id) return;
|
||||||
moduleIssueStore.removeIssueFromModule(workspaceSlug, issue.project, moduleId, issue.id, issue.bridge_id);
|
await moduleIssueStore.removeIssueFromModule(workspaceSlug, issue.project, moduleId, issue.id, issue.bridge_id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDragDrop = (source: any, destination: any, issues: IIssue[], issueWithIds: any) => {
|
const handleDragDrop = (source: any, destination: any, issues: IIssue[], issueWithIds: any) => {
|
||||||
if (calendarHelperStore.handleDragDrop)
|
handleCalenderDragDrop(
|
||||||
calendarHelperStore.handleDragDrop(
|
source,
|
||||||
source,
|
destination,
|
||||||
destination,
|
workspaceSlug,
|
||||||
workspaceSlug,
|
projectId,
|
||||||
projectId,
|
moduleIssueStore,
|
||||||
moduleIssueStore,
|
issues,
|
||||||
issues,
|
issueWithIds,
|
||||||
issueWithIds,
|
moduleId
|
||||||
moduleId
|
);
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -10,35 +10,35 @@ import { useRouter } from "next/router";
|
|||||||
|
|
||||||
export const CalendarLayout: React.FC = observer(() => {
|
export const CalendarLayout: React.FC = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
projectIssues: issueStore,
|
projectIssues: issueStore,
|
||||||
issueCalendarView: issueCalendarViewStore,
|
issueCalendarView: issueCalendarViewStore,
|
||||||
projectIssuesFilter: projectIssueFiltersStore,
|
projectIssuesFilter: projectIssueFiltersStore,
|
||||||
calendarHelpers: calendarHelperStore,
|
calendarHelpers: { handleDragDrop: handleCalenderDragDrop },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const issueActions = {
|
const issueActions = {
|
||||||
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
issueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue);
|
await issueStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
issueStore.removeIssue(workspaceSlug, issue.project, issue.id);
|
await issueStore.removeIssue(workspaceSlug.toString(), issue.project, issue.id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDragDrop = (source: any, destination: any, issues: IIssue[], issueWithIds: any) => {
|
const handleDragDrop = (source: any, destination: any, issues: IIssue[], issueWithIds: any) => {
|
||||||
if (calendarHelperStore.handleDragDrop)
|
if (workspaceSlug && projectId)
|
||||||
calendarHelperStore.handleDragDrop(
|
handleCalenderDragDrop(
|
||||||
source,
|
source,
|
||||||
destination,
|
destination,
|
||||||
workspaceSlug,
|
workspaceSlug.toString(),
|
||||||
projectId,
|
projectId.toString(),
|
||||||
issueStore,
|
issueStore,
|
||||||
issues,
|
issues,
|
||||||
issueWithIds
|
issueWithIds
|
||||||
|
@ -14,32 +14,32 @@ export const ProjectViewCalendarLayout: React.FC = observer(() => {
|
|||||||
viewIssues: projectViewIssuesStore,
|
viewIssues: projectViewIssuesStore,
|
||||||
viewIssuesFilter: projectIssueViewFiltersStore,
|
viewIssuesFilter: projectIssueViewFiltersStore,
|
||||||
projectViewIssueCalendarView: projectViewIssueCalendarViewStore,
|
projectViewIssueCalendarView: projectViewIssueCalendarViewStore,
|
||||||
calendarHelpers: calendarHelperStore,
|
calendarHelpers: { handleDragDrop: handleCalenderDragDrop },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId } = router.query as { workspaceSlug: string; projectId: string };
|
const { workspaceSlug, projectId } = router.query;
|
||||||
|
|
||||||
const issueActions = {
|
const issueActions = {
|
||||||
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
projectViewIssuesStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
|
await projectViewIssuesStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
projectViewIssuesStore.removeIssue(workspaceSlug.toString(), issue.project, issue.id);
|
await projectViewIssuesStore.removeIssue(workspaceSlug.toString(), issue.project, issue.id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDragDrop = (source: any, destination: any, issues: IIssue[], issueWithIds: any) => {
|
const handleDragDrop = (source: any, destination: any, issues: IIssue[], issueWithIds: any) => {
|
||||||
if (calendarHelperStore.handleDragDrop)
|
if (workspaceSlug && projectId)
|
||||||
calendarHelperStore.handleDragDrop(
|
handleCalenderDragDrop(
|
||||||
source,
|
source,
|
||||||
destination,
|
destination,
|
||||||
workspaceSlug,
|
workspaceSlug.toString(),
|
||||||
projectId,
|
projectId.toString(),
|
||||||
projectViewIssuesStore,
|
projectViewIssuesStore,
|
||||||
issues,
|
issues,
|
||||||
issueWithIds
|
issueWithIds
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
|
|
||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
@ -26,7 +25,6 @@ export const ProjectViewAppliedFiltersRoot: React.FC = observer(() => {
|
|||||||
projectState: projectStateStore,
|
projectState: projectStateStore,
|
||||||
projectMember: { projectMembers },
|
projectMember: { projectMembers },
|
||||||
projectViews: projectViewsStore,
|
projectViews: projectViewsStore,
|
||||||
projectViewFilters: projectViewFiltersStore,
|
|
||||||
viewIssuesFilter: { issueFilters, updateFilters },
|
viewIssuesFilter: { issueFilters, updateFilters },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
|
@ -54,11 +54,11 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
|
|||||||
|
|
||||||
const issues = issueIds.map((id) => issuesResponse?.[id]);
|
const issues = issueIds.map((id) => issuesResponse?.[id]);
|
||||||
|
|
||||||
const updateIssue = (issue: IIssue, payload: IBlockUpdateData) => {
|
const updateIssue = async (issue: IIssue, payload: IBlockUpdateData) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
//Todo fix sort order in the structure
|
//Todo fix sort order in the structure
|
||||||
issueStore.updateIssue(
|
await issueStore.updateIssue(
|
||||||
workspaceSlug.toString(),
|
workspaceSlug.toString(),
|
||||||
issue.project,
|
issue.project,
|
||||||
issue.id,
|
issue.id,
|
||||||
@ -101,9 +101,9 @@ export const BaseGanttRoot: React.FC<IBaseGanttRoot> = observer((props: IBaseGan
|
|||||||
workspaceSlug={workspaceSlug.toString()}
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
projectId={peekProjectId.toString()}
|
projectId={peekProjectId.toString()}
|
||||||
issueId={peekIssueId.toString()}
|
issueId={peekIssueId.toString()}
|
||||||
handleIssue={(issueToUpdate) => {
|
handleIssue={async (issueToUpdate) => {
|
||||||
// TODO: update the logic here
|
// TODO: update the logic here
|
||||||
updateIssue(issueToUpdate as IIssue, {});
|
await updateIssue(issueToUpdate as IIssue, {});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -52,9 +52,9 @@ export interface IBaseKanBanLayout {
|
|||||||
kanbanViewStore: IIssueKanBanViewStore;
|
kanbanViewStore: IIssueKanBanViewStore;
|
||||||
QuickActions: FC<IQuickActionProps>;
|
QuickActions: FC<IQuickActionProps>;
|
||||||
issueActions: {
|
issueActions: {
|
||||||
[EIssueActions.DELETE]: (issue: IIssue) => void;
|
[EIssueActions.DELETE]: (issue: IIssue) => Promise<void>;
|
||||||
[EIssueActions.UPDATE]?: (issue: IIssue) => void;
|
[EIssueActions.UPDATE]?: (issue: IIssue) => Promise<void>;
|
||||||
[EIssueActions.REMOVE]?: (issue: IIssue) => void;
|
[EIssueActions.REMOVE]?: (issue: IIssue) => Promise<void>;
|
||||||
};
|
};
|
||||||
showLoader?: boolean;
|
showLoader?: boolean;
|
||||||
viewId?: string;
|
viewId?: string;
|
||||||
@ -169,7 +169,7 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
|||||||
const handleIssues = useCallback(
|
const handleIssues = useCallback(
|
||||||
async (sub_group_by: string | null, group_by: string | null, issue: IIssue, action: EIssueActions) => {
|
async (sub_group_by: string | null, group_by: string | null, issue: IIssue, action: EIssueActions) => {
|
||||||
if (issueActions[action]) {
|
if (issueActions[action]) {
|
||||||
issueActions[action]!(issue);
|
await issueActions[action]!(issue);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[issueActions]
|
[issueActions]
|
||||||
@ -345,8 +345,8 @@ export const BaseKanBanRoot: React.FC<IBaseKanBanLayout> = observer((props: IBas
|
|||||||
workspaceSlug={workspaceSlug.toString()}
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
projectId={peekProjectId.toString()}
|
projectId={peekProjectId.toString()}
|
||||||
issueId={peekIssueId.toString()}
|
issueId={peekIssueId.toString()}
|
||||||
handleIssue={(issueToUpdate) =>
|
handleIssue={async (issueToUpdate) =>
|
||||||
handleIssues(sub_group_by, group_by, issueToUpdate as IIssue, EIssueActions.UPDATE)
|
await handleIssues(sub_group_by, group_by, issueToUpdate as IIssue, EIssueActions.UPDATE)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -17,11 +17,7 @@ export interface ICycleKanBanLayout {}
|
|||||||
|
|
||||||
export const CycleKanBanLayout: React.FC = observer(() => {
|
export const CycleKanBanLayout: React.FC = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, cycleId } = router.query as {
|
const { workspaceSlug, projectId, cycleId } = router.query;
|
||||||
workspaceSlug: string;
|
|
||||||
projectId: string;
|
|
||||||
cycleId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
// store
|
// store
|
||||||
const {
|
const {
|
||||||
@ -35,15 +31,23 @@ export const CycleKanBanLayout: React.FC = observer(() => {
|
|||||||
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug || !cycleId) return;
|
if (!workspaceSlug || !cycleId) return;
|
||||||
|
|
||||||
cycleIssueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue, cycleId);
|
await cycleIssueStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue, cycleId.toString());
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug || !cycleId) return;
|
if (!workspaceSlug || !cycleId) return;
|
||||||
cycleIssueStore.removeIssue(workspaceSlug, issue.project, issue.id, cycleId);
|
|
||||||
|
await cycleIssueStore.removeIssue(workspaceSlug.toString(), issue.project, issue.id, cycleId.toString());
|
||||||
},
|
},
|
||||||
[EIssueActions.REMOVE]: async (issue: IIssue) => {
|
[EIssueActions.REMOVE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug || !cycleId || !issue.bridge_id) return;
|
if (!workspaceSlug || !cycleId || !issue.bridge_id) return;
|
||||||
cycleIssueStore.removeIssueFromCycle(workspaceSlug, issue.project, cycleId, issue.id, issue.bridge_id);
|
|
||||||
|
await cycleIssueStore.removeIssueFromCycle(
|
||||||
|
workspaceSlug.toString(),
|
||||||
|
issue.project,
|
||||||
|
cycleId.toString(),
|
||||||
|
issue.id,
|
||||||
|
issue.bridge_id
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -55,18 +59,18 @@ export const CycleKanBanLayout: React.FC = observer(() => {
|
|||||||
issues: IIssueResponse | undefined,
|
issues: IIssueResponse | undefined,
|
||||||
issueWithIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined
|
issueWithIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined
|
||||||
) => {
|
) => {
|
||||||
if (kanBanHelperStore.handleDragDrop)
|
if (workspaceSlug && projectId && cycleId)
|
||||||
return await kanBanHelperStore.handleDragDrop(
|
return await kanBanHelperStore.handleDragDrop(
|
||||||
source,
|
source,
|
||||||
destination,
|
destination,
|
||||||
workspaceSlug,
|
workspaceSlug.toString(),
|
||||||
projectId,
|
projectId.toString(),
|
||||||
cycleIssueStore,
|
cycleIssueStore,
|
||||||
subGroupBy,
|
subGroupBy,
|
||||||
groupBy,
|
groupBy,
|
||||||
issues,
|
issues,
|
||||||
issueWithIds,
|
issueWithIds,
|
||||||
cycleId
|
cycleId.toString()
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -78,10 +82,12 @@ export const CycleKanBanLayout: React.FC = observer(() => {
|
|||||||
kanbanViewStore={cycleIssueKanBanViewStore}
|
kanbanViewStore={cycleIssueKanBanViewStore}
|
||||||
showLoader={true}
|
showLoader={true}
|
||||||
QuickActions={CycleIssueQuickActions}
|
QuickActions={CycleIssueQuickActions}
|
||||||
viewId={cycleId}
|
viewId={cycleId?.toString() ?? ""}
|
||||||
currentStore={EProjectStore.CYCLE}
|
currentStore={EProjectStore.CYCLE}
|
||||||
handleDragDrop={handleDragDrop}
|
handleDragDrop={handleDragDrop}
|
||||||
addIssuesToView={(issues: string[]) => cycleIssueStore.addIssueToCycle(workspaceSlug, cycleId, issues)}
|
addIssuesToView={(issues: string[]) =>
|
||||||
|
cycleIssueStore.addIssueToCycle(workspaceSlug?.toString() ?? "", cycleId?.toString() ?? "", issues)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -14,7 +14,7 @@ export interface IKanBanLayout {}
|
|||||||
|
|
||||||
export const DraftKanBanLayout: React.FC = observer(() => {
|
export const DraftKanBanLayout: React.FC = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query as { workspaceSlug: string };
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
projectDraftIssues: issueStore,
|
projectDraftIssues: issueStore,
|
||||||
@ -26,12 +26,12 @@ export const DraftKanBanLayout: React.FC = observer(() => {
|
|||||||
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
await issueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue);
|
await issueStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue);
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug) return;
|
if (!workspaceSlug) return;
|
||||||
|
|
||||||
await issueStore.removeIssue(workspaceSlug, issue.project, issue.id);
|
await issueStore.removeIssue(workspaceSlug.toString(), issue.project, issue.id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -17,11 +17,7 @@ export interface IModuleKanBanLayout {}
|
|||||||
|
|
||||||
export const ModuleKanBanLayout: React.FC = observer(() => {
|
export const ModuleKanBanLayout: React.FC = observer(() => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, moduleId } = router.query as {
|
const { workspaceSlug, projectId, moduleId } = router.query;
|
||||||
workspaceSlug: string;
|
|
||||||
projectId: string;
|
|
||||||
moduleId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
// store
|
// store
|
||||||
const {
|
const {
|
||||||
@ -35,15 +31,23 @@ export const ModuleKanBanLayout: React.FC = observer(() => {
|
|||||||
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug || !moduleId) return;
|
if (!workspaceSlug || !moduleId) return;
|
||||||
|
|
||||||
moduleIssueStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue, moduleId);
|
await moduleIssueStore.updateIssue(workspaceSlug.toString(), issue.project, issue.id, issue, moduleId.toString());
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
[EIssueActions.DELETE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug || !moduleId) return;
|
if (!workspaceSlug || !moduleId) return;
|
||||||
moduleIssueStore.removeIssue(workspaceSlug, issue.project, issue.id, moduleId);
|
|
||||||
|
await moduleIssueStore.removeIssue(workspaceSlug.toString(), issue.project, issue.id, moduleId.toString());
|
||||||
},
|
},
|
||||||
[EIssueActions.REMOVE]: async (issue: IIssue) => {
|
[EIssueActions.REMOVE]: async (issue: IIssue) => {
|
||||||
if (!workspaceSlug || !moduleId || !issue.bridge_id) return;
|
if (!workspaceSlug || !moduleId || !issue.bridge_id) return;
|
||||||
moduleIssueStore.removeIssueFromModule(workspaceSlug, issue.project, moduleId, issue.id, issue.bridge_id);
|
|
||||||
|
await moduleIssueStore.removeIssueFromModule(
|
||||||
|
workspaceSlug.toString(),
|
||||||
|
issue.project,
|
||||||
|
moduleId.toString(),
|
||||||
|
issue.id,
|
||||||
|
issue.bridge_id
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -55,18 +59,18 @@ export const ModuleKanBanLayout: React.FC = observer(() => {
|
|||||||
issues: IIssueResponse | undefined,
|
issues: IIssueResponse | undefined,
|
||||||
issueWithIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined
|
issueWithIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined
|
||||||
) => {
|
) => {
|
||||||
if (kanBanHelperStore.handleDragDrop)
|
if (workspaceSlug && projectId && moduleId)
|
||||||
return await kanBanHelperStore.handleDragDrop(
|
return await kanBanHelperStore.handleDragDrop(
|
||||||
source,
|
source,
|
||||||
destination,
|
destination,
|
||||||
workspaceSlug,
|
workspaceSlug.toString(),
|
||||||
projectId,
|
projectId.toString(),
|
||||||
moduleIssueStore,
|
moduleIssueStore,
|
||||||
subGroupBy,
|
subGroupBy,
|
||||||
groupBy,
|
groupBy,
|
||||||
issues,
|
issues,
|
||||||
issueWithIds,
|
issueWithIds,
|
||||||
moduleId
|
moduleId.toString()
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
@ -77,10 +81,12 @@ export const ModuleKanBanLayout: React.FC = observer(() => {
|
|||||||
kanbanViewStore={moduleIssueKanBanViewStore}
|
kanbanViewStore={moduleIssueKanBanViewStore}
|
||||||
showLoader={true}
|
showLoader={true}
|
||||||
QuickActions={ModuleIssueQuickActions}
|
QuickActions={ModuleIssueQuickActions}
|
||||||
viewId={moduleId}
|
viewId={moduleId?.toString() ?? ""}
|
||||||
currentStore={EProjectStore.MODULE}
|
currentStore={EProjectStore.MODULE}
|
||||||
handleDragDrop={handleDragDrop}
|
handleDragDrop={handleDragDrop}
|
||||||
addIssuesToView={(issues: string[]) => moduleIssueStore.addIssueToModule(workspaceSlug, moduleId, issues)}
|
addIssuesToView={(issues: string[]) =>
|
||||||
|
moduleIssueStore.addIssueToModule(workspaceSlug?.toString() ?? "", moduleId?.toString() ?? "", issues)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -45,20 +45,18 @@ export const KanBanLayout: React.FC = observer(() => {
|
|||||||
groupBy: string | null,
|
groupBy: string | null,
|
||||||
issues: IIssueResponse | undefined,
|
issues: IIssueResponse | undefined,
|
||||||
issueWithIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined
|
issueWithIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined
|
||||||
) => {
|
) =>
|
||||||
if (kanBanHelperStore.handleDragDrop)
|
await kanBanHelperStore.handleDragDrop(
|
||||||
return await kanBanHelperStore.handleDragDrop(
|
source,
|
||||||
source,
|
destination,
|
||||||
destination,
|
workspaceSlug,
|
||||||
workspaceSlug,
|
projectId,
|
||||||
projectId,
|
issueStore,
|
||||||
issueStore,
|
subGroupBy,
|
||||||
subGroupBy,
|
groupBy,
|
||||||
groupBy,
|
issues,
|
||||||
issues,
|
issueWithIds
|
||||||
issueWithIds
|
);
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseKanBanRoot
|
<BaseKanBanRoot
|
||||||
|
@ -45,20 +45,18 @@ export const ProjectViewKanBanLayout: React.FC = observer(() => {
|
|||||||
groupBy: string | null,
|
groupBy: string | null,
|
||||||
issues: IIssueResponse | undefined,
|
issues: IIssueResponse | undefined,
|
||||||
issueWithIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined
|
issueWithIds: IGroupedIssues | ISubGroupedIssues | TUnGroupedIssues | undefined
|
||||||
) => {
|
) =>
|
||||||
if (kanBanHelperStore.handleDragDrop)
|
await kanBanHelperStore.handleDragDrop(
|
||||||
return await kanBanHelperStore.handleDragDrop(
|
source,
|
||||||
source,
|
destination,
|
||||||
destination,
|
workspaceSlug,
|
||||||
workspaceSlug,
|
projectId,
|
||||||
projectId,
|
projectViewIssuesStore,
|
||||||
projectViewIssuesStore,
|
subGroupBy,
|
||||||
subGroupBy,
|
groupBy,
|
||||||
groupBy,
|
issues,
|
||||||
issues,
|
issueWithIds
|
||||||
issueWithIds
|
);
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseKanBanRoot
|
<BaseKanBanRoot
|
||||||
|
@ -50,9 +50,9 @@ interface IBaseListRoot {
|
|||||||
| IProfileIssuesStore;
|
| IProfileIssuesStore;
|
||||||
QuickActions: FC<IQuickActionProps>;
|
QuickActions: FC<IQuickActionProps>;
|
||||||
issueActions: {
|
issueActions: {
|
||||||
[EIssueActions.DELETE]: (group_by: string | null, issue: IIssue) => void;
|
[EIssueActions.DELETE]: (group_by: string | null, issue: IIssue) => Promise<void>;
|
||||||
[EIssueActions.UPDATE]?: (group_by: string | null, issue: IIssue) => void;
|
[EIssueActions.UPDATE]?: (group_by: string | null, issue: IIssue) => Promise<void>;
|
||||||
[EIssueActions.REMOVE]?: (group_by: string | null, issue: IIssue) => void;
|
[EIssueActions.REMOVE]?: (group_by: string | null, issue: IIssue) => Promise<void>;
|
||||||
};
|
};
|
||||||
getProjects: (projectStore: IProjectStore) => IProject[] | null;
|
getProjects: (projectStore: IProjectStore) => IProject[] | null;
|
||||||
viewId?: string;
|
viewId?: string;
|
||||||
@ -105,7 +105,7 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
|
|||||||
const members = projectMembers?.map((m) => m.member) ?? null;
|
const members = projectMembers?.map((m) => m.member) ?? null;
|
||||||
const handleIssues = async (issue: IIssue, action: EIssueActions) => {
|
const handleIssues = async (issue: IIssue, action: EIssueActions) => {
|
||||||
if (issueActions[action]) {
|
if (issueActions[action]) {
|
||||||
issueActions[action]!(group_by, issue);
|
await issueActions[action]!(group_by, issue);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ export const BaseListRoot = observer((props: IBaseListRoot) => {
|
|||||||
workspaceSlug={workspaceSlug.toString()}
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
projectId={peekProjectId.toString()}
|
projectId={peekProjectId.toString()}
|
||||||
issueId={peekIssueId.toString()}
|
issueId={peekIssueId.toString()}
|
||||||
handleIssue={(issueToUpdate) => handleIssues(issueToUpdate as IIssue, EIssueActions.UPDATE)}
|
handleIssue={async (issueToUpdate) => await handleIssues(issueToUpdate as IIssue, EIssueActions.UPDATE)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -21,10 +21,10 @@ export const ArchivedIssueListLayout: FC = observer(() => {
|
|||||||
useMobxStore();
|
useMobxStore();
|
||||||
|
|
||||||
const issueActions = {
|
const issueActions = {
|
||||||
[EIssueActions.DELETE]: (group_by: string | null, issue: IIssue) => {
|
[EIssueActions.DELETE]: async (group_by: string | null, issue: IIssue) => {
|
||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
|
|
||||||
archivedIssueStore.removeIssue(workspaceSlug, projectId, issue.id);
|
await archivedIssueStore.removeIssue(workspaceSlug, projectId, issue.id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,17 +22,20 @@ export const CycleListLayout: React.FC = observer(() => {
|
|||||||
const { cycleIssues: cycleIssueStore, cycleIssuesFilter: cycleIssueFilterStore } = useMobxStore();
|
const { cycleIssues: cycleIssueStore, cycleIssuesFilter: cycleIssueFilterStore } = useMobxStore();
|
||||||
|
|
||||||
const issueActions = {
|
const issueActions = {
|
||||||
[EIssueActions.UPDATE]: (group_by: string | null, issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (group_by: string | null, issue: IIssue) => {
|
||||||
if (!workspaceSlug || !cycleId) return;
|
if (!workspaceSlug || !cycleId) return;
|
||||||
cycleIssueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue, cycleId);
|
|
||||||
|
await cycleIssueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue, cycleId);
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: (group_by: string | null, issue: IIssue) => {
|
[EIssueActions.DELETE]: async (group_by: string | null, issue: IIssue) => {
|
||||||
if (!workspaceSlug || !cycleId) return;
|
if (!workspaceSlug || !cycleId) return;
|
||||||
cycleIssueStore.removeIssue(workspaceSlug, issue.project, issue.id, cycleId);
|
|
||||||
|
await cycleIssueStore.removeIssue(workspaceSlug, issue.project, issue.id, cycleId);
|
||||||
},
|
},
|
||||||
[EIssueActions.REMOVE]: (group_by: string | null, issue: IIssue) => {
|
[EIssueActions.REMOVE]: async (group_by: string | null, issue: IIssue) => {
|
||||||
if (!workspaceSlug || !cycleId || !issue.bridge_id) return;
|
if (!workspaceSlug || !cycleId || !issue.bridge_id) return;
|
||||||
cycleIssueStore.removeIssueFromCycle(workspaceSlug, issue.project, cycleId, issue.id, issue.bridge_id);
|
|
||||||
|
await cycleIssueStore.removeIssueFromCycle(workspaceSlug, issue.project, cycleId, issue.id, issue.bridge_id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const getProjects = (projectStore: IProjectStore) => {
|
const getProjects = (projectStore: IProjectStore) => {
|
||||||
|
@ -23,13 +23,15 @@ export const DraftIssueListLayout: FC = observer(() => {
|
|||||||
const { projectDraftIssuesFilter: projectIssuesFilterStore, projectDraftIssues: projectIssuesStore } = useMobxStore();
|
const { projectDraftIssuesFilter: projectIssuesFilterStore, projectDraftIssues: projectIssuesStore } = useMobxStore();
|
||||||
|
|
||||||
const issueActions = {
|
const issueActions = {
|
||||||
[EIssueActions.UPDATE]: (group_by: string | null, issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (group_by: string | null, issue: IIssue) => {
|
||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
projectIssuesStore.updateIssue(workspaceSlug, projectId, issue.id, issue);
|
|
||||||
|
await projectIssuesStore.updateIssue(workspaceSlug, projectId, issue.id, issue);
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: (group_by: string | null, issue: IIssue) => {
|
[EIssueActions.DELETE]: async (group_by: string | null, issue: IIssue) => {
|
||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
projectIssuesStore.removeIssue(workspaceSlug, projectId, issue.id);
|
|
||||||
|
await projectIssuesStore.removeIssue(workspaceSlug, projectId, issue.id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,17 +22,20 @@ export const ModuleListLayout: React.FC = observer(() => {
|
|||||||
const { moduleIssues: moduleIssueStore, moduleIssuesFilter: moduleIssueFilterStore } = useMobxStore();
|
const { moduleIssues: moduleIssueStore, moduleIssuesFilter: moduleIssueFilterStore } = useMobxStore();
|
||||||
|
|
||||||
const issueActions = {
|
const issueActions = {
|
||||||
[EIssueActions.UPDATE]: (group_by: string | null, issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (group_by: string | null, issue: IIssue) => {
|
||||||
if (!workspaceSlug || !moduleId) return;
|
if (!workspaceSlug || !moduleId) return;
|
||||||
moduleIssueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue, moduleId);
|
|
||||||
|
await moduleIssueStore.updateIssue(workspaceSlug, issue.project, issue.id, issue, moduleId);
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: (group_by: string | null, issue: IIssue) => {
|
[EIssueActions.DELETE]: async (group_by: string | null, issue: IIssue) => {
|
||||||
if (!workspaceSlug || !moduleId) return;
|
if (!workspaceSlug || !moduleId) return;
|
||||||
moduleIssueStore.removeIssue(workspaceSlug, issue.project, issue.id, moduleId);
|
|
||||||
|
await moduleIssueStore.removeIssue(workspaceSlug, issue.project, issue.id, moduleId);
|
||||||
},
|
},
|
||||||
[EIssueActions.REMOVE]: (group_by: string | null, issue: IIssue) => {
|
[EIssueActions.REMOVE]: async (group_by: string | null, issue: IIssue) => {
|
||||||
if (!workspaceSlug || !moduleId || !issue.bridge_id) return;
|
if (!workspaceSlug || !moduleId || !issue.bridge_id) return;
|
||||||
moduleIssueStore.removeIssueFromModule(workspaceSlug, issue.project, moduleId, issue.id, issue.bridge_id);
|
|
||||||
|
await moduleIssueStore.removeIssueFromModule(workspaceSlug, issue.project, moduleId, issue.id, issue.bridge_id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -23,13 +23,15 @@ export const ListLayout: FC = observer(() => {
|
|||||||
const { projectIssuesFilter: projectIssuesFilterStore, projectIssues: projectIssuesStore } = useMobxStore();
|
const { projectIssuesFilter: projectIssuesFilterStore, projectIssues: projectIssuesStore } = useMobxStore();
|
||||||
|
|
||||||
const issueActions = {
|
const issueActions = {
|
||||||
[EIssueActions.UPDATE]: (group_by: string | null, issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (group_by: string | null, issue: IIssue) => {
|
||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
projectIssuesStore.updateIssue(workspaceSlug, projectId, issue.id, issue);
|
|
||||||
|
await projectIssuesStore.updateIssue(workspaceSlug, projectId, issue.id, issue);
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: (group_by: string | null, issue: IIssue) => {
|
[EIssueActions.DELETE]: async (group_by: string | null, issue: IIssue) => {
|
||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
projectIssuesStore.removeIssue(workspaceSlug, projectId, issue.id);
|
|
||||||
|
await projectIssuesStore.removeIssue(workspaceSlug, projectId, issue.id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,13 +26,15 @@ export const ProjectViewListLayout: React.FC = observer(() => {
|
|||||||
if (!workspaceSlug || !projectId) return null;
|
if (!workspaceSlug || !projectId) return null;
|
||||||
|
|
||||||
const issueActions = {
|
const issueActions = {
|
||||||
[EIssueActions.UPDATE]: (group_by: string | null, issue: IIssue) => {
|
[EIssueActions.UPDATE]: async (group_by: string | null, issue: IIssue) => {
|
||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
projectViewIssueStore.updateIssue(workspaceSlug, projectId, issue.id, issue);
|
|
||||||
|
await projectViewIssueStore.updateIssue(workspaceSlug, projectId, issue.id, issue);
|
||||||
},
|
},
|
||||||
[EIssueActions.DELETE]: (group_by: string | null, issue: IIssue) => {
|
[EIssueActions.DELETE]: async (group_by: string | null, issue: IIssue) => {
|
||||||
if (!workspaceSlug || !projectId) return;
|
if (!workspaceSlug || !projectId) return;
|
||||||
projectViewIssueStore.removeIssue(workspaceSlug, projectId, issue.id);
|
|
||||||
|
await projectViewIssueStore.removeIssue(workspaceSlug, projectId, issue.id);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -21,8 +21,8 @@ type Props = {
|
|||||||
members?: IUserLite[] | undefined;
|
members?: IUserLite[] | undefined;
|
||||||
labels?: IIssueLabel[] | undefined;
|
labels?: IIssueLabel[] | undefined;
|
||||||
states?: IState[] | undefined;
|
states?: IState[] | undefined;
|
||||||
quickActions: (issue: IIssue,customActionButton?: React.ReactElement) => React.ReactNode;
|
quickActions: (issue: IIssue, customActionButton: any) => React.ReactNode; // TODO: replace any with type
|
||||||
handleIssues: (issue: IIssue, action: EIssueActions) => void;
|
handleIssues: (issue: IIssue, action: EIssueActions) => Promise<void>;
|
||||||
openIssuesListModal?: (() => void) | null;
|
openIssuesListModal?: (() => void) | null;
|
||||||
quickAddCallback?: (
|
quickAddCallback?: (
|
||||||
workspaceSlug: string,
|
workspaceSlug: string,
|
||||||
@ -189,7 +189,7 @@ export const SpreadsheetView: React.FC<Props> = observer((props) => {
|
|||||||
workspaceSlug={workspaceSlug.toString()}
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
projectId={peekProjectId.toString()}
|
projectId={peekProjectId.toString()}
|
||||||
issueId={peekIssueId.toString()}
|
issueId={peekIssueId.toString()}
|
||||||
handleIssue={(issueToUpdate: any) => handleIssues(issueToUpdate, EIssueActions.UPDATE)}
|
handleIssue={async (issueToUpdate: any) => await handleIssues(issueToUpdate, EIssueActions.UPDATE)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1 +0,0 @@
|
|||||||
export * from "./root";
|
|
@ -1,22 +1,25 @@
|
|||||||
// hooks
|
// hooks
|
||||||
import useUserAuth from "hooks/use-user-auth";
|
|
||||||
import useIssueReaction from "hooks/use-issue-reaction";
|
import useIssueReaction from "hooks/use-issue-reaction";
|
||||||
// components
|
// components
|
||||||
import { ReactionSelector } from "components/core";
|
import { ReactionSelector } from "components/core";
|
||||||
// string helpers
|
// string helpers
|
||||||
import { renderEmoji } from "helpers/emoji.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
|
|
||||||
// types
|
// types
|
||||||
type Props = {
|
type Props = {
|
||||||
workspaceSlug?: string | string[];
|
workspaceSlug: string;
|
||||||
projectId?: string | string[];
|
projectId: string;
|
||||||
issueId?: string | string[];
|
issueId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IssueReaction: React.FC<Props> = (props) => {
|
export const IssueReaction: React.FC<Props> = observer((props) => {
|
||||||
const { workspaceSlug, projectId, issueId } = props;
|
const { workspaceSlug, projectId, issueId } = props;
|
||||||
|
|
||||||
const { user } = useUserAuth();
|
const {
|
||||||
|
user: { currentUser },
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
const { reactions, groupedReactions, handleReactionCreate, handleReactionDelete } = useIssueReaction(
|
const { reactions, groupedReactions, handleReactionCreate, handleReactionDelete } = useIssueReaction(
|
||||||
workspaceSlug,
|
workspaceSlug,
|
||||||
@ -27,7 +30,7 @@ export const IssueReaction: React.FC<Props> = (props) => {
|
|||||||
const handleReactionClick = (reaction: string) => {
|
const handleReactionClick = (reaction: string) => {
|
||||||
if (!workspaceSlug || !projectId || !issueId) return;
|
if (!workspaceSlug || !projectId || !issueId) return;
|
||||||
|
|
||||||
const isSelected = reactions?.some((r) => r.actor === user?.id && r.reaction === reaction);
|
const isSelected = reactions?.some((r) => r.actor === currentUser?.id && r.reaction === reaction);
|
||||||
|
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
handleReactionDelete(reaction);
|
handleReactionDelete(reaction);
|
||||||
@ -41,7 +44,7 @@ export const IssueReaction: React.FC<Props> = (props) => {
|
|||||||
<ReactionSelector
|
<ReactionSelector
|
||||||
size="md"
|
size="md"
|
||||||
position="top"
|
position="top"
|
||||||
value={reactions?.filter((reaction) => reaction.actor === user?.id).map((r) => r.reaction) || []}
|
value={reactions?.filter((reaction) => reaction.actor === currentUser?.id).map((r) => r.reaction) || []}
|
||||||
onSelect={handleReactionClick}
|
onSelect={handleReactionClick}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -56,7 +59,7 @@ export const IssueReaction: React.FC<Props> = (props) => {
|
|||||||
}}
|
}}
|
||||||
key={reaction}
|
key={reaction}
|
||||||
className={`flex items-center gap-1 text-custom-text-100 text-sm h-full px-2 py-1 rounded-md ${
|
className={`flex items-center gap-1 text-custom-text-100 text-sm h-full px-2 py-1 rounded-md ${
|
||||||
reactions?.some((r) => r.actor === user?.id && r.reaction === reaction)
|
reactions?.some((r) => r.actor === currentUser?.id && r.reaction === reaction)
|
||||||
? "bg-custom-primary-100/10"
|
? "bg-custom-primary-100/10"
|
||||||
: "bg-custom-background-80"
|
: "bg-custom-background-80"
|
||||||
}`}
|
}`}
|
||||||
@ -64,7 +67,7 @@ export const IssueReaction: React.FC<Props> = (props) => {
|
|||||||
<span>{renderEmoji(reaction)}</span>
|
<span>{renderEmoji(reaction)}</span>
|
||||||
<span
|
<span
|
||||||
className={
|
className={
|
||||||
reactions?.some((r) => r.actor === user?.id && r.reaction === reaction)
|
reactions?.some((r) => r.actor === currentUser?.id && r.reaction === reaction)
|
||||||
? "text-custom-primary-100"
|
? "text-custom-primary-100"
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
@ -76,4 +79,4 @@ export const IssueReaction: React.FC<Props> = (props) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
@ -24,9 +24,10 @@ import { SubIssuesRoot } from "./sub-issues";
|
|||||||
// ui
|
// ui
|
||||||
import { CustomMenu, LayersIcon, StateGroupIcon } from "@plane/ui";
|
import { CustomMenu, LayersIcon, StateGroupIcon } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import { IIssue, IIssueComment } from "types";
|
import { IIssue, IIssueActivity } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { PROJECT_ISSUES_ACTIVITY, SUB_ISSUES } from "constants/fetch-keys";
|
import { PROJECT_ISSUES_ACTIVITY, SUB_ISSUES } from "constants/fetch-keys";
|
||||||
|
// constants
|
||||||
import { EUserWorkspaceRoles } from "constants/workspace";
|
import { EUserWorkspaceRoles } from "constants/workspace";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -41,24 +42,22 @@ const issueCommentService = new IssueCommentService();
|
|||||||
|
|
||||||
export const IssueMainContent: React.FC<Props> = observer((props) => {
|
export const IssueMainContent: React.FC<Props> = observer((props) => {
|
||||||
const { issueDetails, submitChanges, uneditable = false } = props;
|
const { issueDetails, submitChanges, uneditable = false } = props;
|
||||||
|
|
||||||
// states
|
// states
|
||||||
const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved");
|
const [isSubmitting, setIsSubmitting] = useState<"submitting" | "submitted" | "saved">("saved");
|
||||||
|
// router
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug, projectId, issueId } = router.query;
|
const { workspaceSlug, projectId, issueId } = router.query;
|
||||||
|
// toast alert
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
// mobx store
|
||||||
const {
|
const {
|
||||||
user: userStore,
|
user: { currentUser, currentProjectRole },
|
||||||
project: projectStore,
|
project: projectStore,
|
||||||
projectState: { states },
|
projectState: { states },
|
||||||
trackEvent: { postHogEventTracker },
|
trackEvent: { postHogEventTracker },
|
||||||
workspace: { currentWorkspace }
|
workspace: { currentWorkspace }
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
const user = userStore.currentUser ?? undefined;
|
|
||||||
const userRole = userStore.currentProjectRole;
|
|
||||||
const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : undefined;
|
const projectDetails = projectId ? projectStore.project_details[projectId.toString()] : undefined;
|
||||||
const currentIssueState = projectId
|
const currentIssueState = projectId
|
||||||
? states[projectId.toString()]?.find((s) => s.id === issueDetails.state)
|
? states[projectId.toString()]?.find((s) => s.id === issueDetails.state)
|
||||||
@ -67,7 +66,7 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
|
|||||||
const { data: siblingIssues } = useSWR(
|
const { data: siblingIssues } = useSWR(
|
||||||
workspaceSlug && projectId && issueDetails?.parent ? SUB_ISSUES(issueDetails.parent) : null,
|
workspaceSlug && projectId && issueDetails?.parent ? SUB_ISSUES(issueDetails.parent) : null,
|
||||||
workspaceSlug && projectId && issueDetails?.parent
|
workspaceSlug && projectId && issueDetails?.parent
|
||||||
? () => issueService.subIssues(workspaceSlug as string, projectId as string, issueDetails.parent ?? "")
|
? () => issueService.subIssues(workspaceSlug.toString(), projectId.toString(), issueDetails.parent ?? "")
|
||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
const siblingIssuesList = siblingIssues?.sub_issues.filter((i) => i.id !== issueDetails.id);
|
const siblingIssuesList = siblingIssues?.sub_issues.filter((i) => i.id !== issueDetails.id);
|
||||||
@ -79,7 +78,7 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
|
|||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCommentUpdate = async (commentId: string, data: Partial<IIssueComment>) => {
|
const handleCommentUpdate = async (commentId: string, data: Partial<IIssueActivity>) => {
|
||||||
if (!workspaceSlug || !projectId || !issueId) return;
|
if (!workspaceSlug || !projectId || !issueId) return;
|
||||||
|
|
||||||
await issueCommentService
|
await issueCommentService
|
||||||
@ -103,7 +102,7 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCommentDelete = async (commentId: string) => {
|
const handleCommentDelete = async (commentId: string) => {
|
||||||
if (!workspaceSlug || !projectId || !issueId || !user) return;
|
if (!workspaceSlug || !projectId || !issueId || !currentUser) return;
|
||||||
|
|
||||||
mutateIssueActivity((prevData: any) => prevData?.filter((p: any) => p.id !== commentId), false);
|
mutateIssueActivity((prevData: any) => prevData?.filter((p: any) => p.id !== commentId), false);
|
||||||
|
|
||||||
@ -126,8 +125,8 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddComment = async (formData: IIssueComment) => {
|
const handleAddComment = async (formData: IIssueActivity) => {
|
||||||
if (!workspaceSlug || !issueDetails || !user) return;
|
if (!workspaceSlug || !issueDetails || !currentUser) return;
|
||||||
|
|
||||||
await issueCommentService
|
await issueCommentService
|
||||||
.createIssueComment(workspaceSlug.toString(), issueDetails.project, issueDetails.id, formData)
|
.createIssueComment(workspaceSlug.toString(), issueDetails.project, issueDetails.id, formData)
|
||||||
@ -155,7 +154,7 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const isAllowed = !!userRole && userRole >= EUserWorkspaceRoles.MEMBER;
|
const isAllowed = !!currentProjectRole && currentProjectRole >= EUserWorkspaceRoles.MEMBER;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -238,10 +237,16 @@ export const IssueMainContent: React.FC<Props> = observer((props) => {
|
|||||||
isAllowed={isAllowed || !uneditable}
|
isAllowed={isAllowed || !uneditable}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<IssueReaction workspaceSlug={workspaceSlug} issueId={issueId} projectId={projectId} />
|
{workspaceSlug && projectId && (
|
||||||
|
<IssueReaction
|
||||||
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
|
projectId={projectId.toString()}
|
||||||
|
issueId={issueDetails.id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="mt-2 space-y-2">
|
<div className="mt-2 space-y-2">
|
||||||
<SubIssuesRoot parentIssue={issueDetails} user={user} />
|
<SubIssuesRoot parentIssue={issueDetails} user={currentUser ?? undefined} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-3 py-3">
|
<div className="flex flex-col gap-3 py-3">
|
||||||
|
@ -2,32 +2,34 @@ import { FC } from "react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { History } from "lucide-react";
|
import { History } from "lucide-react";
|
||||||
// packages
|
// packages
|
||||||
import { Tooltip } from "@plane/ui";
|
import { Loader, Tooltip } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { ActivityIcon, ActivityMessage } from "components/core";
|
import { ActivityIcon, ActivityMessage } from "components/core";
|
||||||
import { IssueCommentCard } from "./comment-card";
|
import { IssueCommentCard } from "./comment-card";
|
||||||
// helpers
|
// helpers
|
||||||
import { render24HourFormatTime, renderLongDateFormat, timeAgo } from "helpers/date-time.helper";
|
import { render24HourFormatTime, renderLongDateFormat, timeAgo } from "helpers/date-time.helper";
|
||||||
|
// types
|
||||||
|
import { IIssueActivity, IUser } from "types";
|
||||||
|
|
||||||
interface IssueActivityCard {
|
interface IIssueActivityCard {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
issueId: string;
|
issueId: string;
|
||||||
user: any;
|
user: IUser | null;
|
||||||
issueComments: any;
|
issueActivity: IIssueActivity[] | null;
|
||||||
issueCommentUpdate: (comment: any) => void;
|
issueCommentUpdate: (comment: any) => void;
|
||||||
issueCommentRemove: (commentId: string) => void;
|
issueCommentRemove: (commentId: string) => void;
|
||||||
issueCommentReactionCreate: (commentId: string, reaction: string) => void;
|
issueCommentReactionCreate: (commentId: string, reaction: string) => void;
|
||||||
issueCommentReactionRemove: (commentId: string, reaction: string) => void;
|
issueCommentReactionRemove: (commentId: string, reaction: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IssueActivityCard: FC<IssueActivityCard> = (props) => {
|
export const IssueActivityCard: FC<IIssueActivityCard> = (props) => {
|
||||||
const {
|
const {
|
||||||
workspaceSlug,
|
workspaceSlug,
|
||||||
projectId,
|
projectId,
|
||||||
issueId,
|
issueId,
|
||||||
user,
|
user,
|
||||||
issueComments,
|
issueActivity,
|
||||||
issueCommentUpdate,
|
issueCommentUpdate,
|
||||||
issueCommentRemove,
|
issueCommentRemove,
|
||||||
issueCommentReactionCreate,
|
issueCommentReactionCreate,
|
||||||
@ -37,9 +39,9 @@ export const IssueActivityCard: FC<IssueActivityCard> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<div className="flow-root">
|
<div className="flow-root">
|
||||||
<ul role="list" className="-mb-4">
|
<ul role="list" className="-mb-4">
|
||||||
{issueComments &&
|
{issueActivity ? (
|
||||||
issueComments.length > 0 &&
|
issueActivity.length > 0 &&
|
||||||
issueComments.map((activityItem: any, index: any) => {
|
issueActivity.map((activityItem, index) => {
|
||||||
// determines what type of action is performed
|
// determines what type of action is performed
|
||||||
const message = activityItem.field ? <ActivityMessage activity={activityItem} /> : "created the issue.";
|
const message = activityItem.field ? <ActivityMessage activity={activityItem} /> : "created the issue.";
|
||||||
|
|
||||||
@ -47,7 +49,7 @@ export const IssueActivityCard: FC<IssueActivityCard> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<li key={activityItem.id}>
|
<li key={activityItem.id}>
|
||||||
<div className="relative pb-1">
|
<div className="relative pb-1">
|
||||||
{issueComments.length > 1 && index !== issueComments.length - 1 ? (
|
{issueActivity.length > 1 && index !== issueActivity.length - 1 ? (
|
||||||
<span
|
<span
|
||||||
className="absolute top-5 left-5 -ml-[1.5px] h-full w-0.5 bg-custom-background-100"
|
className="absolute top-5 left-5 -ml-[1.5px] h-full w-0.5 bg-custom-background-100"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@ -114,7 +116,7 @@ export const IssueActivityCard: FC<IssueActivityCard> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
} else if ("comment_json" in activityItem)
|
} else if ("comment_html" in activityItem)
|
||||||
return (
|
return (
|
||||||
<div key={activityItem.id} className="mt-4">
|
<div key={activityItem.id} className="mt-4">
|
||||||
<IssueCommentCard
|
<IssueCommentCard
|
||||||
@ -131,7 +133,15 @@ export const IssueActivityCard: FC<IssueActivityCard> = (props) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})
|
||||||
|
) : (
|
||||||
|
<Loader className="space-y-3 mb-3">
|
||||||
|
<Loader.Item height="20px" />
|
||||||
|
<Loader.Item height="20px" />
|
||||||
|
<Loader.Item height="20px" />
|
||||||
|
<Loader.Item height="20px" />
|
||||||
|
</Loader>
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
@ -12,20 +12,20 @@ import { IssueCommentReaction } from "./comment-reaction";
|
|||||||
// helpers
|
// helpers
|
||||||
import { timeAgo } from "helpers/date-time.helper";
|
import { timeAgo } from "helpers/date-time.helper";
|
||||||
// types
|
// types
|
||||||
import type { IIssueComment } from "types";
|
import type { IIssueActivity, IUser } from "types";
|
||||||
|
|
||||||
// services
|
// services
|
||||||
const fileService = new FileService();
|
const fileService = new FileService();
|
||||||
|
|
||||||
type IIssueCommentCard = {
|
type IIssueCommentCard = {
|
||||||
comment: IIssueComment;
|
comment: IIssueActivity;
|
||||||
handleCommentDeletion: (comment: string) => void;
|
handleCommentDeletion: (comment: string) => void;
|
||||||
onSubmit: (data: Partial<IIssueComment>) => void;
|
onSubmit: (data: Partial<IIssueActivity>) => void;
|
||||||
showAccessSpecifier?: boolean;
|
showAccessSpecifier?: boolean;
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
issueId: string;
|
issueId: string;
|
||||||
user: any;
|
user: IUser | null;
|
||||||
issueCommentReactionCreate: (commentId: string, reaction: string) => void;
|
issueCommentReactionCreate: (commentId: string, reaction: string) => void;
|
||||||
issueCommentReactionRemove: (commentId: string, reaction: string) => void;
|
issueCommentReactionRemove: (commentId: string, reaction: string) => void;
|
||||||
};
|
};
|
||||||
@ -57,11 +57,11 @@ export const IssueCommentCard: React.FC<IIssueCommentCard> = (props) => {
|
|||||||
setFocus,
|
setFocus,
|
||||||
watch,
|
watch,
|
||||||
setValue,
|
setValue,
|
||||||
} = useForm<IIssueComment>({
|
} = useForm<IIssueActivity>({
|
||||||
defaultValues: comment,
|
defaultValues: comment,
|
||||||
});
|
});
|
||||||
|
|
||||||
const formSubmit = (formData: Partial<IIssueComment>) => {
|
const formSubmit = (formData: Partial<IIssueActivity>) => {
|
||||||
if (isSubmitting) return;
|
if (isSubmitting) return;
|
||||||
|
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
@ -119,13 +119,10 @@ export const IssueCommentCard: React.FC<IIssueCommentCard> = (props) => {
|
|||||||
deleteFile={fileService.deleteImage}
|
deleteFile={fileService.deleteImage}
|
||||||
restoreFile={fileService.restoreImage}
|
restoreFile={fileService.restoreImage}
|
||||||
ref={editorRef}
|
ref={editorRef}
|
||||||
value={watch("comment_html")}
|
value={watch("comment_html") ?? ""}
|
||||||
debouncedUpdatesEnabled={false}
|
debouncedUpdatesEnabled={false}
|
||||||
customClassName="min-h-[50px] p-3 shadow-sm"
|
customClassName="min-h-[50px] p-3 shadow-sm"
|
||||||
onChange={(comment_json: Object, comment_html: string) => {
|
onChange={(comment_json: Object, comment_html: string) => setValue("comment_html", comment_html)}
|
||||||
setValue("comment_json", comment_json);
|
|
||||||
setValue("comment_html", comment_html);
|
|
||||||
}}
|
|
||||||
mentionSuggestions={editorSuggestions.mentionSuggestions}
|
mentionSuggestions={editorSuggestions.mentionSuggestions}
|
||||||
mentionHighlights={editorSuggestions.mentionHighlights}
|
mentionHighlights={editorSuggestions.mentionHighlights}
|
||||||
/>
|
/>
|
||||||
@ -158,7 +155,7 @@ export const IssueCommentCard: React.FC<IIssueCommentCard> = (props) => {
|
|||||||
)}
|
)}
|
||||||
<LiteReadOnlyEditorWithRef
|
<LiteReadOnlyEditorWithRef
|
||||||
ref={showEditorRef}
|
ref={showEditorRef}
|
||||||
value={comment.comment_html}
|
value={comment.comment_html ?? ""}
|
||||||
customClassName="text-xs border border-custom-border-200 bg-custom-background-100"
|
customClassName="text-xs border border-custom-border-200 bg-custom-background-100"
|
||||||
mentionHighlights={editorSuggestions.mentionHighlights}
|
mentionHighlights={editorSuggestions.mentionHighlights}
|
||||||
/>
|
/>
|
@ -11,16 +11,16 @@ import { LiteTextEditorWithRef } from "@plane/lite-text-editor";
|
|||||||
// ui
|
// ui
|
||||||
import { Button } from "@plane/ui";
|
import { Button } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import type { IIssueComment } from "types";
|
import type { IIssueActivity } from "types";
|
||||||
|
|
||||||
const defaultValues: Partial<IIssueComment> = {
|
const defaultValues: Partial<IIssueActivity> = {
|
||||||
access: "INTERNAL",
|
access: "INTERNAL",
|
||||||
comment_html: "",
|
comment_html: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
type IIssueCommentEditor = {
|
type IIssueCommentEditor = {
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
onSubmit: (data: IIssueComment) => Promise<void>;
|
onSubmit: (data: IIssueActivity) => Promise<void>;
|
||||||
showAccessSpecifier?: boolean;
|
showAccessSpecifier?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -60,9 +60,9 @@ export const IssueCommentEditor: React.FC<IIssueCommentEditor> = (props) => {
|
|||||||
formState: { isSubmitting },
|
formState: { isSubmitting },
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
reset,
|
reset,
|
||||||
} = useForm<IIssueComment>({ defaultValues });
|
} = useForm<IIssueActivity>({ defaultValues });
|
||||||
|
|
||||||
const handleAddComment = async (formData: IIssueComment) => {
|
const handleAddComment = async (formData: IIssueActivity) => {
|
||||||
if (!formData.comment_html || isSubmitting) return;
|
if (!formData.comment_html || isSubmitting) return;
|
||||||
|
|
||||||
await onSubmit(formData).then(() => {
|
await onSubmit(formData).then(() => {
|
||||||
@ -99,7 +99,7 @@ export const IssueCommentEditor: React.FC<IIssueCommentEditor> = (props) => {
|
|||||||
onChange={(comment_json: Object, comment_html: string) => onCommentChange(comment_html)}
|
onChange={(comment_json: Object, comment_html: string) => onCommentChange(comment_html)}
|
||||||
commentAccessSpecifier={
|
commentAccessSpecifier={
|
||||||
showAccessSpecifier
|
showAccessSpecifier
|
||||||
? { accessValue, onAccessChange, showAccessSpecifier, commentAccess }
|
? { accessValue: accessValue ?? "INTERNAL", onAccessChange, showAccessSpecifier, commentAccess }
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
submitButton={
|
submitButton={
|
@ -2,7 +2,7 @@ import { FC } from "react";
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
// components
|
// components
|
||||||
import { IssueReaction } from "../reactions";
|
import { IssuePeekOverviewReactions } from "components/issues";
|
||||||
// hooks
|
// hooks
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// types
|
// types
|
||||||
@ -49,7 +49,7 @@ export const IssueCommentReaction: FC<IIssueCommentReaction> = observer((props)
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<IssueReaction
|
<IssuePeekOverviewReactions
|
||||||
issueReactions={issueReactions}
|
issueReactions={issueReactions}
|
||||||
user={user}
|
user={user}
|
||||||
issueReactionCreate={handleCommentReactionCreate}
|
issueReactionCreate={handleCommentReactionCreate}
|
@ -1,5 +1,5 @@
|
|||||||
export * from "./view";
|
|
||||||
|
|
||||||
export * from "./card";
|
export * from "./card";
|
||||||
|
export * from "./comment-card";
|
||||||
export * from "./comment-editor";
|
export * from "./comment-editor";
|
||||||
export * from "./comment-reaction";
|
export * from "./comment-reaction";
|
||||||
|
export * from "./view";
|
@ -1,29 +1,30 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
// components
|
// components
|
||||||
import { IssueActivityCard } from "./card";
|
import { IssueActivityCard, IssueCommentEditor } from "components/issues";
|
||||||
import { IssueCommentEditor } from "./comment-editor";
|
// types
|
||||||
|
import { IIssueActivity, IUser } from "types";
|
||||||
|
|
||||||
interface IIssueComment {
|
type Props = {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
projectId: string;
|
projectId: string;
|
||||||
issueId: string;
|
issueId: string;
|
||||||
user: any;
|
user: IUser | null;
|
||||||
issueComments: any;
|
issueActivity: IIssueActivity[] | null;
|
||||||
issueCommentCreate: (comment: any) => void;
|
issueCommentCreate: (comment: any) => void;
|
||||||
issueCommentUpdate: (comment: any) => void;
|
issueCommentUpdate: (comment: any) => void;
|
||||||
issueCommentRemove: (commentId: string) => void;
|
issueCommentRemove: (commentId: string) => void;
|
||||||
issueCommentReactionCreate: (commentId: string, reaction: string) => void;
|
issueCommentReactionCreate: (commentId: string, reaction: string) => void;
|
||||||
issueCommentReactionRemove: (commentId: string, reaction: string) => void;
|
issueCommentReactionRemove: (commentId: string, reaction: string) => void;
|
||||||
showCommentAccessSpecifier: boolean;
|
showCommentAccessSpecifier: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const IssueComment: FC<IIssueComment> = (props) => {
|
export const IssueActivity: FC<Props> = (props) => {
|
||||||
const {
|
const {
|
||||||
workspaceSlug,
|
workspaceSlug,
|
||||||
projectId,
|
projectId,
|
||||||
issueId,
|
issueId,
|
||||||
user,
|
user,
|
||||||
issueComments,
|
issueActivity,
|
||||||
issueCommentCreate,
|
issueCommentCreate,
|
||||||
issueCommentUpdate,
|
issueCommentUpdate,
|
||||||
issueCommentRemove,
|
issueCommentRemove,
|
||||||
@ -47,7 +48,7 @@ export const IssueComment: FC<IIssueComment> = (props) => {
|
|||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
issueId={issueId}
|
issueId={issueId}
|
||||||
user={user}
|
user={user}
|
||||||
issueComments={issueComments}
|
issueActivity={issueActivity}
|
||||||
issueCommentUpdate={issueCommentUpdate}
|
issueCommentUpdate={issueCommentUpdate}
|
||||||
issueCommentRemove={issueCommentRemove}
|
issueCommentRemove={issueCommentRemove}
|
||||||
issueCommentReactionCreate={issueCommentReactionCreate}
|
issueCommentReactionCreate={issueCommentReactionCreate}
|
6
web/components/issues/peek-overview/index.ts
Normal file
6
web/components/issues/peek-overview/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export * from "./activity";
|
||||||
|
export * from "./reactions";
|
||||||
|
export * from "./issue-detail";
|
||||||
|
export * from "./properties";
|
||||||
|
export * from "./root";
|
||||||
|
export * from "./view";
|
@ -1,19 +1,22 @@
|
|||||||
import { ChangeEvent, FC, useCallback, useEffect, useState } from "react";
|
import { ChangeEvent, FC, useCallback, useEffect, useState } from "react";
|
||||||
import { Controller, useForm } from "react-hook-form";
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import debounce from "lodash/debounce";
|
||||||
// packages
|
// packages
|
||||||
import { RichTextEditor } from "@plane/rich-text-editor";
|
import { RichTextEditor } from "@plane/rich-text-editor";
|
||||||
// components
|
// mobx store
|
||||||
import { TextArea } from "@plane/ui";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
import { IssueReaction } from "./reactions";
|
|
||||||
// hooks
|
// hooks
|
||||||
import debounce from "lodash/debounce";
|
|
||||||
import useReloadConfirmations from "hooks/use-reload-confirmation";
|
import useReloadConfirmations from "hooks/use-reload-confirmation";
|
||||||
import useEditorSuggestions from "hooks/use-editor-suggestions";
|
import useEditorSuggestions from "hooks/use-editor-suggestions";
|
||||||
|
// components
|
||||||
|
import { IssuePeekOverviewReactions } from "components/issues";
|
||||||
|
// ui
|
||||||
|
import { TextArea } from "@plane/ui";
|
||||||
// types
|
// types
|
||||||
import { IIssue } from "types";
|
import { IIssue, IUser } from "types";
|
||||||
// services
|
// services
|
||||||
import { FileService } from "services/file.service";
|
import { FileService } from "services/file.service";
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
// constants
|
||||||
import { EUserWorkspaceRoles } from "constants/workspace";
|
import { EUserWorkspaceRoles } from "constants/workspace";
|
||||||
|
|
||||||
const fileService = new FileService();
|
const fileService = new FileService();
|
||||||
@ -22,7 +25,7 @@ interface IPeekOverviewIssueDetails {
|
|||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
issue: IIssue;
|
issue: IIssue;
|
||||||
issueReactions: any;
|
issueReactions: any;
|
||||||
user: any;
|
user: IUser | null;
|
||||||
issueUpdate: (issue: Partial<IIssue>) => void;
|
issueUpdate: (issue: Partial<IIssue>) => void;
|
||||||
issueReactionCreate: (reaction: string) => void;
|
issueReactionCreate: (reaction: string) => void;
|
||||||
issueReactionRemove: (reaction: string) => void;
|
issueReactionRemove: (reaction: string) => void;
|
||||||
@ -89,7 +92,7 @@ export const PeekOverviewIssueDetails: FC<IPeekOverviewIssueDetails> = (props) =
|
|||||||
setLocalIssueDescription({ id: issue.id, description_html: issue.description_html });
|
setLocalIssueDescription({ id: issue.id, description_html: issue.description_html });
|
||||||
setLocalTitleValue(issue.name);
|
setLocalTitleValue(issue.name);
|
||||||
}
|
}
|
||||||
}, [issue.id]);
|
}, [issue.id, issue.description_html, issue.name]);
|
||||||
|
|
||||||
const debouncedFormSave = debounce(async () => {
|
const debouncedFormSave = debounce(async () => {
|
||||||
handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted"));
|
handleSubmit(handleDescriptionFormSubmit)().finally(() => setIsSubmitting("submitted"));
|
||||||
@ -104,7 +107,7 @@ export const PeekOverviewIssueDetails: FC<IPeekOverviewIssueDetails> = (props) =
|
|||||||
} else if (isSubmitting === "submitting") {
|
} else if (isSubmitting === "submitting") {
|
||||||
setShowAlert(true);
|
setShowAlert(true);
|
||||||
}
|
}
|
||||||
}, [isSubmitting, setShowAlert]);
|
}, [isSubmitting, setShowAlert, setIsSubmitting]);
|
||||||
|
|
||||||
// reset form values
|
// reset form values
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -165,7 +168,7 @@ export const PeekOverviewIssueDetails: FC<IPeekOverviewIssueDetails> = (props) =
|
|||||||
<Controller
|
<Controller
|
||||||
name="description_html"
|
name="description_html"
|
||||||
control={control}
|
control={control}
|
||||||
render={({ field: { value, onChange } }) => (
|
render={({ field: { onChange } }) => (
|
||||||
<RichTextEditor
|
<RichTextEditor
|
||||||
cancelUploadImage={fileService.cancelUpload}
|
cancelUploadImage={fileService.cancelUpload}
|
||||||
uploadFile={fileService.getUploadFileFunction(workspaceSlug)}
|
uploadFile={fileService.getUploadFileFunction(workspaceSlug)}
|
||||||
@ -190,7 +193,7 @@ export const PeekOverviewIssueDetails: FC<IPeekOverviewIssueDetails> = (props) =
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<IssueReaction
|
<IssuePeekOverviewReactions
|
||||||
issueReactions={issueReactions}
|
issueReactions={issueReactions}
|
||||||
user={user}
|
user={user}
|
||||||
issueReactionCreate={issueReactionCreate}
|
issueReactionCreate={issueReactionCreate}
|
@ -47,8 +47,6 @@ export const PeekOverviewProperties: FC<IPeekOverviewProperties> = observer((pro
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
user: { currentProjectRole },
|
user: { currentProjectRole },
|
||||||
cycleIssues: cycleIssueStore,
|
|
||||||
moduleIssues: { addIssueToModule },
|
|
||||||
issueDetail: { fetchPeekIssueDetails },
|
issueDetail: { fetchPeekIssueDetails },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
@ -1,3 +1,3 @@
|
|||||||
|
export * from "./preview";
|
||||||
export * from "./root";
|
export * from "./root";
|
||||||
export * from "./selector";
|
export * from "./selector";
|
||||||
export * from "./preview";
|
|
@ -1,16 +1,18 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
// components
|
// components
|
||||||
import { IssueReactionPreview, IssueReactionSelector } from "./";
|
import { IssueReactionPreview, IssueReactionSelector } from "components/issues";
|
||||||
|
// types
|
||||||
|
import { IUser } from "types";
|
||||||
|
|
||||||
interface IIssueReaction {
|
interface IIssueReaction {
|
||||||
issueReactions: any;
|
issueReactions: any;
|
||||||
user: any;
|
user: IUser | null;
|
||||||
issueReactionCreate: (reaction: string) => void;
|
issueReactionCreate: (reaction: string) => void;
|
||||||
issueReactionRemove: (reaction: string) => void;
|
issueReactionRemove: (reaction: string) => void;
|
||||||
position?: "top" | "bottom";
|
position?: "top" | "bottom";
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IssueReaction: FC<IIssueReaction> = (props) => {
|
export const IssuePeekOverviewReactions: FC<IIssueReaction> = (props) => {
|
||||||
const { issueReactions, user, issueReactionCreate, issueReactionRemove, position = "bottom" } = props;
|
const { issueReactions, user, issueReactionCreate, issueReactionRemove, position = "bottom" } = props;
|
||||||
|
|
||||||
const handleReaction = (reaction: string) => {
|
const handleReaction = (reaction: string) => {
|
@ -1,17 +1,17 @@
|
|||||||
import { FC, Fragment, ReactNode, useEffect } from "react";
|
import { FC, Fragment, ReactNode, useCallback, useEffect } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import useSWR from "swr";
|
|
||||||
// mobx store
|
// mobx store
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// components
|
// components
|
||||||
import { IssueView } from "./view";
|
import { IssueView } from "components/issues";
|
||||||
// helpers
|
// helpers
|
||||||
import { copyUrlToClipboard } from "helpers/string.helper";
|
import { copyUrlToClipboard } from "helpers/string.helper";
|
||||||
// types
|
// types
|
||||||
import { IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
|
// constants
|
||||||
import { EUserWorkspaceRoles } from "constants/workspace";
|
import { EUserWorkspaceRoles } from "constants/workspace";
|
||||||
|
|
||||||
interface IIssuePeekOverview {
|
interface IIssuePeekOverview {
|
||||||
@ -30,27 +30,45 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
|||||||
const { peekIssueId } = router.query;
|
const { peekIssueId } = router.query;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
user: userStore,
|
user: { currentProjectRole },
|
||||||
issue: issueStore,
|
issue: { removeIssueFromStructure },
|
||||||
issueDetail: issueDetailStore,
|
issueDetail: {
|
||||||
archivedIssueDetail: archivedIssueDetailStore,
|
createIssueComment,
|
||||||
archivedIssues: archivedIssuesStore,
|
updateIssueComment,
|
||||||
project: projectStore,
|
removeIssueComment,
|
||||||
|
creationIssueCommentReaction,
|
||||||
|
removeIssueCommentReaction,
|
||||||
|
createIssueReaction,
|
||||||
|
removeIssueReaction,
|
||||||
|
createIssueSubscription,
|
||||||
|
removeIssueSubscription,
|
||||||
|
getIssue,
|
||||||
|
loader,
|
||||||
|
fetchPeekIssueDetails,
|
||||||
|
setPeekId,
|
||||||
|
fetchIssueActivity,
|
||||||
|
},
|
||||||
|
archivedIssueDetail: {
|
||||||
|
getIssue: getArchivedIssue,
|
||||||
|
loader: archivedIssueLoader,
|
||||||
|
fetchPeekIssueDetails: fetchArchivedPeekIssueDetails,
|
||||||
|
},
|
||||||
|
archivedIssues: { deleteArchivedIssue },
|
||||||
|
project: { currentProjectDetails },
|
||||||
} = useMobxStore();
|
} = useMobxStore();
|
||||||
|
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
const fetchIssueDetail = async () => {
|
const fetchIssueDetail = useCallback(async () => {
|
||||||
if (workspaceSlug && projectId && peekIssueId) {
|
if (workspaceSlug && projectId && peekIssueId) {
|
||||||
if (isArchived)
|
if (isArchived) await fetchArchivedPeekIssueDetails(workspaceSlug, projectId, peekIssueId as string);
|
||||||
await archivedIssueDetailStore.fetchPeekIssueDetails(workspaceSlug, projectId, peekIssueId as string);
|
else await fetchPeekIssueDetails(workspaceSlug, projectId, peekIssueId as string);
|
||||||
else await issueDetailStore.fetchPeekIssueDetails(workspaceSlug, projectId, peekIssueId as string);
|
|
||||||
}
|
}
|
||||||
};
|
}, [fetchArchivedPeekIssueDetails, fetchPeekIssueDetails, workspaceSlug, projectId, peekIssueId, isArchived]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchIssueDetail();
|
fetchIssueDetail();
|
||||||
}, [workspaceSlug, projectId, peekIssueId]);
|
}, [workspaceSlug, projectId, peekIssueId, fetchIssueDetail]);
|
||||||
|
|
||||||
const handleCopyText = (e: React.MouseEvent<HTMLButtonElement>) => {
|
const handleCopyText = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -72,44 +90,43 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const issue = isArchived ? archivedIssueDetailStore.getIssue : issueDetailStore.getIssue;
|
const issue = isArchived ? getArchivedIssue : getIssue;
|
||||||
const isLoading = isArchived ? archivedIssueDetailStore.loader : issueDetailStore.loader;
|
const isLoading = isArchived ? archivedIssueLoader : loader;
|
||||||
|
|
||||||
const issueUpdate = (_data: Partial<IIssue>) => {
|
const issueUpdate = async (_data: Partial<IIssue>) => {
|
||||||
if (handleIssue) handleIssue(_data);
|
if (handleIssue) {
|
||||||
|
await handleIssue(_data);
|
||||||
|
fetchIssueActivity(workspaceSlug, projectId, issueId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const issueReactionCreate = (reaction: string) =>
|
const issueReactionCreate = (reaction: string) => createIssueReaction(workspaceSlug, projectId, issueId, reaction);
|
||||||
issueDetailStore.createIssueReaction(workspaceSlug, projectId, issueId, reaction);
|
|
||||||
|
|
||||||
const issueReactionRemove = (reaction: string) =>
|
const issueReactionRemove = (reaction: string) => removeIssueReaction(workspaceSlug, projectId, issueId, reaction);
|
||||||
issueDetailStore.removeIssueReaction(workspaceSlug, projectId, issueId, reaction);
|
|
||||||
|
|
||||||
const issueCommentCreate = (comment: any) =>
|
const issueCommentCreate = (comment: any) => createIssueComment(workspaceSlug, projectId, issueId, comment);
|
||||||
issueDetailStore.createIssueComment(workspaceSlug, projectId, issueId, comment);
|
|
||||||
|
|
||||||
const issueCommentUpdate = (comment: any) =>
|
const issueCommentUpdate = (comment: any) =>
|
||||||
issueDetailStore.updateIssueComment(workspaceSlug, projectId, issueId, comment?.id, comment);
|
updateIssueComment(workspaceSlug, projectId, issueId, comment?.id, comment);
|
||||||
|
|
||||||
const issueCommentRemove = (commentId: string) =>
|
const issueCommentRemove = (commentId: string) => removeIssueComment(workspaceSlug, projectId, issueId, commentId);
|
||||||
issueDetailStore.removeIssueComment(workspaceSlug, projectId, issueId, commentId);
|
|
||||||
|
|
||||||
const issueCommentReactionCreate = (commentId: string, reaction: string) =>
|
const issueCommentReactionCreate = (commentId: string, reaction: string) =>
|
||||||
issueDetailStore.creationIssueCommentReaction(workspaceSlug, projectId, issueId, commentId, reaction);
|
creationIssueCommentReaction(workspaceSlug, projectId, issueId, commentId, reaction);
|
||||||
|
|
||||||
const issueCommentReactionRemove = (commentId: string, reaction: string) =>
|
const issueCommentReactionRemove = (commentId: string, reaction: string) =>
|
||||||
issueDetailStore.removeIssueCommentReaction(workspaceSlug, projectId, issueId, commentId, reaction);
|
removeIssueCommentReaction(workspaceSlug, projectId, issueId, commentId, reaction);
|
||||||
|
|
||||||
const issueSubscriptionCreate = () => issueDetailStore.createIssueSubscription(workspaceSlug, projectId, issueId);
|
const issueSubscriptionCreate = () => createIssueSubscription(workspaceSlug, projectId, issueId);
|
||||||
|
|
||||||
const issueSubscriptionRemove = () => issueDetailStore.removeIssueSubscription(workspaceSlug, projectId, issueId);
|
const issueSubscriptionRemove = () => removeIssueSubscription(workspaceSlug, projectId, issueId);
|
||||||
|
|
||||||
const handleDeleteIssue = async () => {
|
const handleDeleteIssue = async () => {
|
||||||
if (isArchived) await archivedIssuesStore.deleteArchivedIssue(workspaceSlug, projectId, issue!);
|
if (isArchived) await deleteArchivedIssue(workspaceSlug, projectId, issue!);
|
||||||
else await issueStore.removeIssueFromStructure(workspaceSlug, projectId, issue!);
|
else removeIssueFromStructure(workspaceSlug, projectId, issue!);
|
||||||
const { query } = router;
|
const { query } = router;
|
||||||
if (query.peekIssueId) {
|
if (query.peekIssueId) {
|
||||||
issueDetailStore.setPeekId(null);
|
setPeekId(null);
|
||||||
delete query.peekIssueId;
|
delete query.peekIssueId;
|
||||||
delete query.peekProjectId;
|
delete query.peekProjectId;
|
||||||
router.push({
|
router.push({
|
||||||
@ -119,7 +136,7 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const userRole = userStore.currentProjectRole ?? EUserWorkspaceRoles.GUEST;
|
const userRole = currentProjectRole ?? EUserWorkspaceRoles.GUEST;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
@ -144,7 +161,7 @@ export const IssuePeekOverview: FC<IIssuePeekOverview> = observer((props) => {
|
|||||||
issueSubscriptionRemove={issueSubscriptionRemove}
|
issueSubscriptionRemove={issueSubscriptionRemove}
|
||||||
handleDeleteIssue={handleDeleteIssue}
|
handleDeleteIssue={handleDeleteIssue}
|
||||||
disableUserActions={[5, 10].includes(userRole)}
|
disableUserActions={[5, 10].includes(userRole)}
|
||||||
showCommentAccessSpecifier={projectStore.currentProjectDetails?.is_deployed}
|
showCommentAccessSpecifier={currentProjectDetails?.is_deployed}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</IssueView>
|
</IssueView>
|
@ -3,16 +3,21 @@ import { useRouter } from "next/router";
|
|||||||
import { observer } from "mobx-react-lite";
|
import { observer } from "mobx-react-lite";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { MoveRight, MoveDiagonal, Bell, Link2, Trash2 } from "lucide-react";
|
import { MoveRight, MoveDiagonal, Bell, Link2, Trash2 } from "lucide-react";
|
||||||
|
// mobx store
|
||||||
|
import { useMobxStore } from "lib/mobx/store-provider";
|
||||||
// components
|
// components
|
||||||
import { PeekOverviewIssueDetails } from "./issue-detail";
|
import {
|
||||||
import { PeekOverviewProperties } from "./properties";
|
DeleteArchivedIssueModal,
|
||||||
import { IssueComment } from "./activity";
|
DeleteIssueModal,
|
||||||
|
IssueActivity,
|
||||||
|
IssueUpdateStatus,
|
||||||
|
PeekOverviewIssueDetails,
|
||||||
|
PeekOverviewProperties,
|
||||||
|
} from "components/issues";
|
||||||
|
// ui
|
||||||
import { Button, CenterPanelIcon, CustomSelect, FullScreenPanelIcon, SidePanelIcon, Spinner } from "@plane/ui";
|
import { Button, CenterPanelIcon, CustomSelect, FullScreenPanelIcon, SidePanelIcon, Spinner } from "@plane/ui";
|
||||||
import { DeleteIssueModal, DeleteArchivedIssueModal, IssueUpdateStatus } from "components/issues/";
|
|
||||||
// types
|
// types
|
||||||
import { IIssue } from "types";
|
import { IIssue } from "types";
|
||||||
// hooks
|
|
||||||
import { useMobxStore } from "lib/mobx/store-provider";
|
|
||||||
|
|
||||||
interface IIssueView {
|
interface IIssueView {
|
||||||
workspaceSlug: string;
|
workspaceSlug: string;
|
||||||
@ -41,7 +46,7 @@ interface IIssueView {
|
|||||||
|
|
||||||
type TPeekModes = "side-peek" | "modal" | "full-screen";
|
type TPeekModes = "side-peek" | "modal" | "full-screen";
|
||||||
|
|
||||||
const peekOptions: { key: TPeekModes; icon: any; title: string }[] = [
|
const PEEK_OPTIONS: { key: TPeekModes; icon: any; title: string }[] = [
|
||||||
{
|
{
|
||||||
key: "side-peek",
|
key: "side-peek",
|
||||||
icon: SidePanelIcon,
|
icon: SidePanelIcon,
|
||||||
@ -86,9 +91,12 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { peekIssueId } = router.query as { peekIssueId: string };
|
const { peekIssueId } = router.query;
|
||||||
|
|
||||||
const { user: userStore, issueDetail: issueDetailStore } = useMobxStore();
|
const {
|
||||||
|
user: { currentUser },
|
||||||
|
issueDetail: { fetchIssueSubscription, getIssueActivity, getIssueReactions, getIssueSubscription, setPeekId },
|
||||||
|
} = useMobxStore();
|
||||||
|
|
||||||
const [peekMode, setPeekMode] = useState<TPeekModes>("side-peek");
|
const [peekMode, setPeekMode] = useState<TPeekModes>("side-peek");
|
||||||
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
const [deleteIssueModal, setDeleteIssueModal] = useState(false);
|
||||||
@ -96,7 +104,7 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||||||
|
|
||||||
const updateRoutePeekId = () => {
|
const updateRoutePeekId = () => {
|
||||||
if (issueId != peekIssueId) {
|
if (issueId != peekIssueId) {
|
||||||
issueDetailStore.setPeekId(issueId);
|
setPeekId(issueId);
|
||||||
const { query } = router;
|
const { query } = router;
|
||||||
router.push({
|
router.push({
|
||||||
pathname: router.pathname,
|
pathname: router.pathname,
|
||||||
@ -104,10 +112,13 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeRoutePeekId = () => {
|
const removeRoutePeekId = () => {
|
||||||
const { query } = router;
|
const { query } = router;
|
||||||
|
|
||||||
if (query.peekIssueId) {
|
if (query.peekIssueId) {
|
||||||
issueDetailStore.setPeekId(null);
|
setPeekId(null);
|
||||||
|
|
||||||
delete query.peekIssueId;
|
delete query.peekIssueId;
|
||||||
delete query.peekProjectId;
|
delete query.peekProjectId;
|
||||||
router.push({
|
router.push({
|
||||||
@ -123,18 +134,16 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||||||
: null,
|
: null,
|
||||||
async () => {
|
async () => {
|
||||||
if (workspaceSlug && projectId && issueId && peekIssueId && issueId === peekIssueId) {
|
if (workspaceSlug && projectId && issueId && peekIssueId && issueId === peekIssueId) {
|
||||||
await issueDetailStore.fetchIssueSubscription(workspaceSlug, projectId, issueId);
|
await fetchIssueSubscription(workspaceSlug, projectId, issueId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const issueReactions = issueDetailStore.getIssueReactions || [];
|
const issueReactions = getIssueReactions || [];
|
||||||
const issueComments = issueDetailStore.getIssueComments || [];
|
const issueActivity = getIssueActivity;
|
||||||
const issueSubscription = issueDetailStore.getIssueSubscription || [];
|
const issueSubscription = getIssueSubscription || [];
|
||||||
|
|
||||||
const user = userStore?.currentUser;
|
const currentMode = PEEK_OPTIONS.find((m) => m.key === peekMode);
|
||||||
|
|
||||||
const currentMode = peekOptions.find((m) => m.key === peekMode);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -198,7 +207,7 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{peekOptions.map((mode) => (
|
{PEEK_OPTIONS.map((mode) => (
|
||||||
<CustomSelect.Option key={mode.key} value={mode.key}>
|
<CustomSelect.Option key={mode.key} value={mode.key}>
|
||||||
<div
|
<div
|
||||||
className={`flex items-center gap-1.5 ${
|
className={`flex items-center gap-1.5 ${
|
||||||
@ -219,8 +228,8 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||||||
<div className="flex items-center gap-x-4">
|
<div className="flex items-center gap-x-4">
|
||||||
<IssueUpdateStatus isSubmitting={isSubmitting} />
|
<IssueUpdateStatus isSubmitting={isSubmitting} />
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
{issue?.created_by !== user?.id &&
|
{issue?.created_by !== currentUser?.id &&
|
||||||
!issue?.assignees.includes(user?.id ?? "") &&
|
!issue?.assignees.includes(currentUser?.id ?? "") &&
|
||||||
!router.pathname.includes("[archivedIssueId]") && (
|
!router.pathname.includes("[archivedIssueId]") && (
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
@ -269,7 +278,7 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||||||
issue={issue}
|
issue={issue}
|
||||||
issueUpdate={issueUpdate}
|
issueUpdate={issueUpdate}
|
||||||
issueReactions={issueReactions}
|
issueReactions={issueReactions}
|
||||||
user={user}
|
user={currentUser}
|
||||||
issueReactionCreate={issueReactionCreate}
|
issueReactionCreate={issueReactionCreate}
|
||||||
issueReactionRemove={issueReactionRemove}
|
issueReactionRemove={issueReactionRemove}
|
||||||
/>
|
/>
|
||||||
@ -280,12 +289,12 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||||||
disableUserActions={disableUserActions}
|
disableUserActions={disableUserActions}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<IssueComment
|
<IssueActivity
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
issueId={issueId}
|
issueId={issueId}
|
||||||
user={user}
|
user={currentUser}
|
||||||
issueComments={issueComments}
|
issueActivity={issueActivity}
|
||||||
issueCommentCreate={issueCommentCreate}
|
issueCommentCreate={issueCommentCreate}
|
||||||
issueCommentUpdate={issueCommentUpdate}
|
issueCommentUpdate={issueCommentUpdate}
|
||||||
issueCommentRemove={issueCommentRemove}
|
issueCommentRemove={issueCommentRemove}
|
||||||
@ -305,17 +314,17 @@ export const IssueView: FC<IIssueView> = observer((props) => {
|
|||||||
issue={issue}
|
issue={issue}
|
||||||
issueReactions={issueReactions}
|
issueReactions={issueReactions}
|
||||||
issueUpdate={issueUpdate}
|
issueUpdate={issueUpdate}
|
||||||
user={user}
|
user={currentUser}
|
||||||
issueReactionCreate={issueReactionCreate}
|
issueReactionCreate={issueReactionCreate}
|
||||||
issueReactionRemove={issueReactionRemove}
|
issueReactionRemove={issueReactionRemove}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<IssueComment
|
<IssueActivity
|
||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
issueId={issueId}
|
issueId={issueId}
|
||||||
user={user}
|
user={currentUser}
|
||||||
issueComments={issueComments}
|
issueActivity={issueActivity}
|
||||||
issueCommentCreate={issueCommentCreate}
|
issueCommentCreate={issueCommentCreate}
|
||||||
issueCommentUpdate={issueCommentUpdate}
|
issueCommentUpdate={issueCommentUpdate}
|
||||||
issueCommentRemove={issueCommentRemove}
|
issueCommentRemove={issueCommentRemove}
|
@ -65,7 +65,7 @@ export const SubIssues: React.FC<ISubIssues> = ({
|
|||||||
workspaceSlug={workspaceSlug}
|
workspaceSlug={workspaceSlug}
|
||||||
projectId={peekProjectId.toString()}
|
projectId={peekProjectId.toString()}
|
||||||
issueId={peekIssueId.toString()}
|
issueId={peekIssueId.toString()}
|
||||||
handleIssue={(issueToUpdate) => handleUpdateIssue(issue, { ...issue, ...issueToUpdate })}
|
handleIssue={async (issueToUpdate) => await handleUpdateIssue(issue, { ...issue, ...issueToUpdate })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
|
@ -26,7 +26,7 @@ import { NextPageWithLayout } from "types/app";
|
|||||||
import { IPage, IIssue } from "types";
|
import { IPage, IIssue } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { PAGE_DETAILS, PROJECT_ISSUES_LIST } from "constants/fetch-keys";
|
import { PAGE_DETAILS, PROJECT_ISSUES_LIST } from "constants/fetch-keys";
|
||||||
import { IssuePeekOverview } from "components/issues/issue-peek-overview";
|
import { IssuePeekOverview } from "components/issues/peek-overview";
|
||||||
import { IssueService } from "services/issue";
|
import { IssueService } from "services/issue";
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
import useReloadConfirmations from "hooks/use-reload-confirmation";
|
import useReloadConfirmations from "hooks/use-reload-confirmation";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { APIService } from "services/api.service";
|
import { APIService } from "services/api.service";
|
||||||
// types
|
// types
|
||||||
import { IIssueComment } from "types";
|
import { IIssueActivity } from "types";
|
||||||
// helper
|
// helper
|
||||||
import { API_BASE_URL } from "helpers/common.helper";
|
import { API_BASE_URL } from "helpers/common.helper";
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ export class IssueCommentService extends APIService {
|
|||||||
workspaceSlug: string,
|
workspaceSlug: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
issueId: string,
|
issueId: string,
|
||||||
data: Partial<IIssueComment>
|
data: Partial<IIssueActivity>
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/comments/`, data)
|
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/comments/`, data)
|
||||||
.then((response) => response?.data)
|
.then((response) => response?.data)
|
||||||
@ -35,7 +35,7 @@ export class IssueCommentService extends APIService {
|
|||||||
projectId: string,
|
projectId: string,
|
||||||
issueId: string,
|
issueId: string,
|
||||||
commentId: string,
|
commentId: string,
|
||||||
data: Partial<IIssueComment>
|
data: Partial<IIssueActivity>
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
return this.patch(
|
return this.patch(
|
||||||
`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/comments/${commentId}/`,
|
`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/comments/${commentId}/`,
|
||||||
|
@ -2,7 +2,7 @@ import { API_BASE_URL } from "helpers/common.helper";
|
|||||||
// services
|
// services
|
||||||
import { APIService } from "services/api.service";
|
import { APIService } from "services/api.service";
|
||||||
// types
|
// types
|
||||||
import type { IssueReaction, IssueCommentReaction, IssueReactionForm, IssueCommentReactionForm } from "types";
|
import type { IssueCommentReaction, IssueReactionForm, IssueCommentReactionForm, IIssueReaction } from "types";
|
||||||
|
|
||||||
export class IssueReactionService extends APIService {
|
export class IssueReactionService extends APIService {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -22,7 +22,7 @@ export class IssueReactionService extends APIService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async listIssueReactions(workspaceSlug: string, projectId: string, issueId: string): Promise<IssueReaction[]> {
|
async listIssueReactions(workspaceSlug: string, projectId: string, issueId: string): Promise<IIssueReaction[]> {
|
||||||
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/reactions/`)
|
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/reactions/`)
|
||||||
.then((response) => response?.data)
|
.then((response) => response?.data)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
@ -173,7 +173,7 @@ export class ArchivedIssueDetailStore implements IArchivedIssueDetailStore {
|
|||||||
try {
|
try {
|
||||||
const issueResponse = await this.archivedIssueService.retrieveArchivedIssue(workspaceSlug, projectId, issueId);
|
const issueResponse = await this.archivedIssueService.retrieveArchivedIssue(workspaceSlug, projectId, issueId);
|
||||||
await this.rootStore.issueDetail.fetchIssueReactions(workspaceSlug, projectId, issueId);
|
await this.rootStore.issueDetail.fetchIssueReactions(workspaceSlug, projectId, issueId);
|
||||||
await this.rootStore.issueDetail.fetchIssueComments(workspaceSlug, projectId, issueId);
|
await this.rootStore.issueDetail.fetchIssueActivity(workspaceSlug, projectId, issueId);
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.loader = false;
|
this.loader = false;
|
||||||
|
@ -34,7 +34,6 @@ export class TrackEventStore implements ITrackEventStore {
|
|||||||
group?: { isGrouping: boolean | null; groupType: string | null; gorupId: string | null } | null
|
group?: { isGrouping: boolean | null; groupType: string | null; gorupId: string | null } | null
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
console.log("POSTHOG_EVENT: ", eventName);
|
|
||||||
let extras: any = {
|
let extras: any = {
|
||||||
workspace_name: this.rootStore.workspace.currentWorkspace?.name ?? "",
|
workspace_name: this.rootStore.workspace.currentWorkspace?.name ?? "",
|
||||||
workspace_id: this.rootStore.workspace.currentWorkspace?.id ?? "",
|
workspace_id: this.rootStore.workspace.currentWorkspace?.id ?? "",
|
||||||
@ -70,9 +69,8 @@ export class TrackEventStore implements ITrackEventStore {
|
|||||||
element: this.trackElement ?? "",
|
element: this.trackElement ?? "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
console.log(payload);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
throw error;
|
||||||
}
|
}
|
||||||
this.setTrackElement("");
|
this.setTrackElement("");
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@ import { IssueService, IssueReactionService, IssueCommentService } from "service
|
|||||||
import { NotificationService } from "services/notification.service";
|
import { NotificationService } from "services/notification.service";
|
||||||
// types
|
// types
|
||||||
import { RootStore } from "../root";
|
import { RootStore } from "../root";
|
||||||
import { IIssue } from "types";
|
import type { IIssue, IIssueActivity } from "types";
|
||||||
// constants
|
// constants
|
||||||
import { groupReactionEmojis } from "constants/issue";
|
import { groupReactionEmojis } from "constants/issue";
|
||||||
|
|
||||||
@ -16,18 +16,18 @@ export interface IIssueDetailStore {
|
|||||||
issues: {
|
issues: {
|
||||||
[issueId: string]: IIssue;
|
[issueId: string]: IIssue;
|
||||||
};
|
};
|
||||||
issue_reactions: {
|
issueReactions: {
|
||||||
[issueId: string]: any;
|
[issueId: string]: any;
|
||||||
};
|
};
|
||||||
issue_comments: {
|
issueActivity: {
|
||||||
[issueId: string]: any;
|
[issueId: string]: IIssueActivity[];
|
||||||
};
|
};
|
||||||
issue_comment_reactions: {
|
issueCommentReactions: {
|
||||||
[issueId: string]: {
|
[issueId: string]: {
|
||||||
[comment_id: string]: any;
|
[comment_id: string]: any;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
issue_subscription: {
|
issueSubscription: {
|
||||||
[issueId: string]: any;
|
[issueId: string]: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ export interface IIssueDetailStore {
|
|||||||
// computed
|
// computed
|
||||||
getIssue: IIssue | null;
|
getIssue: IIssue | null;
|
||||||
getIssueReactions: any | null;
|
getIssueReactions: any | null;
|
||||||
getIssueComments: any | null;
|
getIssueActivity: IIssueActivity[] | null;
|
||||||
getIssueCommentReactions: any | null;
|
getIssueCommentReactions: any | null;
|
||||||
getIssueSubscription: any | null;
|
getIssueSubscription: any | null;
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ export interface IIssueDetailStore {
|
|||||||
createIssueReaction: (workspaceSlug: string, projectId: string, issueId: string, reaction: string) => Promise<void>;
|
createIssueReaction: (workspaceSlug: string, projectId: string, issueId: string, reaction: string) => Promise<void>;
|
||||||
removeIssueReaction: (workspaceSlug: string, projectId: string, issueId: string, reaction: string) => Promise<void>;
|
removeIssueReaction: (workspaceSlug: string, projectId: string, issueId: string, reaction: string) => Promise<void>;
|
||||||
|
|
||||||
fetchIssueComments: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
fetchIssueActivity: (workspaceSlug: string, projectId: string, issueId: string) => Promise<void>;
|
||||||
createIssueComment: (workspaceSlug: string, projectId: string, issueId: string, data: any) => Promise<void>;
|
createIssueComment: (workspaceSlug: string, projectId: string, issueId: string, data: any) => Promise<void>;
|
||||||
updateIssueComment: (
|
updateIssueComment: (
|
||||||
workspaceSlug: string,
|
workspaceSlug: string,
|
||||||
@ -96,16 +96,16 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
issues: {
|
issues: {
|
||||||
[issueId: string]: IIssue;
|
[issueId: string]: IIssue;
|
||||||
} = {};
|
} = {};
|
||||||
issue_reactions: {
|
issueReactions: {
|
||||||
[issueId: string]: any;
|
[issueId: string]: any;
|
||||||
} = {};
|
} = {};
|
||||||
issue_comments: {
|
issueActivity: {
|
||||||
|
[issueId: string]: IIssueActivity[];
|
||||||
|
} = {};
|
||||||
|
issueCommentReactions: {
|
||||||
[issueId: string]: any;
|
[issueId: string]: any;
|
||||||
} = {};
|
} = {};
|
||||||
issue_comment_reactions: {
|
issueSubscription: {
|
||||||
[issueId: string]: any;
|
|
||||||
} = {};
|
|
||||||
issue_subscription: {
|
|
||||||
[issueId: string]: any;
|
[issueId: string]: any;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
@ -125,14 +125,14 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
|
|
||||||
peekId: observable.ref,
|
peekId: observable.ref,
|
||||||
issues: observable.ref,
|
issues: observable.ref,
|
||||||
issue_reactions: observable.ref,
|
issueReactions: observable.ref,
|
||||||
issue_comments: observable.ref,
|
issueActivity: observable.ref,
|
||||||
issue_comment_reactions: observable.ref,
|
issueCommentReactions: observable.ref,
|
||||||
issue_subscription: observable.ref,
|
issueSubscription: observable.ref,
|
||||||
|
|
||||||
getIssue: computed,
|
getIssue: computed,
|
||||||
getIssueReactions: computed,
|
getIssueReactions: computed,
|
||||||
getIssueComments: computed,
|
getIssueActivity: computed,
|
||||||
getIssueCommentReactions: computed,
|
getIssueCommentReactions: computed,
|
||||||
getIssueSubscription: computed,
|
getIssueSubscription: computed,
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
createIssueReaction: action,
|
createIssueReaction: action,
|
||||||
removeIssueReaction: action,
|
removeIssueReaction: action,
|
||||||
|
|
||||||
fetchIssueComments: action,
|
fetchIssueActivity: action,
|
||||||
createIssueComment: action,
|
createIssueComment: action,
|
||||||
updateIssueComment: action,
|
updateIssueComment: action,
|
||||||
removeIssueComment: action,
|
removeIssueComment: action,
|
||||||
@ -196,25 +196,25 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
|
|
||||||
get getIssueReactions() {
|
get getIssueReactions() {
|
||||||
if (!this.peekId) return null;
|
if (!this.peekId) return null;
|
||||||
const _reactions = this.issue_reactions[this.peekId];
|
const _reactions = this.issueReactions[this.peekId];
|
||||||
return _reactions || null;
|
return _reactions || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get getIssueComments() {
|
get getIssueActivity() {
|
||||||
if (!this.peekId) return null;
|
if (!this.peekId) return null;
|
||||||
const _comments = this.issue_comments[this.peekId];
|
const activity = this.issueActivity[this.peekId];
|
||||||
return _comments || null;
|
return activity || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get getIssueCommentReactions() {
|
get getIssueCommentReactions() {
|
||||||
if (!this.peekId) return null;
|
if (!this.peekId) return null;
|
||||||
const _commentReactions = this.issue_comment_reactions[this.peekId];
|
const _commentReactions = this.issueCommentReactions[this.peekId];
|
||||||
return _commentReactions || null;
|
return _commentReactions || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
get getIssueSubscription() {
|
get getIssueSubscription() {
|
||||||
if (!this.peekId) return null;
|
if (!this.peekId) return null;
|
||||||
const _commentSubscription = this.issue_subscription[this.peekId];
|
const _commentSubscription = this.issueSubscription[this.peekId];
|
||||||
return _commentSubscription || null;
|
return _commentSubscription || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,7 +292,7 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
|
|
||||||
const issueDetailsResponse = await this.issueService.retrieve(workspaceSlug, projectId, issueId);
|
const issueDetailsResponse = await this.issueService.retrieve(workspaceSlug, projectId, issueId);
|
||||||
await this.fetchIssueReactions(workspaceSlug, projectId, issueId);
|
await this.fetchIssueReactions(workspaceSlug, projectId, issueId);
|
||||||
await this.fetchIssueComments(workspaceSlug, projectId, issueId);
|
await this.fetchIssueActivity(workspaceSlug, projectId, issueId);
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.loader = false;
|
this.loader = false;
|
||||||
@ -319,13 +319,13 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
try {
|
try {
|
||||||
const _reactions = await this.issueReactionService.listIssueReactions(workspaceSlug, projectId, issueId);
|
const _reactions = await this.issueReactionService.listIssueReactions(workspaceSlug, projectId, issueId);
|
||||||
|
|
||||||
const _issue_reactions = {
|
const _issueReactions = {
|
||||||
...this.issue_reactions,
|
...this.issueReactions,
|
||||||
[issueId]: groupReactionEmojis(_reactions),
|
[issueId]: groupReactionEmojis(_reactions),
|
||||||
};
|
};
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issue_reactions = _issue_reactions;
|
this.issueReactions = _issueReactions;
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("error creating the issue reaction", error);
|
console.warn("error creating the issue reaction", error);
|
||||||
@ -346,15 +346,15 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issue_reactions = {
|
this.issueReactions = {
|
||||||
...this.issue_reactions,
|
...this.issueReactions,
|
||||||
[issueId]: _currentReactions,
|
[issueId]: _currentReactions,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issue_reactions = {
|
this.issueReactions = {
|
||||||
...this.issue_reactions,
|
...this.issueReactions,
|
||||||
[issueId]: _currentReactions,
|
[issueId]: _currentReactions,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -375,8 +375,8 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issue_reactions = {
|
this.issueReactions = {
|
||||||
...this.issue_reactions,
|
...this.issueReactions,
|
||||||
[issueId]: _currentReactions,
|
[issueId]: _currentReactions,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -385,8 +385,8 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issue_reactions = {
|
this.issueReactions = {
|
||||||
...this.issue_reactions,
|
...this.issueReactions,
|
||||||
[issueId]: _currentReactions,
|
[issueId]: _currentReactions,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -395,24 +395,25 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// comments
|
fetchIssueActivity = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||||
fetchIssueComments = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
|
||||||
try {
|
try {
|
||||||
const _issueCommentResponse = await this.issueService.getIssueActivities(workspaceSlug, projectId, issueId);
|
const issueActivityResponse = await this.issueService.getIssueActivities(workspaceSlug, projectId, issueId);
|
||||||
|
|
||||||
const _issueComments = {
|
const _issueComments = {
|
||||||
...this.issue_comments,
|
...this.issueActivity,
|
||||||
[issueId]: [..._issueCommentResponse],
|
[issueId]: [...issueActivityResponse],
|
||||||
};
|
};
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issue_comments = _issueComments;
|
this.issueActivity = _issueComments;
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("error creating the issue comment", error);
|
console.warn("error creating the issue comment", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// comments
|
||||||
createIssueComment = async (workspaceSlug: string, projectId: string, issueId: string, data: any) => {
|
createIssueComment = async (workspaceSlug: string, projectId: string, issueId: string, data: any) => {
|
||||||
try {
|
try {
|
||||||
const _issueCommentResponse = await this.issueCommentService.createIssueComment(
|
const _issueCommentResponse = await this.issueCommentService.createIssueComment(
|
||||||
@ -423,18 +424,19 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const _issueComments = {
|
const _issueComments = {
|
||||||
...this.issue_comments,
|
...this.issueActivity,
|
||||||
[issueId]: [...this.issue_comments[issueId], _issueCommentResponse],
|
[issueId]: [...this.issueActivity[issueId], _issueCommentResponse],
|
||||||
};
|
};
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issue_comments = _issueComments;
|
this.issueActivity = _issueComments;
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("error creating the issue comment", error);
|
console.warn("error creating the issue comment", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
updateIssueComment = async (
|
updateIssueComment = async (
|
||||||
workspaceSlug: string,
|
workspaceSlug: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
@ -452,31 +454,32 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const _issueComments = {
|
const _issueComments = {
|
||||||
...this.issue_comments,
|
...this.issueActivity,
|
||||||
[issueId]: this.issue_comments[issueId].map((comment: any) =>
|
[issueId]: this.issueActivity[issueId].map((comment) =>
|
||||||
comment.id === commentId ? _issueCommentResponse : comment
|
comment.id === commentId ? _issueCommentResponse : comment
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issue_comments = _issueComments;
|
this.issueActivity = _issueComments;
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("error updating the issue comment", error);
|
console.warn("error updating the issue comment", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
removeIssueComment = async (workspaceSlug: string, projectId: string, issueId: string, commentId: string) => {
|
removeIssueComment = async (workspaceSlug: string, projectId: string, issueId: string, commentId: string) => {
|
||||||
try {
|
try {
|
||||||
const _issueComments = {
|
const _issueComments = {
|
||||||
...this.issue_comments,
|
...this.issueActivity,
|
||||||
[issueId]: this.issue_comments[issueId].filter((comment: any) => comment.id != commentId),
|
[issueId]: this.issueActivity[issueId]?.filter((comment) => comment.id != commentId),
|
||||||
};
|
};
|
||||||
|
|
||||||
await this.issueCommentService.deleteIssueComment(workspaceSlug, projectId, issueId, commentId);
|
await this.issueCommentService.deleteIssueComment(workspaceSlug, projectId, issueId, commentId);
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issue_comments = _issueComments;
|
this.issueActivity = _issueComments;
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("error removing the issue comment", error);
|
console.warn("error removing the issue comment", error);
|
||||||
@ -484,27 +487,28 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// comment reaction
|
// comment reactions
|
||||||
fetchIssueCommentReactions = async (workspaceSlug: string, projectId: string, issueId: string, commentId: string) => {
|
fetchIssueCommentReactions = async (workspaceSlug: string, projectId: string, issueId: string, commentId: string) => {
|
||||||
try {
|
try {
|
||||||
const _reactions = await this.issueReactionService.listIssueCommentReactions(workspaceSlug, projectId, commentId);
|
const _reactions = await this.issueReactionService.listIssueCommentReactions(workspaceSlug, projectId, commentId);
|
||||||
|
|
||||||
const _issue_comment_reactions = {
|
const _issueCommentReactions = {
|
||||||
...this.issue_comment_reactions,
|
...this.issueCommentReactions,
|
||||||
[issueId]: {
|
[issueId]: {
|
||||||
...this.issue_comment_reactions[issueId],
|
...this.issueCommentReactions[issueId],
|
||||||
[commentId]: groupReactionEmojis(_reactions),
|
[commentId]: groupReactionEmojis(_reactions),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issue_comment_reactions = _issue_comment_reactions;
|
this.issueCommentReactions = _issueCommentReactions;
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("error removing the issue comment", error);
|
console.warn("error removing the issue comment", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
creationIssueCommentReaction = async (
|
creationIssueCommentReaction = async (
|
||||||
workspaceSlug: string,
|
workspaceSlug: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
@ -530,22 +534,23 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
[reaction]: [..._currentReactions?.[reaction], { ..._reaction }],
|
[reaction]: [..._currentReactions?.[reaction], { ..._reaction }],
|
||||||
};
|
};
|
||||||
|
|
||||||
const _issue_comment_reactions = {
|
const _issueCommentReactions = {
|
||||||
...this.issue_comment_reactions,
|
...this.issueCommentReactions,
|
||||||
[issueId]: {
|
[issueId]: {
|
||||||
...this.issue_comment_reactions[issueId],
|
...this.issueCommentReactions[issueId],
|
||||||
[commentId]: _currentReactions,
|
[commentId]: _currentReactions,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issue_comment_reactions = _issue_comment_reactions;
|
this.issueCommentReactions = _issueCommentReactions;
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("error removing the issue comment", error);
|
console.warn("error removing the issue comment", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
removeIssueCommentReaction = async (
|
removeIssueCommentReaction = async (
|
||||||
workspaceSlug: string,
|
workspaceSlug: string,
|
||||||
projectId: string,
|
projectId: string,
|
||||||
@ -565,16 +570,16 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
[reaction]: [..._currentReactions?.[reaction].filter((r: any) => r.actor !== user.id)],
|
[reaction]: [..._currentReactions?.[reaction].filter((r: any) => r.actor !== user.id)],
|
||||||
};
|
};
|
||||||
|
|
||||||
const _issue_comment_reactions = {
|
const _issueCommentReactions = {
|
||||||
...this.issue_comment_reactions,
|
...this.issueCommentReactions,
|
||||||
[issueId]: {
|
[issueId]: {
|
||||||
...this.issue_comment_reactions[issueId],
|
...this.issueCommentReactions[issueId],
|
||||||
[commentId]: _currentReactions,
|
[commentId]: _currentReactions,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issue_comment_reactions = _issue_comment_reactions;
|
this.issueCommentReactions = _issueCommentReactions;
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.issueReactionService.deleteIssueCommentReaction(workspaceSlug, projectId, commentId, reaction);
|
await this.issueReactionService.deleteIssueCommentReaction(workspaceSlug, projectId, commentId, reaction);
|
||||||
@ -585,7 +590,7 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// subscription
|
// subscriptions
|
||||||
fetchIssueSubscription = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
fetchIssueSubscription = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||||
try {
|
try {
|
||||||
const _subscription = await this.notificationService.getIssueNotificationSubscriptionStatus(
|
const _subscription = await this.notificationService.getIssueNotificationSubscriptionStatus(
|
||||||
@ -594,13 +599,13 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
issueId
|
issueId
|
||||||
);
|
);
|
||||||
|
|
||||||
const _issue_subscription = {
|
const _issueSubscription = {
|
||||||
...this.issue_subscription,
|
...this.issueSubscription,
|
||||||
[issueId]: _subscription,
|
[issueId]: _subscription,
|
||||||
};
|
};
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issue_subscription = _issue_subscription;
|
this.issueSubscription = _issueSubscription;
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("error fetching the issue subscription", error);
|
console.warn("error fetching the issue subscription", error);
|
||||||
@ -611,13 +616,13 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
try {
|
try {
|
||||||
await this.notificationService.subscribeToIssueNotifications(workspaceSlug, projectId, issueId);
|
await this.notificationService.subscribeToIssueNotifications(workspaceSlug, projectId, issueId);
|
||||||
|
|
||||||
const _issue_subscription = {
|
const _issueSubscription = {
|
||||||
...this.issue_subscription,
|
...this.issueSubscription,
|
||||||
[issueId]: { subscribed: true },
|
[issueId]: { subscribed: true },
|
||||||
};
|
};
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issue_subscription = _issue_subscription;
|
this.issueSubscription = _issueSubscription;
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn("error creating the issue subscription", error);
|
console.warn("error creating the issue subscription", error);
|
||||||
@ -626,13 +631,13 @@ export class IssueDetailStore implements IIssueDetailStore {
|
|||||||
};
|
};
|
||||||
removeIssueSubscription = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
removeIssueSubscription = async (workspaceSlug: string, projectId: string, issueId: string) => {
|
||||||
try {
|
try {
|
||||||
const _issue_subscription = {
|
const _issueSubscription = {
|
||||||
...this.issue_subscription,
|
...this.issueSubscription,
|
||||||
[issueId]: { subscribed: false },
|
[issueId]: { subscribed: false },
|
||||||
};
|
};
|
||||||
|
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.issue_subscription = _issue_subscription;
|
this.issueSubscription = _issueSubscription;
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.notificationService.unsubscribeFromIssueNotifications(workspaceSlug, projectId, issueId);
|
await this.notificationService.unsubscribeFromIssueNotifications(workspaceSlug, projectId, issueId);
|
||||||
|
15
web/types/issues.d.ts
vendored
15
web/types/issues.d.ts
vendored
@ -181,18 +181,20 @@ export interface IIssueLabelTree extends IIssueLabel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IIssueActivity {
|
export interface IIssueActivity {
|
||||||
|
access?: "EXTERNAL" | "INTERNAL";
|
||||||
actor: string;
|
actor: string;
|
||||||
actor_detail: IUserLite;
|
actor_detail: IUserLite;
|
||||||
attachments: any[];
|
attachments: any[];
|
||||||
comment: string;
|
comment?: string;
|
||||||
|
comment_html?: string;
|
||||||
|
comment_stripped?: string;
|
||||||
created_at: Date;
|
created_at: Date;
|
||||||
created_by: string;
|
created_by: string;
|
||||||
field: string | null;
|
field: string | null;
|
||||||
id: string;
|
id: string;
|
||||||
issue: string | null;
|
issue: string | null;
|
||||||
issue_comment: string | null;
|
issue_comment?: string | null;
|
||||||
issue_detail: {
|
issue_detail: {
|
||||||
description: any;
|
|
||||||
description_html: string;
|
description_html: string;
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@ -212,13 +214,6 @@ export interface IIssueActivity {
|
|||||||
workspace_detail?: IWorkspaceLite;
|
workspace_detail?: IWorkspaceLite;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IIssueComment extends IIssueActivity {
|
|
||||||
access: "EXTERNAL" | "INTERNAL";
|
|
||||||
comment_html: string;
|
|
||||||
comment_json: any;
|
|
||||||
comment_stripped: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IIssueLite {
|
export interface IIssueLite {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
21
web/types/reaction.d.ts
vendored
21
web/types/reaction.d.ts
vendored
@ -1,14 +1,17 @@
|
|||||||
export interface IssueReaction {
|
import { IUserLite } from "./users";
|
||||||
id: string;
|
|
||||||
created_at: Date;
|
export interface IIssueReaction {
|
||||||
updated_at: Date;
|
|
||||||
reaction: string;
|
|
||||||
created_by: string;
|
|
||||||
updated_by: string;
|
|
||||||
project: string;
|
|
||||||
workspace: string;
|
|
||||||
actor: string;
|
actor: string;
|
||||||
|
actor_detail: IUserLite;
|
||||||
|
created_at: Date;
|
||||||
|
created_by: string;
|
||||||
|
id: string;
|
||||||
issue: string;
|
issue: string;
|
||||||
|
project: string;
|
||||||
|
reaction: string;
|
||||||
|
updated_at: Date;
|
||||||
|
updated_by: string;
|
||||||
|
workspace: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IssueReactionForm {
|
export interface IssueReactionForm {
|
||||||
|
Loading…
Reference in New Issue
Block a user