diff --git a/apps/app/components/cycles/active-cycle-details.tsx b/apps/app/components/cycles/active-cycle-details.tsx index 7e8c8f7f7..8dfe36a53 100644 --- a/apps/app/components/cycles/active-cycle-details.tsx +++ b/apps/app/components/cycles/active-cycle-details.tsx @@ -42,13 +42,7 @@ import { truncateText } from "helpers/string.helper"; // types import { ICycle, IIssue } from "types"; // fetch-keys -import { - CYCLE_COMPLETE_LIST, - CYCLE_CURRENT_LIST, - CYCLE_DRAFT_LIST, - CYCLE_ISSUES, - CYCLE_LIST, -} from "constants/fetch-keys"; +import { CURRENT_CYCLE_LIST, CYCLES_LIST, CYCLE_ISSUES } from "constants/fetch-keys"; type TSingleStatProps = { cycle: ICycle; @@ -106,7 +100,7 @@ export const ActiveCycleDetails: React.FC = ({ cycle, isComple if (!workspaceSlug || !projectId || !cycle) return; mutate( - CYCLE_CURRENT_LIST(projectId as string), + CURRENT_CYCLE_LIST(projectId as string), (prevData) => (prevData ?? []).map((c) => ({ ...c, @@ -116,7 +110,7 @@ export const ActiveCycleDetails: React.FC = ({ cycle, isComple ); mutate( - CYCLE_LIST(projectId as string), + CYCLES_LIST(projectId as string), (prevData: any) => (prevData ?? []).map((c: any) => ({ ...c, @@ -142,7 +136,7 @@ export const ActiveCycleDetails: React.FC = ({ cycle, isComple if (!workspaceSlug || !projectId || !cycle) return; mutate( - CYCLE_CURRENT_LIST(projectId as string), + CURRENT_CYCLE_LIST(projectId as string), (prevData) => (prevData ?? []).map((c) => ({ ...c, @@ -152,7 +146,7 @@ export const ActiveCycleDetails: React.FC = ({ cycle, isComple ); mutate( - CYCLE_LIST(projectId as string), + CYCLES_LIST(projectId as string), (prevData: any) => (prevData ?? []).map((c: any) => ({ ...c, diff --git a/apps/app/components/cycles/all-cycles-board.tsx b/apps/app/components/cycles/all-cycles-board.tsx deleted file mode 100644 index 259251fe6..000000000 --- a/apps/app/components/cycles/all-cycles-board.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import { useState } from "react"; - -// components -import { DeleteCycleModal, SingleCycleCard } from "components/cycles"; -import { EmptyState, Loader } from "components/ui"; -// image -import emptyCycle from "public/empty-state/empty-cycle.svg"; -// icon -import { XMarkIcon } from "@heroicons/react/24/outline"; -// types -import { ICycle, SelectCycleType } from "types"; - -type TCycleStatsViewProps = { - cycles: ICycle[] | undefined; - setCreateUpdateCycleModal: React.Dispatch>; - setSelectedCycle: React.Dispatch>; - type: "current" | "upcoming" | "draft"; -}; - -export const AllCyclesBoard: React.FC = ({ - cycles, - setCreateUpdateCycleModal, - setSelectedCycle, - type, -}) => { - const [cycleDeleteModal, setCycleDeleteModal] = useState(false); - const [selectedCycleForDelete, setSelectedCycleForDelete] = useState(); - - const handleDeleteCycle = (cycle: ICycle) => { - setSelectedCycleForDelete({ ...cycle, actionType: "delete" }); - setCycleDeleteModal(true); - }; - - const handleEditCycle = (cycle: ICycle) => { - setSelectedCycle({ ...cycle, actionType: "edit" }); - setCreateUpdateCycleModal(true); - }; - - return ( - <> - - {cycles ? ( - cycles.length > 0 ? ( -
- {cycles.map((cycle) => ( - handleDeleteCycle(cycle)} - handleEditCycle={() => handleEditCycle(cycle)} - /> - ))} -
- ) : type === "current" ? ( -
-

No cycle is present.

-
- ) : ( - - ) - ) : ( - - - - )} - - ); -}; diff --git a/apps/app/components/cycles/all-cycles-list.tsx b/apps/app/components/cycles/all-cycles-list.tsx deleted file mode 100644 index 502e13e8e..000000000 --- a/apps/app/components/cycles/all-cycles-list.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { useState } from "react"; - -// components -import { DeleteCycleModal, SingleCycleList } from "components/cycles"; -import { EmptyState, Loader } from "components/ui"; -// image -import emptyCycle from "public/empty-state/empty-cycle.svg"; -// icon -import { XMarkIcon } from "@heroicons/react/24/outline"; -// types -import { ICycle, SelectCycleType } from "types"; - -type TCycleStatsViewProps = { - cycles: ICycle[] | undefined; - setCreateUpdateCycleModal: React.Dispatch>; - setSelectedCycle: React.Dispatch>; - type: "current" | "upcoming" | "draft"; -}; - -export const AllCyclesList: React.FC = ({ - cycles, - setCreateUpdateCycleModal, - setSelectedCycle, - type, -}) => { - const [cycleDeleteModal, setCycleDeleteModal] = useState(false); - const [selectedCycleForDelete, setSelectedCycleForDelete] = useState(); - - const handleDeleteCycle = (cycle: ICycle) => { - setSelectedCycleForDelete({ ...cycle, actionType: "delete" }); - setCycleDeleteModal(true); - }; - - const handleEditCycle = (cycle: ICycle) => { - setSelectedCycle({ ...cycle, actionType: "edit" }); - setCreateUpdateCycleModal(true); - }; - - return ( - <> - - {cycles ? ( - cycles.length > 0 ? ( -
- {cycles.map((cycle) => ( -
-
- handleDeleteCycle(cycle)} - handleEditCycle={() => handleEditCycle(cycle)} - /> -
-
- ))} -
- ) : type === "current" ? ( -
-

No cycle is present.

-
- ) : ( - - ) - ) : ( - - - - )} - - ); -}; diff --git a/apps/app/components/cycles/completed-cycles.tsx b/apps/app/components/cycles/completed-cycles.tsx deleted file mode 100644 index f561f32d4..000000000 --- a/apps/app/components/cycles/completed-cycles.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import { useState } from "react"; - -import { useRouter } from "next/router"; - -import useSWR from "swr"; - -// services -import cyclesService from "services/cycles.service"; -// components -import { DeleteCycleModal, SingleCycleCard, SingleCycleList } from "components/cycles"; -// icons -import { ExclamationIcon } from "components/icons"; -// types -import { ICycle, SelectCycleType } from "types"; -// fetch-keys -import { CYCLE_COMPLETE_LIST } from "constants/fetch-keys"; -import { EmptyState, Loader } from "components/ui"; -// image -import emptyCycle from "public/empty-state/empty-cycle.svg"; - -export interface CompletedCyclesListProps { - cycleView: string; - setCreateUpdateCycleModal: React.Dispatch>; - setSelectedCycle: React.Dispatch>; -} - -export const CompletedCycles: React.FC = ({ - cycleView, - setCreateUpdateCycleModal, - setSelectedCycle, -}) => { - const [cycleDeleteModal, setCycleDeleteModal] = useState(false); - const [selectedCycleForDelete, setSelectedCycleForDelete] = useState(); - - const router = useRouter(); - const { workspaceSlug, projectId } = router.query; - - const { data: completedCycles } = useSWR( - workspaceSlug && projectId ? CYCLE_COMPLETE_LIST(projectId as string) : null, - workspaceSlug && projectId - ? () => - cyclesService.getCyclesWithParams(workspaceSlug as string, projectId as string, { - cycle_view: "completed", - }) - : null - ); - - const handleDeleteCycle = (cycle: ICycle) => { - setSelectedCycleForDelete({ ...cycle, actionType: "delete" }); - setCycleDeleteModal(true); - }; - - const handleEditCycle = (cycle: ICycle) => { - setSelectedCycle({ ...cycle, actionType: "edit" }); - setCreateUpdateCycleModal(true); - }; - - return ( - <> - - {completedCycles ? ( - completedCycles.length > 0 ? ( -
-
- - Completed cycles are not editable. -
- {cycleView === "list" && ( -
- {completedCycles.map((cycle) => ( -
-
- handleDeleteCycle(cycle)} - handleEditCycle={() => handleEditCycle(cycle)} - isCompleted - /> -
-
- ))} -
- )} - {cycleView === "board" && ( -
- {completedCycles.map((cycle) => ( - handleDeleteCycle(cycle)} - handleEditCycle={() => handleEditCycle(cycle)} - isCompleted - /> - ))} -
- )} -
- ) : ( - - ) - ) : ( - - - - - - )} - - ); -}; diff --git a/apps/app/components/cycles/cycles-list/all-cycles-list.tsx b/apps/app/components/cycles/cycles-list/all-cycles-list.tsx new file mode 100644 index 000000000..11dc08b3e --- /dev/null +++ b/apps/app/components/cycles/cycles-list/all-cycles-list.tsx @@ -0,0 +1,31 @@ +import { useRouter } from "next/router"; + +import useSWR from "swr"; + +// services +import cyclesService from "services/cycles.service"; +// components +import { CyclesView } from "components/cycles"; +// fetch-keys +import { CYCLES_LIST } from "constants/fetch-keys"; + +type Props = { + viewType: string | null; +}; + +export const AllCyclesList: React.FC = ({ viewType }) => { + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { data: allCyclesList } = useSWR( + workspaceSlug && projectId ? CYCLES_LIST(projectId.toString()) : null, + workspaceSlug && projectId + ? () => + cyclesService.getCyclesWithParams(workspaceSlug.toString(), projectId.toString(), { + cycle_view: "all", + }) + : null + ); + + return ; +}; diff --git a/apps/app/components/cycles/cycles-list/completed-cycles-list.tsx b/apps/app/components/cycles/cycles-list/completed-cycles-list.tsx new file mode 100644 index 000000000..bcbb11d4a --- /dev/null +++ b/apps/app/components/cycles/cycles-list/completed-cycles-list.tsx @@ -0,0 +1,31 @@ +import { useRouter } from "next/router"; + +import useSWR from "swr"; + +// services +import cyclesService from "services/cycles.service"; +// components +import { CyclesView } from "components/cycles"; +// fetch-keys +import { COMPLETED_CYCLES_LIST } from "constants/fetch-keys"; + +type Props = { + viewType: string | null; +}; + +export const CompletedCyclesList: React.FC = ({ viewType }) => { + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { data: completedCyclesList } = useSWR( + workspaceSlug && projectId ? COMPLETED_CYCLES_LIST(projectId.toString()) : null, + workspaceSlug && projectId + ? () => + cyclesService.getCyclesWithParams(workspaceSlug.toString(), projectId.toString(), { + cycle_view: "completed", + }) + : null + ); + + return ; +}; diff --git a/apps/app/components/cycles/cycles-list/draft-cycles-list.tsx b/apps/app/components/cycles/cycles-list/draft-cycles-list.tsx new file mode 100644 index 000000000..e64e49dfd --- /dev/null +++ b/apps/app/components/cycles/cycles-list/draft-cycles-list.tsx @@ -0,0 +1,31 @@ +import { useRouter } from "next/router"; + +import useSWR from "swr"; + +// services +import cyclesService from "services/cycles.service"; +// components +import { CyclesView } from "components/cycles"; +// fetch-keys +import { DRAFT_CYCLES_LIST } from "constants/fetch-keys"; + +type Props = { + viewType: string | null; +}; + +export const DraftCyclesList: React.FC = ({ viewType }) => { + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { data: draftCyclesList } = useSWR( + workspaceSlug && projectId ? DRAFT_CYCLES_LIST(projectId.toString()) : null, + workspaceSlug && projectId + ? () => + cyclesService.getCyclesWithParams(workspaceSlug.toString(), projectId.toString(), { + cycle_view: "draft", + }) + : null + ); + + return ; +}; diff --git a/apps/app/components/cycles/cycles-list/index.ts b/apps/app/components/cycles/cycles-list/index.ts new file mode 100644 index 000000000..b4b4fdfb5 --- /dev/null +++ b/apps/app/components/cycles/cycles-list/index.ts @@ -0,0 +1,4 @@ +export * from "./all-cycles-list"; +export * from "./completed-cycles-list"; +export * from "./draft-cycles-list"; +export * from "./upcoming-cycles-list"; diff --git a/apps/app/components/cycles/cycles-list/upcoming-cycles-list.tsx b/apps/app/components/cycles/cycles-list/upcoming-cycles-list.tsx new file mode 100644 index 000000000..44023e618 --- /dev/null +++ b/apps/app/components/cycles/cycles-list/upcoming-cycles-list.tsx @@ -0,0 +1,31 @@ +import { useRouter } from "next/router"; + +import useSWR from "swr"; + +// services +import cyclesService from "services/cycles.service"; +// components +import { CyclesView } from "components/cycles"; +// fetch-keys +import { UPCOMING_CYCLES_LIST } from "constants/fetch-keys"; + +type Props = { + viewType: string | null; +}; + +export const UpcomingCyclesList: React.FC = ({ viewType }) => { + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { data: upcomingCyclesList } = useSWR( + workspaceSlug && projectId ? UPCOMING_CYCLES_LIST(projectId.toString()) : null, + workspaceSlug && projectId + ? () => + cyclesService.getCyclesWithParams(workspaceSlug.toString(), projectId.toString(), { + cycle_view: "upcoming", + }) + : null + ); + + return ; +}; diff --git a/apps/app/components/cycles/cycles-view.tsx b/apps/app/components/cycles/cycles-view.tsx index 0f1c917ce..fc720c251 100644 --- a/apps/app/components/cycles/cycles-view.tsx +++ b/apps/app/components/cycles/cycles-view.tsx @@ -1,246 +1,229 @@ -import React, { useEffect } from "react"; -import dynamic from "next/dynamic"; -// headless ui -import { Tab } from "@headlessui/react"; +import React, { useState } from "react"; + +import { useRouter } from "next/router"; + +import { mutate } from "swr"; + +// services +import cyclesService from "services/cycles.service"; // hooks -import useLocalStorage from "hooks/use-local-storage"; +import useToast from "hooks/use-toast"; // components import { - ActiveCycleDetails, - CompletedCyclesListProps, - AllCyclesBoard, - AllCyclesList, + CreateUpdateCycleModal, CyclesListGanttChartView, + DeleteCycleModal, + SingleCycleCard, + SingleCycleList, } from "components/cycles"; // ui import { EmptyState, Loader } from "components/ui"; -// icons -import { ChartBarIcon, ListBulletIcon, Squares2X2Icon } from "@heroicons/react/24/outline"; +// images import emptyCycle from "public/empty-state/empty-cycle.svg"; +// helpers +import { getDateRangeStatus } from "helpers/date-time.helper"; // types -import { SelectCycleType, ICycle } from "types"; +import { ICycle } from "types"; +// fetch-keys +import { + COMPLETED_CYCLES_LIST, + CURRENT_CYCLE_LIST, + CYCLES_LIST, + DRAFT_CYCLES_LIST, + UPCOMING_CYCLES_LIST, +} from "constants/fetch-keys"; type Props = { - setSelectedCycle: React.Dispatch>; - setCreateUpdateCycleModal: React.Dispatch>; - cyclesCompleteList: ICycle[] | undefined; - currentCycle: ICycle[] | undefined; - upcomingCycles: ICycle[] | undefined; - draftCycles: ICycle[] | undefined; + cycles: ICycle[] | undefined; + viewType: string | null; }; -export const CyclesView: React.FC = ({ - setSelectedCycle, - setCreateUpdateCycleModal, - cyclesCompleteList, - currentCycle, - upcomingCycles, - draftCycles, -}) => { - const { storedValue: cycleTab, setValue: setCycleTab } = useLocalStorage("cycleTab", "All"); - const { storedValue: cyclesView, setValue: setCyclesView } = useLocalStorage("cycleView", "list"); +export const CyclesView: React.FC = ({ cycles, viewType }) => { + const [createUpdateCycleModal, setCreateUpdateCycleModal] = useState(false); + const [selectedCycleToUpdate, setSelectedCycleToUpdate] = useState(null); - const currentTabValue = (tab: string | null) => { - switch (tab) { - case "All": - return 0; - case "Active": - return 1; - case "Upcoming": - return 2; - case "Completed": - return 3; - case "Drafts": - return 4; - default: - return 0; - } + const [deleteCycleModal, setDeleteCycleModal] = useState(false); + const [selectedCycleToDelete, setSelectedCycleToDelete] = useState(null); + + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { setToastAlert } = useToast(); + + const handleEditCycle = (cycle: ICycle) => { + setSelectedCycleToUpdate(cycle); + setCreateUpdateCycleModal(true); }; - const CompletedCycles = dynamic( - () => import("components/cycles").then((a) => a.CompletedCycles), - { - ssr: false, - loading: () => ( - - - - ), - } - ); + const handleDeleteCycle = (cycle: ICycle) => { + setSelectedCycleToDelete(cycle); + setDeleteCycleModal(true); + }; + + const handleAddToFavorites = (cycle: ICycle) => { + if (!workspaceSlug || !projectId) return; + + const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date); + + const fetchKey = + cycleStatus === "current" + ? CURRENT_CYCLE_LIST(projectId as string) + : cycleStatus === "upcoming" + ? UPCOMING_CYCLES_LIST(projectId as string) + : cycleStatus === "completed" + ? COMPLETED_CYCLES_LIST(projectId as string) + : DRAFT_CYCLES_LIST(projectId as string); + + mutate( + fetchKey, + (prevData) => + (prevData ?? []).map((c) => ({ + ...c, + is_favorite: c.id === cycle.id ? true : c.is_favorite, + })), + false + ); + + mutate( + CYCLES_LIST(projectId as string), + (prevData: any) => + (prevData ?? []).map((c: any) => ({ + ...c, + is_favorite: c.id === cycle.id ? true : c.is_favorite, + })), + false + ); + + cyclesService + .addCycleToFavorites(workspaceSlug as string, projectId as string, { + cycle: cycle.id, + }) + .catch(() => { + setToastAlert({ + type: "error", + title: "Error!", + message: "Couldn't add the cycle to favorites. Please try again.", + }); + }); + }; + + const handleRemoveFromFavorites = (cycle: ICycle) => { + if (!workspaceSlug || !projectId) return; + + const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date); + + const fetchKey = + cycleStatus === "current" + ? CURRENT_CYCLE_LIST(projectId as string) + : cycleStatus === "upcoming" + ? UPCOMING_CYCLES_LIST(projectId as string) + : cycleStatus === "completed" + ? COMPLETED_CYCLES_LIST(projectId as string) + : DRAFT_CYCLES_LIST(projectId as string); + + mutate( + fetchKey, + (prevData) => + (prevData ?? []).map((c) => ({ + ...c, + is_favorite: c.id === cycle.id ? false : c.is_favorite, + })), + false + ); + + mutate( + CYCLES_LIST(projectId as string), + (prevData: any) => + (prevData ?? []).map((c: any) => ({ + ...c, + is_favorite: c.id === cycle.id ? false : c.is_favorite, + })), + false + ); + + cyclesService + .removeCycleFromFavorites(workspaceSlug as string, projectId as string, cycle.id) + .catch(() => { + setToastAlert({ + type: "error", + title: "Error!", + message: "Couldn't remove the cycle from favorites. Please try again.", + }); + }); + }; return ( <> -
-

