chore: code refactoring (#5928)

* chore: de dupe code splitting

* chore: code refactor
This commit is contained in:
Anmol Singh Bhatia 2024-10-29 19:39:55 +05:30 committed by GitHub
parent 4bc751b7ab
commit 57eb08c8a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 664 additions and 250 deletions

View file

@ -17,10 +17,16 @@ import {
TIssueOperations,
IssueAttachmentRoot,
} from "@/components/issues";
// constants
import { ISSUE_ARCHIVED, ISSUE_DELETED } from "@/constants/event-tracker";
// helpers
import { getTextContent } from "@/helpers/editor.helper";
// hooks
import { useEventTracker, useProjectInbox, useUser } from "@/hooks/store";
import { useEventTracker, useIssueDetail, useProject, useProjectInbox, useUser } from "@/hooks/store";
import useReloadConfirmations from "@/hooks/use-reload-confirmation";
// store types
import { DeDupeIssuePopoverRoot } from "@/plane-web/components/de-dupe";
import { useDebouncedDuplicateIssues } from "@/plane-web/hooks/use-debounced-duplicate-issues";
import { IInboxIssueStore } from "@/store/inbox/inbox-issue.store";
type Props = {
@ -40,6 +46,8 @@ export const InboxIssueMainContent: React.FC<Props> = observer((props) => {
const { setShowAlert } = useReloadConfirmations(isSubmitting === "submitting");
const { captureIssueEvent } = useEventTracker();
const { loader } = useProjectInbox();
const { getProjectById } = useProject();
const { removeIssue, archiveIssue } = useIssueDetail();
useEffect(() => {
if (isSubmitting === "submitted") {
@ -52,7 +60,17 @@ export const InboxIssueMainContent: React.FC<Props> = observer((props) => {
}
}, [isSubmitting, setShowAlert, setIsSubmitting]);
// dervied values
const issue = inboxIssue.issue;
const projectDetails = issue?.project_id ? getProjectById(issue?.project_id) : undefined;
// debounced duplicate issues swr
const { duplicateIssues } = useDebouncedDuplicateIssues(projectDetails?.workspace.toString(), projectId, {
name: issue?.name,
description_html: getTextContent(issue?.description_html),
issueId: issue?.id,
});
if (!issue) return <></>;
const issueOperations: TIssueOperations = useMemo(
@ -63,7 +81,31 @@ export const InboxIssueMainContent: React.FC<Props> = observer((props) => {
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars, arrow-body-style
remove: async (_workspaceSlug: string, _projectId: string, _issueId: string) => {
return;
try {
await removeIssue(workspaceSlug, projectId, _issueId);
setToast({
title: "Success!",
type: TOAST_TYPE.SUCCESS,
message: "Issue deleted successfully",
});
captureIssueEvent({
eventName: ISSUE_DELETED,
payload: { id: _issueId, state: "SUCCESS", element: "Issue detail page" },
path: pathname,
});
} catch (error) {
console.log("Error in deleting issue:", error);
setToast({
title: "Error!",
type: TOAST_TYPE.ERROR,
message: "Issue delete failed",
});
captureIssueEvent({
eventName: ISSUE_DELETED,
payload: { id: _issueId, state: "FAILED", element: "Issue detail page" },
path: pathname,
});
}
},
update: async (_workspaceSlug: string, _projectId: string, _issueId: string, data: Partial<TIssue>) => {
try {
@ -94,6 +136,23 @@ export const InboxIssueMainContent: React.FC<Props> = observer((props) => {
});
}
},
archive: async (workspaceSlug: string, projectId: string, issueId: string) => {
try {
await archiveIssue(workspaceSlug, projectId, issueId);
captureIssueEvent({
eventName: ISSUE_ARCHIVED,
payload: { id: issueId, state: "SUCCESS", element: "Issue details page" },
path: pathname,
});
} catch (error) {
console.log("Error in archiving issue:", error);
captureIssueEvent({
eventName: ISSUE_ARCHIVED,
payload: { id: issueId, state: "FAILED", element: "Issue details page" },
path: pathname,
});
}
},
}),
[inboxIssue]
);
@ -103,6 +162,16 @@ export const InboxIssueMainContent: React.FC<Props> = observer((props) => {
return (
<>
<div className="rounded-lg space-y-4">
{duplicateIssues.length > 0 && (
<DeDupeIssuePopoverRoot
workspaceSlug={workspaceSlug}
projectId={issue.project_id}
rootIssueId={issue.id}
issues={duplicateIssues}
issueOperations={issueOperations}
isIntakeIssue
/>
)}
<IssueTitleInput
workspaceSlug={workspaceSlug}
projectId={issue.project_id}

View file

@ -1,6 +1,6 @@
"use client";
import { FC, FormEvent, useCallback, useRef, useState } from "react";
import { FC, FormEvent, useCallback, useEffect, useRef, useState } from "react";
import { observer } from "mobx-react";
import { usePathname } from "next/navigation";
// editor
@ -17,18 +17,23 @@ import { ETabIndices } from "@/constants/tab-indices";
import { renderFormattedPayloadDate } from "@/helpers/date-time.helper";
import { getTabIndex } from "@/helpers/tab-indices.helper";
// hooks
import { useEventTracker, useProjectInbox, useWorkspace } from "@/hooks/store";
import { useEventTracker, useProject, useProjectInbox, useWorkspace } from "@/hooks/store";
import { useAppRouter } from "@/hooks/use-app-router";
import useKeypress from "@/hooks/use-keypress";
import { usePlatformOS } from "@/hooks/use-platform-os";
// services
import { DeDupeIssueButtonLabel, DuplicateModalRoot } from "@/plane-web/components/de-dupe";
import { useDebouncedDuplicateIssues } from "@/plane-web/hooks/use-debounced-duplicate-issues";
import { FileService } from "@/services/file.service";
const fileService = new FileService();
type TInboxIssueCreateRoot = {
workspaceSlug: string;
projectId: string;
handleModalClose: () => void;
isDuplicateModalOpen: boolean;
handleDuplicateIssueModal: (value: boolean) => void;
};
export const defaultIssueData: Partial<TIssue> = {
@ -44,7 +49,7 @@ export const defaultIssueData: Partial<TIssue> = {
};
export const InboxIssueCreateRoot: FC<TInboxIssueCreateRoot> = observer((props) => {
const { workspaceSlug, projectId, handleModalClose } = props;
const { workspaceSlug, projectId, handleModalClose, isDuplicateModalOpen, handleDuplicateIssueModal } = props;
// states
const [uploadedAssetIds, setUploadedAssetIds] = useState<string[]>([]);
// router
@ -53,12 +58,15 @@ export const InboxIssueCreateRoot: FC<TInboxIssueCreateRoot> = observer((props)
// refs
const descriptionEditorRef = useRef<EditorRefApi>(null);
const submitBtnRef = useRef<HTMLButtonElement | null>(null);
const formRef = useRef<HTMLFormElement | null>(null);
const modalContainerRef = useRef<HTMLDivElement | null>(null);
// hooks
const { captureIssueEvent } = useEventTracker();
const { createInboxIssue } = useProjectInbox();
const { getWorkspaceBySlug } = useWorkspace();
const workspaceId = getWorkspaceBySlug(workspaceSlug)?.id;
const { isMobile } = usePlatformOS();
const { getProjectById } = useProject();
// states
const [createMore, setCreateMore] = useState<boolean>(false);
const [formSubmitting, setFormSubmitting] = useState(false);
@ -73,8 +81,17 @@ export const InboxIssueCreateRoot: FC<TInboxIssueCreateRoot> = observer((props)
[formData]
);
// derived values
const projectDetails = projectId ? getProjectById(projectId) : undefined;
const { getIndex } = getTabIndex(ETabIndices.INTAKE_ISSUE_FORM, isMobile);
// debounced duplicate issues swr
const { duplicateIssues } = useDebouncedDuplicateIssues(projectDetails?.workspace.toString(), projectId, {
name: formData?.name,
description_html: formData?.description_html,
});
const handleEscKeyDown = (event: KeyboardEvent) => {
if (descriptionEditorRef.current?.isEditorReadyToDiscard()) {
handleModalClose();
@ -90,6 +107,23 @@ export const InboxIssueCreateRoot: FC<TInboxIssueCreateRoot> = observer((props)
useKeypress("Escape", handleEscKeyDown);
useEffect(() => {
const formElement = formRef?.current;
const modalElement = modalContainerRef?.current;
if (!formElement || !modalElement) return;
const resizeObserver = new ResizeObserver(() => {
modalElement.style.maxHeight = `${formElement?.offsetHeight}px`;
});
resizeObserver.observe(formElement);
return () => {
resizeObserver.disconnect();
};
}, [formRef, modalContainerRef]);
const handleFormSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
@ -165,74 +199,109 @@ export const InboxIssueCreateRoot: FC<TInboxIssueCreateRoot> = observer((props)
const isTitleLengthMoreThan255Character = formData?.name ? formData.name.length > 255 : false;
const shouldRenderDuplicateModal = isDuplicateModalOpen && duplicateIssues?.length > 0;
if (!workspaceSlug || !projectId || !workspaceId) return <></>;
return (
<form onSubmit={handleFormSubmit}>
<div className="space-y-5 p-5">
<h3 className="text-xl font-medium text-custom-text-200">Create intake issue</h3>
<div className="space-y-3">
<InboxIssueTitle
data={formData}
handleData={handleFormData}
isTitleLengthMoreThan255Character={isTitleLengthMoreThan255Character}
/>
<InboxIssueDescription
workspaceSlug={workspaceSlug}
projectId={projectId}
workspaceId={workspaceId}
data={formData}
handleData={handleFormData}
editorRef={descriptionEditorRef}
containerClassName="border-[0.5px] border-custom-border-200 py-3 min-h-[150px]"
onEnterKeyPress={() => submitBtnRef?.current?.click()}
onAssetUpload={(assetId) => setUploadedAssetIds((prev) => [...prev, assetId])}
/>
<InboxIssueProperties projectId={projectId} data={formData} handleData={handleFormData} />
</div>
<div className="flex gap-2 bg-transparent w-full">
<div className="rounded-lg w-full">
<form ref={formRef} onSubmit={handleFormSubmit} className="flex flex-col w-full">
<div className="space-y-5 p-5 rounded-t-lg bg-custom-background-100">
<div className="flex items-center justify-between gap-2">
<h3 className="text-xl font-medium text-custom-text-200">Create intake issue</h3>
{duplicateIssues?.length > 0 && (
<button
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
handleDuplicateIssueModal(!isDuplicateModalOpen);
}}
>
<DeDupeIssueButtonLabel
isOpen={isDuplicateModalOpen}
buttonLabel={`${duplicateIssues.length} duplicate issue${duplicateIssues.length > 1 ? "s" : ""} found!`}
/>
</button>
)}
</div>
<div className="space-y-3">
<InboxIssueTitle
data={formData}
handleData={handleFormData}
isTitleLengthMoreThan255Character={isTitleLengthMoreThan255Character}
/>
<InboxIssueDescription
workspaceSlug={workspaceSlug}
projectId={projectId}
workspaceId={workspaceId}
data={formData}
handleData={handleFormData}
editorRef={descriptionEditorRef}
containerClassName="border-[0.5px] border-custom-border-200 py-3 min-h-[150px]"
onEnterKeyPress={() => submitBtnRef?.current?.click()}
onAssetUpload={(assetId) => setUploadedAssetIds((prev) => [...prev, assetId])}
/>
<InboxIssueProperties projectId={projectId} data={formData} handleData={handleFormData} />
</div>
</div>
<div className="px-5 py-4 flex items-center justify-between gap-2 border-t-[0.5px] border-custom-border-200 rounded-b-lg bg-custom-background-100">
<div
className="inline-flex items-center gap-1.5 cursor-pointer"
onClick={() => setCreateMore((prevData) => !prevData)}
role="button"
tabIndex={getIndex("create_more")}
>
<ToggleSwitch value={createMore} onChange={() => {}} size="sm" />
<span className="text-xs">Create more</span>
</div>
<div className="flex items-center gap-3">
<Button
variant="neutral-primary"
size="sm"
type="button"
onClick={() => {
if (descriptionEditorRef.current?.isEditorReadyToDiscard()) {
handleModalClose();
} else {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
message: "Editor is still processing changes. Please wait before proceeding.",
});
}
}}
tabIndex={getIndex("discard_button")}
>
Discard
</Button>
<Button
variant="primary"
ref={submitBtnRef}
size="sm"
type="submit"
loading={formSubmitting}
disabled={isTitleLengthMoreThan255Character}
tabIndex={getIndex("submit_button")}
>
{formSubmitting ? "Creating" : "Create Issue"}
</Button>
</div>
</div>
</form>
</div>
<div className="px-5 py-4 flex items-center justify-between gap-2 border-t-[0.5px] border-custom-border-200">
{shouldRenderDuplicateModal && (
<div
className="inline-flex items-center gap-1.5 cursor-pointer"
onClick={() => setCreateMore((prevData) => !prevData)}
role="button"
tabIndex={getIndex("create_more")}
ref={modalContainerRef}
className="relative flex flex-col gap-2.5 h-full px-3 py-4 rounded-lg shadow-xl bg-pi-50"
style={{ maxHeight: formRef?.current?.offsetHeight ? `${formRef.current.offsetHeight}px` : "436px" }}
>
<ToggleSwitch value={createMore} onChange={() => { }} size="sm" />
<span className="text-xs">Create more</span>
<DuplicateModalRoot
workspaceSlug={workspaceSlug.toString()}
issues={duplicateIssues}
handleDuplicateIssueModal={handleDuplicateIssueModal}
/>
</div>
<div className="flex items-center gap-3">
<Button
variant="neutral-primary"
size="sm"
type="button"
onClick={() => {
if (descriptionEditorRef.current?.isEditorReadyToDiscard()) {
handleModalClose();
} else {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
message: "Editor is still processing changes. Please wait before proceeding.",
});
}
}}
tabIndex={getIndex("discard_button")}
>
Discard
</Button>
<Button
variant="primary"
ref={submitBtnRef}
size="sm"
type="submit"
loading={formSubmitting}
disabled={isTitleLengthMoreThan255Character}
tabIndex={getIndex("submit_button")}
>
{formSubmitting ? "Creating" : "Create Issue"}
</Button>
</div>
</div>
</form>
)}
</div>
);
});

View file

@ -1,4 +1,6 @@
import { FC } from "react";
"use-client";
import { FC, useState } from "react";
// ui
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
// components
@ -13,15 +15,29 @@ type TInboxIssueCreateModalRoot = {
export const InboxIssueCreateModalRoot: FC<TInboxIssueCreateModalRoot> = (props) => {
const { workspaceSlug, projectId, modalState, handleModalClose } = props;
// states
const [isDuplicateModalOpen, setIsDuplicateModalOpen] = useState(false);
// handlers
const handleDuplicateIssueModal = (value: boolean) => setIsDuplicateModalOpen(value);
return (
<ModalCore
isOpen={modalState}
handleClose={handleModalClose}
handleClose={() => {
handleModalClose();
setIsDuplicateModalOpen(false);
}}
position={EModalPosition.TOP}
width={EModalWidth.XXXXL}
width={isDuplicateModalOpen ? EModalWidth.VIXL : EModalWidth.XXXXL}
className="!bg-transparent rounded-lg shadow-none transition-[width] ease-linear"
>
<InboxIssueCreateRoot workspaceSlug={workspaceSlug} projectId={projectId} handleModalClose={handleModalClose} />
<InboxIssueCreateRoot
workspaceSlug={workspaceSlug}
projectId={projectId}
handleModalClose={handleModalClose}
isDuplicateModalOpen={isDuplicateModalOpen}
handleDuplicateIssueModal={handleDuplicateIssueModal}
/>
</ModalCore>
);
};