Skip to content

Commit

Permalink
improvement: merge quick add logic for all layouts. (makeplane#5323)
Browse files Browse the repository at this point in the history
  • Loading branch information
prateekshourya29 authored Aug 7, 2024
1 parent 333a989 commit 49a895f
Show file tree
Hide file tree
Showing 37 changed files with 680 additions and 1,033 deletions.
1 change: 1 addition & 0 deletions web/ce/components/issues/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from "./bulk-operations";
export * from "./worklog";
export * from "./issue-modal";
export * from "./issue-details";
export * from "./quick-add";
1 change: 1 addition & 0 deletions web/ce/components/issues/quick-add/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./root";
74 changes: 74 additions & 0 deletions web/ce/components/issues/quick-add/root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { FC, useEffect, useRef } from "react";
import { observer } from "mobx-react";
import { UseFormRegister, UseFormSetFocus } from "react-hook-form";
// types
import { TIssue } from "@plane/types";
// components
import {
CalendarQuickAddIssueForm,
GanttQuickAddIssueForm,
KanbanQuickAddIssueForm,
ListQuickAddIssueForm,
SpreadsheetQuickAddIssueForm,
TQuickAddIssueForm,
} from "@/components/issues/issue-layouts";
// constants
import { EIssueLayoutTypes } from "@/constants/issue";
// hooks
import { useProject } from "@/hooks/store";
import useKeypress from "@/hooks/use-keypress";
import useOutsideClickDetector from "@/hooks/use-outside-click-detector";

export type TQuickAddIssueFormRoot = {
isOpen: boolean;
layout: EIssueLayoutTypes;
prePopulatedData?: Partial<TIssue>;
projectId: string;
hasError?: boolean;
setFocus: UseFormSetFocus<TIssue>;
register: UseFormRegister<TIssue>;
onSubmit: () => void;
onClose: () => void;
};

export const QuickAddIssueFormRoot: FC<TQuickAddIssueFormRoot> = observer((props) => {
const { isOpen, layout, projectId, hasError = false, setFocus, register, onSubmit, onClose } = props;
// store hooks
const { getProjectById } = useProject();
// derived values
const projectDetail = getProjectById(projectId);
// refs
const ref = useRef<HTMLFormElement>(null);
// click detection
useKeypress("Escape", onClose);
useOutsideClickDetector(ref, onClose);
// set focus on name input
useEffect(() => {
setFocus("name");
}, [setFocus]);

if (!projectDetail) return <></>;

const QUICK_ADD_ISSUE_FORMS: Record<EIssueLayoutTypes, FC<TQuickAddIssueForm>> = {
[EIssueLayoutTypes.LIST]: ListQuickAddIssueForm,
[EIssueLayoutTypes.KANBAN]: KanbanQuickAddIssueForm,
[EIssueLayoutTypes.CALENDAR]: CalendarQuickAddIssueForm,
[EIssueLayoutTypes.GANTT]: GanttQuickAddIssueForm,
[EIssueLayoutTypes.SPREADSHEET]: SpreadsheetQuickAddIssueForm,
};

const CurrentLayoutQuickAddIssueForm = QUICK_ADD_ISSUE_FORMS[layout] ?? null;

if (!CurrentLayoutQuickAddIssueForm) return <></>;

return (
<CurrentLayoutQuickAddIssueForm
ref={ref}
isOpen={isOpen}
projectDetail={projectDetail}
hasError={hasError}
register={register}
onSubmit={onSubmit}
/>
);
});
2 changes: 1 addition & 1 deletion web/core/components/issues/issue-layouts/calendar/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ export * from "./issue-block-root";
export * from "./issue-block";
export * from "./week-days";
export * from "./week-header";
export * from "./quick-add-issue-form";
export * from "./quick-add-issue-actions";
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { observer } from "mobx-react";
import { TIssue, TPaginationData } from "@plane/types";
// components
import { CalendarQuickAddIssueForm, CalendarIssueBlockRoot } from "@/components/issues";
import { CalendarQuickAddIssueActions, CalendarIssueBlockRoot } from "@/components/issues";
// helpers
import { renderFormattedPayloadDate } from "@/helpers/date-time.helper";
import { useIssuesStore } from "@/hooks/use-issue-layout-store";
Expand Down Expand Up @@ -75,9 +75,7 @@ export const CalendarIssueBlocks: React.FC<Props> = observer((props) => {

{enableQuickIssueCreate && !disableIssueCreation && !readOnly && (
<div className="border-b border-custom-border-200 px-1 py-1 md:border-none md:px-2">
<CalendarQuickAddIssueForm
formKey="target_date"
groupId={formattedDatePayload}
<CalendarQuickAddIssueActions
prePopulatedData={{
target_date: formattedDatePayload,
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
"use client";

import { FC, useState } from "react";
import { differenceInCalendarDays } from "date-fns";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import { PlusIcon } from "lucide-react";
// types
import { ISearchIssueResponse, TIssue } from "@plane/types";
// ui
import { TOAST_TYPE, setToast, CustomMenu } from "@plane/ui";
// components
import { ExistingIssuesListModal } from "@/components/core";
import { QuickAddIssueRoot } from "@/components/issues";
// helpers
import { EIssueLayoutTypes } from "@/constants/issue";
import { cn } from "@/helpers/common.helper";
// hooks
import { useIssueDetail } from "@/hooks/store";

type TCalendarQuickAddIssueActions = {
prePopulatedData?: Partial<TIssue>;
quickAddCallback?: (projectId: string | null | undefined, data: TIssue) => Promise<TIssue | undefined>;
addIssuesToView?: (issueIds: string[]) => Promise<any>;
onOpen?: () => void;
};

export const CalendarQuickAddIssueActions: FC<TCalendarQuickAddIssueActions> = observer((props) => {
const { prePopulatedData, quickAddCallback, addIssuesToView, onOpen } = props;
// router
const { workspaceSlug, projectId, moduleId } = useParams();
// states
const [isOpen, setIsOpen] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isExistingIssueModalOpen, setIsExistingIssueModalOpen] = useState(false);
const { updateIssue } = useIssueDetail();
// derived values
const ExistingIssuesListModalPayload = addIssuesToView
? moduleId
? { module: moduleId.toString(), target_date: "none" }
: { cycle: true, target_date: "none" }
: { target_date: "none" };

const handleAddIssuesToView = async (data: ISearchIssueResponse[]) => {
if (!workspaceSlug || !projectId) return;

const issueIds = data.map((i) => i.id);

try {
// To handle all updates in parallel
await Promise.all(
data.map((issue) =>
updateIssue(workspaceSlug.toString(), projectId.toString(), issue.id, prePopulatedData ?? {})
)
);
await addIssuesToView?.(issueIds);
} catch (error) {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
message: "Something went wrong. Please try again.",
});
}
};

const handleNewIssue = () => {
setIsOpen(true);
if (onOpen) onOpen();
};
const handleExistingIssue = () => {
setIsExistingIssueModalOpen(true);
};

if (!projectId) return null;

return (
<>
{workspaceSlug && projectId && (
<ExistingIssuesListModal
workspaceSlug={workspaceSlug.toString()}
projectId={projectId.toString()}
isOpen={isExistingIssueModalOpen}
handleClose={() => setIsExistingIssueModalOpen(false)}
searchParams={ExistingIssuesListModalPayload}
handleOnSubmit={handleAddIssuesToView}
shouldHideIssue={(issue) => {
if (issue.start_date && prePopulatedData?.target_date) {
const issueStartDate = new Date(issue.start_date);
const targetDate = new Date(prePopulatedData.target_date);
const diffInDays = differenceInCalendarDays(targetDate, issueStartDate);
if (diffInDays < 0) return true;
}
return false;
}}
/>
)}
<QuickAddIssueRoot
isQuickAddOpen={isOpen}
setIsQuickAddOpen={(isOpen) => setIsOpen(isOpen)}
layout={EIssueLayoutTypes.CALENDAR}
prePopulatedData={prePopulatedData}
quickAddCallback={quickAddCallback}
customQuickAddButton={
<div
className={cn(
"md:opacity-0 rounded md:border-[0.5px] border-custom-border-200 md:group-hover:opacity-100",
{
block: isMenuOpen,
}
)}
>
<CustomMenu
placement="bottom-start"
menuButtonOnClick={() => setIsMenuOpen(true)}
onMenuClose={() => setIsMenuOpen(false)}
className="w-full"
customButtonClassName="w-full"
customButton={
<div className="flex w-full items-center gap-x-[6px] rounded-md px-2 py-1.5 text-custom-text-350 hover:text-custom-text-300">
<PlusIcon className="h-3.5 w-3.5 stroke-2 flex-shrink-0" />
<span className="text-sm font-medium flex-shrink-0">New issue</span>
</div>
}
>
<CustomMenu.MenuItem onClick={handleNewIssue}>New issue</CustomMenu.MenuItem>
<CustomMenu.MenuItem onClick={handleExistingIssue}>Add existing issue</CustomMenu.MenuItem>
</CustomMenu>
</div>
}
/>
</>
);
});
Loading

0 comments on commit 49a895f

Please sign in to comment.