From a2db04f9ffddbe5e9ee86a15d79fc425d7d1aa12 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Mon, 19 Dec 2022 20:30:09 +0530 Subject: [PATCH] fix: bugs fixed --- .../common/board-view/single-board.tsx | 5 + .../common/board-view/single-issue.tsx | 14 +- .../project/cycles/board-view/index.tsx | 15 + .../cycles/board-view/single-board.tsx | 102 ++-- .../project/cycles/list-view/index.tsx | 527 +++++++++--------- .../project/issues/BoardView/single-board.tsx | 2 +- .../state/create-update-state-inline.tsx | 4 +- .../project/issues/confirm-issue-deletion.tsx | 2 +- .../create-update-issue-modal/index.tsx | 2 +- .../select-cycle.tsx | 6 +- .../select-labels.tsx | 6 +- .../select-priority.tsx | 6 +- .../select-state.tsx | 2 +- .../issue-detail-sidebar/index.tsx | 48 +- .../issue-detail-sidebar/select-assignee.tsx | 11 +- .../issue-detail-sidebar/select-cycle.tsx | 17 +- .../project/issues/list-view/index.tsx | 6 +- .../{ControlSettings.tsx => control.tsx} | 0 .../{GeneralSettings.tsx => general.tsx} | 0 .../{LabelsSettings.tsx => labels/index.tsx} | 138 ++--- .../project/settings/labels/single-label.tsx | 160 ++++++ .../{StatesSettings.tsx => states.tsx} | 86 ++- .../projects/[projectId]/cycles/[cycleId].tsx | 52 +- .../projects/[projectId]/cycles/index.tsx | 8 +- .../projects/[projectId]/issues/[issueId].tsx | 5 +- .../projects/[projectId]/issues/index.tsx | 2 +- .../projects/[projectId]/modules/index.tsx | 8 +- .../pages/projects/[projectId]/settings.tsx | 8 +- apps/app/types/issues.d.ts | 3 +- apps/app/ui/Button/index.tsx | 2 +- apps/app/ui/custom-listbox/index.tsx | 16 +- apps/app/ui/custom-listbox/types.d.ts | 2 +- apps/app/ui/custom-menu/index.tsx | 20 +- apps/app/ui/custom-menu/types.d.ts | 2 + apps/app/ui/index.ts | 1 + 35 files changed, 719 insertions(+), 569 deletions(-) create mode 100644 apps/app/components/common/board-view/single-board.tsx rename apps/app/components/{project => }/common/board-view/single-issue.tsx (96%) rename apps/app/components/project/settings/{ControlSettings.tsx => control.tsx} (100%) rename apps/app/components/project/settings/{GeneralSettings.tsx => general.tsx} (100%) rename apps/app/components/project/settings/{LabelsSettings.tsx => labels/index.tsx} (54%) create mode 100644 apps/app/components/project/settings/labels/single-label.tsx rename apps/app/components/project/settings/{StatesSettings.tsx => states.tsx} (57%) diff --git a/apps/app/components/common/board-view/single-board.tsx b/apps/app/components/common/board-view/single-board.tsx new file mode 100644 index 000000000..aa6baf9a8 --- /dev/null +++ b/apps/app/components/common/board-view/single-board.tsx @@ -0,0 +1,5 @@ +const SingleBoard = () => { + return <>; +}; + +export default SingleBoard; diff --git a/apps/app/components/project/common/board-view/single-issue.tsx b/apps/app/components/common/board-view/single-issue.tsx similarity index 96% rename from apps/app/components/project/common/board-view/single-issue.tsx rename to apps/app/components/common/board-view/single-issue.tsx index a1c7b6ab3..598eaad1e 100644 --- a/apps/app/components/project/common/board-view/single-issue.tsx +++ b/apps/app/components/common/board-view/single-issue.tsx @@ -117,7 +117,7 @@ const SingleIssue: React.FC = ({ leaveFrom="opacity-100" leaveTo="opacity-0" > - + {PRIORITIES?.map((priority) => ( = ({ leaveFrom="opacity-100" leaveTo="opacity-0" > - + {states?.map((state) => ( classNames( active ? "bg-indigo-50" : "bg-white", - "cursor-pointer select-none px-3 py-2" + "flex items-center gap-2 cursor-pointer select-none px-3 py-2" ) } value={state.id} > + {addSpaceIfCamelCase(state.name)} ))} @@ -316,7 +322,7 @@ const SingleIssue: React.FC = ({ leaveFrom="opacity-100" leaveTo="opacity-0" > - + {people?.map((person) => ( void; partialUpdateIssue: (formData: Partial, issueId: string) => void; handleDeleteIssue: React.Dispatch>; + setPreloadedData: React.Dispatch< + React.SetStateAction< + | (Partial & { + actionType: "createIssue" | "edit" | "delete"; + }) + | undefined + > + >; }; const CyclesBoardView: React.FC = ({ @@ -30,6 +38,7 @@ const CyclesBoardView: React.FC = ({ removeIssueFromCycle, partialUpdateIssue, handleDeleteIssue, + setPreloadedData, }) => { const { states } = useUser(); @@ -63,6 +72,12 @@ const CyclesBoardView: React.FC = ({ openCreateIssueModal={openCreateIssueModal} partialUpdateIssue={partialUpdateIssue} handleDeleteIssue={handleDeleteIssue} + setPreloadedData={setPreloadedData} + stateId={ + selectedGroup === "state_detail.name" + ? states?.find((s) => s.name === singleGroup)?.id ?? null + : null + } /> ))} diff --git a/apps/app/components/project/cycles/board-view/single-board.tsx b/apps/app/components/project/cycles/board-view/single-board.tsx index 2ba9bfe50..d40b4ac97 100644 --- a/apps/app/components/project/cycles/board-view/single-board.tsx +++ b/apps/app/components/project/cycles/board-view/single-board.tsx @@ -7,7 +7,7 @@ import workspaceService from "lib/services/workspace.service"; // hooks import useUser from "lib/hooks/useUser"; // components -import SingleIssue from "components/project/common/board-view/single-issue"; +import SingleIssue from "components/common/board-view/single-issue"; // headless ui import { Menu, Transition } from "@headlessui/react"; // ui @@ -35,6 +35,15 @@ type Props = { removeIssueFromCycle: (bridgeId: string) => void; partialUpdateIssue: (formData: Partial, issueId: string) => void; handleDeleteIssue: React.Dispatch>; + setPreloadedData: React.Dispatch< + React.SetStateAction< + | (Partial & { + actionType: "createIssue" | "edit" | "delete"; + }) + | undefined + > + >; + stateId: string | null; }; const SingleCycleBoard: React.FC = ({ @@ -49,6 +58,8 @@ const SingleCycleBoard: React.FC = ({ removeIssueFromCycle, partialUpdateIssue, handleDeleteIssue, + setPreloadedData, + stateId, }) => { // Collapse/Expand const [show, setState] = useState(true); @@ -109,7 +120,18 @@ const SingleCycleBoard: React.FC = ({ - openCreateIssueModal()}> + { + openCreateIssueModal(); + if (selectedGroup !== null) { + setPreloadedData({ + state: stateId !== null ? stateId : undefined, + [selectedGroup]: groupTitle, + actionType: "createIssue", + }); + } + }} + > Create new openIssuesListModal()}> @@ -150,55 +172,35 @@ const SingleCycleBoard: React.FC = ({ ); })} - - - - Add issue - - - + + Add issue + + } + className="mt-1" + optionsPosition="left" + withoutBorder + > + { + openCreateIssueModal(); + if (selectedGroup !== null) { + setPreloadedData({ + state: stateId !== null ? stateId : undefined, + [selectedGroup]: groupTitle, + actionType: "createIssue", + }); + } + }} > - -
- - {({ active }) => ( - - )} - - - {({ active }) => ( - - )} - -
-
-
-
+ Create new +
+ openIssuesListModal()}> + Add an existing issue + +
diff --git a/apps/app/components/project/cycles/list-view/index.tsx b/apps/app/components/project/cycles/list-view/index.tsx index e528489ec..1ad8fa8a9 100644 --- a/apps/app/components/project/cycles/list-view/index.tsx +++ b/apps/app/components/project/cycles/list-view/index.tsx @@ -28,13 +28,21 @@ import workspaceService from "lib/services/workspace.service"; type Props = { groupedByIssues: { - [key: string]: IIssue[]; + [key: string]: (IIssue & { bridge?: string })[]; }; properties: Properties; selectedGroup: NestedKeyOf | null; openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void; openIssuesListModal: () => void; removeIssueFromCycle: (bridgeId: string) => void; + setPreloadedData: React.Dispatch< + React.SetStateAction< + | (Partial & { + actionType: "createIssue" | "edit" | "delete"; + }) + | undefined + > + >; }; const CyclesListView: React.FC = ({ @@ -44,8 +52,9 @@ const CyclesListView: React.FC = ({ openIssuesListModal, properties, removeIssueFromCycle, + setPreloadedData, }) => { - const { activeWorkspace, activeProject } = useUser(); + const { activeWorkspace, activeProject, states } = useUser(); const { data: people } = useSWR( activeWorkspace ? WORKSPACE_MEMBERS : null, @@ -54,273 +63,261 @@ const CyclesListView: React.FC = ({ return (
- {Object.keys(groupedByIssues).map((singleGroup) => ( - - {({ open }) => ( -
-
- -
- - - - {selectedGroup !== null ? ( -

- {singleGroup === null || singleGroup === "null" - ? selectedGroup === "priority" && "No priority" - : addSpaceIfCamelCase(singleGroup)} -

- ) : ( -

All Issues

- )} -

- {groupedByIssues[singleGroup as keyof IIssue].length} -

-
-
-
- - -
- {groupedByIssues[singleGroup] ? ( - groupedByIssues[singleGroup].length > 0 ? ( - groupedByIssues[singleGroup].map((issue: IIssue) => { - const assignees = [ - ...(issue?.assignees_list ?? []), - ...(issue?.assignees ?? []), - ]?.map((assignee) => { - const tempPerson = people?.find( - (p) => p.member.id === assignee - )?.member; + {Object.keys(groupedByIssues).map((singleGroup) => { + const stateId = + selectedGroup === "state_detail.name" + ? states?.find((s) => s.name === singleGroup)?.id ?? null + : null; - return { - avatar: tempPerson?.avatar, - first_name: tempPerson?.first_name, - email: tempPerson?.email, - }; - }); - - return ( -
- -
- {properties.priority && ( -
- {/* {getPriorityIcon(issue.priority ?? "")} */} - {issue.priority ?? "None"} -
-
Priority
-
- {issue.priority ?? "None"} -
-
-
- )} - {properties.state && ( -
- - {addSpaceIfCamelCase(issue?.state_detail.name)} -
-
State
-
{issue?.state_detail.name}
-
-
- )} - {properties.start_date && ( -
- - {issue.start_date - ? renderShortNumericDateFormat(issue.start_date) - : "N/A"} -
-
Started at
-
- {renderShortNumericDateFormat(issue.start_date ?? "")} -
-
-
- )} - {properties.target_date && ( -
- - {issue.target_date - ? renderShortNumericDateFormat(issue.target_date) - : "N/A"} -
-
- Target date -
-
- {renderShortNumericDateFormat(issue.target_date ?? "")} -
-
- {issue.target_date && - (issue.target_date < new Date().toISOString() - ? `Target date has passed by ${findHowManyDaysLeft( - issue.target_date - )} days` - : findHowManyDaysLeft(issue.target_date) <= 3 - ? `Target date is in ${findHowManyDaysLeft( - issue.target_date - )} days` - : "Target date")} -
-
-
- )} - - openCreateIssueModal(issue, "edit")} - > - Edit - - removeIssueFromCycle(issue.bridge ?? "")} - > - Remove from cycle - - Delete permanently - -
-
- ); - }) + return ( + + {({ open }) => ( +
+
+ +
+ + + + {selectedGroup !== null ? ( +

+ {singleGroup === null || singleGroup === "null" + ? selectedGroup === "priority" && "No priority" + : addSpaceIfCamelCase(singleGroup)} +

) : ( -

No issues.

- ) - ) : ( -
- -
- )} -
- - - - - - Add issue - - - - -
- - {({ active }) => ( - - )} - - - {({ active }) => ( - - )} - +

All Issues

+ )} +

+ {groupedByIssues[singleGroup as keyof IIssue].length} +

-
+ +
+ + +
+ {groupedByIssues[singleGroup] ? ( + groupedByIssues[singleGroup].length > 0 ? ( + groupedByIssues[singleGroup].map((issue) => { + const assignees = [ + ...(issue?.assignees_list ?? []), + ...(issue?.assignees ?? []), + ]?.map((assignee) => { + const tempPerson = people?.find( + (p) => p.member.id === assignee + )?.member; + + return { + avatar: tempPerson?.avatar, + first_name: tempPerson?.first_name, + email: tempPerson?.email, + }; + }); + + return ( +
+ +
+ {properties.priority && ( +
+ {/* {getPriorityIcon(issue.priority ?? "")} */} + {issue.priority ?? "None"} +
+
Priority
+
+ {issue.priority ?? "None"} +
+
+
+ )} + {properties.state && ( +
+ + {addSpaceIfCamelCase(issue?.state_detail.name)} +
+
State
+
{issue?.state_detail.name}
+
+
+ )} + {properties.start_date && ( +
+ + {issue.start_date + ? renderShortNumericDateFormat(issue.start_date) + : "N/A"} +
+
Started at
+
+ {renderShortNumericDateFormat(issue.start_date ?? "")} +
+
+
+ )} + {properties.target_date && ( +
+ + {issue.target_date + ? renderShortNumericDateFormat(issue.target_date) + : "N/A"} +
+
+ Target date +
+
+ {renderShortNumericDateFormat(issue.target_date ?? "")} +
+
+ {issue.target_date && + (issue.target_date < new Date().toISOString() + ? `Target date has passed by ${findHowManyDaysLeft( + issue.target_date + )} days` + : findHowManyDaysLeft(issue.target_date) <= 3 + ? `Target date is in ${findHowManyDaysLeft( + issue.target_date + )} days` + : "Target date")} +
+
+
+ )} + + openCreateIssueModal(issue, "edit")} + > + Edit + + removeIssueFromCycle(issue.bridge ?? "")} + > + Remove from cycle + + Delete permanently + +
+
+ ); + }) + ) : ( +

No issues.

+ ) + ) : ( +
+ +
+ )} +
+
- -
- )} -
- ))} +
+ + + Add issue + + } + optionsPosition="left" + withoutBorder + > + { + openCreateIssueModal(); + if (selectedGroup !== null) { + setPreloadedData({ + state: stateId !== null ? stateId : undefined, + [selectedGroup]: singleGroup, + actionType: "createIssue", + }); + } + }} + > + Create new + + openIssuesListModal()}> + Add an existing issue + + +
+
+ )} + + ); + })}
); }; diff --git a/apps/app/components/project/issues/BoardView/single-board.tsx b/apps/app/components/project/issues/BoardView/single-board.tsx index f9f579618..353c02a82 100644 --- a/apps/app/components/project/issues/BoardView/single-board.tsx +++ b/apps/app/components/project/issues/BoardView/single-board.tsx @@ -20,7 +20,7 @@ import { addSpaceIfCamelCase } from "constants/common"; import { WORKSPACE_MEMBERS } from "constants/fetch-keys"; // types import { IIssue, Properties, NestedKeyOf, IWorkspaceMember } from "types"; -import SingleIssue from "components/project/common/board-view/single-issue"; +import SingleIssue from "components/common/board-view/single-issue"; type Props = { selectedGroup: NestedKeyOf | null; diff --git a/apps/app/components/project/issues/BoardView/state/create-update-state-inline.tsx b/apps/app/components/project/issues/BoardView/state/create-update-state-inline.tsx index 10f186bd2..22f224c09 100644 --- a/apps/app/components/project/issues/BoardView/state/create-update-state-inline.tsx +++ b/apps/app/components/project/issues/BoardView/state/create-update-state-inline.tsx @@ -122,7 +122,7 @@ export const CreateUpdateStateInline: React.FC = ({ return (
-
+
{({ open }) => ( <> @@ -150,7 +150,7 @@ export const CreateUpdateStateInline: React.FC = ({ leaveFrom="opacity-100 translate-y-0" leaveTo="opacity-0 translate-y-1" > - + = ({ isOpen, handleClose, data }) => }, false ); - mutate(CYCLE_ISSUES(data.issue_cycle.id)); + mutate(CYCLE_ISSUES(data.issue_cycle?.id ?? "")); setToastAlert({ title: "Success", type: "success", diff --git a/apps/app/components/project/issues/create-update-issue-modal/index.tsx b/apps/app/components/project/issues/create-update-issue-modal/index.tsx index 551979cdf..0fc8edb94 100644 --- a/apps/app/components/project/issues/create-update-issue-modal/index.tsx +++ b/apps/app/components/project/issues/create-update-issue-modal/index.tsx @@ -29,7 +29,7 @@ import CreateUpdateStateModal from "components/project/issues/BoardView/state/cr import CreateUpdateCycleModal from "components/project/cycles/create-update-cycle-modal"; // types import type { IIssue, IssueResponse } from "types"; -// fetching keys +// fetch keys import { PROJECT_ISSUES_DETAILS, PROJECT_ISSUES_LIST, diff --git a/apps/app/components/project/issues/create-update-issue-modal/select-cycle.tsx b/apps/app/components/project/issues/create-update-issue-modal/select-cycle.tsx index cd4dd60c3..74e6c8346 100644 --- a/apps/app/components/project/issues/create-update-issue-modal/select-cycle.tsx +++ b/apps/app/components/project/issues/create-update-issue-modal/select-cycle.tsx @@ -47,14 +47,14 @@ const SelectSprint: React.FC = ({ control, setIsOpen }) => { leaveTo="opacity-0" > -
+
{cycles?.map((cycle) => ( - `relative cursor-pointer select-none p-2 rounded-md ${ - active ? "bg-theme text-white" : "text-gray-900" + `text-gray-900 cursor-pointer select-none p-2 ${ + active ? "bg-indigo-50" : "" }` } > diff --git a/apps/app/components/project/issues/create-update-issue-modal/select-labels.tsx b/apps/app/components/project/issues/create-update-issue-modal/select-labels.tsx index a8ca2ee74..ecedcec15 100644 --- a/apps/app/components/project/issues/create-update-issue-modal/select-labels.tsx +++ b/apps/app/components/project/issues/create-update-issue-modal/select-labels.tsx @@ -99,14 +99,14 @@ const SelectLabels: React.FC = ({ control }) => { leaveTo="opacity-0" > -
+
{issueLabels?.map((label) => ( `${ - active ? "text-white bg-theme" : "text-gray-900" - } flex items-center gap-2 cursor-pointer select-none w-full p-2 rounded-md` + active ? "bg-indigo-50" : "" + } flex items-center gap-2 text-gray-900 cursor-pointer select-none w-full p-2` } value={label.id} > diff --git a/apps/app/components/project/issues/create-update-issue-modal/select-priority.tsx b/apps/app/components/project/issues/create-update-issue-modal/select-priority.tsx index 10cee4c76..08da53cd4 100644 --- a/apps/app/components/project/issues/create-update-issue-modal/select-priority.tsx +++ b/apps/app/components/project/issues/create-update-issue-modal/select-priority.tsx @@ -42,14 +42,14 @@ const SelectPriority: React.FC = ({ control }) => { leaveTo="opacity-0" > -
+
{PRIORITIES.map((priority) => ( `${ - active ? "text-white bg-theme" : "text-gray-900" - } cursor-pointer select-none relative p-2 rounded-md` + active ? "bg-indigo-50" : "" + } text-gray-900 cursor-pointer select-none p-2` } value={priority} > diff --git a/apps/app/components/project/issues/create-update-issue-modal/select-state.tsx b/apps/app/components/project/issues/create-update-issue-modal/select-state.tsx index 8fedc0dca..138984306 100644 --- a/apps/app/components/project/issues/create-update-issue-modal/select-state.tsx +++ b/apps/app/components/project/issues/create-update-issue-modal/select-state.tsx @@ -28,7 +28,7 @@ const SelectState: React.FC = ({ control, setIsOpen }) => { { - return { value: state.id, display: state.name }; + return { value: state.id, display: state.name, color: state.color }; })} value={value} optionsFontsize="sm" diff --git a/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/index.tsx b/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/index.tsx index b1b6bc15e..f4698c5fe 100644 --- a/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/index.tsx +++ b/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/index.tsx @@ -93,6 +93,7 @@ const IssueDetailSidebar: React.FC = ({ console.log(res); reset(defaultValues); issueLabelMutate((prevData) => [...(prevData ?? []), res], false); + submitChanges({ labels_list: [res.id] }); }); }; @@ -103,7 +104,7 @@ const IssueDetailSidebar: React.FC = ({ }); }; - console.log(issueDetail); + console.log(issueDetail?.labels, issueDetail?.labels_list); return ( <> @@ -248,25 +249,32 @@ const IssueDetailSidebar: React.FC = ({
- {issueDetail?.label_details.map((label) => ( - { - const updatedLabels = issueDetail?.labels.filter((l) => l !== label.id); - submitChanges({ - labels_list: updatedLabels, - }); - }} - > + {issueDetail?.labels.map((label) => { + const singleLabel = issueLabels?.find((l) => l.id === label); + + if (!singleLabel) return null; + + return ( - {label.name} - - - ))} + key={singleLabel.id} + className="group flex items-center gap-1 border rounded-2xl text-xs px-1 py-0.5 hover:bg-red-50 hover:border-red-500 cursor-pointer" + onClick={() => { + const updatedLabels = issueDetail?.labels.filter((l) => l !== label); + // submitChanges({ + // labels_list: updatedLabels, + // }); + console.log(updatedLabels); + }} + > + + {singleLabel.name} + + + ); + })} = ({ submitChanges({ labels_list: val })} className="flex-shrink-0" + multiple > {({ open }) => ( <> diff --git a/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/select-assignee.tsx b/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/select-assignee.tsx index cbc636c11..557e16100 100644 --- a/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/select-assignee.tsx +++ b/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/select-assignee.tsx @@ -58,7 +58,7 @@ const SelectAssignee: React.FC = ({ control, submitChanges }) => { > {({ open }) => (
- + = ({ control, submitChanges }) => {
diff --git a/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/select-cycle.tsx b/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/select-cycle.tsx index 80dd4a899..0d8b32c65 100644 --- a/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/select-cycle.tsx +++ b/apps/app/components/project/issues/issue-detail/issue-detail-sidebar/select-cycle.tsx @@ -1,16 +1,17 @@ +// react +import React from "react"; // react-hook-form import { Control, Controller } from "react-hook-form"; // hooks import useUser from "lib/hooks/useUser"; -// headless ui -import { Listbox, Transition } from "@headlessui/react"; +// ui +import { Spinner, CustomSelect } from "ui"; +// icons +import { ArrowPathIcon } from "@heroicons/react/24/outline"; // types import { IIssue } from "types"; +// common import { classNames } from "constants/common"; -import { Spinner } from "ui"; -import React from "react"; -import { ArrowPathIcon, ChevronDownIcon } from "@heroicons/react/24/outline"; -import CustomSelect from "ui/custom-select"; type Props = { control: Control; @@ -29,7 +30,7 @@ const SelectCycle: React.FC = ({ control, handleCycleChange }) => {
( <> = ({ control, handleCycleChange }) => { "hidden truncate sm:block text-left" )} > - {value ? cycles?.find((c) => c.id === value)?.name : "None"} + {value ? cycles?.find((c) => c.id === value.cycle_detail.id)?.name : "None"} } value={value} diff --git a/apps/app/components/project/issues/list-view/index.tsx b/apps/app/components/project/issues/list-view/index.tsx index 4fe8d6162..5d1175a7c 100644 --- a/apps/app/components/project/issues/list-view/index.tsx +++ b/apps/app/components/project/issues/list-view/index.tsx @@ -155,11 +155,11 @@ const ListView: React.FC = ({ {activeProject?.identifier}-{issue.sequence_id} )} - {issue.name} -
+ {issue.name} + {/*
Name
{issue.name}
-
+
*/}
diff --git a/apps/app/components/project/settings/ControlSettings.tsx b/apps/app/components/project/settings/control.tsx similarity index 100% rename from apps/app/components/project/settings/ControlSettings.tsx rename to apps/app/components/project/settings/control.tsx diff --git a/apps/app/components/project/settings/GeneralSettings.tsx b/apps/app/components/project/settings/general.tsx similarity index 100% rename from apps/app/components/project/settings/GeneralSettings.tsx rename to apps/app/components/project/settings/general.tsx diff --git a/apps/app/components/project/settings/LabelsSettings.tsx b/apps/app/components/project/settings/labels/index.tsx similarity index 54% rename from apps/app/components/project/settings/LabelsSettings.tsx rename to apps/app/components/project/settings/labels/index.tsx index df423d91b..459ece444 100644 --- a/apps/app/components/project/settings/LabelsSettings.tsx +++ b/apps/app/components/project/settings/labels/index.tsx @@ -13,7 +13,7 @@ import useUser from "lib/hooks/useUser"; // headless ui import { Popover, Transition, Menu } from "@headlessui/react"; // ui -import { Button, Input, Spinner } from "ui"; +import { Button, CustomMenu, Input, Spinner } from "ui"; // icons import { ChevronDownIcon, @@ -26,6 +26,7 @@ import { import { IIssueLabels } from "types"; // fetch-keys import { PROJECT_ISSUE_LABELS } from "constants/fetch-keys"; +import SingleLabel from "./single-label"; const defaultValues: Partial = { name: "", @@ -68,6 +69,14 @@ const LabelsSettings: React.FC = () => { }); }; + const editLabel = (label: IIssueLabels) => { + setNewLabelForm(true); + setValue("colour", label.colour); + setValue("name", label.name); + setIsUpdating(true); + setLabelidForUpdate(label.id); + }; + const handleLabelUpdate: SubmitHandler = (formData) => { if (!activeWorkspace || !activeProject || isSubmitting) return; issuesServices @@ -98,10 +107,6 @@ const LabelsSettings: React.FC = () => { } }; - const getLabelChildren = (labelId: string) => { - return issueLabels?.filter((l) => l.parent === labelId); - }; - return ( <>
@@ -110,31 +115,38 @@ const LabelsSettings: React.FC = () => {

Labels

Manage the labels of this project.

-
-
- +
+ {({ open }) => ( <> {watch("colour") && watch("colour") !== "" && ( )} - { leaveFrom="opacity-100 translate-y-0" leaveTo="opacity-0 translate-y-1" > - + { )}
- {issueLabels ? ( - issueLabels.map((label) => { - const children = getLabelChildren(label.id); - - return ( - - {children && children.length === 0 ? ( -
-
- -

{label.name}

-
-
- - - - - - - - - -
- -
-
-
-
-
-
- ) : ( -
-

- - This is the label group title -

-
-
-
-
- This is the label title -
- -
-
-
- )} -
- ); - }) - ) : ( -
- -
- )} + <> + {issueLabels ? ( + issueLabels.map((label) => ( + + )) + ) : ( +
+ +
+ )} +
diff --git a/apps/app/components/project/settings/labels/single-label.tsx b/apps/app/components/project/settings/labels/single-label.tsx new file mode 100644 index 000000000..ed84a79a7 --- /dev/null +++ b/apps/app/components/project/settings/labels/single-label.tsx @@ -0,0 +1,160 @@ +// react +import React, { useState } from "react"; +// react-hook-form +import { Controller, useForm } from "react-hook-form"; +// headless ui +import { Popover, Transition } from "@headlessui/react"; +// ui +import { Button, CustomMenu, Input } from "ui"; +// icons +import { PencilIcon, RectangleGroupIcon } from "@heroicons/react/24/outline"; +// types +import { IIssueLabels } from "types"; +import { TwitterPicker } from "react-color"; + +type Props = { + label: IIssueLabels; + issueLabels: IIssueLabels[]; + editLabel: (label: IIssueLabels) => void; + handleLabelDelete: (labelId: string) => void; +}; + +const defaultValues: Partial = { + name: "", + colour: "#ff0000", +}; + +const SingleLabel: React.FC = ({ label, issueLabels, editLabel, handleLabelDelete }) => { + const [newLabelForm, setNewLabelForm] = useState(true); + + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + setError, + watch, + reset, + control, + } = useForm({ defaultValues }); + + const children = issueLabels?.filter((l) => l.parent === label.id); + + return ( + <> + {children && children.length === 0 ? ( +
+
+
+ +
{label.name}
+
+ + Convert to group + { + editLabel(label); + }} + > + Edit + + handleLabelDelete(label.id)}> + Delete + + +
+
+
+ + {({ open }) => ( + <> + + {watch("colour") && watch("colour") !== "" && ( + + )} + + + + + ( + onChange(value.hex)} + /> + )} + /> + + + + )} + +
+
+ +
+ + +
+
+ ) : ( +
+

+ + This is the label group title +

+
+
+
+
+ This is the label title +
+ +
+
+
+ )} + + ); +}; + +export default SingleLabel; diff --git a/apps/app/components/project/settings/StatesSettings.tsx b/apps/app/components/project/settings/states.tsx similarity index 57% rename from apps/app/components/project/settings/StatesSettings.tsx rename to apps/app/components/project/settings/states.tsx index cfb642c69..4cc02a6b1 100644 --- a/apps/app/components/project/settings/StatesSettings.tsx +++ b/apps/app/components/project/settings/states.tsx @@ -56,50 +56,7 @@ const StatesSettings: React.FC = ({ projectId }) => { Add
-
-
- {groupedStates[key]?.map((state) => - state.id !== selectedState ? ( -
-
-
-

{addSpaceIfCamelCase(state.name)}

-
-
- - -
-
- ) : ( -
- { - setActiveGroup(null); - setSelectedState(null); - }} - workspaceSlug={activeWorkspace?.slug} - data={states?.find((state) => state.id === selectedState) ?? null} - selectedGroup={key as keyof StateGroup} - /> -
- ) - )} -
+
{key === activeGroup && ( = ({ projectId }) => { selectedGroup={key as keyof StateGroup} /> )} + {groupedStates[key]?.map((state) => + state.id !== selectedState ? ( +
+
+
+
{addSpaceIfCamelCase(state.name)}
+
+
+ + +
+
+ ) : ( +
+ { + setActiveGroup(null); + setSelectedState(null); + }} + workspaceSlug={activeWorkspace?.slug} + data={states?.find((state) => state.id === selectedState) ?? null} + selectedGroup={key as keyof StateGroup} + /> +
+ ) + )}
))} diff --git a/apps/app/pages/projects/[projectId]/cycles/[cycleId].tsx b/apps/app/pages/projects/[projectId]/cycles/[cycleId].tsx index 641a93825..666f92e6a 100644 --- a/apps/app/pages/projects/[projectId]/cycles/[cycleId].tsx +++ b/apps/app/pages/projects/[projectId]/cycles/[cycleId].tsx @@ -1,7 +1,6 @@ // react import React, { useState } from "react"; // next -import Link from "next/link"; import { useRouter } from "next/router"; // swr import useSWR, { mutate } from "swr"; @@ -12,6 +11,9 @@ import AppLayout from "layouts/app-layout"; // components import CyclesListView from "components/project/cycles/list-view"; import CyclesBoardView from "components/project/cycles/board-view"; +import CreateUpdateIssuesModal from "components/project/issues/create-update-issue-modal"; +import CycleIssuesListModal from "components/project/cycles/cycle-issues-list-modal"; +import ConfirmIssueDeletion from "components/project/issues/confirm-issue-deletion"; // services import issuesServices from "lib/services/issues.service"; import cycleServices from "lib/services/cycles.service"; @@ -28,23 +30,11 @@ import { BreadcrumbItem, Breadcrumbs, CustomMenu } from "ui"; import { Squares2X2Icon } from "@heroicons/react/20/solid"; import { ArrowPathIcon, ChevronDownIcon, ListBulletIcon } from "@heroicons/react/24/outline"; // types -import { - CycleIssueResponse, - IIssue, - IssueResponse, - NestedKeyOf, - Properties, - SelectIssue, - SelectSprintType, -} from "types"; +import { CycleIssueResponse, IIssue, NestedKeyOf, Properties, SelectIssue } from "types"; // fetch-keys -import { CYCLE_ISSUES, PROJECT_ISSUES_LIST, PROJECT_MEMBERS } from "constants/fetch-keys"; -// constants +import { CYCLE_ISSUES, PROJECT_MEMBERS } from "constants/fetch-keys"; +// common import { classNames, replaceUnderscoreIfSnakeCase } from "constants/common"; -import CreateUpdateIssuesModal from "components/project/issues/create-update-issue-modal"; -import CycleIssuesListModal from "components/project/cycles/cycle-issues-list-modal"; -import ConfirmCycleDeletion from "components/project/cycles/confirm-cycle-deletion"; -import ConfirmIssueDeletion from "components/project/issues/confirm-issue-deletion"; const groupByOptions: Array<{ name: string; key: NestedKeyOf | null }> = [ { name: "State", key: "state_detail.name" }, @@ -77,15 +67,16 @@ const filterIssueOptions: Array<{ }, ]; -type Props = {}; - -const SingleCycle: React.FC = () => { +const SingleCycle: React.FC = () => { const [isIssueModalOpen, setIsIssueModalOpen] = useState(false); - const [selectedCycle, setSelectedCycle] = useState(); const [selectedIssues, setSelectedIssues] = useState(); const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false); const [deleteIssue, setDeleteIssue] = useState(undefined); + const [preloadedData, setPreloadedData] = useState< + (Partial & { actionType: "createIssue" | "edit" | "delete" }) | undefined + >(undefined); + const { activeWorkspace, activeProject, cycles, issues } = useUser(); const router = useRouter(); @@ -149,15 +140,8 @@ const SingleCycle: React.FC = () => { issue?: IIssue, actionType: "create" | "edit" | "delete" = "create" ) => { - const cycle = cycles?.find((cycle) => cycle.id === cycleId); - if (cycle) { - setSelectedCycle({ - ...cycle, - actionType: "create-issue", - }); - if (issue) setSelectedIssues({ ...issue, actionType }); - setIsIssueModalOpen(true); - } + if (issue) setSelectedIssues({ ...issue, actionType }); + setIsIssueModalOpen(true); }; const openIssuesListModal = () => { @@ -229,13 +213,9 @@ const SingleCycle: React.FC = () => { return ( <> @@ -429,6 +409,7 @@ const SingleCycle: React.FC = () => { openCreateIssueModal={openCreateIssueModal} openIssuesListModal={openIssuesListModal} removeIssueFromCycle={removeIssueFromCycle} + setPreloadedData={setPreloadedData} /> ) : (
@@ -442,6 +423,7 @@ const SingleCycle: React.FC = () => { openIssuesListModal={openIssuesListModal} handleDeleteIssue={setDeleteIssue} partialUpdateIssue={partialUpdateIssue} + setPreloadedData={setPreloadedData} />
)} diff --git a/apps/app/pages/projects/[projectId]/cycles/index.tsx b/apps/app/pages/projects/[projectId]/cycles/index.tsx index 1106d5134..00b81839a 100644 --- a/apps/app/pages/projects/[projectId]/cycles/index.tsx +++ b/apps/app/pages/projects/[projectId]/cycles/index.tsx @@ -106,7 +106,13 @@ const ProjectSprints: NextPage = () => { } Icon={PlusIcon} - action={() => setCreateUpdateCycleModal(true)} + action={() => { + const e = new KeyboardEvent("keydown", { + ctrlKey: true, + key: "q", + }); + document.dispatchEvent(e); + }} />
diff --git a/apps/app/pages/projects/[projectId]/issues/[issueId].tsx b/apps/app/pages/projects/[projectId]/issues/[issueId].tsx index 3c0a648a6..ce444df53 100644 --- a/apps/app/pages/projects/[projectId]/issues/[issueId].tsx +++ b/apps/app/pages/projects/[projectId]/issues/[issueId].tsx @@ -94,7 +94,7 @@ const IssueDetail: NextPage = () => { blockers_list: [], blocked_list: [], target_date: new Date().toString(), - cycle: "", + issue_cycle: null, labels_list: [], }, }); @@ -171,6 +171,7 @@ const IssueDetail: NextPage = () => { assignees_list: issueDetail.assignees_list ?? issueDetail.assignee_details?.map((user) => user.id), labels_list: issueDetail.labels_list ?? issueDetail.labels?.map((label) => label), + labels: issueDetail.labels_list ?? issueDetail.labels?.map((label) => label), }); }, [issueDetail, reset]); @@ -211,7 +212,7 @@ const IssueDetail: NextPage = () => { } }; - // console.log(issueDetail); + console.log(issueDetail); return ( | null }> = [ { name: "State", key: "state_detail.name" }, { name: "Priority", key: "priority" }, - { name: "Cycle", key: "issue_cycle.cycle_detail.name" }, + // { name: "Cycle", key: "issue_cycle.cycle_detail.name" }, { name: "Created By", key: "created_by" }, { name: "None", key: null }, ]; diff --git a/apps/app/pages/projects/[projectId]/modules/index.tsx b/apps/app/pages/projects/[projectId]/modules/index.tsx index d5055dd44..e95d40b79 100644 --- a/apps/app/pages/projects/[projectId]/modules/index.tsx +++ b/apps/app/pages/projects/[projectId]/modules/index.tsx @@ -80,13 +80,17 @@ const ProjectModules: NextPage = () => { title="Create a new module" description={ - Use
Ctrl/Command + Q
{" "} + Use
Ctrl/Command + M
{" "} shortcut to create a new cycle
} Icon={PlusIcon} action={() => { - return; + const e = new KeyboardEvent("keydown", { + ctrlKey: true, + key: "m", + }); + document.dispatchEvent(e); }} /> diff --git a/apps/app/pages/projects/[projectId]/settings.tsx b/apps/app/pages/projects/[projectId]/settings.tsx index 5878082d1..a030dc300 100644 --- a/apps/app/pages/projects/[projectId]/settings.tsx +++ b/apps/app/pages/projects/[projectId]/settings.tsx @@ -27,22 +27,22 @@ import { Breadcrumbs, BreadcrumbItem } from "ui/Breadcrumbs"; // types import type { IProject, IWorkspace } from "types"; -const GeneralSettings = dynamic(() => import("components/project/settings/GeneralSettings"), { +const GeneralSettings = dynamic(() => import("components/project/settings/general"), { loading: () =>

Loading...

, ssr: false, }); -const ControlSettings = dynamic(() => import("components/project/settings/ControlSettings"), { +const ControlSettings = dynamic(() => import("components/project/settings/control"), { loading: () =>

Loading...

, ssr: false, }); -const StatesSettings = dynamic(() => import("components/project/settings/StatesSettings"), { +const StatesSettings = dynamic(() => import("components/project/settings/states"), { loading: () =>

Loading...

, ssr: false, }); -const LabelsSettings = dynamic(() => import("components/project/settings/LabelsSettings"), { +const LabelsSettings = dynamic(() => import("components/project/settings/labels"), { loading: () =>

Loading...

, ssr: false, }); diff --git a/apps/app/types/issues.d.ts b/apps/app/types/issues.d.ts index 5a9a916ed..685623424 100644 --- a/apps/app/types/issues.d.ts +++ b/apps/app/types/issues.d.ts @@ -30,7 +30,6 @@ export interface IIssue { label_details: any[]; assignee_details: IUser[]; assignees_list: string[]; - bridge?: string; blocked_by_issue_details: any[]; blocked_issues: BlockeIssue[]; blocker_issues: BlockeIssue[]; @@ -51,7 +50,7 @@ export interface IIssue { updated_at: Date; updated_by: string; workspace: string; - }; + } | null; description: any; priority: string | null; start_date: string | null; diff --git a/apps/app/ui/Button/index.tsx b/apps/app/ui/Button/index.tsx index 3aea14920..bb1d3b54c 100644 --- a/apps/app/ui/Button/index.tsx +++ b/apps/app/ui/Button/index.tsx @@ -39,7 +39,7 @@ const Button = React.forwardRef( disabled ? "opacity-70" : "" } text-white shadow-sm bg-theme hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 border border-transparent` : theme === "secondary" - ? "border bg-white hover:bg-gray-100" + ? "border bg-transparent hover:bg-gray-100" : theme === "success" ? `${ disabled ? "opacity-70" : "" diff --git a/apps/app/ui/custom-listbox/index.tsx b/apps/app/ui/custom-listbox/index.tsx index 835b51ed1..91c81263a 100644 --- a/apps/app/ui/custom-listbox/index.tsx +++ b/apps/app/ui/custom-listbox/index.tsx @@ -92,7 +92,7 @@ const CustomListbox: React.FC = ({ : "" } rounded-md py-1 ring-1 ring-black ring-opacity-5 focus:outline-none z-10`} > -
+
{options ? ( options.length > 0 ? ( options.map((option) => ( @@ -100,8 +100,8 @@ const CustomListbox: React.FC = ({ key={option.value} className={({ active }) => `${ - active ? "text-white bg-theme" : "text-gray-900" - } cursor-pointer select-none relative p-2 rounded-md` + active ? "bg-indigo-50" : "" + } text-gray-900 cursor-pointer select-none relative p-2` } value={option.value} > @@ -115,8 +115,16 @@ const CustomListbox: React.FC = ({ : value === option.value) ? "font-semibold" : "font-normal" - } block truncate`} + } flex items-center gap-2 truncate`} > + {option.color && ( + + )} {option.display} diff --git a/apps/app/ui/custom-listbox/types.d.ts b/apps/app/ui/custom-listbox/types.d.ts index 4c69c4e8e..4adc068e8 100644 --- a/apps/app/ui/custom-listbox/types.d.ts +++ b/apps/app/ui/custom-listbox/types.d.ts @@ -1,7 +1,7 @@ export type Props = { title?: string; label?: string; - options?: Array<{ display: string; value: any }>; + options?: Array<{ display: string; value: any; color?: string }>; icon?: JSX.Element; value: any; onChange: (value: any) => void; diff --git a/apps/app/ui/custom-menu/index.tsx b/apps/app/ui/custom-menu/index.tsx index 46a244ba3..b8e8160a4 100644 --- a/apps/app/ui/custom-menu/index.tsx +++ b/apps/app/ui/custom-menu/index.tsx @@ -15,11 +15,13 @@ const CustomMenu = ({ label, className = "", ellipsis = false, - width, + width = "auto", textAlignment, + withoutBorder = false, + optionsPosition = "right", }: Props) => { return ( - +
{ellipsis ? ( @@ -27,16 +29,20 @@ const CustomMenu = ({ ) : ( {label} - )}
@@ -51,9 +57,9 @@ const CustomMenu = ({ leaveTo="transform opacity-0 scale-95" >
{children}
diff --git a/apps/app/ui/custom-menu/types.d.ts b/apps/app/ui/custom-menu/types.d.ts index b3f3c9552..a21ec2583 100644 --- a/apps/app/ui/custom-menu/types.d.ts +++ b/apps/app/ui/custom-menu/types.d.ts @@ -5,6 +5,8 @@ export type Props = { ellipsis?: boolean; width?: "auto"; textAlignment?: "left" | "center" | "right"; + withoutBorder?: boolean; + optionsPosition?: "left" | "right"; }; export type MenuItemProps = { diff --git a/apps/app/ui/index.ts b/apps/app/ui/index.ts index 94e451654..f51ae91a2 100644 --- a/apps/app/ui/index.ts +++ b/apps/app/ui/index.ts @@ -4,6 +4,7 @@ export { default as Select } from "./Select"; export { default as TextArea } from "./TextArea"; export { default as CustomListbox } from "./custom-listbox"; export { default as CustomMenu } from "./custom-menu"; +export { default as CustomSelect } from "./custom-select"; export { default as Spinner } from "./Spinner"; export { default as Tooltip } from "./Tooltip"; export { default as SearchListbox } from "./search-listbox";