From fb87bfc140d98f2a8bd14cd1485ab81dc696f5df Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Sat, 16 Sep 2023 01:38:02 +0530 Subject: [PATCH] style: attributes empty state design --- .../attribute-display/checkbox.tsx | 5 +- .../attribute-display/email.tsx | 7 +- .../attribute-display/file.tsx | 12 +- .../attribute-display/number.tsx | 7 +- .../attribute-display/select.tsx | 56 +- .../attribute-display/text.tsx | 7 +- .../attribute-display/url.tsx | 7 +- .../checkbox-attribute-form.tsx | 4 +- .../issue-modal-attributes-list.tsx | 16 +- web/components/issues/form.tsx | 25 +- web/components/issues/modal.tsx | 710 +++++++++--------- web/constants/custom-attributes.ts | 2 +- 12 files changed, 461 insertions(+), 397 deletions(-) diff --git a/web/components/custom-attributes/attribute-display/checkbox.tsx b/web/components/custom-attributes/attribute-display/checkbox.tsx index 6d7a94fbb..c4bd663ed 100644 --- a/web/components/custom-attributes/attribute-display/checkbox.tsx +++ b/web/components/custom-attributes/attribute-display/checkbox.tsx @@ -11,7 +11,7 @@ export const CustomCheckboxAttribute: React.FC = ({ const handleUpdateCheckbox = (val: boolean | string) => onChange(val.toString()); return ( - <> +
{attributeDetails.extra_settings.representation === "toggle_switch" ? ( ) : ( @@ -23,6 +23,7 @@ export const CustomCheckboxAttribute: React.FC = ({ />
)} - + {attributeDetails.display_name} + ); }; diff --git a/web/components/custom-attributes/attribute-display/email.tsx b/web/components/custom-attributes/attribute-display/email.tsx index 304ac4d06..354638ae2 100644 --- a/web/components/custom-attributes/attribute-display/email.tsx +++ b/web/components/custom-attributes/attribute-display/email.tsx @@ -45,8 +45,11 @@ export const CustomEmailAttribute: React.FC {!isEditing && ( -
setIsEditing(true)}> - {value ?? "Empty"} +
setIsEditing(true)} + > + {value && value !== "" ? value : `Enter ${attributeDetails.display_name}`}
)} {isEditing && ( diff --git a/web/components/custom-attributes/attribute-display/file.tsx b/web/components/custom-attributes/attribute-display/file.tsx index 023902637..807f4a0ed 100644 --- a/web/components/custom-attributes/attribute-display/file.tsx +++ b/web/components/custom-attributes/attribute-display/file.tsx @@ -107,20 +107,20 @@ export const CustomFileAttribute: React.FC - + {isDragActive ? (

Drop here...

) : fileError ? ( -

{fileError}

+

{fileError}

) : isUploading ? ( -

Uploading...

+

Uploading...

) : ( -

Upload {value && value !== "" ? "new " : ""}file

+

Upload {value && value !== "" ? "new " : ""}file

)}
diff --git a/web/components/custom-attributes/attribute-display/number.tsx b/web/components/custom-attributes/attribute-display/number.tsx index 11f86de18..006a75ae7 100644 --- a/web/components/custom-attributes/attribute-display/number.tsx +++ b/web/components/custom-attributes/attribute-display/number.tsx @@ -83,7 +83,12 @@ export const CustomNumberAttribute: React.FC ) : ( - "Empty" +
setIsEditing(true)} + > + {value ?? `Enter ${attributeDetails.display_name}`} +
)} )} diff --git a/web/components/custom-attributes/attribute-display/select.tsx b/web/components/custom-attributes/attribute-display/select.tsx index 8072abfe8..e893d3deb 100644 --- a/web/components/custom-attributes/attribute-display/select.tsx +++ b/web/components/custom-attributes/attribute-display/select.tsx @@ -69,26 +69,52 @@ export const CustomSelectAttribute: React.FC< if (multiple) comboboxProps.multiple = true; return ( - + {({ open }: { open: boolean }) => { if (open) handleOnOpen(); return ( <> - - {Array.isArray(value) - ? value.length > 0 - ? value.map((v) => options.find((o) => o.id === v)?.display_name).join(", ") - : `Select ${attributeDetails.display_name}` - : value} + + {value ? ( + Array.isArray(value) ? ( + value.length > 0 ? ( +
+ {value.map((v) => { + const optionDetails = options.find((o) => o.id === v); + + return ( + + {optionDetails?.display_name} + + ); + })} +
+ ) : ( +
+ Select {attributeDetails.display_name} +
+ ) + ) : ( + o.id === value)?.color}40`, + }} + > + {options.find((o) => o.id === value)?.display_name} + + ) + ) : ( +
+ Select {attributeDetails.display_name} +
+ )}
{!isEditing && ( -
setIsEditing(true)}> - {value ?? "Empty"} +
setIsEditing(true)} + > + {value && value !== "" ? value : `Enter ${attributeDetails.display_name}`}
)} {isEditing && ( diff --git a/web/components/custom-attributes/attribute-display/url.tsx b/web/components/custom-attributes/attribute-display/url.tsx index 7d0c86389..6e34fe7d4 100644 --- a/web/components/custom-attributes/attribute-display/url.tsx +++ b/web/components/custom-attributes/attribute-display/url.tsx @@ -43,8 +43,11 @@ export const CustomUrlAttribute: React.FC return (
{!isEditing && ( -
setIsEditing(true)}> - {value ?? "Empty"} +
setIsEditing(true)} + > + {value && value !== "" ? value : `Enter ${attributeDetails.display_name}`}
)} {isEditing && ( diff --git a/web/components/custom-attributes/attribute-forms/checkbox-attribute-form.tsx b/web/components/custom-attributes/attribute-forms/checkbox-attribute-form.tsx index 2154e371e..2fcceb67a 100644 --- a/web/components/custom-attributes/attribute-forms/checkbox-attribute-form.tsx +++ b/web/components/custom-attributes/attribute-forms/checkbox-attribute-form.tsx @@ -41,7 +41,7 @@ export const CheckboxAttributeForm: React.FC = ({ control }) = ({ control }) diff --git a/web/components/custom-attributes/attributes-list/issue-modal-attributes-list.tsx b/web/components/custom-attributes/attributes-list/issue-modal-attributes-list.tsx index c0a0539e8..b96435a6a 100644 --- a/web/components/custom-attributes/attributes-list/issue-modal-attributes-list.tsx +++ b/web/components/custom-attributes/attributes-list/issue-modal-attributes-list.tsx @@ -23,7 +23,7 @@ import { Loader } from "components/ui"; type Props = { entityId: string; issueId: string; - onChange: (attributeId: string, val: string[]) => Promise; + onChange: (attributeId: string, val: string[]) => void; projectId: string; values: { [key: string]: string[] }; }; @@ -65,7 +65,7 @@ export const IssueModalCustomAttributesList: React.FC = observer( issueId={issueId} onChange={(val: string) => onChange(attribute.id, [val])} projectId={projectId} - value={attribute.default_value === "checked" ? true : false} + value={values[attribute.id]?.[0] === "true" ? true : false} /> )} {attribute.type === "datetime" && ( @@ -75,7 +75,7 @@ export const IssueModalCustomAttributesList: React.FC = observer( onChange={(val: string) => onChange(attribute.id, [val])} projectId={projectId} value={ - attribute.default_value !== "" ? new Date(attribute.default_value) : undefined + values[attribute.id]?.[0] ? new Date(values[attribute.id]?.[0]) : undefined } /> )} @@ -85,7 +85,7 @@ export const IssueModalCustomAttributesList: React.FC = observer( issueId={issueId} onChange={(val: string) => onChange(attribute.id, [val])} projectId={projectId} - value={attribute.default_value} + value={values[attribute.id]?.[0]} /> )} {attribute.type === "file" && ( @@ -103,7 +103,7 @@ export const IssueModalCustomAttributesList: React.FC = observer( issueId={issueId} onChange={(val: string[]) => onChange(attribute.id, val)} projectId={projectId} - value={[]} + value={values[attribute.id] ?? []} multiple /> )} @@ -114,9 +114,7 @@ export const IssueModalCustomAttributesList: React.FC = observer( onChange={(val: string) => onChange(attribute.id, [val])} projectId={projectId} value={ - attribute.default_value !== "" - ? parseInt(attribute.default_value, 10) - : undefined + values[attribute.id]?.[0] ? parseInt(values[attribute.id]?.[0]) : undefined } /> )} @@ -126,7 +124,7 @@ export const IssueModalCustomAttributesList: React.FC = observer( issueId={issueId} onChange={(val: string) => onChange(attribute.id, [val])} projectId={projectId} - value={attribute.default_value !== "" ? attribute.default_value : undefined} + value={values[attribute.id]?.[0]} /> )} {attribute.type === "select" && ( diff --git a/web/components/issues/form.tsx b/web/components/issues/form.tsx index d5967b306..aa2661985 100644 --- a/web/components/issues/form.tsx +++ b/web/components/issues/form.tsx @@ -65,6 +65,8 @@ export interface IssueFormProps { user: ICurrentUserResponse | undefined; setIsConfirmDiscardOpen: React.Dispatch>; handleFormDirty: (payload: Partial | null) => void; + customAttributesList: { [key: string]: string[] }; + handleCustomAttributesChange: (attributeId: string, val: string[]) => void; fieldsToShow: ( | "project" | "name" @@ -95,6 +97,8 @@ export const IssueForm: FC = observer((props) => { user, fieldsToShow, handleFormDirty, + customAttributesList, + handleCustomAttributesChange, } = props; const [stateModal, setStateModal] = useState(false); @@ -105,7 +109,6 @@ export const IssueForm: FC = observer((props) => { const [gptAssistantModal, setGptAssistantModal] = useState(false); const [iAmFeelingLucky, setIAmFeelingLucky] = useState(false); - const [attributesList, setAttributesList] = useState<{ [key: string]: string[] }>({}); const { setValue: setValueInLocalStorage } = useLocalStorage("draftedIssue", null); const editorRef = useRef(null); @@ -115,9 +118,6 @@ export const IssueForm: FC = observer((props) => { const { setToastAlert } = useToast(); - const { customAttributeValues: customAttributeValuesStore } = useMobxStore(); - const { createAttributeValue } = customAttributeValuesStore; - const { register, formState: { errors, isSubmitting, isDirty }, @@ -157,10 +157,6 @@ export const IssueForm: FC = observer((props) => { if (!workspaceSlug) return; - await createAttributeValue(workspaceSlug.toString(), projectId, watch("id"), { - issue_properties: attributesList, - }); - setGptAssistantModal(false); reset({ @@ -251,8 +247,6 @@ export const IssueForm: FC = observer((props) => { const maxDate = targetDate ? new Date(targetDate) : null; maxDate?.setDate(maxDate.getDate()); - console.log("attributesList", attributesList); - return ( <> {projectId && ( @@ -579,16 +573,9 @@ export const IssueForm: FC = observer((props) => { { - console.log(val); - - setAttributesList((prev) => ({ - ...prev, - [attributeId]: val, - })); - }} + onChange={handleCustomAttributesChange} projectId={projectId} - values={attributesList} + values={customAttributesList} /> )}
diff --git a/web/components/issues/modal.tsx b/web/components/issues/modal.tsx index d6ab43491..12e629694 100644 --- a/web/components/issues/modal.tsx +++ b/web/components/issues/modal.tsx @@ -39,6 +39,8 @@ import { } from "constants/fetch-keys"; // constants import { INBOX_ISSUE_SOURCE } from "constants/inbox"; +import { observer } from "mobx-react-lite"; +import { useMobxStore } from "lib/mobx/store-provider"; export interface IssuesModalProps { data?: IIssue | null; @@ -50,6 +52,7 @@ export interface IssuesModalProps { | "project" | "name" | "description" + | "entity" | "state" | "priority" | "assignee" @@ -63,221 +66,275 @@ export interface IssuesModalProps { onSubmit?: (data: Partial) => Promise; } -export const CreateUpdateIssueModal: React.FC = ({ - data, - handleClose, - isOpen, - isUpdatingSingleIssue = false, - prePopulateData, - fieldsToShow = ["all"], - onSubmit, -}) => { - // states - const [createMore, setCreateMore] = useState(false); - const [formDirtyState, setFormDirtyState] = useState(null); - const [showConfirmDiscard, setShowConfirmDiscard] = useState(false); - const [activeProject, setActiveProject] = useState(null); +export const CreateUpdateIssueModal: React.FC = observer( + ({ + data, + handleClose, + isOpen, + isUpdatingSingleIssue = false, + prePopulateData, + fieldsToShow = ["all"], + onSubmit, + }) => { + // states + const [createMore, setCreateMore] = useState(false); + const [formDirtyState, setFormDirtyState] = useState(null); + const [showConfirmDiscard, setShowConfirmDiscard] = useState(false); + const [activeProject, setActiveProject] = useState(null); - const router = useRouter(); - const { workspaceSlug, projectId, cycleId, moduleId, viewId, inboxId } = router.query; + const [customAttributesList, setCustomAttributesList] = useState<{ [key: string]: string[] }>( + {} + ); - const { displayFilters, params } = useIssuesView(); - const { params: calendarParams } = useCalendarIssuesView(); - const { ...viewGanttParams } = params; - const { params: inboxParams } = useInboxView(); - const { params: spreadsheetParams } = useSpreadsheetIssuesView(); + const router = useRouter(); + const { workspaceSlug, projectId, cycleId, moduleId, viewId, inboxId } = router.query; - const { user } = useUser(); - const { projects } = useProjects(); + const { customAttributeValues: customAttributeValuesStore } = useMobxStore(); + const { createAttributeValue } = customAttributeValuesStore; - const { groupedIssues, mutateMyIssues } = useMyIssues(workspaceSlug?.toString()); + const { displayFilters, params } = useIssuesView(); + const { params: calendarParams } = useCalendarIssuesView(); + const { ...viewGanttParams } = params; + const { params: inboxParams } = useInboxView(); + const { params: spreadsheetParams } = useSpreadsheetIssuesView(); - const { setToastAlert } = useToast(); + const { user } = useUser(); + const { projects } = useProjects(); - if (cycleId) prePopulateData = { ...prePopulateData, cycle: cycleId as string }; - if (moduleId) prePopulateData = { ...prePopulateData, module: moduleId as string }; - if (router.asPath.includes("my-issues") || router.asPath.includes("assigned")) - prePopulateData = { - ...prePopulateData, - assignees: [...(prePopulateData?.assignees ?? []), user?.id ?? ""], + const { groupedIssues, mutateMyIssues } = useMyIssues(workspaceSlug?.toString()); + + const { setToastAlert } = useToast(); + + if (cycleId) prePopulateData = { ...prePopulateData, cycle: cycleId as string }; + if (moduleId) prePopulateData = { ...prePopulateData, module: moduleId as string }; + if (router.asPath.includes("my-issues") || router.asPath.includes("assigned")) + prePopulateData = { + ...prePopulateData, + assignees: [...(prePopulateData?.assignees ?? []), user?.id ?? ""], + }; + + const onClose = () => { + if (formDirtyState !== null) { + setShowConfirmDiscard(true); + } else { + handleClose(); + setActiveProject(null); + setCustomAttributesList({}); + } }; - const onClose = () => { - if (formDirtyState !== null) { - setShowConfirmDiscard(true); - } else { + const onDiscardClose = () => { handleClose(); setActiveProject(null); - } - }; - - const onDiscardClose = () => { - handleClose(); - setActiveProject(null); - }; - - const handleFormDirty = (data: any) => { - setFormDirtyState(data); - }; - - useEffect(() => { - // if modal is closed, reset active project to null - // and return to avoid activeProject being set to some other project - if (!isOpen) { - setActiveProject(null); - return; - } - - // if data is present, set active project to the project of the - // issue. This has more priority than the project in the url. - if (data && data.project) { - setActiveProject(data.project); - return; - } - - // if data is not present, set active project to the project - // in the url. This has the least priority. - if (projects && projects.length > 0 && !activeProject) - setActiveProject(projects?.find((p) => p.id === projectId)?.id ?? projects?.[0].id ?? null); - }, [activeProject, data, projectId, projects, isOpen]); - - const addIssueToCycle = async (issueId: string, cycleId: string) => { - if (!workspaceSlug || !activeProject) return; - - await issuesService - .addIssueToCycle( - workspaceSlug as string, - activeProject ?? "", - cycleId, - { - issues: [issueId], - }, - user - ) - .then(() => { - if (cycleId) { - mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId, params)); - mutate(CYCLE_DETAILS(cycleId as string)); - } - }); - }; - - const addIssueToModule = async (issueId: string, moduleId: string) => { - if (!workspaceSlug || !activeProject) return; - - await modulesService - .addIssuesToModule( - workspaceSlug as string, - activeProject ?? "", - moduleId as string, - { - issues: [issueId], - }, - user - ) - .then(() => { - if (moduleId) { - mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params)); - mutate(MODULE_DETAILS(moduleId as string)); - } - }); - }; - - const addIssueToInbox = async (formData: Partial) => { - if (!workspaceSlug || !activeProject || !inboxId) return; - - const payload = { - issue: { - name: formData.name, - description: formData.description, - description_html: formData.description_html, - priority: formData.priority, - }, - source: INBOX_ISSUE_SOURCE, }; - await inboxServices - .createInboxIssue( - workspaceSlug.toString(), - activeProject.toString(), - inboxId.toString(), - payload, - user - ) - .then((res) => { - setToastAlert({ - type: "success", - title: "Success!", - message: "Issue created successfully.", - }); + const handleFormDirty = (data: any) => { + setFormDirtyState(data); + }; - router.push( - `/${workspaceSlug}/projects/${activeProject}/inbox/${inboxId}?inboxIssueId=${res.issue_inbox[0].id}` - ); + useEffect(() => { + // if modal is closed, reset active project to null + // and return to avoid activeProject being set to some other project + if (!isOpen) { + setActiveProject(null); + return; + } - mutate(INBOX_ISSUES(inboxId.toString(), inboxParams)); - }) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Issue could not be created. Please try again.", - }); - }); - }; + // if data is present, set active project to the project of the + // issue. This has more priority than the project in the url. + if (data && data.project) { + setActiveProject(data.project); + return; + } - const calendarFetchKey = cycleId - ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), calendarParams) - : moduleId - ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), calendarParams) - : viewId - ? VIEW_ISSUES(viewId.toString(), calendarParams) - : PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject?.toString() ?? "", calendarParams); + // if data is not present, set active project to the project + // in the url. This has the least priority. + if (projects && projects.length > 0 && !activeProject) + setActiveProject(projects?.find((p) => p.id === projectId)?.id ?? projects?.[0].id ?? null); + }, [activeProject, data, projectId, projects, isOpen]); - const spreadsheetFetchKey = cycleId - ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), spreadsheetParams) - : moduleId - ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), spreadsheetParams) - : viewId - ? VIEW_ISSUES(viewId.toString(), spreadsheetParams) - : PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject?.toString() ?? "", spreadsheetParams); + const addIssueToCycle = async (issueId: string, cycleId: string) => { + if (!workspaceSlug || !activeProject) return; - const ganttFetchKey = cycleId - ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString()) - : moduleId - ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString()) - : viewId - ? VIEW_ISSUES(viewId.toString(), viewGanttParams) - : PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject?.toString() ?? ""); - - const createIssue = async (payload: Partial) => { - if (!workspaceSlug || !activeProject) return; - - if (inboxId) await addIssueToInbox(payload); - else await issuesService - .createIssues(workspaceSlug as string, activeProject ?? "", payload, user) - .then(async (res) => { - mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params)); - if (payload.cycle && payload.cycle !== "") await addIssueToCycle(res.id, payload.cycle); - if (payload.module && payload.module !== "") - await addIssueToModule(res.id, payload.module); + .addIssueToCycle( + workspaceSlug as string, + activeProject ?? "", + cycleId, + { + issues: [issueId], + }, + user + ) + .then(() => { + if (cycleId) { + mutate(CYCLE_ISSUES_WITH_PARAMS(cycleId, params)); + mutate(CYCLE_DETAILS(cycleId as string)); + } + }); + }; - if (displayFilters.layout === "calendar") mutate(calendarFetchKey); - if (displayFilters.layout === "gantt_chart") - mutate(ganttFetchKey, { - start_target_date: true, - order_by: "sort_order", - }); - if (displayFilters.layout === "spreadsheet") mutate(spreadsheetFetchKey); - if (groupedIssues) mutateMyIssues(); + const addIssueToModule = async (issueId: string, moduleId: string) => { + if (!workspaceSlug || !activeProject) return; + await modulesService + .addIssuesToModule( + workspaceSlug as string, + activeProject ?? "", + moduleId as string, + { + issues: [issueId], + }, + user + ) + .then(() => { + if (moduleId) { + mutate(MODULE_ISSUES_WITH_PARAMS(moduleId as string, params)); + mutate(MODULE_DETAILS(moduleId as string)); + } + }); + }; + + const addIssueToInbox = async (formData: Partial) => { + if (!workspaceSlug || !activeProject || !inboxId) return; + + const payload = { + issue: { + name: formData.name, + description: formData.description, + description_html: formData.description_html, + priority: formData.priority, + }, + source: INBOX_ISSUE_SOURCE, + }; + + await inboxServices + .createInboxIssue( + workspaceSlug.toString(), + activeProject.toString(), + inboxId.toString(), + payload, + user + ) + .then((res) => { setToastAlert({ type: "success", title: "Success!", message: "Issue created successfully.", }); + router.push( + `/${workspaceSlug}/projects/${activeProject}/inbox/${inboxId}?inboxIssueId=${res.issue_inbox[0].id}` + ); + + mutate(INBOX_ISSUES(inboxId.toString(), inboxParams)); + }) + .catch(() => { + setToastAlert({ + type: "error", + title: "Error!", + message: "Issue could not be created. Please try again.", + }); + }); + }; + + const calendarFetchKey = cycleId + ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), calendarParams) + : moduleId + ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), calendarParams) + : viewId + ? VIEW_ISSUES(viewId.toString(), calendarParams) + : PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject?.toString() ?? "", calendarParams); + + const spreadsheetFetchKey = cycleId + ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString(), spreadsheetParams) + : moduleId + ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString(), spreadsheetParams) + : viewId + ? VIEW_ISSUES(viewId.toString(), spreadsheetParams) + : PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject?.toString() ?? "", spreadsheetParams); + + const ganttFetchKey = cycleId + ? CYCLE_ISSUES_WITH_PARAMS(cycleId.toString()) + : moduleId + ? MODULE_ISSUES_WITH_PARAMS(moduleId.toString()) + : viewId + ? VIEW_ISSUES(viewId.toString(), viewGanttParams) + : PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject?.toString() ?? ""); + + const createIssue = async (payload: Partial) => { + if (!workspaceSlug || !activeProject) return; + + let issueToReturn: Partial = {}; + + if (inboxId) await addIssueToInbox(payload); + else + await issuesService + .createIssues(workspaceSlug as string, activeProject ?? "", payload, user) + .then(async (res) => { + issueToReturn = res; + mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params)); + if (payload.cycle && payload.cycle !== "") await addIssueToCycle(res.id, payload.cycle); + if (payload.module && payload.module !== "") + await addIssueToModule(res.id, payload.module); + + if (displayFilters.layout === "calendar") mutate(calendarFetchKey); + if (displayFilters.layout === "gantt_chart") + mutate(ganttFetchKey, { + start_target_date: true, + order_by: "sort_order", + }); + if (displayFilters.layout === "spreadsheet") mutate(spreadsheetFetchKey); + if (groupedIssues) mutateMyIssues(); + + setToastAlert({ + type: "success", + title: "Success!", + message: "Issue created successfully.", + }); + + if (payload.assignees_list?.some((assignee) => assignee === user?.id)) + mutate(USER_ISSUE(workspaceSlug as string)); + + if (payload.parent && payload.parent !== "") mutate(SUB_ISSUES(payload.parent)); + }) + .catch(() => { + setToastAlert({ + type: "error", + title: "Error!", + message: "Issue could not be created. Please try again.", + }); + }); + + if (!createMore) onDiscardClose(); + + return issueToReturn; + }; + + const createDraftIssue = async () => { + if (!workspaceSlug || !activeProject || !user) return; + + const payload: Partial = { + ...formDirtyState, + }; + + await issuesService + .createDraftIssue(workspaceSlug as string, activeProject ?? "", payload, user) + .then(() => { + mutate(PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params)); + if (groupedIssues) mutateMyIssues(); + + setToastAlert({ + type: "success", + title: "Success!", + message: "Draft Issue created successfully.", + }); + + handleClose(); + setActiveProject(null); + setFormDirtyState(null); + setShowConfirmDiscard(false); + if (payload.assignees_list?.some((assignee) => assignee === user?.id)) mutate(USER_ISSUE(workspaceSlug as string)); @@ -290,163 +347,144 @@ export const CreateUpdateIssueModal: React.FC = ({ message: "Issue could not be created. Please try again.", }); }); - - if (!createMore) onDiscardClose(); - }; - - const createDraftIssue = async () => { - if (!workspaceSlug || !activeProject || !user) return; - - const payload: Partial = { - ...formDirtyState, }; - await issuesService - .createDraftIssue(workspaceSlug as string, activeProject ?? "", payload, user) - .then(() => { - mutate(PROJECT_DRAFT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params)); - if (groupedIssues) mutateMyIssues(); + const updateIssue = async (payload: Partial) => { + if (!user) return; - setToastAlert({ - type: "success", - title: "Success!", - message: "Draft Issue created successfully.", + let issueToReturn: Partial = {}; + + await issuesService + .patchIssue(workspaceSlug as string, activeProject ?? "", data?.id ?? "", payload, user) + .then((res) => { + issueToReturn = res; + + if (isUpdatingSingleIssue) { + mutate(PROJECT_ISSUES_DETAILS, (prevData) => ({ ...prevData, ...res }), false); + } else { + if (displayFilters.layout === "calendar") mutate(calendarFetchKey); + if (displayFilters.layout === "spreadsheet") mutate(spreadsheetFetchKey); + if (payload.parent) mutate(SUB_ISSUES(payload.parent.toString())); + mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params)); + } + + if (payload.cycle && payload.cycle !== "") addIssueToCycle(res.id, payload.cycle); + if (payload.module && payload.module !== "") addIssueToModule(res.id, payload.module); + + if (!createMore) onDiscardClose(); + + setToastAlert({ + type: "success", + title: "Success!", + message: "Issue updated successfully.", + }); + }) + .catch(() => { + setToastAlert({ + type: "error", + title: "Error!", + message: "Issue could not be updated. Please try again.", + }); }); - handleClose(); - setActiveProject(null); - setFormDirtyState(null); - setShowConfirmDiscard(false); - - if (payload.assignees_list?.some((assignee) => assignee === user?.id)) - mutate(USER_ISSUE(workspaceSlug as string)); - - if (payload.parent && payload.parent !== "") mutate(SUB_ISSUES(payload.parent)); - }) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Issue could not be created. Please try again.", - }); - }); - }; - - const updateIssue = async (payload: Partial) => { - if (!user) return; - - await issuesService - .patchIssue(workspaceSlug as string, activeProject ?? "", data?.id ?? "", payload, user) - .then((res) => { - if (isUpdatingSingleIssue) { - mutate(PROJECT_ISSUES_DETAILS, (prevData) => ({ ...prevData, ...res }), false); - } else { - if (displayFilters.layout === "calendar") mutate(calendarFetchKey); - if (displayFilters.layout === "spreadsheet") mutate(spreadsheetFetchKey); - if (payload.parent) mutate(SUB_ISSUES(payload.parent.toString())); - mutate(PROJECT_ISSUES_LIST_WITH_PARAMS(activeProject ?? "", params)); - } - - if (payload.cycle && payload.cycle !== "") addIssueToCycle(res.id, payload.cycle); - if (payload.module && payload.module !== "") addIssueToModule(res.id, payload.module); - - if (!createMore) onDiscardClose(); - - setToastAlert({ - type: "success", - title: "Success!", - message: "Issue updated successfully.", - }); - }) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Issue could not be updated. Please try again.", - }); - }); - }; - - const handleFormSubmit = async (formData: Partial) => { - if (!workspaceSlug || !activeProject) return; - - const payload: Partial = { - ...formData, - assignees_list: formData.assignees ?? [], - labels_list: formData.labels ?? [], - description: formData.description ?? "", - description_html: formData.description_html ?? "

", + return issueToReturn; }; - if (!data) await createIssue(payload); - else await updateIssue(payload); + const handleFormSubmit = async (formData: Partial) => { + if (!workspaceSlug || !activeProject) return; - if (onSubmit) await onSubmit(payload); - }; + const payload: Partial = { + ...formData, + assignees_list: formData.assignees ?? [], + labels_list: formData.labels ?? [], + description: formData.description ?? "", + description_html: formData.description_html ?? "

", + }; - if (!projects || projects.length === 0) return null; + let issueResponse: Partial | undefined = {}; - return ( - <> - setShowConfirmDiscard(false)} - onConfirm={createDraftIssue} - onDiscard={() => { - handleClose(); - setActiveProject(null); - setFormDirtyState(null); - setShowConfirmDiscard(false); - }} - /> + if (!data) issueResponse = await createIssue(payload); + else issueResponse = await updateIssue(payload); - - - -
- + if (issueResponse && issueResponse.id && Object.keys(customAttributesList).length > 0) + await createAttributeValue(workspaceSlug.toString(), activeProject, issueResponse.id, { + issue_properties: customAttributesList, + }); -
-
- - - - - + if (onSubmit) await onSubmit(payload); + }; + + if (!projects || projects.length === 0) return null; + + return ( + <> + setShowConfirmDiscard(false)} + onConfirm={createDraftIssue} + onDiscard={() => { + handleClose(); + setActiveProject(null); + setFormDirtyState(null); + setShowConfirmDiscard(false); + }} + /> + + + + +
+ + +
+
+ + + { + setCustomAttributesList((prev) => ({ + ...prev, + [attributeId]: val, + })); + }} + fieldsToShow={fieldsToShow} + handleFormDirty={handleFormDirty} + /> + + +
-
-
-
- - ); -}; +
+
+ + ); + } +); diff --git a/web/constants/custom-attributes.ts b/web/constants/custom-attributes.ts index 01052427f..12a7d56dc 100644 --- a/web/constants/custom-attributes.ts +++ b/web/constants/custom-attributes.ts @@ -24,7 +24,7 @@ export const CUSTOM_ATTRIBUTES_LIST: { } = { checkbox: { defaultFormValues: { - default_value: "checked", + default_value: "true", display_name: "", extra_settings: { representation: "check",