feat: modules, style: cycles, all menus
This commit is contained in:
parent
830af71474
commit
278fd6cdd0
49 changed files with 1863 additions and 1530 deletions
|
|
@ -21,7 +21,7 @@ import useUser from "lib/hooks/useUser";
|
|||
import useIssuesFilter from "lib/hooks/useIssuesFilter";
|
||||
import useIssuesProperties from "lib/hooks/useIssuesProperties";
|
||||
// headless ui
|
||||
import { Menu, Popover, Transition } from "@headlessui/react";
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
// ui
|
||||
import { BreadcrumbItem, Breadcrumbs, CustomMenu } from "ui";
|
||||
// icons
|
||||
|
|
@ -41,6 +41,8 @@ import { CYCLE_ISSUES, PROJECT_MEMBERS } from "constants/fetch-keys";
|
|||
// constants
|
||||
import { classNames, replaceUnderscoreIfSnakeCase } from "constants/common";
|
||||
import CreateUpdateIssuesModal from "components/project/issues/CreateUpdateIssueModal";
|
||||
import CycleIssuesListModal from "components/project/cycles/cycle-issues-list-modal";
|
||||
import ConfirmCycleDeletion from "components/project/cycles/confirm-cycle-deletion";
|
||||
|
||||
const groupByOptions: Array<{ name: string; key: NestedKeyOf<IIssue> | null }> = [
|
||||
{ name: "State", key: "state_detail.name" },
|
||||
|
|
@ -79,8 +81,9 @@ const SingleCycle: React.FC<Props> = () => {
|
|||
const [isIssueModalOpen, setIsIssueModalOpen] = useState(false);
|
||||
const [selectedCycle, setSelectedCycle] = useState<SelectSprintType>();
|
||||
const [selectedIssues, setSelectedIssues] = useState<SelectIssue>();
|
||||
const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false);
|
||||
|
||||
const { activeWorkspace, activeProject, cycles } = useUser();
|
||||
const { activeWorkspace, activeProject, cycles, issues } = useUser();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
|
|
@ -98,9 +101,8 @@ const SingleCycle: React.FC<Props> = () => {
|
|||
cycleServices.getCycleIssues(activeWorkspace?.slug, activeProject?.id, cycleId as string)
|
||||
: null
|
||||
);
|
||||
|
||||
const cycleIssuesArray = cycleIssues?.map((issue) => {
|
||||
return issue.issue_details;
|
||||
return { bridge: issue.id, ...issue.issue_details };
|
||||
});
|
||||
|
||||
const { data: members } = useSWR(
|
||||
|
|
@ -143,6 +145,10 @@ const SingleCycle: React.FC<Props> = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const openIssuesListModal = () => {
|
||||
setCycleIssuesListModal(true);
|
||||
};
|
||||
|
||||
const addIssueToCycle = (cycleId: string, issueId: string) => {
|
||||
if (!activeWorkspace || !activeProject?.id) return;
|
||||
|
||||
|
|
@ -202,16 +208,16 @@ const SingleCycle: React.FC<Props> = () => {
|
|||
// console.log(result);
|
||||
};
|
||||
|
||||
const removeIssueFromCycle = (cycleId: string, bridgeId: string) => {
|
||||
const removeIssueFromCycle = (bridgeId: string) => {
|
||||
if (activeWorkspace && activeProject) {
|
||||
mutate<CycleIssueResponse[]>(
|
||||
CYCLE_ISSUES(cycleId),
|
||||
CYCLE_ISSUES(cycleId as string),
|
||||
(prevData) => prevData?.filter((p) => p.id !== bridgeId),
|
||||
false
|
||||
);
|
||||
|
||||
issuesServices
|
||||
.removeIssueFromCycle(activeWorkspace.slug, activeProject.id, cycleId, bridgeId)
|
||||
.removeIssueFromCycle(activeWorkspace.slug, activeProject.id, cycleId as string, bridgeId)
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
})
|
||||
|
|
@ -234,6 +240,12 @@ const SingleCycle: React.FC<Props> = () => {
|
|||
setIsOpen={setIsIssueModalOpen}
|
||||
projectId={activeProject?.id}
|
||||
/>
|
||||
<CycleIssuesListModal
|
||||
isOpen={cycleIssuesListModal}
|
||||
handleClose={() => setCycleIssuesListModal(false)}
|
||||
issues={issues}
|
||||
cycleId={cycleId as string}
|
||||
/>
|
||||
<AppLayout
|
||||
breadcrumbs={
|
||||
<Breadcrumbs>
|
||||
|
|
@ -244,38 +256,25 @@ const SingleCycle: React.FC<Props> = () => {
|
|||
</Breadcrumbs>
|
||||
}
|
||||
left={
|
||||
<Menu as="div" className="relative inline-block">
|
||||
<Menu.Button className="flex items-center gap-1 border ml-2 px-2 py-1 rounded hover:bg-gray-100 text-xs font-medium">
|
||||
<ArrowPathIcon className="h-3 w-3" />
|
||||
{cycles?.find((c) => c.id === cycleId)?.name}
|
||||
</Menu.Button>
|
||||
|
||||
<Transition
|
||||
as={React.Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="absolute left-3 mt-2 p-1 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none z-10">
|
||||
{cycles?.map((cycle) => (
|
||||
<Menu.Item key={cycle.id}>
|
||||
<Link href={`/projects/${activeProject?.id}/cycles/${cycle.id}`}>
|
||||
<a
|
||||
className={`block text-left p-2 text-gray-900 hover:bg-theme hover:text-white rounded-md text-xs whitespace-nowrap w-full ${
|
||||
cycle.id === cycleId ? "bg-theme text-white" : ""
|
||||
}`}
|
||||
>
|
||||
{cycle.name}
|
||||
</a>
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
<CustomMenu
|
||||
label={
|
||||
<>
|
||||
<ArrowPathIcon className="h-3 w-3" />
|
||||
{cycles?.find((c) => c.id === cycleId)?.name}
|
||||
</>
|
||||
}
|
||||
className="ml-1.5"
|
||||
>
|
||||
{cycles?.map((cycle) => (
|
||||
<CustomMenu.MenuItem
|
||||
key={cycle.id}
|
||||
renderAs="a"
|
||||
href={`/projects/${activeProject?.id}/cycles/${cycle.id}`}
|
||||
>
|
||||
{cycle.name}
|
||||
</CustomMenu.MenuItem>
|
||||
))}
|
||||
</CustomMenu>
|
||||
}
|
||||
right={
|
||||
<div className="flex items-center gap-2">
|
||||
|
|
@ -327,7 +326,7 @@ const SingleCycle: React.FC<Props> = () => {
|
|||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 translate-y-1"
|
||||
>
|
||||
<Popover.Panel className="absolute mr-5 right-1/2 z-10 mt-1 w-screen max-w-xs translate-x-1/2 transform p-3 bg-white rounded-lg shadow-lg overflow-hidden">
|
||||
<Popover.Panel className="absolute right-0 z-10 mt-1 w-screen max-w-xs transform p-3 bg-white rounded-lg shadow-lg overflow-hidden">
|
||||
<div className="relative flex flex-col gap-1 gap-y-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<h4 className="text-sm text-gray-600">Group by</h4>
|
||||
|
|
@ -336,6 +335,7 @@ const SingleCycle: React.FC<Props> = () => {
|
|||
groupByOptions.find((option) => option.key === groupByProperty)
|
||||
?.name ?? "Select"
|
||||
}
|
||||
width="auto"
|
||||
>
|
||||
{groupByOptions.map((option) => (
|
||||
<CustomMenu.MenuItem
|
||||
|
|
@ -354,6 +354,7 @@ const SingleCycle: React.FC<Props> = () => {
|
|||
orderByOptions.find((option) => option.key === orderBy)?.name ??
|
||||
"Select"
|
||||
}
|
||||
width="auto"
|
||||
>
|
||||
{orderByOptions.map((option) =>
|
||||
groupByProperty === "priority" && option.key === "priority" ? null : (
|
||||
|
|
@ -374,6 +375,7 @@ const SingleCycle: React.FC<Props> = () => {
|
|||
filterIssueOptions.find((option) => option.key === filterIssue)
|
||||
?.name ?? "Select"
|
||||
}
|
||||
width="auto"
|
||||
>
|
||||
{filterIssueOptions.map((option) => (
|
||||
<CustomMenu.MenuItem
|
||||
|
|
@ -420,9 +422,7 @@ const SingleCycle: React.FC<Props> = () => {
|
|||
selectedGroup={groupByProperty}
|
||||
properties={properties}
|
||||
openCreateIssueModal={openCreateIssueModal}
|
||||
openIssuesListModal={() => {
|
||||
return;
|
||||
}}
|
||||
openIssuesListModal={openIssuesListModal}
|
||||
removeIssueFromCycle={removeIssueFromCycle}
|
||||
/>
|
||||
) : (
|
||||
|
|
@ -434,9 +434,7 @@ const SingleCycle: React.FC<Props> = () => {
|
|||
selectedGroup={groupByProperty}
|
||||
members={members}
|
||||
openCreateIssueModal={openCreateIssueModal}
|
||||
openIssuesListModal={() => {
|
||||
return;
|
||||
}}
|
||||
openIssuesListModal={openIssuesListModal}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -5,46 +5,31 @@ import { useRouter } from "next/router";
|
|||
import type { NextPage } from "next";
|
||||
// swr
|
||||
import useSWR from "swr";
|
||||
// hoc
|
||||
import withAuth from "lib/hoc/withAuthWrapper";
|
||||
// services
|
||||
import sprintService from "lib/services/cycles.service";
|
||||
// hooks
|
||||
import useUser from "lib/hooks/useUser";
|
||||
import useIssuesProperties from "lib/hooks/useIssuesProperties";
|
||||
// fetching keys
|
||||
import { CYCLE_LIST } from "constants/fetch-keys";
|
||||
// hoc
|
||||
import withAuth from "lib/hoc/withAuthWrapper";
|
||||
// layouts
|
||||
import AppLayout from "layouts/app-layout";
|
||||
// components
|
||||
import CycleIssuesListModal from "components/project/cycles/CycleIssuesListModal";
|
||||
import ConfirmIssueDeletion from "components/project/issues/confirm-issue-deletion";
|
||||
import ConfirmSprintDeletion from "components/project/cycles/ConfirmCycleDeletion";
|
||||
import CreateUpdateIssuesModal from "components/project/issues/CreateUpdateIssueModal";
|
||||
import CreateUpdateSprintsModal from "components/project/cycles/CreateUpdateCyclesModal";
|
||||
import CreateUpdateCycleModal from "components/project/cycles/create-update-cycle-modal";
|
||||
import CycleStatsView from "components/project/cycles/stats-view";
|
||||
// headless ui
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
// ui
|
||||
import { BreadcrumbItem, Breadcrumbs, HeaderButton, Spinner, EmptySpace, EmptySpaceItem } from "ui";
|
||||
// icons
|
||||
import { ArrowPathIcon, ChevronDownIcon, PlusIcon } from "@heroicons/react/24/outline";
|
||||
import { ArrowPathIcon, PlusIcon } from "@heroicons/react/24/outline";
|
||||
// types
|
||||
import { IIssue, ICycle, SelectSprintType, SelectIssue, Properties } from "types";
|
||||
// constants
|
||||
import { classNames, replaceUnderscoreIfSnakeCase } from "constants/common";
|
||||
import { ICycle, SelectSprintType } from "types";
|
||||
// fetching keys
|
||||
import { CYCLE_LIST } from "constants/fetch-keys";
|
||||
|
||||
const ProjectSprints: NextPage = () => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [selectedSprint, setSelectedSprint] = useState<SelectSprintType>();
|
||||
const [selectedCycle, setSelectedCycle] = useState<SelectSprintType>();
|
||||
const [createUpdateCycleModal, setCreateUpdateCycleModal] = useState(false);
|
||||
|
||||
const [isIssueModalOpen, setIsIssueModalOpen] = useState(false);
|
||||
const [selectedIssues, setSelectedIssues] = useState<SelectIssue>();
|
||||
const [deleteIssue, setDeleteIssue] = useState<string | undefined>();
|
||||
const [cycleIssuesListModal, setCycleIssuesListModal] = useState(false);
|
||||
const [cycleId, setCycleId] = useState("");
|
||||
|
||||
const { activeWorkspace, activeProject, issues } = useUser();
|
||||
const { activeWorkspace, activeProject } = useUser();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
|
|
@ -57,45 +42,13 @@ const ProjectSprints: NextPage = () => {
|
|||
: null
|
||||
);
|
||||
|
||||
const [properties, setProperties] = useIssuesProperties(
|
||||
activeWorkspace?.slug,
|
||||
projectId as string
|
||||
);
|
||||
|
||||
const openCreateIssueModal = (
|
||||
cycleId: string,
|
||||
issue?: IIssue,
|
||||
actionType: "create" | "edit" | "delete" = "create"
|
||||
) => {
|
||||
const cycle = cycles?.find((cycle) => cycle.id === cycleId);
|
||||
if (cycle) {
|
||||
setSelectedSprint({
|
||||
...cycle,
|
||||
actionType: "create-issue",
|
||||
});
|
||||
if (issue) setSelectedIssues({ ...issue, actionType });
|
||||
setIsIssueModalOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
const openIssuesListModal = (cycleId: string) => {
|
||||
setCycleId(cycleId);
|
||||
setCycleIssuesListModal(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) return;
|
||||
if (createUpdateCycleModal) return;
|
||||
const timer = setTimeout(() => {
|
||||
setSelectedSprint(undefined);
|
||||
setSelectedCycle(undefined);
|
||||
clearTimeout(timer);
|
||||
}, 500);
|
||||
}, [isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedIssues?.actionType === "delete") {
|
||||
setDeleteIssue(selectedIssues.id);
|
||||
}
|
||||
}, [selectedIssues]);
|
||||
}, [createUpdateCycleModal]);
|
||||
|
||||
return (
|
||||
<AppLayout
|
||||
|
|
@ -109,101 +62,33 @@ const ProjectSprints: NextPage = () => {
|
|||
</Breadcrumbs>
|
||||
}
|
||||
right={
|
||||
<div className="flex items-center gap-2">
|
||||
<Popover className="relative">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<Popover.Button
|
||||
className={classNames(
|
||||
open ? "bg-gray-100 text-gray-900" : "text-gray-500",
|
||||
"group flex gap-2 items-center rounded-md bg-transparent text-xs font-medium hover:bg-gray-100 hover:text-gray-900 focus:outline-none border p-2"
|
||||
)}
|
||||
>
|
||||
<span>View</span>
|
||||
<ChevronDownIcon className="h-4 w-4" aria-hidden="true" />
|
||||
</Popover.Button>
|
||||
|
||||
<Transition
|
||||
as={React.Fragment}
|
||||
enter="transition ease-out duration-200"
|
||||
enterFrom="opacity-0 translate-y-1"
|
||||
enterTo="opacity-100 translate-y-0"
|
||||
leave="transition ease-in duration-150"
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 translate-y-1"
|
||||
>
|
||||
<Popover.Panel className="absolute mr-5 right-1/2 z-10 mt-1 w-screen max-w-xs translate-x-1/2 transform p-4 bg-white rounded-lg shadow-lg overflow-hidden">
|
||||
<div className="relative flex flex-col gap-1 gap-y-4">
|
||||
<div className="relative flex flex-col gap-1">
|
||||
<h4 className="text-base text-gray-600">Properties</h4>
|
||||
<div>
|
||||
{Object.keys(properties).map((key) => (
|
||||
<button
|
||||
key={key}
|
||||
type="button"
|
||||
className={`px-2 py-1 inline capitalize rounded border border-theme text-sm m-1 ${
|
||||
properties[key as keyof Properties]
|
||||
? "border-theme bg-theme text-white"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => setProperties(key as keyof Properties)}
|
||||
>
|
||||
{replaceUnderscoreIfSnakeCase(key)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Popover.Panel>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Popover>
|
||||
<HeaderButton Icon={PlusIcon} label="Add Cycle" onClick={() => setIsOpen(true)} />
|
||||
</div>
|
||||
<HeaderButton
|
||||
Icon={PlusIcon}
|
||||
label="Add Cycle"
|
||||
onClick={() => {
|
||||
const e = new KeyboardEvent("keydown", {
|
||||
ctrlKey: true,
|
||||
key: "q",
|
||||
});
|
||||
document.dispatchEvent(e);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<CreateUpdateSprintsModal
|
||||
isOpen={
|
||||
isOpen &&
|
||||
selectedSprint?.actionType !== "delete" &&
|
||||
selectedSprint?.actionType !== "create-issue"
|
||||
}
|
||||
setIsOpen={setIsOpen}
|
||||
data={selectedSprint}
|
||||
<CreateUpdateCycleModal
|
||||
isOpen={createUpdateCycleModal}
|
||||
setIsOpen={setCreateUpdateCycleModal}
|
||||
projectId={projectId as string}
|
||||
/>
|
||||
<ConfirmSprintDeletion
|
||||
isOpen={isOpen && !!selectedSprint && selectedSprint.actionType === "delete"}
|
||||
setIsOpen={setIsOpen}
|
||||
data={selectedSprint}
|
||||
/>
|
||||
<ConfirmIssueDeletion
|
||||
handleClose={() => setDeleteIssue(undefined)}
|
||||
isOpen={!!deleteIssue}
|
||||
data={selectedIssues}
|
||||
/>
|
||||
<CreateUpdateIssuesModal
|
||||
isOpen={
|
||||
isIssueModalOpen &&
|
||||
selectedSprint?.actionType === "create-issue" &&
|
||||
selectedIssues?.actionType !== "delete"
|
||||
}
|
||||
data={selectedIssues}
|
||||
prePopulateData={{ sprints: selectedSprint?.id }}
|
||||
setIsOpen={setIsOpen}
|
||||
projectId={projectId as string}
|
||||
/>
|
||||
<CycleIssuesListModal
|
||||
isOpen={cycleIssuesListModal}
|
||||
handleClose={() => setCycleIssuesListModal(false)}
|
||||
issues={issues}
|
||||
cycleId={cycleId}
|
||||
data={selectedCycle}
|
||||
/>
|
||||
{cycles ? (
|
||||
cycles.length > 0 ? (
|
||||
<div className="space-y-5">
|
||||
<CycleStatsView cycles={cycles} />
|
||||
<CycleStatsView
|
||||
cycles={cycles}
|
||||
setCreateUpdateCycleModal={setCreateUpdateCycleModal}
|
||||
setSelectedCycle={setSelectedCycle}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-full h-full flex flex-col justify-center items-center px-4">
|
||||
|
|
@ -221,7 +106,7 @@ const ProjectSprints: NextPage = () => {
|
|||
</span>
|
||||
}
|
||||
Icon={PlusIcon}
|
||||
action={() => setIsOpen(true)}
|
||||
action={() => setCreateUpdateCycleModal(true)}
|
||||
/>
|
||||
</EmptySpace>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import React, { useCallback, useEffect, useState } from "react";
|
|||
// swr
|
||||
import useSWR, { mutate } from "swr";
|
||||
// react hook form
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { useForm } from "react-hook-form";
|
||||
// headless ui
|
||||
import { Disclosure, Menu, Tab, Transition } from "@headlessui/react";
|
||||
// services
|
||||
|
|
@ -28,7 +28,7 @@ import AppLayout from "layouts/app-layout";
|
|||
// components
|
||||
import CreateUpdateIssuesModal from "components/project/issues/CreateUpdateIssueModal";
|
||||
import IssueCommentSection from "components/project/issues/issue-detail/comment/IssueCommentSection";
|
||||
import AddAsSubIssue from "components/command-palette/addAsSubIssue";
|
||||
import AddAsSubIssue from "components/project/issues/issue-detail/add-as-sub-issue";
|
||||
import ConfirmIssueDeletion from "components/project/issues/confirm-issue-deletion";
|
||||
// common
|
||||
import { debounce } from "constants/common";
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@ const ProjectIssues: NextPage = () => {
|
|||
groupByOptions.find((option) => option.key === groupByProperty)?.name ??
|
||||
"Select"
|
||||
}
|
||||
width="auto"
|
||||
>
|
||||
{groupByOptions.map((option) => (
|
||||
<CustomMenu.MenuItem
|
||||
|
|
@ -237,6 +238,7 @@ const ProjectIssues: NextPage = () => {
|
|||
orderByOptions.find((option) => option.key === orderBy)?.name ??
|
||||
"Select"
|
||||
}
|
||||
width="auto"
|
||||
>
|
||||
{orderByOptions.map((option) =>
|
||||
groupByProperty === "priority" && option.key === "priority" ? null : (
|
||||
|
|
@ -257,6 +259,7 @@ const ProjectIssues: NextPage = () => {
|
|||
filterIssueOptions.find((option) => option.key === filterIssue)?.name ??
|
||||
"Select"
|
||||
}
|
||||
width="auto"
|
||||
>
|
||||
{filterIssueOptions.map((option) => (
|
||||
<CustomMenu.MenuItem
|
||||
|
|
|
|||
104
apps/app/pages/projects/[projectId]/modules/index.tsx
Normal file
104
apps/app/pages/projects/[projectId]/modules/index.tsx
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// next
|
||||
import type { NextPage } from "next";
|
||||
import useSWR from "swr";
|
||||
import { useRouter } from "next/router";
|
||||
// layouts
|
||||
import AppLayout from "layouts/app-layout";
|
||||
// hoc
|
||||
import withAuth from "lib/hoc/withAuthWrapper";
|
||||
// services
|
||||
import modulesService from "lib/services/modules.service";
|
||||
// hooks
|
||||
import useUser from "lib/hooks/useUser";
|
||||
// ui
|
||||
import { BreadcrumbItem, Breadcrumbs, EmptySpace, EmptySpaceItem, HeaderButton, Spinner } from "ui";
|
||||
// icons
|
||||
import { PlusIcon, RectangleGroupIcon } from "@heroicons/react/24/outline";
|
||||
// types
|
||||
import { IModule } from "types/modules";
|
||||
// fetch-keys
|
||||
import { MODULE_LIST } from "constants/fetch-keys";
|
||||
|
||||
const ProjectModules: NextPage = () => {
|
||||
const { activeWorkspace, activeProject } = useUser();
|
||||
|
||||
const router = useRouter();
|
||||
const { projectId } = router.query;
|
||||
|
||||
const { data: modules } = useSWR<IModule[]>(
|
||||
activeWorkspace && projectId ? MODULE_LIST(projectId as string) : null,
|
||||
activeWorkspace && projectId
|
||||
? () => modulesService.getModules(activeWorkspace.slug, projectId as string)
|
||||
: null
|
||||
);
|
||||
|
||||
console.log(modules);
|
||||
|
||||
return (
|
||||
<AppLayout
|
||||
meta={{
|
||||
title: "Plane - Modules",
|
||||
}}
|
||||
breadcrumbs={
|
||||
<Breadcrumbs>
|
||||
<BreadcrumbItem title="Projects" link="/projects" />
|
||||
<BreadcrumbItem title={`${activeProject?.name ?? "Project"} Modules`} />
|
||||
</Breadcrumbs>
|
||||
}
|
||||
right={
|
||||
<HeaderButton
|
||||
Icon={PlusIcon}
|
||||
label="Add Module"
|
||||
onClick={() => {
|
||||
const e = new KeyboardEvent("keydown", {
|
||||
ctrlKey: true,
|
||||
key: "m",
|
||||
});
|
||||
document.dispatchEvent(e);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
{modules ? (
|
||||
modules.length > 0 ? (
|
||||
<div className="space-y-5">
|
||||
{modules.map((module) => (
|
||||
<div key={module.id} className="bg-white p-3 rounded-md">
|
||||
<h3>{module.name}</h3>
|
||||
<p className="text-gray-500 text-sm mt-2">{module.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-full h-full flex flex-col justify-center items-center px-4">
|
||||
<EmptySpace
|
||||
title="You don't have any module yet."
|
||||
description="A cycle is a fixed time period where a team commits to a set number of issues from their backlog. Cycles are usually one, two, or four weeks long."
|
||||
Icon={RectangleGroupIcon}
|
||||
>
|
||||
<EmptySpaceItem
|
||||
title="Create a new module"
|
||||
description={
|
||||
<span>
|
||||
Use <pre className="inline bg-gray-100 px-2 py-1 rounded">Ctrl/Command + Q</pre>{" "}
|
||||
shortcut to create a new cycle
|
||||
</span>
|
||||
}
|
||||
Icon={PlusIcon}
|
||||
action={() => {
|
||||
return;
|
||||
}}
|
||||
/>
|
||||
</EmptySpace>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="w-full h-full flex justify-center items-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
)}
|
||||
</AppLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default withAuth(ProjectModules);
|
||||
|
|
@ -57,7 +57,23 @@ const Projects: NextPage = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<AppLayout>
|
||||
<AppLayout
|
||||
breadcrumbs={
|
||||
<Breadcrumbs>
|
||||
<BreadcrumbItem title={`${activeWorkspace?.name ?? "Workspace"} Projects`} />
|
||||
</Breadcrumbs>
|
||||
}
|
||||
right={
|
||||
<HeaderButton
|
||||
Icon={PlusIcon}
|
||||
label="Add Project"
|
||||
onClick={() => {
|
||||
const e = new KeyboardEvent("keydown", { key: "p", ctrlKey: true });
|
||||
document.dispatchEvent(e);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ConfirmProjectDeletion
|
||||
isOpen={!!deleteProject}
|
||||
onClose={() => setDeleteProject(null)}
|
||||
|
|
@ -91,20 +107,6 @@ const Projects: NextPage = () => {
|
|||
</div>
|
||||
) : (
|
||||
<div className="h-full w-full space-y-5">
|
||||
<Breadcrumbs>
|
||||
<BreadcrumbItem title={`${activeWorkspace?.name ?? "Workspace"} Projects`} />
|
||||
</Breadcrumbs>
|
||||
<div className="flex items-center justify-between cursor-pointer w-full">
|
||||
<h2 className="text-2xl font-medium">Projects</h2>
|
||||
<HeaderButton
|
||||
Icon={PlusIcon}
|
||||
label="Add Project"
|
||||
onClick={() => {
|
||||
const e = new KeyboardEvent("keydown", { key: "p", ctrlKey: true });
|
||||
document.dispatchEvent(e);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{projects.map((item) => (
|
||||
<ProjectMemberInvitations
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue