diff --git a/apps/app/components/project/cycles/CycleView.tsx b/apps/app/components/project/cycles/CycleView.tsx index f46079165..b88883ae0 100644 --- a/apps/app/components/project/cycles/CycleView.tsx +++ b/apps/app/components/project/cycles/CycleView.tsx @@ -21,10 +21,11 @@ import type { CycleViewProps as Props, CycleIssueResponse, IssueResponse } from // fetch keys import { CYCLE_ISSUES } from "constants/fetch-keys"; // constants -import { renderShortNumericDateFormat } from "constants/common"; +import { addSpaceIfCamelCase, renderShortNumericDateFormat } from "constants/common"; import issuesServices from "lib/services/issues.services"; import StrictModeDroppable from "components/dnd/StrictModeDroppable"; import { Draggable } from "react-beautiful-dnd"; +import { CalendarDaysIcon } from "@heroicons/react/24/outline"; const CycleView: React.FC = ({ cycle, @@ -70,14 +71,13 @@ const CycleView: React.FC = ({ /> {({ open }) => ( -
-
- +
+
+

{cycle.name}

@@ -93,11 +93,15 @@ const CycleView: React.FC = ({ {cycle.end_date ? renderShortNumericDateFormat(cycle.end_date) : ""}

+

{cycleIssues?.length}

- + @@ -134,7 +138,11 @@ const CycleView: React.FC = ({ {(provided) => ( -
+
{cycleIssues ? ( cycleIssues.length > 0 ? ( cycleIssues.map((issue, index) => ( @@ -145,7 +153,7 @@ const CycleView: React.FC = ({ > {(provided, snapshot) => (
= ({
- - {issue.issue_details.state_detail?.name} - +
+ + {issue.issue_details.start_date + ? renderShortNumericDateFormat( + issue.issue_details.start_date + ) + : "N/A"} +
+
+ + {addSpaceIfCamelCase(issue.issue_details.state_detail.name)} +
@@ -261,49 +277,51 @@ const CycleView: React.FC = ({ - - - - Add issue - +
+ + + + Add issue + - - -
- - {(active) => ( - - )} - - - {(active) => ( - - )} - -
-
-
-
+ + +
+ + {(active) => ( + + )} + + + {(active) => ( + + )} + +
+
+
+
+
)} diff --git a/apps/app/components/project/issues/BoardView/SingleBoard.tsx b/apps/app/components/project/issues/BoardView/SingleBoard.tsx index 2c0426e93..08efcdac3 100644 --- a/apps/app/components/project/issues/BoardView/SingleBoard.tsx +++ b/apps/app/components/project/issues/BoardView/SingleBoard.tsx @@ -1,15 +1,10 @@ import React, { useState } from "react"; // Next imports import Link from "next/link"; +import Image from "next/image"; // React beautiful dnd import { Draggable } from "react-beautiful-dnd"; import StrictModeDroppable from "components/dnd/StrictModeDroppable"; -// common -import { - addSpaceIfCamelCase, - findHowManyDaysLeft, - renderShortNumericDateFormat, -} from "constants/common"; // types import { IIssue, Properties, NestedKeyOf } from "types"; // icons @@ -20,7 +15,13 @@ import { EllipsisHorizontalIcon, PlusIcon, } from "@heroicons/react/24/outline"; -import Image from "next/image"; +import User from "public/user.png"; +// common +import { + addSpaceIfCamelCase, + findHowManyDaysLeft, + renderShortNumericDateFormat, +} from "constants/common"; import { getPriorityIcon } from "constants/global"; type Props = { @@ -193,17 +194,19 @@ const SingleBoard: React.FC = ({ {properties.priority && (
{/* {getPriorityIcon(childIssue.priority ?? "")} */} - {childIssue.priority} + {childIssue.priority ?? "None"}
)} {properties.state && ( @@ -285,7 +288,15 @@ const SingleBoard: React.FC = ({ ) ) ) : ( - No assignee. +
+ No user +
)}
)} diff --git a/apps/app/components/project/issues/ListView/index.tsx b/apps/app/components/project/issues/ListView/index.tsx index c4a7e19f1..c12495849 100644 --- a/apps/app/components/project/issues/ListView/index.tsx +++ b/apps/app/components/project/issues/ListView/index.tsx @@ -5,10 +5,20 @@ import Link from "next/link"; import Image from "next/image"; // swr import useSWR, { mutate } from "swr"; +// headless ui +import { Disclosure, Listbox, Menu, Transition } from "@headlessui/react"; // ui -import { Listbox, Transition } from "@headlessui/react"; +import { Spinner } from "ui"; // icons -import { PencilIcon, TrashIcon } from "@heroicons/react/24/outline"; +import { + ChevronDownIcon, + PlusIcon, + CalendarDaysIcon, + EllipsisHorizontalIcon, +} from "@heroicons/react/24/outline"; +import User from "public/user.png"; +// components +import CreateUpdateIssuesModal from "components/project/issues/CreateUpdateIssueModal"; // types import { IIssue, IssueResponse, NestedKeyOf, Properties, WorkspaceMember } from "types"; // hooks @@ -20,7 +30,12 @@ import { PROJECT_ISSUES_LIST, WORKSPACE_MEMBERS } from "constants/fetch-keys"; import issuesServices from "lib/services/issues.services"; import workspaceService from "lib/services/workspace.service"; // constants -import { addSpaceIfCamelCase, classNames, renderShortNumericDateFormat } from "constants/common"; +import { + addSpaceIfCamelCase, + classNames, + findHowManyDaysLeft, + renderShortNumericDateFormat, +} from "constants/common"; // types type Props = { @@ -38,6 +53,11 @@ const ListView: React.FC = ({ setSelectedIssue, handleDeleteIssue, }) => { + const [isCreateIssuesModalOpen, setIsCreateIssuesModalOpen] = useState(false); + const [preloadedData, setPreloadedData] = useState< + (Partial & { actionType: "createIssue" | "edit" | "delete" }) | undefined + >(undefined); + const { activeWorkspace, activeProject, states } = useUser(); const partialUpdateIssue = (formData: Partial, issueId: string) => { @@ -66,348 +86,449 @@ const ListView: React.FC = ({ ); return ( -
- {Object.keys(groupedByIssues).map((singleGroup) => ( -
-
-
- - {selectedGroup !== null ? ( - - - - - - ) : ( - - - - - - )} - - {groupedByIssues[singleGroup].length > 0 - ? groupedByIssues[singleGroup].map((issue: IIssue, index: number) => { - const assignees = [ - ...(issue?.assignees_list ?? []), - ...(issue?.assignees ?? []), - ]?.map( - (assignee) => people?.find((p) => p.member.id === assignee)?.member.email - ); + + ) : ( +

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; - return ( -
- - {Object.keys(properties).map( - (key) => - properties[key as keyof Properties] && ( - - {(key as keyof Properties) === "key" ? ( - - ) : (key as keyof Properties) === "priority" ? ( - - ) : (key as keyof Properties) === "assignee" ? ( - - ) : (key as keyof Properties) === "state" ? ( - - ) : (key as keyof Properties) === "target_date" ? ( - - ) : ( - - )} - - ) - )} - - - ); - }) - : null} - -
-
- {selectedGroup === "state_detail.name" ? ( - s.name === singleGroup)?.color, - }} - > - ) : null} + <> + +
+ {Object.keys(groupedByIssues).map((singleGroup) => ( + + {({ open }) => ( +
+
+ +
+ + + + {selectedGroup !== null ? ( +

{singleGroup === null || singleGroup === "null" ? selectedGroup === "priority" && "No priority" : addSpaceIfCamelCase(singleGroup)} - - {groupedByIssues[singleGroup as keyof IIssue].length} - -

-
- ALL ISSUES - - {groupedByIssues[singleGroup as keyof IIssue].length} - -
- - {issue.name} - - - {activeProject?.identifier}-{issue.sequence_id} - - { - partialUpdateIssue({ priority: data }, issue.id); - }} - className="flex-shrink-0" - > - {({ open }) => ( - <> -
- - + +
+ {properties.priority && ( + { + partialUpdateIssue({ priority: data }, issue.id); + }} + className="flex-shrink-0" + > + {({ open }) => ( + <> +
+ + {issue.priority ?? "None"} + + + + + {PRIORITIES?.map((priority) => ( + + classNames( + active ? "bg-indigo-50" : "bg-white", + "cursor-pointer capitalize select-none px-3 py-2" + ) + } + value={priority} > - {issue.priority ?? "None"} - - + {priority} + + ))} + + +
+ + )} +
+ )} + {properties.state && ( + { + partialUpdateIssue({ state: data }, issue.id); + }} + className="flex-shrink-0" + > + {({ open }) => ( + <> +
+ + + {addSpaceIfCamelCase(issue.state_detail.name)} + - - - {PRIORITIES?.map((priority) => ( - - classNames( - active ? "bg-indigo-50" : "bg-white", - "cursor-pointer capitalize select-none px-3 py-2" - ) - } - value={priority} - > - {priority} - - ))} - - -
- - )} -
-
- { - const newData = issue.assignees ?? []; - if (newData.includes(data)) { - newData.splice(newData.indexOf(data), 1); - } else { - newData.push(data); - } - partialUpdateIssue( - { assignees_list: newData }, - issue.id - ); - }} - className="flex-shrink-0" - > - {({ open }) => ( - <> -
- - {() => { - if (assignees.length > 0) - return ( - <> - {assignees.map((assignee, index) => ( -
- {assignee} -
- ))} - - ); - else return None; - }} -
- - - - {people?.map((person) => ( - - classNames( - active ? "bg-indigo-50" : "bg-white", - "cursor-pointer select-none px-3 py-2" - ) - } - value={person.member.id} - > -
- {person.member.avatar && - person.member.avatar !== "" ? ( -
- avatar -
- ) : ( -

- {person.member.first_name.charAt(0)} -

- )} -

{person.member.first_name}

+ + + {states?.map((state) => ( + + classNames( + active ? "bg-indigo-50" : "bg-white", + "cursor-pointer select-none px-3 py-2" + ) + } + value={state.id} + > + {addSpaceIfCamelCase(state.name)} + + ))} + + +
+ + )} + + )} + {properties.start_date && ( +
+ + {issue.start_date + ? renderShortNumericDateFormat(issue.start_date) + : "N/A"} +
+ )} + {properties.target_date && ( +
+ + {issue.target_date + ? renderShortNumericDateFormat(issue.target_date) + : "N/A"} + {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"} + + )} +
+ )} + {properties.assignee && ( + { + const newData = issue.assignees ?? []; + if (newData.includes(data)) { + newData.splice(newData.indexOf(data), 1); + } else { + newData.push(data); + } + partialUpdateIssue({ assignees_list: newData }, issue.id); + }} + className="relative flex-shrink-0" + > + {({ open }) => ( + <> +
+ +
+ {assignees.length > 0 ? ( + assignees.map((assignee, index: number) => ( +
+ {assignee.avatar && assignee.avatar !== "" ? ( +
+ {assignee?.first_name}
- - ))} - - + ) : ( +
+ {assignee.first_name?.charAt(0)} +
+ )} +
+ )) + ) : ( +
+ No user +
+ )}
- - )} - -
- { - partialUpdateIssue({ state: data }, issue.id); - }} - className="flex-shrink-0" - > - {({ open }) => ( - <> -
- - - {addSpaceIfCamelCase(issue.state_detail.name)} - - + - - - {states?.map((state) => ( - - classNames( - active ? "bg-indigo-50" : "bg-white", - "cursor-pointer select-none px-3 py-2" - ) - } - value={state.id} - > - {addSpaceIfCamelCase(state.name)} - - ))} - - -
- - )} -
-
- {issue.target_date - ? renderShortNumericDateFormat(issue.target_date) - : "-"} - - {issue[key as keyof IIssue] ?? - (issue[key as keyof IIssue] as any)?.name ?? - "None"} - -
- - + + + {people?.map((person) => ( + + classNames( + active ? "bg-indigo-50" : "bg-white", + "cursor-pointer select-none px-3 py-2" + ) + } + value={person.member.id} + > +
+ {person.member.avatar && + person.member.avatar !== "" ? ( +
+ avatar +
+ ) : ( +
+ {person.member.first_name && + person.member.first_name !== "" + ? person.member.first_name.charAt(0) + : person.member.email.charAt(0)} +
+ )} +

+ {person.member.first_name && + person.member.first_name !== "" + ? person.member.first_name + : person.member.email} +

+
+
+ ))} +
+
+
+ + )} + + )} + + + + + + + + + +
+ +
+
+
+
+ -
-
-
-
- ))} -
+ ); + }) + ) : ( +

No issues.

+ ) + ) : ( +
+ +
+ )} +
+
+ +
+ +
+
+ )} + + ))} +
+ ); }; diff --git a/apps/app/pages/projects/[projectId]/cycles.tsx b/apps/app/pages/projects/[projectId]/cycles.tsx index 9afacd9ff..bde054440 100644 --- a/apps/app/pages/projects/[projectId]/cycles.tsx +++ b/apps/app/pages/projects/[projectId]/cycles.tsx @@ -203,7 +203,7 @@ const ProjectSprints: NextPage = () => { /> {cycles ? ( cycles.length > 0 ? ( -
+
diff --git a/apps/app/public/user.png b/apps/app/public/user.png new file mode 100644 index 000000000..bc0251228 Binary files /dev/null and b/apps/app/public/user.png differ