Cycles

-
- - - -
-
- { - switch (i) { - case 0: - return setCycleTab("All"); - case 1: - return setCycleTab("Active"); - case 2: - return setCycleTab("Upcoming"); - case 3: - return setCycleTab("Completed"); - case 4: - return setCycleTab("Drafts"); - default: - return setCycleTab("All"); - } - }} - > -
- - {["All", "Active", "Upcoming", "Completed", "Drafts"].map((tab, index) => { - if ( - cyclesView === "gantt_chart" && - (tab === "Active" || tab === "Drafts" || tab === "Completed") - ) - return null; - - return ( - - `rounded-3xl border px-6 py-1 outline-none ${ - selected - ? "border-brand-accent bg-brand-accent text-white font-medium" - : "border-brand-base bg-brand-base hover:bg-brand-surface-2" - }` - } - > - {tab} - - ); - })} - -
- - - {cyclesView === "list" && ( - - )} - {cyclesView === "board" && ( - - )} - {cyclesView === "gantt_chart" && ( - - )} - - {cyclesView !== "gantt_chart" && ( - - {currentCycle?.[0] ? ( - - ) : ( - setCreateUpdateCycleModal(false)} + data={selectedCycleToUpdate} + /> + + {cycles ? ( + cycles.length > 0 ? ( + viewType === "list" ? ( +
+ {cycles.map((cycle) => ( +
+
+ handleDeleteCycle(cycle)} + handleEditCycle={() => handleEditCycle(cycle)} + handleAddToFavorites={() => handleAddToFavorites(cycle)} + handleRemoveFromFavorites={() => handleRemoveFromFavorites(cycle)} + /> +
+
+ ))} +
+ ) : viewType === "board" ? ( +
+ {cycles.map((cycle) => ( + handleDeleteCycle(cycle)} + handleEditCycle={() => handleEditCycle(cycle)} + handleAddToFavorites={() => handleAddToFavorites(cycle)} + handleRemoveFromFavorites={() => handleRemoveFromFavorites(cycle)} /> - )} - - )} - - {cyclesView === "list" && ( - - )} - {cyclesView === "board" && ( - - )} - {cyclesView === "gantt_chart" && ( - - )} - - - - - {cyclesView !== "gantt_chart" && ( - - {cyclesView === "list" && ( - - )} - {cyclesView === "board" && ( - - )} - - )} - - + ))} +
+ ) : ( + + ) + ) : ( + + ) + ) : viewType === "list" ? ( + + + + + + ) : viewType === "board" ? ( + + + + + + ) : ( + + + + )} ); }; diff --git a/apps/app/components/cycles/delete-cycle-modal.tsx b/apps/app/components/cycles/delete-cycle-modal.tsx index 894e1b4eb..187c9694c 100644 --- a/apps/app/components/cycles/delete-cycle-modal.tsx +++ b/apps/app/components/cycles/delete-cycle-modal.tsx @@ -18,15 +18,15 @@ import type { ICycle } from "types"; type TConfirmCycleDeletionProps = { isOpen: boolean; setIsOpen: React.Dispatch>; - data?: ICycle; + data?: ICycle | null; }; // fetch-keys import { - CYCLE_COMPLETE_LIST, - CYCLE_CURRENT_LIST, - CYCLE_DRAFT_LIST, - CYCLE_LIST, - CYCLE_UPCOMING_LIST, + COMPLETED_CYCLES_LIST, + CURRENT_CYCLE_LIST, + CYCLES_LIST, + DRAFT_CYCLES_LIST, + UPCOMING_CYCLES_LIST, } from "constants/fetch-keys"; import { getDateRangeStatus } from "helpers/date-time.helper"; @@ -58,12 +58,12 @@ export const DeleteCycleModal: React.FC = ({ const cycleType = getDateRangeStatus(data.start_date, data.end_date); const fetchKey = cycleType === "current" - ? CYCLE_CURRENT_LIST(projectId as string) + ? CURRENT_CYCLE_LIST(projectId as string) : cycleType === "upcoming" - ? CYCLE_UPCOMING_LIST(projectId as string) + ? UPCOMING_CYCLES_LIST(projectId as string) : cycleType === "completed" - ? CYCLE_COMPLETE_LIST(projectId as string) - : CYCLE_DRAFT_LIST(projectId as string); + ? COMPLETED_CYCLES_LIST(projectId as string) + : DRAFT_CYCLES_LIST(projectId as string); mutate( fetchKey, @@ -76,7 +76,7 @@ export const DeleteCycleModal: React.FC = ({ ); mutate( - CYCLE_LIST(projectId as string), + CYCLES_LIST(projectId as string), (prevData: any) => { if (!prevData) return; return prevData.filter((cycle: any) => cycle.id !== data?.id); diff --git a/apps/app/components/cycles/empty-cycle.tsx b/apps/app/components/cycles/empty-cycle.tsx deleted file mode 100644 index af2f12a11..000000000 --- a/apps/app/components/cycles/empty-cycle.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React from "react"; -import { LinearProgressIndicator } from "components/ui"; - -export const EmptyCycle = () => { - const emptyCycleData = [ - { - id: 1, - name: "backlog", - value: 20, - color: "#DEE2E6", - }, - { - id: 2, - name: "unstarted", - value: 14, - color: "#26B5CE", - }, - { - id: 3, - name: "started", - value: 27, - color: "#F7AE59", - }, - { - id: 4, - name: "cancelled", - value: 15, - color: "#D687FF", - }, - { - id: 5, - name: "completed", - value: 14, - color: "#09A953", - }, - ]; - return ( -
-
-
-
- Cycle Name -
- - -
-
- -
- -
-
- -
-
- Cycle Name -
- - -
-
- -
- -
-
-
- -
-

