From 9ec0b6f4ccb8d4b795019be62c1da1686638dab5 Mon Sep 17 00:00:00 2001 From: Prateek Shourya Date: Fri, 28 Mar 2025 20:18:14 +0530 Subject: [PATCH] [WEB-3712] improvement: create draft work item logic --- .../ui/src/dropdowns/custom-search-select.tsx | 18 ++++--- packages/ui/src/dropdowns/helper.tsx | 1 + .../issues/issue-modal/template-select.tsx | 1 + .../issues/issue-modal/draft-issue-layout.tsx | 47 ++++++++++++------- .../components/issues/issue-modal/form.tsx | 33 +++++++++---- 5 files changed, 66 insertions(+), 34 deletions(-) diff --git a/packages/ui/src/dropdowns/custom-search-select.tsx b/packages/ui/src/dropdowns/custom-search-select.tsx index 4302c12fdc7..e592f0dc2bc 100644 --- a/packages/ui/src/dropdowns/custom-search-select.tsx +++ b/packages/ui/src/dropdowns/custom-search-select.tsx @@ -1,18 +1,15 @@ -import React, { useRef, useState } from "react"; -import { usePopper } from "react-popper"; import { Combobox } from "@headlessui/react"; import { Check, ChevronDown, Info, Search } from "lucide-react"; +import React, { useRef, useState } from "react"; import { createPortal } from "react-dom"; -// plane helpers +import { usePopper } from "react-popper"; +// plane imports import { useOutsideClickDetector } from "@plane/hooks"; -// hooks -import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down"; -// helpers +// local imports import { cn } from "../../helpers"; -// types -import { ICustomSearchSelectProps } from "./helper"; -// local components +import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down"; import { Tooltip } from "../tooltip"; +import { ICustomSearchSelectProps } from "./helper"; export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { const { @@ -36,6 +33,7 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { optionsClassName = "", value, tabIndex, + noResultsMessage = "No matches found", } = props; const [query, setQuery] = useState(""); @@ -201,7 +199,7 @@ export const CustomSearchSelect = (props: ICustomSearchSelectProps) => { )) ) : ( -

No matches found

+

{noResultsMessage}

) ) : (

Loading...

diff --git a/packages/ui/src/dropdowns/helper.tsx b/packages/ui/src/dropdowns/helper.tsx index a7ef61c3de0..4ce12d9f8ca 100644 --- a/packages/ui/src/dropdowns/helper.tsx +++ b/packages/ui/src/dropdowns/helper.tsx @@ -43,6 +43,7 @@ interface CustomSearchSelectProps { footerOption?: JSX.Element; onChange: any; onClose?: () => void; + noResultsMessage?: string; options: | { value: any; diff --git a/web/ce/components/issues/issue-modal/template-select.tsx b/web/ce/components/issues/issue-modal/template-select.tsx index 679e9419dec..6d58bb33c93 100644 --- a/web/ce/components/issues/issue-modal/template-select.tsx +++ b/web/ce/components/issues/issue-modal/template-select.tsx @@ -8,6 +8,7 @@ export type TWorkItemTemplateSelect = { placeholder?: string; renderChevron?: boolean; dropDownContainerClassName?: string; + handleModalClose: () => void; handleFormChange?: () => void; }; diff --git a/web/core/components/issues/issue-modal/draft-issue-layout.tsx b/web/core/components/issues/issue-modal/draft-issue-layout.tsx index 0e714cca44d..e10ca34b786 100644 --- a/web/core/components/issues/issue-modal/draft-issue-layout.tsx +++ b/web/core/components/issues/issue-modal/draft-issue-layout.tsx @@ -38,27 +38,34 @@ export const DraftIssueLayout: React.FC = observer((props) => { const { createIssue } = useWorkspaceDraftIssues(); const { t } = useTranslation(); + const sanitizeChanges = (): Partial => { + const sanitizedChanges = { ...changesMade }; + Object.entries(sanitizedChanges).forEach(([key, value]) => { + const issueKey = key as keyof TIssue; + if (value === null || value === undefined || value === "") delete sanitizedChanges[issueKey]; + if (typeof value === "object" && isEmpty(value)) delete sanitizedChanges[issueKey]; + if (Array.isArray(value) && value.length === 0) delete sanitizedChanges[issueKey]; + if (issueKey === "project_id") delete sanitizedChanges.project_id; + if (issueKey === "priority" && value && value === "none") delete sanitizedChanges.priority; + if ( + issueKey === "description_html" && + changesMade?.description_html && + isEmptyHtmlString(changesMade.description_html, ["img"]) + ) + delete sanitizedChanges.description_html; + }); + return sanitizedChanges; + }; + const handleClose = () => { + // If the user is updating an existing work item, we don't need to show the discard modal if (data?.id) { onClose(); setIssueDiscardModal(false); } else { if (changesMade) { - Object.entries(changesMade).forEach(([key, value]) => { - const issueKey = key as keyof TIssue; - if (value === null || value === undefined || value === "") delete changesMade[issueKey]; - if (typeof value === "object" && isEmpty(value)) delete changesMade[issueKey]; - if (Array.isArray(value) && value.length === 0) delete changesMade[issueKey]; - if (issueKey === "project_id") delete changesMade.project_id; - if (issueKey === "priority" && value && value === "none") delete changesMade.priority; - if ( - issueKey === "description_html" && - changesMade.description_html && - isEmptyHtmlString(changesMade.description_html, ["img"]) - ) - delete changesMade.description_html; - }); - if (isEmpty(changesMade)) { + const sanitizedChanges = sanitizeChanges(); + if (isEmpty(sanitizedChanges)) { onClose(); setIssueDiscardModal(false); } else setIssueDiscardModal(true); @@ -119,6 +126,14 @@ export const DraftIssueLayout: React.FC = observer((props) => { } }; + const handleDraftAndClose = () => { + const sanitizedChanges = sanitizeChanges(); + if (!data?.id && !isEmpty(sanitizedChanges)) { + handleCreateDraftIssue(); + } + onClose(); + }; + return ( <> = observer((props) => { onClose(); }} /> - + ); }); diff --git a/web/core/components/issues/issue-modal/form.tsx b/web/core/components/issues/issue-modal/form.tsx index c278540e5c5..ca37cbf24a8 100644 --- a/web/core/components/issues/issue-modal/form.tsx +++ b/web/core/components/issues/issue-modal/form.tsx @@ -61,6 +61,7 @@ export interface IssueFormProps { }; isDuplicateModalOpen: boolean; handleDuplicateIssueModal: (isOpen: boolean) => void; + handleDraftAndClose?: () => void; isProjectSelectionDisabled?: boolean; storeType: EIssuesStoreType; } @@ -86,6 +87,7 @@ export const IssueFormRoot: FC = observer((props) => { }, isDuplicateModalOpen, handleDuplicateIssueModal, + handleDraftAndClose, isProjectSelectionDisabled = false, storeType, } = props; @@ -235,14 +237,22 @@ export const IssueFormRoot: FC = observer((props) => { await onSubmit(submitData, is_draft_issue) .then(() => { setGptAssistantModal(false); - reset({ - ...DEFAULT_WORK_ITEM_FORM_VALUES, - ...(isCreateMoreToggleEnabled ? { ...data } : {}), - project_id: getValues<"project_id">("project_id"), - type_id: getValues<"type_id">("type_id"), - description_html: data?.description_html ?? "

", - }); - editorRef?.current?.clearEditor(); + if (isCreateMoreToggleEnabled && workItemTemplateId) { + handleTemplateChange({ + workspaceSlug: workspaceSlug?.toString(), + reset, + editorRef, + }); + } else { + reset({ + ...DEFAULT_WORK_ITEM_FORM_VALUES, + ...(isCreateMoreToggleEnabled ? { ...data } : {}), + project_id: getValues<"project_id">("project_id"), + type_id: getValues<"type_id">("type_id"), + description_html: data?.description_html ?? "

", + }); + editorRef?.current?.clearEditor(); + } }) .catch((error) => { console.error(error); @@ -389,6 +399,13 @@ export const IssueFormRoot: FC = observer((props) => { { + if (handleDraftAndClose) { + handleDraftAndClose(); + } else { + onClose(); + } + }} handleFormChange={handleFormChange} renderChevron />