From d673aedf482bbae5c40864fd313fb0ff663f3f32 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Sun, 5 Feb 2023 16:57:37 +0530 Subject: [PATCH] refactor: integrated global list view everywhere --- .../components/core/board-view/all-boards.tsx | 249 +-------- .../core/board-view/board-header.tsx | 4 +- apps/app/components/core/board-view/index.ts | 4 + .../core/board-view/single-board.tsx | 26 +- .../core/board-view/single-issue.tsx | 18 +- apps/app/components/core/index.ts | 5 +- ...filter-view.tsx => issues-view-filter.tsx} | 0 apps/app/components/core/issues-view.tsx | 408 ++++++++++++++ .../components/core/list-view/all-lists.tsx | 60 +++ apps/app/components/core/list-view/index.ts | 3 + .../core/list-view/single-issue.tsx | 509 +++++++++--------- .../components/core/list-view/single-list.tsx | 142 +++++ .../components/issues/delete-issue-modal.tsx | 2 +- apps/app/components/issues/index.ts | 1 - apps/app/components/issues/list-view.tsx | 182 ------- apps/app/components/issues/modal.tsx | 4 +- apps/app/components/issues/sidebar.tsx | 2 +- apps/app/components/modules/index.ts | 1 - apps/app/components/modules/list-view.tsx | 194 ------- .../project/cycles/list-view/index.tsx | 199 ------- apps/app/hooks/use-issue-view.tsx | 4 +- .../projects/[projectId]/cycles/[cycleId].tsx | 24 +- .../projects/[projectId]/issues/index.tsx | 44 +- .../[projectId]/modules/[moduleId].tsx | 25 +- 24 files changed, 938 insertions(+), 1172 deletions(-) create mode 100644 apps/app/components/core/board-view/index.ts rename apps/app/components/core/{issues-filter-view.tsx => issues-view-filter.tsx} (100%) create mode 100644 apps/app/components/core/issues-view.tsx create mode 100644 apps/app/components/core/list-view/all-lists.tsx create mode 100644 apps/app/components/core/list-view/index.ts create mode 100644 apps/app/components/core/list-view/single-list.tsx delete mode 100644 apps/app/components/issues/list-view.tsx delete mode 100644 apps/app/components/modules/list-view.tsx delete mode 100644 apps/app/components/project/cycles/list-view/index.tsx diff --git a/apps/app/components/core/board-view/all-boards.tsx b/apps/app/components/core/board-view/all-boards.tsx index f9b1fcfa2..76b20e5fa 100644 --- a/apps/app/components/core/board-view/all-boards.tsx +++ b/apps/app/components/core/board-view/all-boards.tsx @@ -1,244 +1,40 @@ -import { useCallback, useState } from "react"; - -import { useRouter } from "next/router"; - -import useSWR, { mutate } from "swr"; - // react-beautiful-dnd import { DragDropContext, Draggable, DropResult } from "react-beautiful-dnd"; -// services -import issuesService from "services/issues.service"; -import stateService from "services/state.service"; // hooks import useIssueView from "hooks/use-issue-view"; // components import StrictModeDroppable from "components/dnd/StrictModeDroppable"; -import { CommonSingleBoard } from "components/core/board-view/single-board"; -import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues"; +import { SingleBoard } from "components/core/board-view/single-board"; // types -import { - CycleIssueResponse, - IIssue, - IssueResponse, - IState, - ModuleIssueResponse, - UserAuth, -} from "types"; -// fetch-keys -import { CYCLE_ISSUES, MODULE_ISSUES, PROJECT_ISSUES_LIST, STATE_LIST } from "constants/fetch-keys"; +import { IIssue, IProjectMember, IState, UserAuth } from "types"; type Props = { - type?: "issue" | "cycle" | "module"; + type: "issue" | "cycle" | "module"; issues: IIssue[]; - openIssuesListModal?: () => void; - handleDeleteIssue: React.Dispatch>; + states: IState[] | undefined; + members: IProjectMember[] | undefined; + addIssueToState: (groupTitle: string, stateId: string | null) => void; + openIssuesListModal?: (() => void) | null; + handleDeleteIssue: (issue: IIssue) => void; + handleOnDragEnd: (result: DropResult) => void; userAuth: UserAuth; }; export const AllBoards: React.FC = ({ - type = "issue", + type, issues, + states, + members, + addIssueToState, openIssuesListModal, handleDeleteIssue, + handleOnDragEnd, userAuth, }) => { - const [createIssueModal, setCreateIssueModal] = useState(false); - const [isIssueDeletionOpen, setIsIssueDeletionOpen] = useState(false); - const [issueDeletionData, setIssueDeletionData] = useState(); - const [preloadedData, setPreloadedData] = useState< - (Partial & { actionType: "createIssue" | "edit" | "delete" }) | undefined - >(undefined); - - const router = useRouter(); - const { workspaceSlug, projectId, cycleId, moduleId } = router.query; - - const { issueView, groupedByIssues, groupByProperty: selectedGroup } = useIssueView(issues); - - const { data: states, mutate: mutateState } = useSWR( - workspaceSlug && projectId ? STATE_LIST(projectId as string) : null, - workspaceSlug - ? () => stateService.getStates(workspaceSlug as string, projectId as string) - : null - ); - - const handleOnDragEnd = useCallback( - (result: DropResult) => { - if (!result.destination || !workspaceSlug || !projectId) return; - - const { source, destination, type } = result; - - if (type === "state") { - const newStates = Array.from(states ?? []); - const [reorderedState] = newStates.splice(source.index, 1); - newStates.splice(destination.index, 0, reorderedState); - const prevSequenceNumber = newStates[destination.index - 1]?.sequence; - const nextSequenceNumber = newStates[destination.index + 1]?.sequence; - - const sequenceNumber = - prevSequenceNumber && nextSequenceNumber - ? (prevSequenceNumber + nextSequenceNumber) / 2 - : nextSequenceNumber - ? nextSequenceNumber - 15000 / 2 - : prevSequenceNumber - ? prevSequenceNumber + 15000 / 2 - : 15000; - - newStates[destination.index].sequence = sequenceNumber; - - mutateState(newStates, false); - - stateService - .patchState( - workspaceSlug as string, - projectId as string, - newStates[destination.index].id, - { - sequence: sequenceNumber, - } - ) - .then((response) => { - console.log(response); - }) - .catch((err) => { - console.error(err); - }); - } else { - const draggedItem = groupedByIssues[source.droppableId][source.index]; - if (source.droppableId !== destination.droppableId) { - const sourceGroup = source.droppableId; // source group id - const destinationGroup = destination.droppableId; // destination group id - - if (!sourceGroup || !destinationGroup) return; - - if (selectedGroup === "priority") { - // update the removed item for mutation - draggedItem.priority = destinationGroup; - - // patch request - issuesService.patchIssue(workspaceSlug as string, projectId as string, draggedItem.id, { - priority: destinationGroup, - }); - } else if (selectedGroup === "state_detail.name") { - const destinationState = states?.find((s) => s.name === destinationGroup); - const destinationStateId = destinationState?.id; - - // update the removed item for mutation - if (!destinationStateId || !destinationState) return; - draggedItem.state = destinationStateId; - draggedItem.state_detail = destinationState; - - mutate( - PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string), - (prevData) => { - if (!prevData) return prevData; - - const updatedIssues = prevData.results.map((issue) => { - if (issue.id === draggedItem.id) - return { - ...draggedItem, - state_detail: destinationState, - state: destinationStateId, - }; - - return issue; - }); - - return { - ...prevData, - results: updatedIssues, - }; - }, - false - ); - - if (cycleId) - mutate( - CYCLE_ISSUES(cycleId as string), - (prevData) => { - if (!prevData) return prevData; - const updatedIssues = prevData.map((issue) => { - if (issue.issue_detail.id === draggedItem.id) { - return { - ...issue, - issue_detail: { - ...draggedItem, - state_detail: destinationState, - state: destinationStateId, - }, - }; - } - return issue; - }); - return [...updatedIssues]; - }, - false - ); - - if (moduleId) - mutate( - MODULE_ISSUES(moduleId as string), - (prevData) => { - if (!prevData) return prevData; - const updatedIssues = prevData.map((issue) => { - if (issue.issue_detail.id === draggedItem.id) { - return { - ...issue, - issue_detail: { - ...draggedItem, - state_detail: destinationState, - state: destinationStateId, - }, - }; - } - return issue; - }); - return [...updatedIssues]; - }, - false - ); - - // patch request - issuesService - .patchIssue(workspaceSlug as string, projectId as string, draggedItem.id, { - state: destinationStateId, - }) - .then((res) => { - mutate(CYCLE_ISSUES(cycleId as string)); - mutate(MODULE_ISSUES(moduleId as string)); - mutate(PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string)); - }); - } - } - } - }, - [ - workspaceSlug, - cycleId, - moduleId, - mutateState, - groupedByIssues, - projectId, - selectedGroup, - states, - ] - ); - - if (issueView !== "kanban") return <>; + const { groupedByIssues, groupByProperty: selectedGroup } = useIssueView(issues); return ( <> - setIsIssueDeletionOpen(false)} - data={issueDeletionData} - /> - setCreateIssueModal(false)} - prePopulateData={{ - ...preloadedData, - }} - /> {groupedByIssues ? (
@@ -265,7 +61,7 @@ export const AllBoards: React.FC = ({ return ( {(provided, snapshot) => ( - = ({ groupTitle={singleGroup} groupedByIssues={groupedByIssues} selectedGroup={selectedGroup} - addIssueToState={() => { - setCreateIssueModal(true); - if (selectedGroup) - setPreloadedData({ - state: stateId !== null ? stateId : undefined, - [selectedGroup]: singleGroup, - actionType: "createIssue", - }); - }} + members={members} + addIssueToState={() => addIssueToState(singleGroup, stateId)} handleDeleteIssue={handleDeleteIssue} - openIssuesListModal={type !== "issue" ? openIssuesListModal : null} + openIssuesListModal={openIssuesListModal ?? null} userAuth={userAuth} /> )} diff --git a/apps/app/components/core/board-view/board-header.tsx b/apps/app/components/core/board-view/board-header.tsx index 9afbe694a..ba4d2c02a 100644 --- a/apps/app/components/core/board-view/board-header.tsx +++ b/apps/app/components/core/board-view/board-header.tsx @@ -27,7 +27,7 @@ type Props = { addIssueToState: () => void; }; -const BoardHeader: React.FC = ({ +export const BoardHeader: React.FC = ({ isCollapsed, setIsCollapsed, provided, @@ -103,5 +103,3 @@ const BoardHeader: React.FC = ({
); - -export default BoardHeader; diff --git a/apps/app/components/core/board-view/index.ts b/apps/app/components/core/board-view/index.ts new file mode 100644 index 000000000..6e5cdf8bf --- /dev/null +++ b/apps/app/components/core/board-view/index.ts @@ -0,0 +1,4 @@ +export * from "./all-boards"; +export * from "./board-header"; +export * from "./single-board"; +export * from "./single-issue"; diff --git a/apps/app/components/core/board-view/single-board.tsx b/apps/app/components/core/board-view/single-board.tsx index 029066c1f..c38ddd5ea 100644 --- a/apps/app/components/core/board-view/single-board.tsx +++ b/apps/app/components/core/board-view/single-board.tsx @@ -1,27 +1,20 @@ -import { Dispatch, SetStateAction, useState } from "react"; +import { useState } from "react"; import { useRouter } from "next/router"; -import useSWR from "swr"; - // react-beautiful-dnd import StrictModeDroppable from "components/dnd/StrictModeDroppable"; import { Draggable, DraggableProvided, DraggableStateSnapshot } from "react-beautiful-dnd"; -// services -import workspaceService from "services/workspace.service"; // hooks import useIssuesProperties from "hooks/use-issue-properties"; // components -import BoardHeader from "components/core/board-view/board-header"; -import SingleIssue from "components/core/board-view/single-issue"; +import { BoardHeader, SingleBoardIssue } from "components/core"; // ui import { CustomMenu } from "components/ui"; // icons import { PlusIcon } from "@heroicons/react/24/outline"; // types -import { IIssue, IWorkspaceMember, NestedKeyOf, UserAuth } from "types"; -// fetch-keys -import { WORKSPACE_MEMBERS } from "constants/fetch-keys"; +import { IIssue, IProjectMember, NestedKeyOf, UserAuth } from "types"; type Props = { type?: "issue" | "cycle" | "module"; @@ -33,13 +26,14 @@ type Props = { [key: string]: IIssue[]; }; selectedGroup: NestedKeyOf | null; + members: IProjectMember[] | undefined; addIssueToState: () => void; - handleDeleteIssue?: Dispatch> | undefined; + handleDeleteIssue: (issue: IIssue) => void; openIssuesListModal?: (() => void) | null; userAuth: UserAuth; }; -export const CommonSingleBoard: React.FC = ({ +export const SingleBoard: React.FC = ({ type = "issue", provided, snapshot, @@ -47,6 +41,7 @@ export const CommonSingleBoard: React.FC = ({ groupTitle, groupedByIssues, selectedGroup, + members, addIssueToState, handleDeleteIssue, openIssuesListModal, @@ -60,11 +55,6 @@ export const CommonSingleBoard: React.FC = ({ const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string); - const { data: members } = useSWR( - workspaceSlug ? WORKSPACE_MEMBERS : null, - workspaceSlug ? () => workspaceService.workspaceMembers(workspaceSlug as string) : null - ); - const createdBy = selectedGroup === "created_by" ? members?.find((m) => m.member.id === groupTitle)?.member.first_name ?? "loading..." @@ -126,7 +116,7 @@ export const CommonSingleBoard: React.FC = ({ {...provided.draggableProps} {...provided.dragHandleProps} > - [] | (Partial | undefined)[]; - people: IWorkspaceMember[] | undefined; - handleDeleteIssue?: React.Dispatch>; + people: IProjectMember[] | undefined; + handleDeleteIssue: (issue: IIssue) => void; userAuth: UserAuth; }; -const SingleBoardIssue: React.FC = ({ +export const SingleBoardIssue: React.FC = ({ type, typeId, issue, @@ -164,12 +164,12 @@ const SingleBoardIssue: React.FC = ({ }`} >
- {handleDeleteIssue && !isNotAllowed && ( + {!isNotAllowed && (
@@ -400,7 +400,7 @@ const SingleBoardIssue: React.FC = ({ {people?.map((person) => ( `cursor-pointer select-none p-2 ${active ? "bg-indigo-50" : "bg-white"}` } @@ -457,5 +457,3 @@ const SingleBoardIssue: React.FC = ({
); }; - -export default SingleBoardIssue; diff --git a/apps/app/components/core/index.ts b/apps/app/components/core/index.ts index 7cfd6d6f5..0865ea441 100644 --- a/apps/app/components/core/index.ts +++ b/apps/app/components/core/index.ts @@ -1,5 +1,8 @@ +export * from "./board-view"; +export * from "./list-view"; export * from "./bulk-delete-issues-modal"; export * from "./existing-issues-list-modal"; export * from "./image-upload-modal"; -export * from "./issues-filter-view"; +export * from "./issues-view-filter"; +export * from "./issues-view"; export * from "./not-authorized-view"; diff --git a/apps/app/components/core/issues-filter-view.tsx b/apps/app/components/core/issues-view-filter.tsx similarity index 100% rename from apps/app/components/core/issues-filter-view.tsx rename to apps/app/components/core/issues-view-filter.tsx diff --git a/apps/app/components/core/issues-view.tsx b/apps/app/components/core/issues-view.tsx new file mode 100644 index 000000000..ac236f0fc --- /dev/null +++ b/apps/app/components/core/issues-view.tsx @@ -0,0 +1,408 @@ +import { useCallback, useState } from "react"; + +import { useRouter } from "next/router"; + +import useSWR, { mutate } from "swr"; + +// react-beautiful-dnd +import { DropResult } from "react-beautiful-dnd"; +// services +import issuesService from "services/issues.service"; +import stateService from "services/state.service"; +import projectService from "services/project.service"; +// hooks +import useIssueView from "hooks/use-issue-view"; +// components +import { AllLists, AllBoards } from "components/core"; +import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues"; +// types +import { + CycleIssueResponse, + IIssue, + IssueResponse, + IState, + ModuleIssueResponse, + UserAuth, +} from "types"; +// fetch-keys +import { + CYCLE_ISSUES, + MODULE_ISSUES, + PROJECT_ISSUES_LIST, + PROJECT_MEMBERS, + STATE_LIST, +} from "constants/fetch-keys"; + +type Props = { + type?: "issue" | "cycle" | "module"; + issues: IIssue[]; + openIssuesListModal?: () => void; + userAuth: UserAuth; +}; + +export const IssuesView: React.FC = ({ + type = "issue", + issues, + openIssuesListModal, + userAuth, +}) => { + // create issue modal + const [createIssueModal, setCreateIssueModal] = useState(false); + const [preloadedData, setPreloadedData] = useState< + (Partial & { actionType: "createIssue" | "edit" | "delete" }) | undefined + >(undefined); + + // updates issue modal + const [editIssueModal, setEditIssueModal] = useState(false); + const [issueToEdit, setIssueToEdit] = useState< + (IIssue & { actionType: "edit" | "delete" }) | undefined + >(undefined); + + // delete issue modal + const [deleteIssueModal, setDeleteIssueModal] = useState(false); + const [issueToDelete, setIssueToDelete] = useState(null); + + const router = useRouter(); + const { workspaceSlug, projectId, cycleId, moduleId } = router.query; + + const { issueView, groupedByIssues, groupByProperty: selectedGroup } = useIssueView(issues); + + const { data: states, mutate: mutateState } = useSWR( + workspaceSlug && projectId ? STATE_LIST(projectId as string) : null, + workspaceSlug + ? () => stateService.getStates(workspaceSlug as string, projectId as string) + : null + ); + + const { data: members } = useSWR( + projectId ? PROJECT_MEMBERS(projectId as string) : null, + workspaceSlug && projectId + ? () => projectService.projectMembers(workspaceSlug as string, projectId as string) + : null + ); + + const handleOnDragEnd = useCallback( + (result: DropResult) => { + if (!result.destination || !workspaceSlug || !projectId) return; + + const { source, destination, type } = result; + + if (type === "state") { + const newStates = Array.from(states ?? []); + const [reorderedState] = newStates.splice(source.index, 1); + newStates.splice(destination.index, 0, reorderedState); + const prevSequenceNumber = newStates[destination.index - 1]?.sequence; + const nextSequenceNumber = newStates[destination.index + 1]?.sequence; + + const sequenceNumber = + prevSequenceNumber && nextSequenceNumber + ? (prevSequenceNumber + nextSequenceNumber) / 2 + : nextSequenceNumber + ? nextSequenceNumber - 15000 / 2 + : prevSequenceNumber + ? prevSequenceNumber + 15000 / 2 + : 15000; + + newStates[destination.index].sequence = sequenceNumber; + + mutateState(newStates, false); + + stateService + .patchState( + workspaceSlug as string, + projectId as string, + newStates[destination.index].id, + { + sequence: sequenceNumber, + } + ) + .then((response) => { + console.log(response); + }) + .catch((err) => { + console.error(err); + }); + } else { + const draggedItem = groupedByIssues[source.droppableId][source.index]; + if (source.droppableId !== destination.droppableId) { + const sourceGroup = source.droppableId; // source group id + const destinationGroup = destination.droppableId; // destination group id + + if (!sourceGroup || !destinationGroup) return; + + if (selectedGroup === "priority") { + // update the removed item for mutation + draggedItem.priority = destinationGroup; + + if (cycleId) + mutate( + CYCLE_ISSUES(cycleId as string), + (prevData) => { + if (!prevData) return prevData; + const updatedIssues = prevData.map((issue) => { + if (issue.issue_detail.id === draggedItem.id) { + return { + ...issue, + issue_detail: { + ...draggedItem, + priority: destinationGroup, + }, + }; + } + return issue; + }); + return [...updatedIssues]; + }, + false + ); + + if (moduleId) + mutate( + MODULE_ISSUES(moduleId as string), + (prevData) => { + if (!prevData) return prevData; + const updatedIssues = prevData.map((issue) => { + if (issue.issue_detail.id === draggedItem.id) { + return { + ...issue, + issue_detail: { + ...draggedItem, + priority: destinationGroup, + }, + }; + } + return issue; + }); + return [...updatedIssues]; + }, + false + ); + + mutate( + PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string), + (prevData) => { + if (!prevData) return prevData; + + const updatedIssues = prevData.results.map((issue) => { + if (issue.id === draggedItem.id) + return { + ...draggedItem, + priority: destinationGroup, + }; + + return issue; + }); + + return { + ...prevData, + results: updatedIssues, + }; + }, + false + ); + + // patch request + issuesService + .patchIssue(workspaceSlug as string, projectId as string, draggedItem.id, { + priority: destinationGroup, + }) + .then((res) => { + mutate( + cycleId + ? CYCLE_ISSUES(cycleId as string) + : CYCLE_ISSUES(draggedItem.issue_cycle?.cycle ?? "") + ); + mutate( + moduleId + ? MODULE_ISSUES(moduleId as string) + : MODULE_ISSUES(draggedItem.issue_module?.module ?? "") + ); + mutate(PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string)); + }); + } else if (selectedGroup === "state_detail.name") { + const destinationState = states?.find((s) => s.name === destinationGroup); + const destinationStateId = destinationState?.id; + + // update the removed item for mutation + if (!destinationStateId || !destinationState) return; + draggedItem.state = destinationStateId; + draggedItem.state_detail = destinationState; + + if (cycleId) + mutate( + CYCLE_ISSUES(cycleId as string), + (prevData) => { + if (!prevData) return prevData; + const updatedIssues = prevData.map((issue) => { + if (issue.issue_detail.id === draggedItem.id) { + return { + ...issue, + issue_detail: { + ...draggedItem, + state_detail: destinationState, + state: destinationStateId, + }, + }; + } + return issue; + }); + return [...updatedIssues]; + }, + false + ); + + if (moduleId) + mutate( + MODULE_ISSUES(moduleId as string), + (prevData) => { + if (!prevData) return prevData; + const updatedIssues = prevData.map((issue) => { + if (issue.issue_detail.id === draggedItem.id) { + return { + ...issue, + issue_detail: { + ...draggedItem, + state_detail: destinationState, + state: destinationStateId, + }, + }; + } + return issue; + }); + return [...updatedIssues]; + }, + false + ); + + mutate( + PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string), + (prevData) => { + if (!prevData) return prevData; + + const updatedIssues = prevData.results.map((issue) => { + if (issue.id === draggedItem.id) + return { + ...draggedItem, + state_detail: destinationState, + state: destinationStateId, + }; + + return issue; + }); + + return { + ...prevData, + results: updatedIssues, + }; + }, + false + ); + + // patch request + issuesService + .patchIssue(workspaceSlug as string, projectId as string, draggedItem.id, { + state: destinationStateId, + }) + .then((res) => { + mutate( + cycleId + ? CYCLE_ISSUES(cycleId as string) + : CYCLE_ISSUES(draggedItem.issue_cycle?.cycle ?? "") + ); + mutate( + moduleId + ? MODULE_ISSUES(moduleId as string) + : MODULE_ISSUES(draggedItem.issue_module?.module ?? "") + ); + mutate(PROJECT_ISSUES_LIST(workspaceSlug as string, projectId as string)); + }); + } + } + } + }, + [ + workspaceSlug, + cycleId, + moduleId, + mutateState, + groupedByIssues, + projectId, + selectedGroup, + states, + ] + ); + + const addIssueToState = (groupTitle: string, stateId: string | null) => { + setCreateIssueModal(true); + if (selectedGroup) + setPreloadedData({ + state: stateId ?? undefined, + [selectedGroup]: groupTitle, + actionType: "createIssue", + }); + else setPreloadedData({ actionType: "createIssue" }); + }; + + const handleEditIssue = (issue: IIssue) => { + setEditIssueModal(true); + setIssueToEdit({ + ...issue, + actionType: "edit", + cycle: issue.issue_cycle ? issue.issue_cycle.cycle : null, + module: issue.issue_module ? issue.issue_module.module : null, + }); + }; + + const handleDeleteIssue = (issue: IIssue) => { + setDeleteIssueModal(true); + setIssueToDelete(issue); + }; + + return ( + <> + setCreateIssueModal(false)} + prePopulateData={{ + ...preloadedData, + }} + /> + setEditIssueModal(false)} + data={issueToEdit} + /> + setDeleteIssueModal(false)} + isOpen={deleteIssueModal} + data={issueToDelete} + /> + {issueView === "list" ? ( + + ) : ( + + )} + + ); +}; diff --git a/apps/app/components/core/list-view/all-lists.tsx b/apps/app/components/core/list-view/all-lists.tsx new file mode 100644 index 000000000..1f5ebcb2d --- /dev/null +++ b/apps/app/components/core/list-view/all-lists.tsx @@ -0,0 +1,60 @@ +// hooks +import useIssueView from "hooks/use-issue-view"; +// components +import { SingleList } from "components/core/list-view/single-list"; +// types +import { IIssue, IProjectMember, IState, UserAuth } from "types"; + +// types +type Props = { + type: "issue" | "cycle" | "module"; + issues: IIssue[]; + states: IState[] | undefined; + members: IProjectMember[] | undefined; + addIssueToState: (groupTitle: string, stateId: string | null) => void; + openIssuesListModal?: (() => void) | null; + handleEditIssue: (issue: IIssue) => void; + handleDeleteIssue: (issue: IIssue) => void; + userAuth: UserAuth; +}; + +export const AllLists: React.FC = ({ + type, + issues, + states, + members, + addIssueToState, + openIssuesListModal, + handleEditIssue, + handleDeleteIssue, + userAuth, +}) => { + const { groupedByIssues, groupByProperty: selectedGroup } = useIssueView(issues); + + return ( +
+ {Object.keys(groupedByIssues).map((singleGroup) => { + const stateId = + selectedGroup === "state_detail.name" + ? states?.find((s) => s.name === singleGroup)?.id ?? null + : null; + + return ( + addIssueToState(singleGroup, stateId)} + handleEditIssue={handleEditIssue} + handleDeleteIssue={handleDeleteIssue} + openIssuesListModal={type !== "issue" ? openIssuesListModal : null} + userAuth={userAuth} + /> + ); + })} +
+ ); +}; diff --git a/apps/app/components/core/list-view/index.ts b/apps/app/components/core/list-view/index.ts new file mode 100644 index 000000000..c515ed1c2 --- /dev/null +++ b/apps/app/components/core/list-view/index.ts @@ -0,0 +1,3 @@ +export * from "./all-lists"; +export * from "./single-issue"; +export * from "./single-list"; diff --git a/apps/app/components/core/list-view/single-issue.tsx b/apps/app/components/core/list-view/single-issue.tsx index 68c71ca1a..1f2995889 100644 --- a/apps/app/components/core/list-view/single-issue.tsx +++ b/apps/app/components/core/list-view/single-issue.tsx @@ -47,20 +47,20 @@ type Props = { properties: Properties; editIssue: () => void; removeIssue?: () => void; + handleDeleteIssue: (issue: IIssue) => void; userAuth: UserAuth; }; -const SingleListIssue: React.FC = ({ +export const SingleListIssue: React.FC = ({ type, typeId, issue, properties, editIssue, removeIssue, + handleDeleteIssue, userAuth, }) => { - const [deleteIssue, setDeleteIssue] = useState(); - const router = useRouter(); const { workspaceSlug, projectId } = router.query; @@ -151,284 +151,275 @@ const SingleListIssue: React.FC = ({ const isNotAllowed = userAuth.isGuest || userAuth.isViewer; return ( - <> - setDeleteIssue(undefined)} - isOpen={!!deleteIssue} - data={deleteIssue} - /> -
-
- + +
+ {properties.priority && ( + { + partialUpdateIssue({ priority: data }); }} - /> - - - {properties.key && ( - - {issue.project_detail?.identifier}-{issue.sequence_id} - - )} - {issue.name} - - -
-
- {properties.priority && ( - { - partialUpdateIssue({ priority: data }); - }} - className="group relative flex-shrink-0" - disabled={isNotAllowed} - > - {({ open }) => ( - <> -
- - {getPriorityIcon( - issue.priority && issue.priority !== "" ? issue.priority ?? "" : "None", - "text-sm" - )} - + className="group relative flex-shrink-0" + disabled={isNotAllowed} + > + {({ open }) => ( + <> +
+ + {getPriorityIcon( + issue.priority && issue.priority !== "" ? issue.priority ?? "" : "None", + "text-sm" + )} + - - - {PRIORITIES?.map((priority) => ( - - `flex cursor-pointer select-none items-center gap-x-2 px-3 py-2 capitalize ${ - active ? "bg-indigo-50" : "bg-white" - }` - } - value={priority} - > - {getPriorityIcon(priority, "text-sm")} - {priority ?? "None"} - - ))} - - + + + {PRIORITIES?.map((priority) => ( + + `flex cursor-pointer select-none items-center gap-x-2 px-3 py-2 capitalize ${ + active ? "bg-indigo-50" : "bg-white" + }` + } + value={priority} + > + {getPriorityIcon(priority, "text-sm")} + {priority ?? "None"} + + ))} + + +
+
+
Priority
+
+ {issue.priority ?? "None"}
-
-
Priority
-
- {issue.priority ?? "None"} -
-
- - )} - - )} - {properties.state && ( - + + )} + + )} + {properties.state && ( + + + {addSpaceIfCamelCase(issue.state_detail.name)} + + } + value={issue.state} + onChange={(data: string) => { + partialUpdateIssue({ state: data }); + }} + maxHeight="md" + noChevron + disabled={isNotAllowed} + > + {states?.map((state) => ( + <> - {addSpaceIfCamelCase(issue.state_detail.name)} + {addSpaceIfCamelCase(state.name)} - } - value={issue.state} - onChange={(data: string) => { - partialUpdateIssue({ state: data }); - }} - maxHeight="md" - noChevron - disabled={isNotAllowed} - > - {states?.map((state) => ( - - <> - - {addSpaceIfCamelCase(state.name)} - - - ))} - - )} - {/* {properties.cycle && !typeId && ( + + ))} + + )} + {/* {properties.cycle && !typeId && (
{issue.issue_cycle ? issue.issue_cycle.cycle_detail.name : "None"}
)} */} - {properties.due_date && ( -
- - partialUpdateIssue({ - target_date: val, - }) - } - className={issue?.target_date ? "w-[6.5rem]" : "w-[3rem] text-center"} - /> -
-
Due date
-
{renderShortNumericDateFormat(issue.target_date ?? "")}
-
- {issue.target_date - ? issue.target_date < new Date().toISOString() - ? `Due date has passed by ${findHowManyDaysLeft(issue.target_date)} days` - : findHowManyDaysLeft(issue.target_date) <= 3 - ? `Due date is in ${findHowManyDaysLeft(issue.target_date)} days` - : "Due date" - : "N/A"} -
+ {properties.due_date && ( +
+ + partialUpdateIssue({ + target_date: val, + }) + } + className={issue?.target_date ? "w-[6.5rem]" : "w-[3rem] text-center"} + /> +
+
Due date
+
{renderShortNumericDateFormat(issue.target_date ?? "")}
+
+ {issue.target_date + ? issue.target_date < new Date().toISOString() + ? `Due date has passed by ${findHowManyDaysLeft(issue.target_date)} days` + : findHowManyDaysLeft(issue.target_date) <= 3 + ? `Due date is in ${findHowManyDaysLeft(issue.target_date)} days` + : "Due date" + : "N/A"}
- )} - {properties.sub_issue_count && ( -
- {issue.sub_issues_count} {issue.sub_issues_count === 1 ? "sub-issue" : "sub-issues"} -
- )} - {properties.assignee && ( - { - const newData = issue.assignees ?? []; +
+ )} + {properties.sub_issue_count && ( +
+ {issue.sub_issues_count} {issue.sub_issues_count === 1 ? "sub-issue" : "sub-issues"} +
+ )} + {properties.assignee && ( + { + const newData = issue.assignees ?? []; - if (newData.includes(data)) newData.splice(newData.indexOf(data), 1); - else newData.push(data); + if (newData.includes(data)) newData.splice(newData.indexOf(data), 1); + else newData.push(data); - partialUpdateIssue({ assignees_list: newData }); - }} - className="group relative flex-shrink-0" - disabled={isNotAllowed} - > - {({ open }) => ( - <> -
- -
- -
-
- - + {({ open }) => ( + <> +
+ +
- - {people?.map((person) => ( - - `flex items-center gap-x-1 cursor-pointer select-none p-2 ${ - active ? "bg-indigo-50" : "" - } ${ - selected || issue.assignees?.includes(person.member.id) - ? "bg-indigo-50 font-medium" - : "font-normal" - }` - } - value={person.member.id} - > - -

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

-
- ))} -
- -
-
-
Assigned to
-
- {issue.assignee_details?.length > 0 - ? issue.assignee_details.map((assignee) => assignee.first_name).join(", ") - : "No one"} +
+ + + + + {people?.map((person) => ( + + `flex items-center gap-x-1 cursor-pointer select-none p-2 ${ + active ? "bg-indigo-50" : "" + } ${ + selected || issue.assignees?.includes(person.member.id) + ? "bg-indigo-50 font-medium" + : "font-normal" + }` + } + value={person.member.id} + > + +

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

+
+ ))} +
+
+
+
+
Assigned to
+
+ {issue.assignee_details?.length > 0 + ? issue.assignee_details.map((assignee) => assignee.first_name).join(", ") + : "No one"}
- - )} - - )} - {type && !isNotAllowed && ( - - Edit - {type !== "issue" && ( - - <>Remove from {type} - - )} - setDeleteIssue(issue)}> - Delete permanently +
+ + )} + + )} + {type && !isNotAllowed && ( + + Edit + {type !== "issue" && ( + + <>Remove from {type} - - )} -
+ )} + handleDeleteIssue(issue)}> + Delete permanently + + + )}
- +
); }; - -export default SingleListIssue; diff --git a/apps/app/components/core/list-view/single-list.tsx b/apps/app/components/core/list-view/single-list.tsx new file mode 100644 index 000000000..15299e1a5 --- /dev/null +++ b/apps/app/components/core/list-view/single-list.tsx @@ -0,0 +1,142 @@ +import { useRouter } from "next/router"; + +// headless ui +import { Disclosure, Transition } from "@headlessui/react"; +// hooks +import useIssuesProperties from "hooks/use-issue-properties"; +// components +import { SingleListIssue } from "components/core"; +// icons +import { ChevronDownIcon, PlusIcon } from "@heroicons/react/24/outline"; +// helpers +import { addSpaceIfCamelCase } from "helpers/string.helper"; +// types +import { IIssue, IProjectMember, NestedKeyOf, UserAuth } from "types"; + +type Props = { + type?: "issue" | "cycle" | "module"; + groupTitle: string; + groupedByIssues: { + [key: string]: IIssue[]; + }; + selectedGroup: NestedKeyOf | null; + members: IProjectMember[] | undefined; + addIssueToState: () => void; + handleEditIssue: (issue: IIssue) => void; + handleDeleteIssue: (issue: IIssue) => void; + openIssuesListModal?: (() => void) | null; + userAuth: UserAuth; +}; + +export const SingleList: React.FC = ({ + type = "issue", + groupTitle, + groupedByIssues, + selectedGroup, + members, + addIssueToState, + handleEditIssue, + handleDeleteIssue, + openIssuesListModal, + userAuth, +}) => { + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string); + + const createdBy = + selectedGroup === "created_by" + ? members?.find((m) => m.member.id === groupTitle)?.member.first_name ?? "loading..." + : null; + + return ( + + {({ open }) => ( +
+
+ +
+ + + + {selectedGroup !== null ? ( +

+ {groupTitle === null || groupTitle === "null" + ? "None" + : createdBy + ? createdBy + : addSpaceIfCamelCase(groupTitle)} +

+ ) : ( +

All Issues

+ )} +

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

+
+
+
+ + +
+ {groupedByIssues[groupTitle] ? ( + groupedByIssues[groupTitle].length > 0 ? ( + groupedByIssues[groupTitle].map((issue: IIssue) => { + const assignees = [ + ...(issue?.assignees_list ?? []), + ...(issue?.assignees ?? []), + ]?.map((assignee) => { + const tempPerson = members?.find((p) => p.member.id === assignee)?.member; + + return tempPerson; + }); + + return ( + handleEditIssue(issue)} + handleDeleteIssue={handleDeleteIssue} + userAuth={userAuth} + /> + ); + }) + ) : ( +

No issues.

+ ) + ) : ( +
Loading...
+ )} +
+
+
+
+ {type === "issue" ? ( + + ) : null} +
+
+ )} +
+ ); +}; diff --git a/apps/app/components/issues/delete-issue-modal.tsx b/apps/app/components/issues/delete-issue-modal.tsx index c64e4965b..cb13b789e 100644 --- a/apps/app/components/issues/delete-issue-modal.tsx +++ b/apps/app/components/issues/delete-issue-modal.tsx @@ -22,7 +22,7 @@ import { CYCLE_ISSUES, PROJECT_ISSUES_LIST, MODULE_ISSUES } from "constants/fetc type Props = { isOpen: boolean; handleClose: () => void; - data?: IIssue; + data: IIssue | null; }; export const DeleteIssueModal: React.FC = ({ isOpen, handleClose, data }) => { diff --git a/apps/app/components/issues/index.ts b/apps/app/components/issues/index.ts index f2c6a9c64..ab62034b5 100644 --- a/apps/app/components/issues/index.ts +++ b/apps/app/components/issues/index.ts @@ -4,7 +4,6 @@ export * from "./activity"; export * from "./delete-issue-modal"; export * from "./description-form"; export * from "./form"; -export * from "./list-view"; export * from "./modal"; export * from "./my-issues-list-item"; export * from "./parent-issues-list-modal"; diff --git a/apps/app/components/issues/list-view.tsx b/apps/app/components/issues/list-view.tsx deleted file mode 100644 index 2ba5d0b24..000000000 --- a/apps/app/components/issues/list-view.tsx +++ /dev/null @@ -1,182 +0,0 @@ -import React, { useState } from "react"; - -import { useRouter } from "next/router"; - -import useSWR from "swr"; - -import { Disclosure, Transition } from "@headlessui/react"; -// hooks -import { ChevronDownIcon, PlusIcon } from "@heroicons/react/24/outline"; -import useIssuesProperties from "hooks/use-issue-properties"; -import useIssueView from "hooks/use-issue-view"; -// services -import stateService from "services/state.service"; -import workspaceService from "services/workspace.service"; -// ui -import { Spinner } from "components/ui"; -// components -import { CreateUpdateIssueModal } from "components/issues/modal"; -import SingleListIssue from "components/core/list-view/single-issue"; -// helpers -import { addSpaceIfCamelCase } from "helpers/string.helper"; -// types -import { IIssue, IWorkspaceMember, UserAuth } from "types"; -// fetch-keys -import { STATE_LIST, WORKSPACE_MEMBERS } from "constants/fetch-keys"; - -// types -type Props = { - issues: IIssue[]; - handleEditIssue: (issue: IIssue) => void; - userAuth: UserAuth; -}; - -export const IssuesListView: React.FC = ({ issues, handleEditIssue, userAuth }) => { - const [isCreateIssuesModalOpen, setIsCreateIssuesModalOpen] = useState(false); - const [preloadedData, setPreloadedData] = useState< - (Partial & { actionType: "createIssue" | "edit" | "delete" }) | undefined - >(undefined); - - const router = useRouter(); - const { workspaceSlug, projectId } = router.query; - - const { issueView, groupedByIssues, groupByProperty: selectedGroup } = useIssueView(issues); - - const { data: states } = useSWR( - workspaceSlug && projectId ? STATE_LIST(projectId as string) : null, - workspaceSlug && projectId - ? () => stateService.getStates(workspaceSlug as string, projectId as string) - : null - ); - - const { data: people } = useSWR( - workspaceSlug ? WORKSPACE_MEMBERS : null, - workspaceSlug ? () => workspaceService.workspaceMembers(workspaceSlug as string) : null - ); - - const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string); - - if (issueView !== "list") return <>; - - return ( - <> - setIsCreateIssuesModalOpen(false)} - prePopulateData={{ - ...preloadedData, - }} - /> -
- {Object.keys(groupedByIssues).map((singleGroup) => ( - - {({ open }) => ( -
-
- -
- - - - {selectedGroup !== null ? ( -

- {singleGroup === null || singleGroup === "null" - ? selectedGroup === "priority" && "No priority" - : selectedGroup === "created_by" - ? people?.find((p) => p.member.id === singleGroup)?.member - ?.first_name ?? "Loading..." - : 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; - - return tempPerson; - }); - - return ( - handleEditIssue(issue)} - userAuth={userAuth} - /> - ); - }) - ) : ( -

No issues.

- ) - ) : ( -
- -
- )} -
-
-
-
- -
-
- )} -
- ))} -
- - ); -}; diff --git a/apps/app/components/issues/modal.tsx b/apps/app/components/issues/modal.tsx index 57c5bd62f..c7ff0c2d7 100644 --- a/apps/app/components/issues/modal.tsx +++ b/apps/app/components/issues/modal.tsx @@ -175,7 +175,7 @@ export const CreateUpdateIssueModal: React.FC = ({ .then((res) => { if (isUpdatingSingleIssue) { mutate(PROJECT_ISSUES_DETAILS, (prevData) => ({ ...prevData, ...res }), false); - } else + } else { mutate( PROJECT_ISSUES_LIST(workspaceSlug as string, activeProject ?? ""), (prevData) => ({ @@ -186,8 +186,10 @@ export const CreateUpdateIssueModal: React.FC = ({ }), }) ); + } if (payload.cycle && payload.cycle !== "") addIssueToCycle(res.id, payload.cycle); + if (payload.module && payload.module !== "") addIssueToModule(res.id, payload.module); if (!createMore) handleClose(); diff --git a/apps/app/components/issues/sidebar.tsx b/apps/app/components/issues/sidebar.tsx index 5868e884b..5f023e41e 100644 --- a/apps/app/components/issues/sidebar.tsx +++ b/apps/app/components/issues/sidebar.tsx @@ -130,7 +130,7 @@ export const IssueDetailsSidebar: React.FC = ({ setDeleteIssueModal(false)} isOpen={deleteIssueModal} - data={issueDetail} + data={issueDetail ?? null} />
diff --git a/apps/app/components/modules/index.ts b/apps/app/components/modules/index.ts index 3821cd16b..6f3121292 100644 --- a/apps/app/components/modules/index.ts +++ b/apps/app/components/modules/index.ts @@ -2,7 +2,6 @@ export * from "./select"; export * from "./sidebar-select"; export * from "./delete-module-modal"; export * from "./form"; -export * from "./list-view"; export * from "./modal"; export * from "./module-link-modal"; export * from "./sidebar"; diff --git a/apps/app/components/modules/list-view.tsx b/apps/app/components/modules/list-view.tsx deleted file mode 100644 index 884e00be6..000000000 --- a/apps/app/components/modules/list-view.tsx +++ /dev/null @@ -1,194 +0,0 @@ -import React from "react"; -import { useRouter } from "next/router"; -import useSWR from "swr"; - -// icons -import { Disclosure, Transition } from "@headlessui/react"; -import { PlusIcon, ChevronDownIcon } from "@heroicons/react/20/solid"; -// services -import workspaceService from "services/workspace.service"; -import stateService from "services/state.service"; -// hooks -import useIssuesProperties from "hooks/use-issue-properties"; -import useIssueView from "hooks/use-issue-view"; -// components -import SingleListIssue from "components/core/list-view/single-issue"; -// ui -import { CustomMenu, Spinner } from "components/ui"; -// helpers -import { addSpaceIfCamelCase } from "helpers/string.helper"; -// types -import { IIssue, IWorkspaceMember, UserAuth } from "types"; -// fetch-keys -import { STATE_LIST, WORKSPACE_MEMBERS } from "constants/fetch-keys"; - -type Props = { - issues: IIssue[]; - openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void; - openIssuesListModal: () => void; - removeIssueFromModule: (issueId: string) => void; - setPreloadedData: React.Dispatch< - React.SetStateAction< - | (Partial & { - actionType: "createIssue" | "edit" | "delete"; - }) - | null - > - >; - userAuth: UserAuth; -}; - -export const ModulesListView: React.FC = ({ - issues, - openCreateIssueModal, - openIssuesListModal, - removeIssueFromModule, - setPreloadedData, - userAuth, -}) => { - const router = useRouter(); - const { workspaceSlug, projectId, moduleId } = router.query; - - const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string); - const { issueView, groupedByIssues, groupByProperty: selectedGroup } = useIssueView(issues); - - const { data: people } = useSWR( - workspaceSlug ? WORKSPACE_MEMBERS : null, - workspaceSlug ? () => workspaceService.workspaceMembers(workspaceSlug as string) : null - ); - - const { data: states } = useSWR( - workspaceSlug && projectId ? STATE_LIST(projectId as string) : null, - workspaceSlug && projectId - ? () => stateService.getStates(workspaceSlug as string, projectId as string) - : null - ); - - if (issueView !== "list") return <>; - - return ( -
- {Object.keys(groupedByIssues).map((singleGroup) => { - const stateId = - selectedGroup === "state_detail.name" - ? states?.find((s) => s.name === singleGroup)?.id ?? null - : null; - - return ( - - {({ 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) => { - 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 ( - openCreateIssueModal(issue, "edit")} - removeIssue={() => removeIssueFromModule(issue.bridge ?? "")} - userAuth={userAuth} - /> - ); - }) - ) : ( -

No issues.

- ) - ) : ( -
- -
- )} -
-
-
-
- - - Add issue - - } - optionsPosition="left" - noBorder - > - { - 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/cycles/list-view/index.tsx b/apps/app/components/project/cycles/list-view/index.tsx deleted file mode 100644 index f3a435001..000000000 --- a/apps/app/components/project/cycles/list-view/index.tsx +++ /dev/null @@ -1,199 +0,0 @@ -import React from "react"; - -import { useRouter } from "next/router"; - -import useSWR from "swr"; - -// headless ui -import { Disclosure, Transition } from "@headlessui/react"; -// icons -import { PlusIcon, ChevronDownIcon } from "@heroicons/react/20/solid"; -// services -import workspaceService from "services/workspace.service"; -import stateService from "services/state.service"; -// hooks -import useIssuesProperties from "hooks/use-issue-properties"; -import useIssueView from "hooks/use-issue-view"; -// components -import SingleListIssue from "components/core/list-view/single-issue"; -// ui -import { CustomMenu, Spinner } from "components/ui"; -// helpers -import { addSpaceIfCamelCase } from "helpers/string.helper"; -// types -import { IIssue, IWorkspaceMember, UserAuth } from "types"; -// fetch-keys -import { WORKSPACE_MEMBERS, STATE_LIST } from "constants/fetch-keys"; - -type Props = { - issues: IIssue[]; - openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void; - openIssuesListModal: () => void; - removeIssueFromCycle: (bridgeId: string) => void; - setPreloadedData: React.Dispatch< - React.SetStateAction< - | (Partial & { - actionType: "createIssue" | "edit" | "delete"; - }) - | null - > - >; - userAuth: UserAuth; -}; - -const CyclesListView: React.FC = ({ - issues, - openCreateIssueModal, - openIssuesListModal, - removeIssueFromCycle, - setPreloadedData, - userAuth, -}) => { - const router = useRouter(); - const { workspaceSlug, projectId, cycleId } = router.query; - - const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string); - const { issueView, groupedByIssues, groupByProperty: selectedGroup } = useIssueView(issues); - - const { data: people } = useSWR( - workspaceSlug ? WORKSPACE_MEMBERS : null, - workspaceSlug ? () => workspaceService.workspaceMembers(workspaceSlug as string) : null - ); - - const { data: states } = useSWR( - workspaceSlug && projectId ? STATE_LIST(projectId as string) : null, - workspaceSlug && projectId - ? () => stateService.getStates(workspaceSlug as string, projectId as string) - : null - ); - - if (issueView !== "list") return <>; - - return ( -
- {Object.keys(groupedByIssues).map((singleGroup) => { - const stateId = - selectedGroup === "state_detail.name" - ? states?.find((s) => s.name === singleGroup)?.id ?? null - : null; - - return ( - - {({ 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) => { - 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 ( - openCreateIssueModal(issue, "edit")} - removeIssue={() => removeIssueFromCycle(issue.bridge ?? "")} - userAuth={userAuth} - /> - ); - }) - ) : ( -

No issues.

- ) - ) : ( -
- -
- )} -
-
-
-
- - - Add issue - - } - optionsPosition="left" - noBorder - > - { - openCreateIssueModal(); - if (selectedGroup !== null) { - setPreloadedData({ - state: stateId !== null ? stateId : undefined, - [selectedGroup]: singleGroup, - actionType: "createIssue", - }); - } - }} - > - Create new - - openIssuesListModal()}> - Add an existing issue - - -
-
- )} -
- ); - })} -
- ); -}; - -export default CyclesListView; diff --git a/apps/app/hooks/use-issue-view.tsx b/apps/app/hooks/use-issue-view.tsx index bb12fd392..516459d7e 100644 --- a/apps/app/hooks/use-issue-view.tsx +++ b/apps/app/hooks/use-issue-view.tsx @@ -65,6 +65,8 @@ const useIssueView = (projectIssues: IIssue[]) => { ...groupBy(projectIssues ?? [], groupByProperty ?? ""), }; + if (groupByProperty === "priority") delete groupedByIssues.None; + if (orderBy) { groupedByIssues = Object.fromEntries( Object.entries(groupedByIssues).map(([key, value]) => [ @@ -74,7 +76,7 @@ const useIssueView = (projectIssues: IIssue[]) => { ); } - if (filterIssue !== null) { + if (filterIssue) { if (filterIssue === "activeIssue") { const filteredStates = states?.filter( (state) => state.group === "started" || state.group === "unstarted" diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx index 87215fade..935a35913 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx @@ -11,11 +11,9 @@ import AppLayout from "layouts/app-layout"; // contexts import { IssueViewContextProvider } from "contexts/issue-view.context"; // components -import CyclesListView from "components/project/cycles/list-view"; -import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues"; -import { ExistingIssuesListModal, IssuesFilterView } from "components/core"; +import { CreateUpdateIssueModal } from "components/issues"; +import { ExistingIssuesListModal, IssuesFilterView, IssuesView } from "components/core"; import CycleDetailSidebar from "components/project/cycles/cycle-detail-sidebar"; -import { AllBoards } from "components/core/board-view/all-boards"; // services import issuesServices from "services/issues.service"; import cycleServices from "services/cycles.service"; @@ -42,7 +40,6 @@ const SingleCycle: React.FC = (props) => { const [isIssueModalOpen, setIsIssueModalOpen] = useState(false); const [selectedIssues, setSelectedIssues] = useState(); const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false); - const [deleteIssue, setDeleteIssue] = useState(undefined); const [cycleSidebar, setCycleSidebar] = useState(true); const [preloadedData, setPreloadedData] = useState< @@ -178,11 +175,6 @@ const SingleCycle: React.FC = (props) => { issues={issues?.results.filter((i) => !i.issue_cycle) ?? []} handleOnSubmit={handleAddIssuesToCycle} /> - setDeleteIssue(undefined)} - isOpen={!!deleteIssue} - data={issues?.results.find((issue) => issue.id === deleteIssue)} - /> @@ -234,21 +226,15 @@ const SingleCycle: React.FC = (props) => { {cycleIssuesArray ? ( cycleIssuesArray.length > 0 ? (
- - + /> */} +
) : (
= (props) => { const [selectedIssue, setSelectedIssue] = useState< (IIssue & { actionType: "edit" | "delete" }) | undefined >(undefined); - const [deleteIssue, setDeleteIssue] = useState(undefined); const { query: { workspaceSlug, projectId }, @@ -61,13 +59,14 @@ const ProjectIssues: NextPage = (props) => { } }, [isOpen]); - const handleEditIssue = (issue: IIssue) => { - setIsOpen(true); - setSelectedIssue({ ...issue, actionType: "edit" }); - }; - return ( + setIsOpen(false)} + data={selectedIssue} + /> @@ -93,34 +92,15 @@ const ProjectIssues: NextPage = (props) => {
} > - setIsOpen(false)} - data={selectedIssue} - /> - setDeleteIssue(undefined)} - isOpen={!!deleteIssue} - data={projectIssues?.results.find((issue) => issue.id === deleteIssue)} - /> {!projectIssues ? (
) : projectIssues.count > 0 ? ( - <> - p.parent === null) ?? []} - handleEditIssue={handleEditIssue} - userAuth={props} - /> - p.parent === null) ?? []} - handleDeleteIssue={setDeleteIssue} - userAuth={props} - /> - + p.parent === null) ?? []} + userAuth={props} + /> ) : (
= (props) => { const [selectedIssues, setSelectedIssues] = useState(null); const [moduleIssuesListModal, setModuleIssuesListModal] = useState(false); const [createUpdateIssueModal, setCreateUpdateIssueModal] = useState(false); - const [deleteIssue, setDeleteIssue] = useState(undefined); const [selectedModuleForDelete, setSelectedModuleForDelete] = useState(); const [preloadedData, setPreloadedData] = useState< (Partial & { actionType: "createIssue" | "edit" | "delete" }) | null @@ -189,11 +187,6 @@ const SingleModule: React.FC = (props) => { issues={issues?.results.filter((i) => !i.issue_module) ?? []} handleOnSubmit={handleAddIssuesToModule} /> - setDeleteIssue(undefined)} - isOpen={!!deleteIssue} - data={moduleIssuesArray?.find((issue) => issue.id === deleteIssue)} - /> = (props) => { {moduleIssuesArray ? ( moduleIssuesArray.length > 0 ? (
- - + /> */} +
) : (