Create New Cycle

-

- Sprint more effectively with Cycles by confining your project
to a fixed amount of - time. Create new cycle now. -

-
-
- ); -}; diff --git a/apps/app/components/cycles/form.tsx b/apps/app/components/cycles/form.tsx index a0bd781ce..5c0b2e080 100644 --- a/apps/app/components/cycles/form.tsx +++ b/apps/app/components/cycles/form.tsx @@ -12,7 +12,7 @@ type Props = { handleFormSubmit: (values: Partial) => Promise; handleClose: () => void; status: boolean; - data?: ICycle; + data?: ICycle | null; }; const defaultValues: Partial = { @@ -28,7 +28,6 @@ export const CycleForm: React.FC = ({ handleFormSubmit, handleClose, stat formState: { errors, isSubmitting }, handleSubmit, control, - watch, reset, } = useForm({ defaultValues, diff --git a/apps/app/components/cycles/index.ts b/apps/app/components/cycles/index.ts index 3151529c5..40355d574 100644 --- a/apps/app/components/cycles/index.ts +++ b/apps/app/components/cycles/index.ts @@ -1,18 +1,15 @@ +export * from "./cycles-list"; export * from "./active-cycle-details"; -export * from "./cycles-view"; -export * from "./completed-cycles"; +export * from "./active-cycle-stats"; export * from "./cycles-list-gantt-chart"; -export * from "./all-cycles-board"; -export * from "./all-cycles-list"; +export * from "./cycles-view"; export * from "./delete-cycle-modal"; export * from "./form"; export * from "./gantt-chart"; export * from "./modal"; export * from "./select"; export * from "./sidebar"; -export * from "./single-cycle-list"; export * from "./single-cycle-card"; -export * from "./empty-cycle"; +export * from "./single-cycle-list"; export * from "./transfer-issues-modal"; export * from "./transfer-issues"; -export * from "./active-cycle-stats"; diff --git a/apps/app/components/cycles/modal.tsx b/apps/app/components/cycles/modal.tsx index 8a0c6b8ad..e27b5ac32 100644 --- a/apps/app/components/cycles/modal.tsx +++ b/apps/app/components/cycles/modal.tsx @@ -18,18 +18,18 @@ import { getDateRangeStatus, isDateGreaterThanToday } from "helpers/date-time.he import type { ICycle } from "types"; // fetch keys import { - CYCLE_COMPLETE_LIST, - CYCLE_CURRENT_LIST, - CYCLE_DRAFT_LIST, - CYCLE_INCOMPLETE_LIST, - CYCLE_LIST, - CYCLE_UPCOMING_LIST, + COMPLETED_CYCLES_LIST, + CURRENT_CYCLE_LIST, + CYCLES_LIST, + DRAFT_CYCLES_LIST, + INCOMPLETE_CYCLES_LIST, + UPCOMING_CYCLES_LIST, } from "constants/fetch-keys"; type CycleModalProps = { isOpen: boolean; handleClose: () => void; - data?: ICycle; + data?: ICycle | null; }; export const CreateUpdateCycleModal: React.FC = ({ @@ -43,24 +43,26 @@ export const CreateUpdateCycleModal: React.FC = ({ const { setToastAlert } = useToast(); const createCycle = async (payload: Partial) => { + if (!workspaceSlug || !projectId) return; + await cycleService - .createCycle(workspaceSlug as string, projectId as string, payload) + .createCycle(workspaceSlug.toString(), projectId.toString(), payload) .then((res) => { switch (getDateRangeStatus(res.start_date, res.end_date)) { case "completed": - mutate(CYCLE_COMPLETE_LIST(projectId as string)); + mutate(COMPLETED_CYCLES_LIST(projectId.toString())); break; case "current": - mutate(CYCLE_CURRENT_LIST(projectId as string)); + mutate(CURRENT_CYCLE_LIST(projectId.toString())); break; case "upcoming": - mutate(CYCLE_UPCOMING_LIST(projectId as string)); + mutate(UPCOMING_CYCLES_LIST(projectId.toString())); break; default: - mutate(CYCLE_DRAFT_LIST(projectId as string)); + mutate(DRAFT_CYCLES_LIST(projectId.toString())); } - mutate(CYCLE_INCOMPLETE_LIST(projectId as string)); - mutate(CYCLE_LIST(projectId as string)); + mutate(INCOMPLETE_CYCLES_LIST(projectId.toString())); + mutate(CYCLES_LIST(projectId.toString())); handleClose(); setToastAlert({ @@ -69,7 +71,7 @@ export const CreateUpdateCycleModal: React.FC = ({ message: "Cycle created successfully.", }); }) - .catch((err) => { + .catch(() => { setToastAlert({ type: "error", title: "Error!", @@ -79,39 +81,41 @@ export const CreateUpdateCycleModal: React.FC = ({ }; const updateCycle = async (cycleId: string, payload: Partial) => { + if (!workspaceSlug || !projectId) return; + await cycleService - .updateCycle(workspaceSlug as string, projectId as string, cycleId, payload) + .updateCycle(workspaceSlug.toString(), projectId.toString(), cycleId, payload) .then((res) => { switch (getDateRangeStatus(data?.start_date, data?.end_date)) { case "completed": - mutate(CYCLE_COMPLETE_LIST(projectId as string)); + mutate(COMPLETED_CYCLES_LIST(projectId.toString())); break; case "current": - mutate(CYCLE_CURRENT_LIST(projectId as string)); + mutate(CURRENT_CYCLE_LIST(projectId.toString())); break; case "upcoming": - mutate(CYCLE_UPCOMING_LIST(projectId as string)); + mutate(UPCOMING_CYCLES_LIST(projectId.toString())); break; default: - mutate(CYCLE_DRAFT_LIST(projectId as string)); + mutate(DRAFT_CYCLES_LIST(projectId.toString())); } - mutate(CYCLE_LIST(projectId as string)); + mutate(CYCLES_LIST(projectId.toString())); if ( getDateRangeStatus(data?.start_date, data?.end_date) != getDateRangeStatus(res.start_date, res.end_date) ) { switch (getDateRangeStatus(res.start_date, res.end_date)) { case "completed": - mutate(CYCLE_COMPLETE_LIST(projectId as string)); + mutate(COMPLETED_CYCLES_LIST(projectId.toString())); break; case "current": - mutate(CYCLE_CURRENT_LIST(projectId as string)); + mutate(CURRENT_CYCLE_LIST(projectId.toString())); break; case "upcoming": - mutate(CYCLE_UPCOMING_LIST(projectId as string)); + mutate(UPCOMING_CYCLES_LIST(projectId.toString())); break; default: - mutate(CYCLE_DRAFT_LIST(projectId as string)); + mutate(DRAFT_CYCLES_LIST(projectId.toString())); } } diff --git a/apps/app/components/cycles/select.tsx b/apps/app/components/cycles/select.tsx index 854749ec5..5a6f00ada 100644 --- a/apps/app/components/cycles/select.tsx +++ b/apps/app/components/cycles/select.tsx @@ -14,7 +14,7 @@ import cycleServices from "services/cycles.service"; // components import { CreateUpdateCycleModal } from "components/cycles"; // fetch-keys -import { CYCLE_LIST } from "constants/fetch-keys"; +import { CYCLES_LIST } from "constants/fetch-keys"; export type IssueCycleSelectProps = { projectId: string; @@ -36,7 +36,7 @@ export const CycleSelect: React.FC = ({ const { workspaceSlug } = router.query; const { data: cycles } = useSWR( - workspaceSlug && projectId ? CYCLE_LIST(projectId) : null, + workspaceSlug && projectId ? CYCLES_LIST(projectId) : null, workspaceSlug && projectId ? () => cycleServices.getCyclesWithParams(workspaceSlug as string, projectId as string, { diff --git a/apps/app/components/cycles/single-cycle-card.tsx b/apps/app/components/cycles/single-cycle-card.tsx index b6349b2ad..3cc2eaa38 100644 --- a/apps/app/components/cycles/single-cycle-card.tsx +++ b/apps/app/components/cycles/single-cycle-card.tsx @@ -4,20 +4,17 @@ import Link from "next/link"; import Image from "next/image"; import { useRouter } from "next/router"; -import { mutate } from "swr"; - -// services -import cyclesService from "services/cycles.service"; +// headless ui +import { Disclosure, Transition } from "@headlessui/react"; // hooks import useToast from "hooks/use-toast"; +// components +import { SingleProgressStats } from "components/core"; // ui import { CustomMenu, LinearProgressIndicator, Tooltip } from "components/ui"; -import { Disclosure, Transition } from "@headlessui/react"; -import { AssigneesList, Avatar } from "components/ui/avatar"; -import { SingleProgressStats } from "components/core"; - +import { AssigneesList } from "components/ui/avatar"; // icons -import { CalendarDaysIcon, ExclamationCircleIcon } from "@heroicons/react/20/solid"; +import { CalendarDaysIcon } from "@heroicons/react/20/solid"; import { TargetIcon, ContrastIcon, @@ -42,19 +39,13 @@ import { import { copyTextToClipboard, truncateText } from "helpers/string.helper"; // types import { ICycle } from "types"; -// fetch-keys -import { - CYCLE_COMPLETE_LIST, - CYCLE_CURRENT_LIST, - CYCLE_DRAFT_LIST, - CYCLE_LIST, - CYCLE_UPCOMING_LIST, -} from "constants/fetch-keys"; type TSingleStatProps = { cycle: ICycle; handleEditCycle: () => void; handleDeleteCycle: () => void; + handleAddToFavorites: () => void; + handleRemoveFromFavorites: () => void; isCompleted?: boolean; }; @@ -90,6 +81,8 @@ export const SingleCycleCard: React.FC = ({ cycle, handleEditCycle, handleDeleteCycle, + handleAddToFavorites, + handleRemoveFromFavorites, isCompleted = false, }) => { const router = useRouter(); @@ -101,94 +94,6 @@ export const SingleCycleCard: React.FC = ({ const endDate = new Date(cycle.end_date ?? ""); const startDate = new Date(cycle.start_date ?? ""); - const handleAddToFavorites = () => { - if (!workspaceSlug || !projectId || !cycle) return; - - const fetchKey = - cycleStatus === "current" - ? CYCLE_CURRENT_LIST(projectId as string) - : cycleStatus === "upcoming" - ? CYCLE_UPCOMING_LIST(projectId as string) - : cycleStatus === "completed" - ? CYCLE_COMPLETE_LIST(projectId as string) - : CYCLE_DRAFT_LIST(projectId as string); - - mutate( - fetchKey, - (prevData) => - (prevData ?? []).map((c) => ({ - ...c, - is_favorite: c.id === cycle.id ? true : c.is_favorite, - })), - false - ); - - mutate( - CYCLE_LIST(projectId as string), - (prevData: any) => - (prevData ?? []).map((c: any) => ({ - ...c, - is_favorite: c.id === cycle.id ? true : c.is_favorite, - })), - false - ); - - cyclesService - .addCycleToFavorites(workspaceSlug as string, projectId as string, { - cycle: cycle.id, - }) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Couldn't add the cycle to favorites. Please try again.", - }); - }); - }; - - const handleRemoveFromFavorites = () => { - if (!workspaceSlug || !projectId || !cycle) return; - - const fetchKey = - cycleStatus === "current" - ? CYCLE_CURRENT_LIST(projectId as string) - : cycleStatus === "upcoming" - ? CYCLE_UPCOMING_LIST(projectId as string) - : cycleStatus === "completed" - ? CYCLE_COMPLETE_LIST(projectId as string) - : CYCLE_DRAFT_LIST(projectId as string); - - mutate( - fetchKey, - (prevData) => - (prevData ?? []).map((c) => ({ - ...c, - is_favorite: c.id === cycle.id ? false : c.is_favorite, - })), - false - ); - - mutate( - CYCLE_LIST(projectId as string), - (prevData: any) => - (prevData ?? []).map((c: any) => ({ - ...c, - is_favorite: c.id === cycle.id ? false : c.is_favorite, - })), - false - ); - - cyclesService - .removeCycleFromFavorites(workspaceSlug as string, projectId as string, cycle.id) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Couldn't remove the cycle from favorites. Please try again.", - }); - }); - }; - const handleCopyText = () => { const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : ""; diff --git a/apps/app/components/cycles/single-cycle-list.tsx b/apps/app/components/cycles/single-cycle-list.tsx index aa2bf0975..cadabba7f 100644 --- a/apps/app/components/cycles/single-cycle-list.tsx +++ b/apps/app/components/cycles/single-cycle-list.tsx @@ -4,17 +4,12 @@ import Link from "next/link"; import Image from "next/image"; import { useRouter } from "next/router"; -import { mutate } from "swr"; - -// services -import cyclesService from "services/cycles.service"; // hooks import useToast from "hooks/use-toast"; // ui import { CustomMenu, LinearProgressIndicator, Tooltip } from "components/ui"; - // icons -import { CalendarDaysIcon, ExclamationCircleIcon } from "@heroicons/react/20/solid"; +import { CalendarDaysIcon } from "@heroicons/react/20/solid"; import { TargetIcon, ContrastIcon, @@ -33,20 +28,13 @@ import { import { copyTextToClipboard, truncateText } from "helpers/string.helper"; // types import { ICycle } from "types"; -// fetch-keys -import { - CYCLE_COMPLETE_LIST, - CYCLE_CURRENT_LIST, - CYCLE_DRAFT_LIST, - CYCLE_LIST, - CYCLE_UPCOMING_LIST, -} from "constants/fetch-keys"; -import { type } from "os"; type TSingleStatProps = { cycle: ICycle; handleEditCycle: () => void; handleDeleteCycle: () => void; + handleAddToFavorites: () => void; + handleRemoveFromFavorites: () => void; isCompleted?: boolean; }; @@ -124,6 +112,8 @@ export const SingleCycleList: React.FC = ({ cycle, handleEditCycle, handleDeleteCycle, + handleAddToFavorites, + handleRemoveFromFavorites, isCompleted = false, }) => { const router = useRouter(); @@ -135,94 +125,6 @@ export const SingleCycleList: React.FC = ({ const endDate = new Date(cycle.end_date ?? ""); const startDate = new Date(cycle.start_date ?? ""); - const handleAddToFavorites = () => { - if (!workspaceSlug || !projectId || !cycle) return; - - const fetchKey = - cycleStatus === "current" - ? CYCLE_CURRENT_LIST(projectId as string) - : cycleStatus === "upcoming" - ? CYCLE_UPCOMING_LIST(projectId as string) - : cycleStatus === "completed" - ? CYCLE_COMPLETE_LIST(projectId as string) - : CYCLE_DRAFT_LIST(projectId as string); - - mutate( - fetchKey, - (prevData) => - (prevData ?? []).map((c) => ({ - ...c, - is_favorite: c.id === cycle.id ? true : c.is_favorite, - })), - false - ); - - mutate( - CYCLE_LIST(projectId as string), - (prevData: any) => - (prevData ?? []).map((c: any) => ({ - ...c, - is_favorite: c.id === cycle.id ? true : c.is_favorite, - })), - false - ); - - cyclesService - .addCycleToFavorites(workspaceSlug as string, projectId as string, { - cycle: cycle.id, - }) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Couldn't add the cycle to favorites. Please try again.", - }); - }); - }; - - const handleRemoveFromFavorites = () => { - if (!workspaceSlug || !projectId || !cycle) return; - - const fetchKey = - cycleStatus === "current" - ? CYCLE_CURRENT_LIST(projectId as string) - : cycleStatus === "upcoming" - ? CYCLE_UPCOMING_LIST(projectId as string) - : cycleStatus === "completed" - ? CYCLE_COMPLETE_LIST(projectId as string) - : CYCLE_DRAFT_LIST(projectId as string); - - mutate( - fetchKey, - (prevData) => - (prevData ?? []).map((c) => ({ - ...c, - is_favorite: c.id === cycle.id ? false : c.is_favorite, - })), - false - ); - - mutate( - CYCLE_LIST(projectId as string), - (prevData: any) => - (prevData ?? []).map((c: any) => ({ - ...c, - is_favorite: c.id === cycle.id ? false : c.is_favorite, - })), - false - ); - - cyclesService - .removeCycleFromFavorites(workspaceSlug as string, projectId as string, cycle.id) - .catch(() => { - setToastAlert({ - type: "error", - title: "Error!", - message: "Couldn't remove the cycle from favorites. Please try again.", - }); - }); - }; - const handleCopyText = () => { const originURL = typeof window !== "undefined" && window.location.origin ? window.location.origin : ""; @@ -250,7 +152,7 @@ export const SingleCycleList: React.FC = ({ return (
-
+
diff --git a/apps/app/components/cycles/transfer-issues-modal.tsx b/apps/app/components/cycles/transfer-issues-modal.tsx index 8d44c14d0..30db2f320 100644 --- a/apps/app/components/cycles/transfer-issues-modal.tsx +++ b/apps/app/components/cycles/transfer-issues-modal.tsx @@ -10,16 +10,16 @@ import { Dialog, Transition } from "@headlessui/react"; import cyclesService from "services/cycles.service"; // hooks import useToast from "hooks/use-toast"; +import useIssuesView from "hooks/use-issues-view"; //icons import { MagnifyingGlassIcon, XMarkIcon } from "@heroicons/react/24/outline"; -import { ContrastIcon, CyclesIcon, ExclamationIcon, TransferIcon } from "components/icons"; +import { ContrastIcon, ExclamationIcon, TransferIcon } from "components/icons"; // fetch-key -import { CYCLE_INCOMPLETE_LIST, CYCLE_ISSUES_WITH_PARAMS } from "constants/fetch-keys"; +import { CYCLE_ISSUES_WITH_PARAMS, INCOMPLETE_CYCLES_LIST } from "constants/fetch-keys"; // types import { ICycle } from "types"; //helper import { getDateRangeStatus } from "helpers/date-time.helper"; -import useIssuesView from "hooks/use-issues-view"; type Props = { isOpen: boolean; @@ -57,7 +57,7 @@ export const TransferIssuesModal: React.FC = ({ isOpen, handleClose }) => }; const { data: incompleteCycles } = useSWR( - workspaceSlug && projectId ? CYCLE_INCOMPLETE_LIST(projectId as string) : null, + workspaceSlug && projectId ? INCOMPLETE_CYCLES_LIST(projectId as string) : null, workspaceSlug && projectId ? () => cyclesService.getCyclesWithParams(workspaceSlug as string, projectId as string, { diff --git a/apps/app/components/issues/sidebar-select/cycle.tsx b/apps/app/components/issues/sidebar-select/cycle.tsx index d9e7772a3..ad112ab52 100644 --- a/apps/app/components/issues/sidebar-select/cycle.tsx +++ b/apps/app/components/issues/sidebar-select/cycle.tsx @@ -16,7 +16,7 @@ import { ContrastIcon } from "components/icons"; // types import { ICycle, IIssue, UserAuth } from "types"; // fetch-keys -import { CYCLE_ISSUES, CYCLE_INCOMPLETE_LIST, ISSUE_DETAILS } from "constants/fetch-keys"; +import { CYCLE_ISSUES, INCOMPLETE_CYCLES_LIST, ISSUE_DETAILS } from "constants/fetch-keys"; type Props = { issueDetail: IIssue | undefined; @@ -33,7 +33,7 @@ export const SidebarCycleSelect: React.FC = ({ const { workspaceSlug, projectId, issueId } = router.query; const { data: incompleteCycles } = useSWR( - workspaceSlug && projectId ? CYCLE_INCOMPLETE_LIST(projectId as string) : null, + workspaceSlug && projectId ? INCOMPLETE_CYCLES_LIST(projectId as string) : null, workspaceSlug && projectId ? () => cyclesService.getCyclesWithParams(workspaceSlug as string, projectId as string, { diff --git a/apps/app/constants/fetch-keys.ts b/apps/app/constants/fetch-keys.ts index 5b286bcc1..75b187b2b 100644 --- a/apps/app/constants/fetch-keys.ts +++ b/apps/app/constants/fetch-keys.ts @@ -71,9 +71,18 @@ export const PROJECT_ISSUE_LABELS = (projectId: string) => export const PROJECT_GITHUB_REPOSITORY = (projectId: string) => `PROJECT_GITHUB_REPOSITORY_${projectId.toUpperCase()}`; -export const CYCLE_LIST = (projectId: string) => `CYCLE_LIST_${projectId.toUpperCase()}`; -export const CYCLE_INCOMPLETE_LIST = (projectId: string) => - `CYCLE_INCOMPLETE_LIST_${projectId.toUpperCase()}`; +// cycles +export const CYCLES_LIST = (projectId: string) => `CYCLE_LIST_${projectId.toUpperCase()}`; +export const INCOMPLETE_CYCLES_LIST = (projectId: string) => + `INCOMPLETE_CYCLES_LIST_${projectId.toUpperCase()}`; +export const CURRENT_CYCLE_LIST = (projectId: string) => + `CURRENT_CYCLE_LIST_${projectId.toUpperCase()}`; +export const UPCOMING_CYCLES_LIST = (projectId: string) => + `UPCOMING_CYCLES_LIST_${projectId.toUpperCase()}`; +export const DRAFT_CYCLES_LIST = (projectId: string) => + `DRAFT_CYCLES_LIST_${projectId.toUpperCase()}`; +export const COMPLETED_CYCLES_LIST = (projectId: string) => + `COMPLETED_CYCLES_LIST_${projectId.toUpperCase()}`; export const CYCLE_ISSUES = (cycleId: string) => `CYCLE_ISSUES_${cycleId.toUpperCase()}`; export const CYCLE_ISSUES_WITH_PARAMS = (cycleId: string, params?: any) => { if (!params) return `CYCLE_ISSUES_WITH_PARAMS_${cycleId.toUpperCase()}`; @@ -84,15 +93,6 @@ export const CYCLE_ISSUES_WITH_PARAMS = (cycleId: string, params?: any) => { }; export const CYCLE_DETAILS = (cycleId: string) => `CYCLE_DETAILS_${cycleId.toUpperCase()}`; -export const CYCLE_CURRENT_LIST = (projectId: string) => - `CYCLE_CURRENT_LIST${projectId.toUpperCase()}`; -export const CYCLE_UPCOMING_LIST = (projectId: string) => - `CYCLE_UPCOMING_LIST${projectId.toUpperCase()}`; -export const CYCLE_DRAFT_LIST = (projectId: string) => - `CYCLE_DRAFT_LIST_${projectId.toUpperCase()}`; -export const CYCLE_COMPLETE_LIST = (projectId: string) => - `CYCLE_COMPLETE_LIST_${projectId.toUpperCase()}`; - export const STATES_LIST = (projectId: string) => `STATES_LIST_${projectId.toUpperCase()}`; export const STATE_DETAILS = "STATE_DETAILS"; diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx index 3e5fafe6f..08c04a736 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/[cycleId].tsx @@ -30,7 +30,7 @@ import { getDateRangeStatus } from "helpers/date-time.helper"; // fetch-keys import { CYCLE_ISSUES, - CYCLE_LIST, + CYCLES_LIST, PROJECT_DETAILS, CYCLE_DETAILS, PROJECT_ISSUES_LIST, @@ -54,7 +54,7 @@ const SingleCycle: React.FC = () => { ); const { data: cycles } = useSWR( - workspaceSlug && projectId ? CYCLE_LIST(projectId as string) : null, + workspaceSlug && projectId ? CYCLES_LIST(projectId as string) : null, workspaceSlug && projectId ? () => cycleServices.getCyclesWithParams(workspaceSlug as string, projectId as string, { diff --git a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx index fd6df9832..4573cd53c 100644 --- a/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx +++ b/apps/app/pages/[workspaceSlug]/projects/[projectId]/cycles/index.tsx @@ -3,92 +3,44 @@ import React, { useEffect, useState } from "react"; import { useRouter } from "next/router"; import useSWR from "swr"; + +// headless ui +import { Tab } from "@headlessui/react"; // hooks +import useLocalStorage from "hooks/use-local-storage"; // services import cycleService from "services/cycles.service"; import projectService from "services/project.service"; // layouts import { ProjectAuthorizationWrapper } from "layouts/auth-layout"; // components -import { CreateUpdateCycleModal, CyclesView } from "components/cycles"; +import { + ActiveCycleDetails, + AllCyclesList, + CompletedCyclesList, + CreateUpdateCycleModal, + DraftCyclesList, + UpcomingCyclesList, +} from "components/cycles"; // ui import { PrimaryButton } from "components/ui"; import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs"; // icons -import { PlusIcon } from "@heroicons/react/24/outline"; +import { ListBulletIcon, PlusIcon, Squares2X2Icon } from "@heroicons/react/24/outline"; // types import { SelectCycleType } from "types"; import type { NextPage } from "next"; // fetch-keys -import { - CYCLE_DRAFT_LIST, - PROJECT_DETAILS, - CYCLE_UPCOMING_LIST, - CYCLE_CURRENT_LIST, - CYCLE_LIST, -} from "constants/fetch-keys"; +import { CURRENT_CYCLE_LIST, PROJECT_DETAILS } from "constants/fetch-keys"; + +const tabsList = ["All", "Active", "Upcoming", "Completed", "Drafts"]; const ProjectCycles: NextPage = () => { const [selectedCycle, setSelectedCycle] = useState(); const [createUpdateCycleModal, setCreateUpdateCycleModal] = useState(false); - const router = useRouter(); - const { workspaceSlug, projectId } = router.query; - - const { data: activeProject } = useSWR( - workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null, - workspaceSlug && projectId - ? () => projectService.getProject(workspaceSlug as string, projectId as string) - : null - ); - - const { data: draftCycles } = useSWR( - workspaceSlug && projectId ? CYCLE_DRAFT_LIST(projectId as string) : null, - workspaceSlug && projectId - ? () => - cycleService.getCyclesWithParams(workspaceSlug as string, projectId as string, { - cycle_view: "draft", - }) - : null - ); - - const { data: currentCycle } = useSWR( - workspaceSlug && projectId ? CYCLE_CURRENT_LIST(projectId as string) : null, - workspaceSlug && projectId - ? () => - cycleService.getCyclesWithParams(workspaceSlug as string, projectId as string, { - cycle_view: "current", - }) - : null - ); - - const { data: upcomingCycles } = useSWR( - workspaceSlug && projectId ? CYCLE_UPCOMING_LIST(projectId as string) : null, - workspaceSlug && projectId - ? () => - cycleService.getCyclesWithParams(workspaceSlug as string, projectId as string, { - cycle_view: "upcoming", - }) - : null - ); - - const { data: cyclesCompleteList } = useSWR( - workspaceSlug && projectId ? CYCLE_LIST(projectId as string) : null, - workspaceSlug && projectId - ? () => - cycleService.getCyclesWithParams(workspaceSlug as string, projectId as string, { - cycle_view: "all", - }) - : null - ); - - useEffect(() => { - if (createUpdateCycleModal) return; - const timer = setTimeout(() => { - setSelectedCycle(undefined); - clearTimeout(timer); - }, 500); - }, [createUpdateCycleModal]); + const { storedValue: cycleTab, setValue: setCycleTab } = useLocalStorage("cycleTab", "All"); + const { storedValue: cyclesView, setValue: setCyclesView } = useLocalStorage("cycleView", "list"); const currentTabValue = (tab: string | null) => { switch (tab) { @@ -107,6 +59,34 @@ const ProjectCycles: NextPage = () => { } }; + const router = useRouter(); + const { workspaceSlug, projectId } = router.query; + + const { data: activeProject } = useSWR( + workspaceSlug && projectId ? PROJECT_DETAILS(projectId as string) : null, + workspaceSlug && projectId + ? () => projectService.getProject(workspaceSlug as string, projectId as string) + : null + ); + + const { data: currentCycle } = useSWR( + workspaceSlug && projectId ? CURRENT_CYCLE_LIST(projectId as string) : null, + workspaceSlug && projectId + ? () => + cycleService.getCyclesWithParams(workspaceSlug as string, projectId as string, { + cycle_view: "current", + }) + : null + ); + + useEffect(() => { + if (createUpdateCycleModal) return; + const timer = setTimeout(() => { + setSelectedCycle(undefined); + clearTimeout(timer); + }, 500); + }, [createUpdateCycleModal]); + return ( { data={selectedCycle} />
- +
+

Cycles

+
+ + + +
+
+ { + switch (i) { + case 0: + return setCycleTab("All"); + case 1: + return setCycleTab("Active"); + case 2: + return setCycleTab("Upcoming"); + case 3: + return setCycleTab("Completed"); + case 4: + return setCycleTab("Drafts"); + default: + return setCycleTab("All"); + } + }} + > + + {tabsList.map((tab, index) => { + if (cyclesView === "gantt_chart" && (tab === "Active" || tab === "Drafts")) + return null; + + return ( + + `rounded-3xl border px-6 py-1 outline-none ${ + selected + ? "border-brand-accent bg-brand-accent text-white font-medium" + : "border-brand-base bg-brand-base hover:bg-brand-surface-2" + }` + } + > + {tab} + + ); + })} + + + + + + {cyclesView !== "gantt_chart" && ( + + {currentCycle?.[0] ? ( + + ) : ( +
+

+ No active cycle is present. +

+
+ )} +
+ )} + + + + + + + {cyclesView !== "gantt_chart" && ( + + + + )} +
+
);