refactor: created global component for kanban board

This commit is contained in:
Aaryan Khandelwal 2023-02-03 16:03:27 +05:30
parent 4f85773a48
commit 58eda658c8
31 changed files with 247 additions and 683 deletions

View File

@ -35,7 +35,6 @@ export const EmailCodeForm = ({ onSuccess }: any) => {
}); });
const onSubmit = ({ email }: EmailCodeFormValues) => { const onSubmit = ({ email }: EmailCodeFormValues) => {
console.log(email);
authenticationService authenticationService
.emailCode({ email }) .emailCode({ email })
.then((res) => { .then((res) => {

View File

@ -1,37 +1,38 @@
// TODO: Refactor this component: into a different file, use this file to export the components // TODO: Refactor this component: into a different file, use this file to export the components
import React, { useState, useCallback, useEffect } from "react"; import React, { useState, useCallback, useEffect } from "react";
// next
import { useRouter } from "next/router"; import { useRouter } from "next/router";
// swr
import useSWR from "swr"; import useSWR from "swr";
// hooks
// headless ui
import { Combobox, Dialog, Transition } from "@headlessui/react"; import { Combobox, Dialog, Transition } from "@headlessui/react";
// services
import userService from "services/user.service";
// hooks
import useTheme from "hooks/use-theme";
import useToast from "hooks/use-toast";
import useUser from "hooks/use-user";
// components
import ShortcutsModal from "components/command-palette/shortcuts";
import { BulkDeleteIssuesModal } from "components/core";
import { CreateProjectModal } from "components/project";
import { CreateUpdateIssueModal } from "components/issues";
import { CreateUpdateModuleModal } from "components/modules";
import CreateUpdateCycleModal from "components/project/cycles/create-update-cycle-modal";
// ui
import { Button } from "components/ui";
// icons
import { import {
FolderIcon, FolderIcon,
RectangleStackIcon, RectangleStackIcon,
ClipboardDocumentListIcon, ClipboardDocumentListIcon,
MagnifyingGlassIcon, MagnifyingGlassIcon,
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
import useTheme from "hooks/use-theme";
import useToast from "hooks/use-toast";
import useUser from "hooks/use-user";
// services
import userService from "services/user.service";
// components
import ShortcutsModal from "components/command-palette/shortcuts";
import { CreateProjectModal } from "components/project";
import { CreateUpdateIssueModal } from "components/issues";
import { CreateUpdateModuleModal } from "components/modules";
import CreateUpdateCycleModal from "components/project/cycles/create-update-cycle-modal";
import BulkDeleteIssuesModal from "components/common/bulk-delete-issues-modal";
// headless ui
// helpers // helpers
import { copyTextToClipboard } from "helpers/string.helper"; import { copyTextToClipboard } from "helpers/string.helper";
// types // types
import { IIssue } from "types"; import { IIssue } from "types";
// ui
import { Button } from "components/ui";
// icons
// fetch-keys // fetch-keys
import { USER_ISSUE } from "constants/fetch-keys"; import { USER_ISSUE } from "constants/fetch-keys";
@ -111,7 +112,6 @@ const CommandPalette: React.FC = () => {
if (!router.query.issueId) return; if (!router.query.issueId) return;
const url = new URL(window.location.href); const url = new URL(window.location.href);
console.log(url);
copyTextToClipboard(url.href) copyTextToClipboard(url.href)
.then(() => { .then(() => {
setToastAlert({ setToastAlert({

View File

@ -1,3 +0,0 @@
const SingleBoard = () => <></>;
export default SingleBoard;

View File

@ -22,7 +22,7 @@ type Props = {
selectedGroup: NestedKeyOf<IIssue> | null; selectedGroup: NestedKeyOf<IIssue> | null;
groupTitle: string; groupTitle: string;
createdBy: string | null; createdBy: string | null;
bgColor: string; bgColor?: string;
addIssueToState: () => void; addIssueToState: () => void;
provided?: DraggableProvided; provided?: DraggableProvided;
}; };

View File

@ -1,70 +1,68 @@
import React, { useState } from "react"; import { Dispatch, SetStateAction, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// react-beautiful-dnd // react-beautiful-dnd
import { Draggable } from "react-beautiful-dnd"; import StrictModeDroppable from "components/dnd/StrictModeDroppable";
import { Draggable, DraggableProvided, DraggableStateSnapshot } from "react-beautiful-dnd";
// services // services
import workspaceService from "services/workspace.service"; import workspaceService from "services/workspace.service";
// hooks
import useIssuesProperties from "hooks/use-issue-properties";
// components // components
import SingleIssue from "components/common/board-view/single-issue"; import BoardHeader from "components/core/board-view/board-header";
import StrictModeDroppable from "components/dnd/StrictModeDroppable"; import SingleIssue from "components/core/board-view/single-issue";
import BoardHeader from "components/common/board-view/board-header";
// ui
import { CustomMenu } from "components/ui";
// icons // icons
import { PlusIcon } from "@heroicons/react/24/outline"; import { PlusIcon } from "@heroicons/react/24/outline";
// types // types
import { IIssue, IWorkspaceMember, NestedKeyOf, Properties, UserAuth } from "types"; import { IIssue, IWorkspaceMember, NestedKeyOf, UserAuth } from "types";
// fetch-keys // fetch-keys
import { WORKSPACE_MEMBERS } from "constants/fetch-keys"; import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
type Props = { type Props = {
properties: Properties; provided?: DraggableProvided;
snapshot?: DraggableStateSnapshot;
bgColor?: string;
groupTitle: string;
groupedByIssues: { groupedByIssues: {
[key: string]: IIssue[]; [key: string]: IIssue[];
}; };
selectedGroup: NestedKeyOf<IIssue> | null; selectedGroup: NestedKeyOf<IIssue> | null;
groupTitle: string; addIssueToState: () => void;
createdBy: string | null; handleDeleteIssue?: Dispatch<SetStateAction<string | undefined>> | undefined;
bgColor?: string;
openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void;
openIssuesListModal: () => void;
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
setPreloadedData: React.Dispatch<
React.SetStateAction<
| (Partial<IIssue> & {
actionType: "createIssue" | "edit" | "delete";
})
| null
>
>;
stateId: string | null;
userAuth: UserAuth; userAuth: UserAuth;
}; };
const SingleModuleBoard: React.FC<Props> = ({ export const CommonSingleBoard: React.FC<Props> = ({
properties, provided,
snapshot,
bgColor,
groupTitle,
groupedByIssues, groupedByIssues,
selectedGroup, selectedGroup,
groupTitle, addIssueToState,
createdBy,
bgColor,
openCreateIssueModal,
openIssuesListModal,
handleDeleteIssue, handleDeleteIssue,
setPreloadedData,
stateId,
userAuth, userAuth,
}) => { }) => {
// collapse/expand // collapse/expand
const [isCollapsed, setIsCollapsed] = useState(true); const [isCollapsed, setIsCollapsed] = useState(true);
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query;
const { workspaceSlug, cycleId } = router.query; const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string);
const { data: members } = useSWR<IWorkspaceMember[]>(
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..."
: null;
if (selectedGroup === "priority") if (selectedGroup === "priority")
groupTitle === "high" groupTitle === "high"
@ -75,32 +73,25 @@ const SingleModuleBoard: React.FC<Props> = ({
? (bgColor = "#22c55e") ? (bgColor = "#22c55e")
: (bgColor = "#ff0000"); : (bgColor = "#ff0000");
const { data: people } = useSWR<IWorkspaceMember[]>(
workspaceSlug ? WORKSPACE_MEMBERS : null,
workspaceSlug ? () => workspaceService.workspaceMembers(workspaceSlug as string) : null
);
return ( return (
<div className={`h-full flex-shrink-0 rounded ${!isCollapsed ? "" : "w-80 border bg-gray-50"}`}> <div
className={`h-full flex-shrink-0 rounded ${
snapshot && snapshot.isDragging ? "border-theme shadow-lg" : ""
} ${!isCollapsed ? "" : "w-80 border bg-gray-50"}`}
ref={provided?.innerRef}
{...provided?.draggableProps}
>
<div className={`${!isCollapsed ? "" : "flex h-full flex-col space-y-3 overflow-y-auto"}`}> <div className={`${!isCollapsed ? "" : "flex h-full flex-col space-y-3 overflow-y-auto"}`}>
<BoardHeader <BoardHeader
addIssueToState={() => { addIssueToState={addIssueToState}
openCreateIssueModal(); bgColor={bgColor}
if (selectedGroup !== null) {
setPreloadedData({
state: stateId !== null ? stateId : undefined,
[selectedGroup]: groupTitle,
actionType: "createIssue",
});
}
}}
bgColor={bgColor ?? ""}
createdBy={createdBy} createdBy={createdBy}
groupTitle={groupTitle} groupTitle={groupTitle}
groupedByIssues={groupedByIssues} groupedByIssues={groupedByIssues}
isCollapsed={isCollapsed} isCollapsed={isCollapsed}
setIsCollapsed={setIsCollapsed} setIsCollapsed={setIsCollapsed}
selectedGroup={selectedGroup} selectedGroup={selectedGroup}
provided={provided}
/> />
<StrictModeDroppable key={groupTitle} droppableId={groupTitle}> <StrictModeDroppable key={groupTitle} droppableId={groupTitle}>
{(provided, snapshot) => ( {(provided, snapshot) => (
@ -116,7 +107,7 @@ const SingleModuleBoard: React.FC<Props> = ({
...(childIssue?.assignees_list ?? []), ...(childIssue?.assignees_list ?? []),
...(childIssue?.assignees ?? []), ...(childIssue?.assignees ?? []),
]?.map((assignee) => { ]?.map((assignee) => {
const tempPerson = people?.find((p) => p.member.id === assignee)?.member; const tempPerson = members?.find((p) => p.member.id === assignee)?.member;
return tempPerson; return tempPerson;
}); });
@ -130,13 +121,11 @@ const SingleModuleBoard: React.FC<Props> = ({
{...provided.dragHandleProps} {...provided.dragHandleProps}
> >
<SingleIssue <SingleIssue
type="cycle"
typeId={cycleId as string}
issue={childIssue} issue={childIssue}
properties={properties} properties={properties}
snapshot={snapshot} snapshot={snapshot}
people={members}
assignees={assignees} assignees={assignees}
people={people}
handleDeleteIssue={handleDeleteIssue} handleDeleteIssue={handleDeleteIssue}
userAuth={userAuth} userAuth={userAuth}
/> />
@ -146,35 +135,14 @@ const SingleModuleBoard: React.FC<Props> = ({
); );
})} })}
{provided.placeholder} {provided.placeholder}
<CustomMenu {/* <button
label={ type="button"
<span className="flex items-center gap-1"> className="flex items-center rounded p-2 text-xs font-medium outline-none duration-300 hover:bg-gray-100"
<PlusIcon className="h-3 w-3" /> onClick={addIssueToState}
Add issue
</span>
}
className="mt-1"
optionsPosition="left"
noBorder
> >
<CustomMenu.MenuItem <PlusIcon className="mr-1 h-3 w-3" />
onClick={() => { Create
openCreateIssueModal(); </button> */}
if (selectedGroup !== null) {
setPreloadedData({
state: stateId !== null ? stateId : undefined,
[selectedGroup]: groupTitle,
actionType: "createIssue",
});
}
}}
>
Create new
</CustomMenu.MenuItem>
<CustomMenu.MenuItem onClick={() => openIssuesListModal()}>
Add an existing issue
</CustomMenu.MenuItem>
</CustomMenu>
</div> </div>
)} )}
</StrictModeDroppable> </StrictModeDroppable>
@ -182,5 +150,3 @@ const SingleModuleBoard: React.FC<Props> = ({
</div> </div>
); );
}; };
export default SingleModuleBoard;

View File

@ -8,7 +8,7 @@ import useSWR, { mutate } from "swr";
import { SubmitHandler, useForm } from "react-hook-form"; import { SubmitHandler, useForm } from "react-hook-form";
// services // services
import { Combobox, Dialog, Transition } from "@headlessui/react"; import { Combobox, Dialog, Transition } from "@headlessui/react";
import { FolderIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline"; import { MagnifyingGlassIcon } from "@heroicons/react/24/outline";
import issuesServices from "services/issues.service"; import issuesServices from "services/issues.service";
import projectService from "services/project.service"; import projectService from "services/project.service";
// hooks // hooks
@ -32,7 +32,7 @@ type Props = {
setIsOpen: React.Dispatch<React.SetStateAction<boolean>>; setIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
}; };
const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) => { export const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
const [query, setQuery] = useState(""); const [query, setQuery] = useState("");
const router = useRouter(); const router = useRouter();
@ -256,5 +256,3 @@ const BulkDeleteIssuesModal: React.FC<Props> = ({ isOpen, setIsOpen }) => {
</Transition.Root> </Transition.Root>
); );
}; };
export default BulkDeleteIssuesModal;

View File

@ -32,7 +32,7 @@ type Props = {
handleOnSubmit: any; handleOnSubmit: any;
}; };
const ExistingIssuesListModal: React.FC<Props> = ({ export const ExistingIssuesListModal: React.FC<Props> = ({
isOpen, isOpen,
handleClose: onClose, handleClose: onClose,
issues, issues,
@ -233,5 +233,3 @@ const ExistingIssuesListModal: React.FC<Props> = ({
</> </>
); );
}; };
export default ExistingIssuesListModal;

View File

@ -1 +1,5 @@
export * from "./not-authorized-view"; export * from "./not-authorized-view";
export * from "./bulk-delete-issues-modal";
export * from "./existing-issues-list-modal";
export * from "./image-upload-modal";
export * from "./view";

View File

@ -23,7 +23,7 @@ type Props = {
issues?: IIssue[]; issues?: IIssue[];
}; };
const View: React.FC<Props> = ({ issues }) => { export const IssuesFilterView: React.FC<Props> = ({ issues }) => {
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId } = router.query; const { workspaceSlug, projectId } = router.query;
@ -203,5 +203,3 @@ const View: React.FC<Props> = ({ issues }) => {
</> </>
); );
}; };
export default View;

View File

@ -5,16 +5,14 @@ import { useRouter } from "next/router";
import useSWR, { mutate } from "swr"; import useSWR, { mutate } from "swr";
// react-beautiful-dnd // react-beautiful-dnd
import { DragDropContext, DropResult } from "react-beautiful-dnd"; import { DragDropContext, Draggable, DropResult } from "react-beautiful-dnd";
// hook // hook
import useIssuesProperties from "hooks/use-issue-properties";
import useIssueView from "hooks/use-issue-view"; import useIssueView from "hooks/use-issue-view";
// services // services
import stateServices from "services/state.service"; import stateServices from "services/state.service";
import issuesServices from "services/issues.service"; import issuesServices from "services/issues.service";
import projectService from "services/project.service";
// components // components
import SingleBoard from "components/issues/board-view/single-board"; import { CommonSingleBoard } from "components/core/board-view/single-board";
import StrictModeDroppable from "components/dnd/StrictModeDroppable"; import StrictModeDroppable from "components/dnd/StrictModeDroppable";
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues"; import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
// ui // ui
@ -22,7 +20,7 @@ import { Spinner } from "components/ui";
// types // types
import type { IState, IIssue, IssueResponse, UserAuth } from "types"; import type { IState, IIssue, IssueResponse, UserAuth } from "types";
// fetch-keys // fetch-keys
import { STATE_LIST, PROJECT_ISSUES_LIST, PROJECT_MEMBERS } from "constants/fetch-keys"; import { STATE_LIST, PROJECT_ISSUES_LIST } from "constants/fetch-keys";
type Props = { type Props = {
issues: IIssue[]; issues: IIssue[];
@ -30,7 +28,7 @@ type Props = {
userAuth: UserAuth; userAuth: UserAuth;
}; };
const BoardView: React.FC<Props> = ({ issues, handleDeleteIssue, userAuth }) => { export const IssuesBoardView: React.FC<Props> = ({ issues, handleDeleteIssue, userAuth }) => {
const [createIssueModal, setCreateIssueModal] = useState(false); const [createIssueModal, setCreateIssueModal] = useState(false);
const [isIssueDeletionOpen, setIsIssueDeletionOpen] = useState(false); const [isIssueDeletionOpen, setIsIssueDeletionOpen] = useState(false);
const [issueDeletionData, setIssueDeletionData] = useState<IIssue | undefined>(); const [issueDeletionData, setIssueDeletionData] = useState<IIssue | undefined>();
@ -50,21 +48,6 @@ const BoardView: React.FC<Props> = ({ issues, handleDeleteIssue, userAuth }) =>
: null : null
); );
const { data: members } = useSWR(
workspaceSlug && projectId ? PROJECT_MEMBERS(projectId as string) : null,
workspaceSlug && projectId
? () => projectService.projectMembers(workspaceSlug as string, projectId as string)
: null,
{
onErrorRetry(err, _, __, revalidate, revalidateOpts) {
if (err?.status === 403) return;
setTimeout(() => revalidate(revalidateOpts), 5000);
},
}
);
const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string);
const handleOnDragEnd = useCallback( const handleOnDragEnd = useCallback(
(result: DropResult) => { (result: DropResult) => {
if (!result.destination || !workspaceSlug || !projectId) return; if (!result.destination || !workspaceSlug || !projectId) return;
@ -208,36 +191,42 @@ const BoardView: React.FC<Props> = ({ issues, handleDeleteIssue, userAuth }) =>
ref={provided.innerRef} ref={provided.innerRef}
> >
<div className="flex h-full gap-x-4 overflow-x-auto overflow-y-hidden"> <div className="flex h-full gap-x-4 overflow-x-auto overflow-y-hidden">
{Object.keys(groupedByIssues).map((singleGroup, index) => ( {Object.keys(groupedByIssues).map((singleGroup, index) => {
<SingleBoard const stateId =
key={singleGroup} selectedGroup === "state_detail.name"
selectedGroup={selectedGroup} ? states?.find((s) => s.name === singleGroup)?.id ?? null
groupTitle={singleGroup} : null;
createdBy={
selectedGroup === "created_by" return (
? members?.find((m) => m.member.id === singleGroup)?.member <Draggable key={singleGroup} draggableId={singleGroup} index={index}>
.first_name ?? "loading..." {(provided, snapshot) => (
: null <CommonSingleBoard
} provided={provided}
groupedByIssues={groupedByIssues} snapshot={snapshot}
index={index} bgColor={
setIsIssueOpen={setCreateIssueModal} selectedGroup === "state_detail.name"
properties={properties} ? states?.find((s) => s.name === singleGroup)?.color
setPreloadedData={setPreloadedData} : "#000000"
stateId={ }
selectedGroup === "state_detail.name" groupTitle={singleGroup}
? states?.find((s) => s.name === singleGroup)?.id ?? null groupedByIssues={groupedByIssues}
: null selectedGroup={selectedGroup}
} addIssueToState={() => {
bgColor={ setCreateIssueModal(true);
selectedGroup === "state_detail.name" if (selectedGroup)
? states?.find((s) => s.name === singleGroup)?.color setPreloadedData({
: "#000000" state: stateId !== null ? stateId : undefined,
} [selectedGroup]: singleGroup,
handleDeleteIssue={handleDeleteIssue} actionType: "createIssue",
userAuth={userAuth} });
/> }}
))} handleDeleteIssue={handleDeleteIssue}
userAuth={userAuth}
/>
)}
</Draggable>
);
})}
</div> </div>
{provided.placeholder} {provided.placeholder}
</div> </div>
@ -254,5 +243,3 @@ const BoardView: React.FC<Props> = ({ issues, handleDeleteIssue, userAuth }) =>
</> </>
); );
}; };
export default BoardView;

View File

@ -1,174 +0,0 @@
import React, { useState } from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
// react-beautiful-dnd
import { Draggable } from "react-beautiful-dnd";
// components
import StrictModeDroppable from "components/dnd/StrictModeDroppable";
import SingleIssue from "components/common/board-view/single-issue";
import BoardHeader from "components/common/board-view/board-header";
// icons
import { PlusIcon } from "@heroicons/react/24/outline";
// services
import workspaceService from "services/workspace.service";
// types
import { IIssue, Properties, NestedKeyOf, IWorkspaceMember, UserAuth } from "types";
// fetch-keys
import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
type Props = {
selectedGroup: NestedKeyOf<IIssue> | null;
groupTitle: string;
groupedByIssues: {
[key: string]: IIssue[];
};
index: number;
setIsIssueOpen: React.Dispatch<React.SetStateAction<boolean>>;
properties: Properties;
setPreloadedData: React.Dispatch<
React.SetStateAction<
| (Partial<IIssue> & {
actionType: "createIssue" | "edit" | "delete";
})
| undefined
>
>;
bgColor?: string;
stateId: string | null;
createdBy: string | null;
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
userAuth: UserAuth;
};
const SingleBoard: React.FC<Props> = ({
selectedGroup,
groupTitle,
groupedByIssues,
index,
setIsIssueOpen,
properties,
setPreloadedData,
bgColor = "#0f2b16",
stateId,
createdBy,
handleDeleteIssue,
userAuth,
}) => {
// Collapse/Expand
const [isCollapsed, setIsCollapsed] = useState(true);
const router = useRouter();
const { workspaceSlug } = router.query;
if (selectedGroup === "priority")
groupTitle === "high"
? (bgColor = "#dc2626")
: groupTitle === "medium"
? (bgColor = "#f97316")
: groupTitle === "low"
? (bgColor = "#22c55e")
: (bgColor = "#ff0000");
const { data: people } = useSWR<IWorkspaceMember[]>(
workspaceSlug ? WORKSPACE_MEMBERS : null,
workspaceSlug ? () => workspaceService.workspaceMembers(workspaceSlug as string) : null
);
const addIssueToState = () => {
setIsIssueOpen(true);
if (selectedGroup)
setPreloadedData({
state: stateId !== null ? stateId : undefined,
[selectedGroup]: groupTitle,
actionType: "createIssue",
});
};
return (
<Draggable draggableId={groupTitle} index={index}>
{(provided, snapshot) => (
<div
className={`h-full flex-shrink-0 rounded ${
snapshot.isDragging ? "border-theme shadow-lg" : ""
} ${!isCollapsed ? "" : "w-80 border bg-gray-50"}`}
ref={provided.innerRef}
{...provided.draggableProps}
>
<div
className={`${!isCollapsed ? "" : "flex h-full flex-col space-y-3 overflow-y-auto"}`}
>
<BoardHeader
addIssueToState={addIssueToState}
bgColor={bgColor}
createdBy={createdBy}
groupTitle={groupTitle}
groupedByIssues={groupedByIssues}
isCollapsed={isCollapsed}
setIsCollapsed={setIsCollapsed}
selectedGroup={selectedGroup}
provided={provided}
/>
<StrictModeDroppable key={groupTitle} droppableId={groupTitle}>
{(provided, snapshot) => (
<div
className={`mt-3 h-full space-y-3 overflow-y-auto px-3 pb-3 ${
snapshot.isDraggingOver ? "bg-indigo-50 bg-opacity-50" : ""
} ${!isCollapsed ? "hidden" : "block"}`}
{...provided.droppableProps}
ref={provided.innerRef}
>
{groupedByIssues[groupTitle].map((childIssue, index: number) => {
const assignees = [
...(childIssue?.assignees_list ?? []),
...(childIssue?.assignees ?? []),
]?.map((assignee) => {
const tempPerson = people?.find((p) => p.member.id === assignee)?.member;
return tempPerson;
});
return (
<Draggable key={childIssue.id} draggableId={childIssue.id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<SingleIssue
issue={childIssue}
properties={properties}
snapshot={snapshot}
people={people}
assignees={assignees}
handleDeleteIssue={handleDeleteIssue}
userAuth={userAuth}
/>
</div>
)}
</Draggable>
);
})}
{provided.placeholder}
<button
type="button"
className="flex items-center rounded p-2 text-xs font-medium outline-none duration-300 hover:bg-gray-100"
onClick={addIssueToState}
>
<PlusIcon className="mr-1 h-3 w-3" />
Create
</button>
</div>
)}
</StrictModeDroppable>
</div>
</div>
)}
</Draggable>
);
};
export default SingleBoard;

View File

@ -1,7 +1,7 @@
export * from "./board-view";
export * from "./comment"; export * from "./comment";
export * from "./sidebar-select"; export * from "./sidebar-select";
export * from "./activity"; export * from "./activity";
export * from "./board-view";
export * from "./delete-issue-modal"; export * from "./delete-issue-modal";
export * from "./description-form"; export * from "./description-form";
export * from "./form"; export * from "./form";

View File

@ -16,7 +16,7 @@ import workspaceService from "services/workspace.service";
import { Spinner } from "components/ui"; import { Spinner } from "components/ui";
// components // components
import { CreateUpdateIssueModal } from "components/issues/modal"; import { CreateUpdateIssueModal } from "components/issues/modal";
import SingleListIssue from "components/common/list-view/single-issue"; import SingleListIssue from "components/core/list-view/single-issue";
// helpers // helpers
import { addSpaceIfCamelCase } from "helpers/string.helper"; import { addSpaceIfCamelCase } from "helpers/string.helper";
// types // types

View File

@ -209,6 +209,8 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
const payload: Partial<IIssue> = { const payload: Partial<IIssue> = {
...formData, ...formData,
description: formData.description ? formData.description : "",
description_html: formData.description_html ? formData.description_html : "<p></p>",
}; };
if (!data) await createIssue(payload); if (!data) await createIssue(payload);

View File

@ -177,7 +177,6 @@ export const SidebarBlockedSelect: React.FC<Props> = ({
<form> <form>
<Combobox <Combobox
onChange={(val: string) => { onChange={(val: string) => {
console.log("Triggered");
const selectedIssues = watchBlocked("blocked_issue_ids"); const selectedIssues = watchBlocked("blocked_issue_ids");
if (selectedIssues.includes(val)) if (selectedIssues.includes(val))
setValue( setValue(

View File

@ -151,7 +151,7 @@ export const SubIssuesListModal: React.FC<Props> = ({ isOpen, handleClose, paren
}} }}
> >
<span <span
className={`block h-1.5 w-1.5 rounded-full`} className="block flex-shrink-0 h-1.5 w-1.5 rounded-full"
style={{ style={{
backgroundColor: issue.state_detail.color, backgroundColor: issue.state_detail.color,
}} }}

View File

@ -113,7 +113,7 @@ export const SubIssuesList: FC<SubIssueListProps> = ({
<Link href={`/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`}> <Link href={`/${workspaceSlug}/projects/${projectId}/issues/${issue.id}`}>
<a className="flex items-center gap-2 rounded text-xs"> <a className="flex items-center gap-2 rounded text-xs">
<span <span
className={`block h-1.5 w-1.5 rounded-full`} className="block flex-shrink-0 h-1.5 w-1.5 rounded-full"
style={{ style={{
backgroundColor: issue.state_detail.color, backgroundColor: issue.state_detail.color,
}} }}

View File

@ -10,20 +10,18 @@ import { DragDropContext, DropResult } from "react-beautiful-dnd";
import stateService from "services/state.service"; import stateService from "services/state.service";
import issuesService from "services/issues.service"; import issuesService from "services/issues.service";
// hooks // hooks
import useIssuesProperties from "hooks/use-issue-properties";
import useIssueView from "hooks/use-issue-view"; import useIssueView from "hooks/use-issue-view";
// components // components
import SingleBoard from "components/modules/board-view/single-board"; import { CommonSingleBoard } from "components/core/board-view/single-board";
// ui // ui
import { Spinner } from "components/ui"; import { Spinner } from "components/ui";
// types // types
import { IIssue, IProjectMember, ModuleIssueResponse, UserAuth } from "types"; import { IIssue, ModuleIssueResponse, UserAuth } from "types";
// constants // constants
import { STATE_LIST, MODULE_ISSUES } from "constants/fetch-keys"; import { STATE_LIST, MODULE_ISSUES } from "constants/fetch-keys";
type Props = { type Props = {
issues: IIssue[]; issues: IIssue[];
members: IProjectMember[] | undefined;
openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void; openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void;
openIssuesListModal: () => void; openIssuesListModal: () => void;
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>; handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
@ -38,9 +36,8 @@ type Props = {
userAuth: UserAuth; userAuth: UserAuth;
}; };
const ModulesBoardView: React.FC<Props> = ({ export const ModulesBoardView: React.FC<Props> = ({
issues, issues,
members,
openCreateIssueModal, openCreateIssueModal,
openIssuesListModal, openIssuesListModal,
handleDeleteIssue, handleDeleteIssue,
@ -50,7 +47,6 @@ const ModulesBoardView: React.FC<Props> = ({
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, moduleId } = router.query; const { workspaceSlug, projectId, moduleId } = router.query;
const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string);
const { issueView, groupedByIssues, groupByProperty: selectedGroup } = useIssueView(issues); const { issueView, groupedByIssues, groupByProperty: selectedGroup } = useIssueView(issues);
const { data: states } = useSWR( const { data: states } = useSWR(
@ -134,36 +130,39 @@ const ModulesBoardView: React.FC<Props> = ({
<div className="h-full w-full overflow-hidden"> <div className="h-full w-full overflow-hidden">
<div className="h-full w-full"> <div className="h-full w-full">
<div className="flex h-full gap-x-4 overflow-x-auto overflow-y-hidden"> <div className="flex h-full gap-x-4 overflow-x-auto overflow-y-hidden">
{Object.keys(groupedByIssues).map((singleGroup) => ( {Object.keys(groupedByIssues).map((singleGroup) => {
<SingleBoard const stateId =
key={singleGroup} selectedGroup === "state_detail.name"
selectedGroup={selectedGroup} ? states?.find((s) => s.name === singleGroup)?.id ?? null
groupTitle={singleGroup} : null;
createdBy={
selectedGroup === "created_by" return (
? members?.find((m) => m.member.id === singleGroup)?.member.first_name ?? <CommonSingleBoard
"loading..." key={singleGroup}
: null bgColor={
} selectedGroup === "state_detail.name"
groupedByIssues={groupedByIssues} ? states?.find((s) => s.name === singleGroup)?.color
bgColor={ : "#000000"
selectedGroup === "state_detail.name" }
? states?.find((s) => s.name === singleGroup)?.color groupTitle={singleGroup}
: "#000000" groupedByIssues={groupedByIssues}
} selectedGroup={selectedGroup}
properties={properties} addIssueToState={() => {
openIssuesListModal={openIssuesListModal} openCreateIssueModal();
openCreateIssueModal={openCreateIssueModal} if (selectedGroup !== null) {
handleDeleteIssue={handleDeleteIssue} setPreloadedData({
setPreloadedData={setPreloadedData} state: stateId !== null ? stateId : undefined,
stateId={ [selectedGroup]: singleGroup,
selectedGroup === "state_detail.name" actionType: "createIssue",
? states?.find((s) => s.name === singleGroup)?.id ?? null });
: null }
} }}
userAuth={userAuth} // openIssuesListModal={openIssuesListModal}
/> handleDeleteIssue={handleDeleteIssue}
))} userAuth={userAuth}
/>
);
})}
</div> </div>
</div> </div>
</div> </div>
@ -177,5 +176,3 @@ const ModulesBoardView: React.FC<Props> = ({
</> </>
); );
}; };
export default ModulesBoardView;

View File

@ -1,185 +0,0 @@
import React, { useState } from "react";
import { useRouter } from "next/router";
import useSWR from "swr";
// react-beautiful-dnd
import { Draggable } from "react-beautiful-dnd";
// services
import workspaceService from "services/workspace.service";
// components
import SingleIssue from "components/common/board-view/single-issue";
import StrictModeDroppable from "components/dnd/StrictModeDroppable";
import BoardHeader from "components/common/board-view/board-header";
// ui
import { CustomMenu } from "components/ui";
// icons
import { PlusIcon } from "@heroicons/react/24/outline";
// types
import { IIssue, IWorkspaceMember, NestedKeyOf, Properties, UserAuth } from "types";
// fetch-keys
import { WORKSPACE_MEMBERS } from "constants/fetch-keys";
type Props = {
properties: Properties;
groupedByIssues: {
[key: string]: IIssue[];
};
selectedGroup: NestedKeyOf<IIssue> | null;
groupTitle: string;
createdBy: string | null;
bgColor?: string;
openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void;
openIssuesListModal: () => void;
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
setPreloadedData: React.Dispatch<
React.SetStateAction<
| (Partial<IIssue> & {
actionType: "createIssue" | "edit" | "delete";
})
| null
>
>;
stateId: string | null;
userAuth: UserAuth;
};
const SingleModuleBoard: React.FC<Props> = ({
properties,
groupedByIssues,
selectedGroup,
groupTitle,
createdBy,
bgColor,
openCreateIssueModal,
openIssuesListModal,
handleDeleteIssue,
setPreloadedData,
stateId,
userAuth,
}) => {
// Collapse/Expand
const [isCollapsed, setIsCollapsed] = useState(true);
const router = useRouter();
const { workspaceSlug, moduleId } = router.query;
if (selectedGroup === "priority")
groupTitle === "high"
? (bgColor = "#dc2626")
: groupTitle === "medium"
? (bgColor = "#f97316")
: groupTitle === "low"
? (bgColor = "#22c55e")
: (bgColor = "#ff0000");
const { data: people } = useSWR<IWorkspaceMember[]>(
workspaceSlug ? WORKSPACE_MEMBERS : null,
workspaceSlug ? () => workspaceService.workspaceMembers(workspaceSlug as string) : null
);
return (
<div className={`h-full flex-shrink-0 rounded ${!isCollapsed ? "" : "w-80 border bg-gray-50"}`}>
<div className={`${!isCollapsed ? "" : "flex h-full flex-col space-y-3 overflow-y-auto"}`}>
<BoardHeader
addIssueToState={() => {
openCreateIssueModal();
if (selectedGroup !== null) {
setPreloadedData({
state: stateId !== null ? stateId : undefined,
[selectedGroup]: groupTitle,
actionType: "createIssue",
});
}
}}
bgColor={bgColor ?? ""}
createdBy={createdBy}
groupTitle={groupTitle}
groupedByIssues={groupedByIssues}
isCollapsed={isCollapsed}
setIsCollapsed={setIsCollapsed}
selectedGroup={selectedGroup}
/>
<StrictModeDroppable key={groupTitle} droppableId={groupTitle}>
{(provided, snapshot) => (
<div
className={`mt-3 h-full space-y-3 overflow-y-auto px-3 pb-3 ${
snapshot.isDraggingOver ? "bg-indigo-50 bg-opacity-50" : ""
} ${!isCollapsed ? "hidden" : "block"}`}
{...provided.droppableProps}
ref={provided.innerRef}
>
{groupedByIssues[groupTitle].map((issue, index: number) => {
const assignees = [
...(issue?.assignees_list ?? []),
...(issue?.assignees ?? []),
]?.map((assignee) => {
const tempPerson = people?.find((p) => p.member.id === assignee)?.member;
return tempPerson;
});
return (
<Draggable key={issue.id} draggableId={issue.id} index={index}>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<SingleIssue
type="module"
typeId={moduleId as string}
issue={issue}
properties={properties}
snapshot={snapshot}
assignees={assignees}
people={people}
handleDeleteIssue={handleDeleteIssue}
userAuth={userAuth}
/>
</div>
)}
</Draggable>
);
})}
{provided.placeholder}
<CustomMenu
label={
<span className="flex items-center gap-1">
<PlusIcon className="h-3 w-3" />
Add issue
</span>
}
className="mt-1"
optionsPosition="left"
noBorder
>
<CustomMenu.MenuItem
onClick={() => {
openCreateIssueModal();
if (selectedGroup !== null) {
setPreloadedData({
state: stateId !== null ? stateId : undefined,
[selectedGroup]: groupTitle,
actionType: "createIssue",
});
}
}}
>
Create new
</CustomMenu.MenuItem>
<CustomMenu.MenuItem onClick={() => openIssuesListModal()}>
Add an existing issue
</CustomMenu.MenuItem>
</CustomMenu>
</div>
)}
</StrictModeDroppable>
</div>
</div>
);
};
export default SingleModuleBoard;

View File

@ -1,6 +1,6 @@
export * from "./board-view";
export * from "./select"; export * from "./select";
export * from "./sidebar-select"; export * from "./sidebar-select";
export * from "./board-view";
export * from "./delete-module-modal"; export * from "./delete-module-modal";
export * from "./form"; export * from "./form";
export * from "./list-view"; export * from "./list-view";

View File

@ -12,7 +12,7 @@ import stateService from "services/state.service";
import useIssuesProperties from "hooks/use-issue-properties"; import useIssuesProperties from "hooks/use-issue-properties";
import useIssueView from "hooks/use-issue-view"; import useIssueView from "hooks/use-issue-view";
// components // components
import SingleListIssue from "components/common/list-view/single-issue"; import SingleListIssue from "components/core/list-view/single-issue";
// ui // ui
import { CustomMenu, Spinner } from "components/ui"; import { CustomMenu, Spinner } from "components/ui";
// helpers // helpers

View File

@ -6,21 +6,19 @@ import { useRouter } from "next/router";
import { DragDropContext, DropResult } from "react-beautiful-dnd"; import { DragDropContext, DropResult } from "react-beautiful-dnd";
import stateService from "services/state.service"; import stateService from "services/state.service";
// hooks // hooks
import useIssuesProperties from "hooks/use-issue-properties";
import useIssueView from "hooks/use-issue-view"; import useIssueView from "hooks/use-issue-view";
// components // components
import SingleBoard from "components/project/cycles/board-view/single-board"; import { CommonSingleBoard } from "components/core/board-view/single-board";
// ui // ui
import { Spinner } from "components/ui"; import { Spinner } from "components/ui";
// types // types
import { CycleIssueResponse, IIssue, IProjectMember, UserAuth } from "types"; import { CycleIssueResponse, IIssue, UserAuth } from "types";
import issuesService from "services/issues.service"; import issuesService from "services/issues.service";
// constants // constants
import { STATE_LIST, CYCLE_ISSUES } from "constants/fetch-keys"; import { STATE_LIST, CYCLE_ISSUES } from "constants/fetch-keys";
type Props = { type Props = {
issues: IIssue[]; issues: IIssue[];
members: IProjectMember[] | undefined;
openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void; openCreateIssueModal: (issue?: IIssue, actionType?: "create" | "edit" | "delete") => void;
openIssuesListModal: () => void; openIssuesListModal: () => void;
handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>; handleDeleteIssue: React.Dispatch<React.SetStateAction<string | undefined>>;
@ -37,7 +35,6 @@ type Props = {
const CyclesBoardView: React.FC<Props> = ({ const CyclesBoardView: React.FC<Props> = ({
issues, issues,
members,
openCreateIssueModal, openCreateIssueModal,
openIssuesListModal, openIssuesListModal,
handleDeleteIssue, handleDeleteIssue,
@ -47,7 +44,6 @@ const CyclesBoardView: React.FC<Props> = ({
const router = useRouter(); const router = useRouter();
const { workspaceSlug, projectId, cycleId } = router.query; const { workspaceSlug, projectId, cycleId } = router.query;
const [properties] = useIssuesProperties(workspaceSlug as string, projectId as string);
const { issueView, groupedByIssues, groupByProperty: selectedGroup } = useIssueView(issues); const { issueView, groupedByIssues, groupByProperty: selectedGroup } = useIssueView(issues);
const { data: states } = useSWR( const { data: states } = useSWR(
@ -131,36 +127,39 @@ const CyclesBoardView: React.FC<Props> = ({
<div className="h-full w-full overflow-hidden"> <div className="h-full w-full overflow-hidden">
<div className="h-full w-full"> <div className="h-full w-full">
<div className="flex h-full gap-x-4 overflow-x-auto overflow-y-hidden"> <div className="flex h-full gap-x-4 overflow-x-auto overflow-y-hidden">
{Object.keys(groupedByIssues).map((singleGroup) => ( {Object.keys(groupedByIssues).map((singleGroup) => {
<SingleBoard const stateId =
key={singleGroup} selectedGroup === "state_detail.name"
selectedGroup={selectedGroup} ? states?.find((s) => s.name === singleGroup)?.id ?? null
groupTitle={singleGroup} : null;
createdBy={
selectedGroup === "created_by" return (
? members?.find((m) => m.member.id === singleGroup)?.member.first_name ?? <CommonSingleBoard
"loading..." key={singleGroup}
: null bgColor={
} selectedGroup === "state_detail.name"
groupedByIssues={groupedByIssues} ? states?.find((s) => s.name === singleGroup)?.color
bgColor={ : "#000000"
selectedGroup === "state_detail.name" }
? states?.find((s) => s.name === singleGroup)?.color groupTitle={singleGroup}
: "#000000" groupedByIssues={groupedByIssues}
} selectedGroup={selectedGroup}
properties={properties} addIssueToState={() => {
openIssuesListModal={openIssuesListModal} openCreateIssueModal();
openCreateIssueModal={openCreateIssueModal} if (selectedGroup !== null) {
handleDeleteIssue={handleDeleteIssue} setPreloadedData({
setPreloadedData={setPreloadedData} state: stateId !== null ? stateId : undefined,
stateId={ [selectedGroup]: singleGroup,
selectedGroup === "state_detail.name" actionType: "createIssue",
? states?.find((s) => s.name === singleGroup)?.id ?? null });
: null }
} }}
userAuth={userAuth} // openIssuesListModal={openIssuesListModal}
/> handleDeleteIssue={handleDeleteIssue}
))} userAuth={userAuth}
/>
);
})}
</div> </div>
</div> </div>
</div> </div>

View File

@ -15,7 +15,7 @@ import stateService from "services/state.service";
import useIssuesProperties from "hooks/use-issue-properties"; import useIssuesProperties from "hooks/use-issue-properties";
import useIssueView from "hooks/use-issue-view"; import useIssueView from "hooks/use-issue-view";
// components // components
import SingleListIssue from "components/common/list-view/single-issue"; import SingleListIssue from "components/core/list-view/single-issue";
// ui // ui
import { CustomMenu, Spinner } from "components/ui"; import { CustomMenu, Spinner } from "components/ui";
// helpers // helpers

View File

@ -3,8 +3,29 @@ import React, { useEffect, useState } from "react";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import useSWR from "swr"; import useSWR from "swr";
// react-hook-form
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
// lib
import { requiredAuth } from "lib/auth";
// services
import projectService from "services/project.service";
// hooks
import useUser from "hooks/use-user";
import useToast from "hooks/use-toast";
// layouts
import AppLayout from "layouts/app-layout";
// services
import userService from "services/user.service";
import workspaceService from "services/workspace.service";
// components
import { ImageUploadModal } from "components/core";
// ui
import { Button, Input, Spinner } from "components/ui";
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
// icons
import { import {
ChevronRightIcon, ChevronRightIcon,
ClipboardDocumentListIcon, ClipboardDocumentListIcon,
@ -14,30 +35,11 @@ import {
UserPlusIcon, UserPlusIcon,
XMarkIcon, XMarkIcon,
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
import type { NextPage, NextPageContext } from "next";
// hooks
import type { IIssue, IUser } from "types";
import useUser from "hooks/use-user";
import useToast from "hooks/use-toast";
// lib
import { requiredAuth } from "lib/auth";
// services
import projectService from "services/project.service";
// layouts
import AppLayout from "layouts/app-layout";
// constants
import { USER_ISSUE, USER_WORKSPACE_INVITATIONS, PROJECTS_LIST } from "constants/fetch-keys";
// services
import userService from "services/user.service";
import workspaceService from "services/workspace.service";
// components
import { ImageUploadModal } from "components/common/image-upload-modal";
// ui
import { Button, Input, Spinner } from "components/ui";
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
// icons
// types // types
import type { NextPage, NextPageContext } from "next";
import type { IIssue, IUser } from "types";
// fetch-keys
import { USER_ISSUE, USER_WORKSPACE_INVITATIONS, PROJECTS_LIST } from "constants/fetch-keys";
const defaultValues: Partial<IUser> = { const defaultValues: Partial<IUser> = {
avatar: "", avatar: "",

View File

@ -14,9 +14,8 @@ import { IssueViewContextProvider } from "contexts/issue-view.context";
import CyclesListView from "components/project/cycles/list-view"; import CyclesListView from "components/project/cycles/list-view";
import CyclesBoardView from "components/project/cycles/board-view"; import CyclesBoardView from "components/project/cycles/board-view";
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues"; import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
import ExistingIssuesListModal from "components/common/existing-issues-list-modal"; import { ExistingIssuesListModal, IssuesFilterView } from "components/core";
import CycleDetailSidebar from "components/project/cycles/cycle-detail-sidebar"; import CycleDetailSidebar from "components/project/cycles/cycle-detail-sidebar";
import View from "components/core/view";
// services // services
import issuesServices from "services/issues.service"; import issuesServices from "services/issues.service";
import cycleServices from "services/cycles.service"; import cycleServices from "services/cycles.service";
@ -35,7 +34,6 @@ import {
CYCLE_ISSUES, CYCLE_ISSUES,
CYCLE_LIST, CYCLE_LIST,
PROJECT_ISSUES_LIST, PROJECT_ISSUES_LIST,
PROJECT_MEMBERS,
PROJECT_DETAILS, PROJECT_DETAILS,
CYCLE_DETAILS, CYCLE_DETAILS,
} from "constants/fetch-keys"; } from "constants/fetch-keys";
@ -107,19 +105,6 @@ const SingleCycle: React.FC<UserAuth> = (props) => {
cycle: cycleId as string, cycle: cycleId as string,
})); }));
const { data: members } = useSWR(
workspaceSlug && projectId ? PROJECT_MEMBERS(workspaceSlug as string) : null,
workspaceSlug && projectId
? () => projectService.projectMembers(workspaceSlug as string, projectId as string)
: null,
{
onErrorRetry(err, _, __, revalidate, revalidateOpts) {
if (err?.status === 403) return;
setTimeout(() => revalidate(revalidateOpts), 5000);
},
}
);
const openCreateIssueModal = ( const openCreateIssueModal = (
issue?: IIssue, issue?: IIssue,
actionType: "create" | "edit" | "delete" = "create" actionType: "create" | "edit" | "delete" = "create"
@ -233,7 +218,7 @@ const SingleCycle: React.FC<UserAuth> = (props) => {
<div <div
className={`flex items-center gap-2 ${cycleSidebar ? "mr-[24rem]" : ""} duration-300`} className={`flex items-center gap-2 ${cycleSidebar ? "mr-[24rem]" : ""} duration-300`}
> >
<View issues={cycleIssuesArray ?? []} /> <IssuesFilterView issues={cycleIssuesArray ?? []} />
<button <button
type="button" type="button"
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-100 ${ className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-100 ${
@ -259,7 +244,6 @@ const SingleCycle: React.FC<UserAuth> = (props) => {
/> />
<CyclesBoardView <CyclesBoardView
issues={cycleIssuesArray ?? []} issues={cycleIssuesArray ?? []}
members={members}
openCreateIssueModal={openCreateIssueModal} openCreateIssueModal={openCreateIssueModal}
openIssuesListModal={openIssuesListModal} openIssuesListModal={openIssuesListModal}
handleDeleteIssue={setDeleteIssue} handleDeleteIssue={setDeleteIssue}

View File

@ -13,9 +13,13 @@ import AppLayout from "layouts/app-layout";
// contexts // contexts
import { IssueViewContextProvider } from "contexts/issue-view.context"; import { IssueViewContextProvider } from "contexts/issue-view.context";
// components // components
import BoardView from "components/issues/board-view"; import {
import { CreateUpdateIssueModal, DeleteIssueModal, IssuesListView } from "components/issues"; CreateUpdateIssueModal,
import View from "components/core/view"; DeleteIssueModal,
IssuesBoardView,
IssuesListView,
} from "components/issues";
import { IssuesFilterView } from "components/core";
// ui // ui
import { Spinner, EmptySpace, EmptySpaceItem, HeaderButton } from "components/ui"; import { Spinner, EmptySpace, EmptySpaceItem, HeaderButton } from "components/ui";
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
@ -77,7 +81,9 @@ const ProjectIssues: NextPage<UserAuth> = (props) => {
} }
right={ right={
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<View issues={projectIssues?.results.filter((p) => p.parent === null) ?? []} /> <IssuesFilterView
issues={projectIssues?.results.filter((p) => p.parent === null) ?? []}
/>
<HeaderButton <HeaderButton
Icon={PlusIcon} Icon={PlusIcon}
label="Add Issue" label="Add Issue"
@ -113,7 +119,7 @@ const ProjectIssues: NextPage<UserAuth> = (props) => {
handleEditIssue={handleEditIssue} handleEditIssue={handleEditIssue}
userAuth={props} userAuth={props}
/> />
<BoardView <IssuesBoardView
issues={projectIssues?.results.filter((p) => p.parent === null) ?? []} issues={projectIssues?.results.filter((p) => p.parent === null) ?? []}
handleDeleteIssue={setDeleteIssue} handleDeleteIssue={setDeleteIssue}
userAuth={props} userAuth={props}

View File

@ -8,18 +8,20 @@ import useSWR, { mutate } from "swr";
import { requiredAdmin, requiredAuth } from "lib/auth"; import { requiredAdmin, requiredAuth } from "lib/auth";
// services // services
import modulesService from "services/modules.service"; import modulesService from "services/modules.service";
import projectService from "services/project.service";
import issuesService from "services/issues.service"; import issuesService from "services/issues.service";
// layouts // layouts
import AppLayout from "layouts/app-layout"; import AppLayout from "layouts/app-layout";
// contexts // contexts
import { IssueViewContextProvider } from "contexts/issue-view.context"; import { IssueViewContextProvider } from "contexts/issue-view.context";
// components // components
import ExistingIssuesListModal from "components/common/existing-issues-list-modal"; import { ExistingIssuesListModal, IssuesFilterView } from "components/core";
import ModulesBoardView from "components/modules/board-view";
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues"; import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
import { DeleteModuleModal, ModuleDetailsSidebar, ModulesListView } from "components/modules"; import {
import View from "components/core/view"; DeleteModuleModal,
ModuleDetailsSidebar,
ModulesListView,
ModulesBoardView,
} from "components/modules";
// ui // ui
import { CustomMenu, EmptySpace, EmptySpaceItem, Spinner } from "components/ui"; import { CustomMenu, EmptySpace, EmptySpaceItem, Spinner } from "components/ui";
import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
@ -47,7 +49,6 @@ import {
MODULE_ISSUES, MODULE_ISSUES,
MODULE_LIST, MODULE_LIST,
PROJECT_ISSUES_LIST, PROJECT_ISSUES_LIST,
PROJECT_MEMBERS,
} from "constants/fetch-keys"; } from "constants/fetch-keys";
const SingleModule: React.FC<UserAuth> = (props) => { const SingleModule: React.FC<UserAuth> = (props) => {
@ -105,19 +106,6 @@ const SingleModule: React.FC<UserAuth> = (props) => {
: null : null
); );
const { data: members } = useSWR(
workspaceSlug && projectId ? PROJECT_MEMBERS(projectId as string) : null,
workspaceSlug && projectId
? () => projectService.projectMembers(workspaceSlug as string, projectId as string)
: null,
{
onErrorRetry(err, _, __, revalidate, revalidateOpts) {
if (err?.status === 403) return;
setTimeout(() => revalidate(revalidateOpts), 5000);
},
}
);
const moduleIssuesArray = moduleIssues?.map((issue) => ({ const moduleIssuesArray = moduleIssues?.map((issue) => ({
...issue.issue_detail, ...issue.issue_detail,
sub_issues_count: issue.sub_issues_count, sub_issues_count: issue.sub_issues_count,
@ -254,7 +242,7 @@ const SingleModule: React.FC<UserAuth> = (props) => {
<div <div
className={`flex items-center gap-2 ${moduleSidebar ? "mr-[24rem]" : ""} duration-300`} className={`flex items-center gap-2 ${moduleSidebar ? "mr-[24rem]" : ""} duration-300`}
> >
<View issues={moduleIssuesArray ?? []} /> <IssuesFilterView issues={moduleIssuesArray ?? []} />
<button <button
type="button" type="button"
className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-100 ${ className={`grid h-7 w-7 place-items-center rounded p-1 outline-none duration-300 hover:bg-gray-100 ${
@ -280,7 +268,6 @@ const SingleModule: React.FC<UserAuth> = (props) => {
/> />
<ModulesBoardView <ModulesBoardView
issues={moduleIssuesArray ?? []} issues={moduleIssuesArray ?? []}
members={members}
openCreateIssueModal={openCreateIssueModal} openCreateIssueModal={openCreateIssueModal}
openIssuesListModal={openIssuesListModal} openIssuesListModal={openIssuesListModal}
handleDeleteIssue={setDeleteIssue} handleDeleteIssue={setDeleteIssue}

View File

@ -21,7 +21,7 @@ import SettingsLayout from "layouts/settings-layout";
// hooks // hooks
import useToast from "hooks/use-toast"; import useToast from "hooks/use-toast";
// components // components
import { ImageUploadModal } from "components/common/image-upload-modal"; import { ImageUploadModal } from "components/core";
import ConfirmWorkspaceDeletion from "components/workspace/confirm-workspace-deletion"; import ConfirmWorkspaceDeletion from "components/workspace/confirm-workspace-deletion";
// ui // ui
import { Spinner, Button, Input, CustomSelect } from "components/ui"; import { Spinner, Button, Input, CustomSelect } from "components/ui";