chore: cycles loading, fix: cycles favorite mutation (#379)
This commit is contained in:
parent
b54a1f221f
commit
64978969a0
10 changed files with 206 additions and 132 deletions
|
|
@ -14,6 +14,7 @@ import { CompletedCycleIcon } from "components/icons";
|
|||
import { ICycle, SelectCycleType } from "types";
|
||||
// fetch-keys
|
||||
import { CYCLE_COMPLETE_LIST } from "constants/fetch-keys";
|
||||
import { Loader } from "components/ui";
|
||||
|
||||
export interface CompletedCyclesListProps {
|
||||
setCreateUpdateCycleModal: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
|
|
@ -49,38 +50,42 @@ export const CompletedCyclesList: React.FC<CompletedCyclesListProps> = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
{completedCycles && (
|
||||
<>
|
||||
<DeleteCycleModal
|
||||
isOpen={
|
||||
cycleDeleteModal &&
|
||||
!!selectedCycleForDelete &&
|
||||
selectedCycleForDelete.actionType === "delete"
|
||||
}
|
||||
setIsOpen={setCycleDeleteModal}
|
||||
data={selectedCycleForDelete}
|
||||
/>
|
||||
{completedCycles?.completed_cycles.length > 0 ? (
|
||||
<div className="grid grid-cols-1 gap-9 md:grid-cols-2 lg:grid-cols-3">
|
||||
{completedCycles.completed_cycles.map((cycle) => (
|
||||
<SingleCycleCard
|
||||
key={cycle.id}
|
||||
cycle={cycle}
|
||||
handleDeleteCycle={() => handleDeleteCycle(cycle)}
|
||||
handleEditCycle={() => handleEditCycle(cycle)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center gap-4 text-center">
|
||||
<CompletedCycleIcon height="56" width="56" />
|
||||
<h3 className="text-gray-500">
|
||||
No completed cycles yet. Create with{" "}
|
||||
<pre className="inline rounded bg-gray-200 px-2 py-1">Q</pre>.
|
||||
</h3>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
<DeleteCycleModal
|
||||
isOpen={
|
||||
cycleDeleteModal &&
|
||||
!!selectedCycleForDelete &&
|
||||
selectedCycleForDelete.actionType === "delete"
|
||||
}
|
||||
setIsOpen={setCycleDeleteModal}
|
||||
data={selectedCycleForDelete}
|
||||
/>
|
||||
{completedCycles ? (
|
||||
completedCycles.completed_cycles.length > 0 ? (
|
||||
<div className="grid grid-cols-1 gap-9 md:grid-cols-2 lg:grid-cols-3">
|
||||
{completedCycles.completed_cycles.map((cycle) => (
|
||||
<SingleCycleCard
|
||||
key={cycle.id}
|
||||
cycle={cycle}
|
||||
handleDeleteCycle={() => handleDeleteCycle(cycle)}
|
||||
handleEditCycle={() => handleEditCycle(cycle)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center gap-4 text-center">
|
||||
<CompletedCycleIcon height="56" width="56" />
|
||||
<h3 className="text-gray-500">
|
||||
No completed cycles yet. Create with{" "}
|
||||
<pre className="inline rounded bg-gray-200 px-2 py-1">Q</pre>.
|
||||
</h3>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<Loader className="grid grid-cols-1 gap-9 md:grid-cols-2 lg:grid-cols-3">
|
||||
<Loader.Item height="200px" />
|
||||
<Loader.Item height="200px" />
|
||||
<Loader.Item height="200px" />
|
||||
</Loader>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,12 +6,13 @@ import { DeleteCycleModal, SingleCycleCard } from "components/cycles";
|
|||
import { CompletedCycleIcon, CurrentCycleIcon, UpcomingCycleIcon } from "components/icons";
|
||||
// types
|
||||
import { ICycle, SelectCycleType } from "types";
|
||||
import { Loader } from "components/ui";
|
||||
|
||||
type TCycleStatsViewProps = {
|
||||
cycles: ICycle[];
|
||||
cycles: ICycle[] | undefined;
|
||||
setCreateUpdateCycleModal: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setSelectedCycle: React.Dispatch<React.SetStateAction<SelectCycleType>>;
|
||||
type: "current" | "upcoming" | "completed";
|
||||
type: "current" | "upcoming" | "draft";
|
||||
};
|
||||
|
||||
export const CyclesList: React.FC<TCycleStatsViewProps> = ({
|
||||
|
|
@ -44,31 +45,37 @@ export const CyclesList: React.FC<TCycleStatsViewProps> = ({
|
|||
setIsOpen={setCycleDeleteModal}
|
||||
data={selectedCycleForDelete}
|
||||
/>
|
||||
{cycles.length > 0 ? (
|
||||
<div className="grid grid-cols-1 gap-9 md:grid-cols-2 lg:grid-cols-3">
|
||||
{cycles.map((cycle) => (
|
||||
<SingleCycleCard
|
||||
key={cycle.id}
|
||||
cycle={cycle}
|
||||
handleDeleteCycle={() => handleDeleteCycle(cycle)}
|
||||
handleEditCycle={() => handleEditCycle(cycle)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{cycles ? (
|
||||
cycles.length > 0 ? (
|
||||
<div className="grid grid-cols-1 gap-9 md:grid-cols-2 lg:grid-cols-3">
|
||||
{cycles.map((cycle) => (
|
||||
<SingleCycleCard
|
||||
key={cycle.id}
|
||||
cycle={cycle}
|
||||
handleDeleteCycle={() => handleDeleteCycle(cycle)}
|
||||
handleEditCycle={() => handleEditCycle(cycle)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center gap-4 text-center">
|
||||
{type === "upcoming" ? (
|
||||
<UpcomingCycleIcon height="56" width="56" />
|
||||
) : type === "draft" ? (
|
||||
<CompletedCycleIcon height="56" width="56" />
|
||||
) : (
|
||||
<CurrentCycleIcon height="56" width="56" />
|
||||
)}
|
||||
<h3 className="text-gray-500">
|
||||
No {type} {type === "current" ? "cycle" : "cycles"} yet. Create with{" "}
|
||||
<pre className="inline rounded bg-gray-200 px-2 py-1">Q</pre>.
|
||||
</h3>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center gap-4 text-center">
|
||||
{type === "upcoming" ? (
|
||||
<UpcomingCycleIcon height="56" width="56" />
|
||||
) : type === "completed" ? (
|
||||
<CompletedCycleIcon height="56" width="56" />
|
||||
) : (
|
||||
<CurrentCycleIcon height="56" width="56" />
|
||||
)}
|
||||
<h3 className="text-gray-500">
|
||||
No {type} {type === "current" ? "cycle" : "cycles"} yet. Create with{" "}
|
||||
<pre className="inline rounded bg-gray-200 px-2 py-1">Q</pre>.
|
||||
</h3>
|
||||
</div>
|
||||
<Loader className="grid grid-cols-1 gap-9 md:grid-cols-2 lg:grid-cols-3">
|
||||
<Loader.Item height="300px" />
|
||||
</Loader>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -43,11 +43,7 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
|
|||
await cycleService
|
||||
.createCycle(workspaceSlug as string, projectId as string, payload)
|
||||
.then((res) => {
|
||||
switch (
|
||||
res?.start_date && res.end_date
|
||||
? getDateRangeStatus(res?.start_date, res.end_date)
|
||||
: "draft"
|
||||
) {
|
||||
switch (getDateRangeStatus(res.start_date, res.end_date)) {
|
||||
case "completed":
|
||||
mutate(CYCLE_COMPLETE_LIST(projectId as string));
|
||||
break;
|
||||
|
|
@ -81,11 +77,7 @@ export const CreateUpdateCycleModal: React.FC<CycleModalProps> = ({
|
|||
await cycleService
|
||||
.updateCycle(workspaceSlug as string, projectId as string, cycleId, payload)
|
||||
.then((res) => {
|
||||
switch (
|
||||
res?.start_date && res.end_date
|
||||
? getDateRangeStatus(res?.start_date, res.end_date)
|
||||
: "draft"
|
||||
) {
|
||||
switch (getDateRangeStatus(res.start_date, res.end_date)) {
|
||||
case "completed":
|
||||
mutate(CYCLE_COMPLETE_LIST(projectId as string));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -17,13 +17,25 @@ import { Disclosure, Transition } from "@headlessui/react";
|
|||
import { CalendarDaysIcon } from "@heroicons/react/20/solid";
|
||||
import { ChevronDownIcon, PencilIcon, StarIcon } from "@heroicons/react/24/outline";
|
||||
// helpers
|
||||
import { renderShortDateWithYearFormat } from "helpers/date-time.helper";
|
||||
import { getDateRangeStatus, renderShortDateWithYearFormat } from "helpers/date-time.helper";
|
||||
import { groupBy } from "helpers/array.helper";
|
||||
import { capitalizeFirstLetter, copyTextToClipboard } from "helpers/string.helper";
|
||||
// types
|
||||
import { CycleIssueResponse, ICycle } from "types";
|
||||
import {
|
||||
CompletedCyclesResponse,
|
||||
CurrentAndUpcomingCyclesResponse,
|
||||
CycleIssueResponse,
|
||||
DraftCyclesResponse,
|
||||
ICycle,
|
||||
} from "types";
|
||||
// fetch-keys
|
||||
import { CYCLE_ISSUES, CYCLE_LIST } from "constants/fetch-keys";
|
||||
import {
|
||||
CYCLE_COMPLETE_LIST,
|
||||
CYCLE_CURRENT_AND_UPCOMING_LIST,
|
||||
CYCLE_DRAFT_LIST,
|
||||
CYCLE_ISSUES,
|
||||
CYCLE_LIST,
|
||||
} from "constants/fetch-keys";
|
||||
|
||||
type TSingleStatProps = {
|
||||
cycle: ICycle;
|
||||
|
|
@ -75,15 +87,45 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = (props) => {
|
|||
cycle: cycle.id,
|
||||
})
|
||||
.then(() => {
|
||||
mutate<ICycle[]>(
|
||||
CYCLE_LIST(projectId as string),
|
||||
(prevData) =>
|
||||
(prevData ?? []).map((c) => ({
|
||||
...c,
|
||||
is_favorite: c.id === cycle.id ? true : c.is_favorite,
|
||||
})),
|
||||
false
|
||||
);
|
||||
const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date);
|
||||
|
||||
if (cycleStatus === "current" || cycleStatus === "upcoming")
|
||||
mutate<CurrentAndUpcomingCyclesResponse>(
|
||||
CYCLE_CURRENT_AND_UPCOMING_LIST(projectId as string),
|
||||
(prevData) => ({
|
||||
current_cycle: (prevData?.current_cycle ?? []).map((c) => ({
|
||||
...c,
|
||||
is_favorite: c.id === cycle.id ? true : c.is_favorite,
|
||||
})),
|
||||
upcoming_cycle: (prevData?.upcoming_cycle ?? []).map((c) => ({
|
||||
...c,
|
||||
is_favorite: c.id === cycle.id ? true : c.is_favorite,
|
||||
})),
|
||||
}),
|
||||
false
|
||||
);
|
||||
else if (cycleStatus === "completed")
|
||||
mutate<CompletedCyclesResponse>(
|
||||
CYCLE_COMPLETE_LIST(projectId as string),
|
||||
(prevData) => ({
|
||||
completed_cycles: (prevData?.completed_cycles ?? []).map((c) => ({
|
||||
...c,
|
||||
is_favorite: c.id === cycle.id ? true : c.is_favorite,
|
||||
})),
|
||||
}),
|
||||
false
|
||||
);
|
||||
else
|
||||
mutate<DraftCyclesResponse>(
|
||||
CYCLE_DRAFT_LIST(projectId as string),
|
||||
(prevData) => ({
|
||||
draft_cycles: (prevData?.draft_cycles ?? []).map((c) => ({
|
||||
...c,
|
||||
is_favorite: c.id === cycle.id ? true : c.is_favorite,
|
||||
})),
|
||||
}),
|
||||
false
|
||||
);
|
||||
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
|
|
@ -106,15 +148,45 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = (props) => {
|
|||
cyclesService
|
||||
.removeCycleFromFavorites(workspaceSlug as string, projectId as string, cycle.id)
|
||||
.then(() => {
|
||||
mutate<ICycle[]>(
|
||||
CYCLE_LIST(projectId as string),
|
||||
(prevData) =>
|
||||
(prevData ?? []).map((c) => ({
|
||||
...c,
|
||||
is_favorite: c.id === cycle.id ? false : c.is_favorite,
|
||||
})),
|
||||
false
|
||||
);
|
||||
const cycleStatus = getDateRangeStatus(cycle.start_date, cycle.end_date);
|
||||
|
||||
if (cycleStatus === "current" || cycleStatus === "upcoming")
|
||||
mutate<CurrentAndUpcomingCyclesResponse>(
|
||||
CYCLE_CURRENT_AND_UPCOMING_LIST(projectId as string),
|
||||
(prevData) => ({
|
||||
current_cycle: (prevData?.current_cycle ?? []).map((c) => ({
|
||||
...c,
|
||||
is_favorite: c.id === cycle.id ? false : c.is_favorite,
|
||||
})),
|
||||
upcoming_cycle: (prevData?.upcoming_cycle ?? []).map((c) => ({
|
||||
...c,
|
||||
is_favorite: c.id === cycle.id ? false : c.is_favorite,
|
||||
})),
|
||||
}),
|
||||
false
|
||||
);
|
||||
else if (cycleStatus === "completed")
|
||||
mutate<CompletedCyclesResponse>(
|
||||
CYCLE_COMPLETE_LIST(projectId as string),
|
||||
(prevData) => ({
|
||||
completed_cycles: (prevData?.completed_cycles ?? []).map((c) => ({
|
||||
...c,
|
||||
is_favorite: c.id === cycle.id ? false : c.is_favorite,
|
||||
})),
|
||||
}),
|
||||
false
|
||||
);
|
||||
else
|
||||
mutate<DraftCyclesResponse>(
|
||||
CYCLE_DRAFT_LIST(projectId as string),
|
||||
(prevData) => ({
|
||||
draft_cycles: (prevData?.draft_cycles ?? []).map((c) => ({
|
||||
...c,
|
||||
is_favorite: c.id === cycle.id ? false : c.is_favorite,
|
||||
})),
|
||||
}),
|
||||
false
|
||||
);
|
||||
|
||||
setToastAlert({
|
||||
type: "success",
|
||||
|
|
@ -167,16 +239,12 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = (props) => {
|
|||
</a>
|
||||
</Link>
|
||||
{cycle.is_favorite ? (
|
||||
<button onClick={handleRemoveFromFavorites} className="p-1 ">
|
||||
<span>
|
||||
<StarIcon className="h-4 w-4 text-orange-400" fill="#f6ad55" />
|
||||
</span>
|
||||
<button onClick={handleRemoveFromFavorites}>
|
||||
<StarIcon className="h-4 w-4 text-orange-400" fill="#f6ad55" />
|
||||
</button>
|
||||
) : (
|
||||
<button onClick={handleAddToFavorites} className="p-1">
|
||||
<span>
|
||||
<StarIcon className="h-4 w-4 " color="#858E96" />
|
||||
</span>
|
||||
<button onClick={handleAddToFavorites}>
|
||||
<StarIcon className="h-4 w-4 " color="#858E96" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue