mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
chore: cycles loading, fix: cycles favorite mutation (#379)
This commit is contained in:
parent
b54a1f221f
commit
64978969a0
@ -204,7 +204,7 @@ export const CommandPalette: React.FC = () => {
|
||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-25 transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-20 overflow-y-auto p-4 sm:p-6 md:p-20">
|
||||
<div className="fixed inset-0 z-20 p-4 sm:p-6 md:p-20">
|
||||
<Transition.Child
|
||||
as={React.Fragment}
|
||||
enter="ease-out duration-300"
|
||||
@ -214,7 +214,7 @@ export const CommandPalette: React.FC = () => {
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-white bg-opacity-80 shadow-2xl ring-1 ring-black ring-opacity-5 backdrop-blur backdrop-filter transition-all">
|
||||
<Dialog.Panel className="relative mx-auto max-w-2xl transform divide-y divide-gray-500 divide-opacity-10 rounded-xl bg-white shadow-2xl ring-1 ring-black ring-opacity-5 transition-all">
|
||||
<Combobox
|
||||
onChange={(value: any) => {
|
||||
if (value?.url) router.push(value.url);
|
||||
|
@ -129,7 +129,7 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
|
||||
{filteredIssues.length > 0 ? (
|
||||
<li className="p-2">
|
||||
{query === "" && (
|
||||
<h2 className="mt-4 mb-2 px-3 text-xs font-semibold text-gray-900">
|
||||
<h2 className="mb-2 px-3 text-xs font-semibold text-gray-900">
|
||||
Select issues to add
|
||||
</h2>
|
||||
)}
|
||||
@ -175,18 +175,6 @@ export const ExistingIssuesListModal: React.FC<Props> = ({
|
||||
</div>
|
||||
)}
|
||||
</Combobox.Options>
|
||||
|
||||
{query !== "" && filteredIssues.length === 0 && (
|
||||
<div className="py-14 px-6 text-center sm:px-14">
|
||||
<RectangleStackIcon
|
||||
className="mx-auto h-6 w-6 text-gray-900 text-opacity-40"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<p className="mt-4 text-sm text-gray-900">
|
||||
We couldn{"'"}t find any issue with that term. Please try again.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</Combobox>
|
||||
)}
|
||||
/>
|
||||
|
@ -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>>;
|
||||
@ -48,8 +49,6 @@ export const CompletedCyclesList: React.FC<CompletedCyclesListProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{completedCycles && (
|
||||
<>
|
||||
<DeleteCycleModal
|
||||
isOpen={
|
||||
@ -60,7 +59,8 @@ export const CompletedCyclesList: React.FC<CompletedCyclesListProps> = ({
|
||||
setIsOpen={setCycleDeleteModal}
|
||||
data={selectedCycleForDelete}
|
||||
/>
|
||||
{completedCycles?.completed_cycles.length > 0 ? (
|
||||
{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
|
||||
@ -79,8 +79,13 @@ export const CompletedCyclesList: React.FC<CompletedCyclesListProps> = ({
|
||||
<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,7 +45,8 @@ export const CyclesList: React.FC<TCycleStatsViewProps> = ({
|
||||
setIsOpen={setCycleDeleteModal}
|
||||
data={selectedCycleForDelete}
|
||||
/>
|
||||
{cycles.length > 0 ? (
|
||||
{cycles ? (
|
||||
cycles.length > 0 ? (
|
||||
<div className="grid grid-cols-1 gap-9 md:grid-cols-2 lg:grid-cols-3">
|
||||
{cycles.map((cycle) => (
|
||||
<SingleCycleCard
|
||||
@ -59,7 +61,7 @@ export const CyclesList: React.FC<TCycleStatsViewProps> = ({
|
||||
<div className="flex flex-col items-center justify-center gap-4 text-center">
|
||||
{type === "upcoming" ? (
|
||||
<UpcomingCycleIcon height="56" width="56" />
|
||||
) : type === "completed" ? (
|
||||
) : type === "draft" ? (
|
||||
<CompletedCycleIcon height="56" width="56" />
|
||||
) : (
|
||||
<CurrentCycleIcon height="56" width="56" />
|
||||
@ -69,6 +71,11 @@ export const CyclesList: React.FC<TCycleStatsViewProps> = ({
|
||||
<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,13 +87,43 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = (props) => {
|
||||
cycle: cycle.id,
|
||||
})
|
||||
.then(() => {
|
||||
mutate<ICycle[]>(
|
||||
CYCLE_LIST(projectId as string),
|
||||
(prevData) =>
|
||||
(prevData ?? []).map((c) => ({
|
||||
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
|
||||
);
|
||||
|
||||
@ -106,13 +148,43 @@ 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) => ({
|
||||
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
|
||||
);
|
||||
|
||||
@ -167,16 +239,12 @@ export const SingleCycleCard: React.FC<TSingleStatProps> = (props) => {
|
||||
</a>
|
||||
</Link>
|
||||
{cycle.is_favorite ? (
|
||||
<button onClick={handleRemoveFromFavorites} className="p-1 ">
|
||||
<span>
|
||||
<button onClick={handleRemoveFromFavorites}>
|
||||
<StarIcon className="h-4 w-4 text-orange-400" fill="#f6ad55" />
|
||||
</span>
|
||||
</button>
|
||||
) : (
|
||||
<button onClick={handleAddToFavorites} className="p-1">
|
||||
<span>
|
||||
<button onClick={handleAddToFavorites}>
|
||||
<StarIcon className="h-4 w-4 " color="#858E96" />
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
@ -231,7 +231,7 @@ export const CreateUpdateIssueModal: React.FC<IssuesModalProps> = ({
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="mt-10 flex min-h-full items-start justify-center p-4 text-center sm:p-0 md:mt-20">
|
||||
<div className="mt-10 flex items-start justify-center p-4 text-center sm:p-0 md:mt-20">
|
||||
<Transition.Child
|
||||
as={React.Fragment}
|
||||
enter="ease-out duration-300"
|
||||
|
@ -89,7 +89,9 @@ export const timeAgo = (time: any) => {
|
||||
return time;
|
||||
};
|
||||
|
||||
export const getDateRangeStatus = (startDate: string , endDate: string ) => {
|
||||
export const getDateRangeStatus = (startDate: string | null, endDate: string | null) => {
|
||||
if (!startDate || !endDate) return "draft";
|
||||
|
||||
const now = new Date();
|
||||
const start = new Date(startDate);
|
||||
const end = new Date(endDate);
|
||||
@ -101,12 +103,25 @@ export const getDateRangeStatus = (startDate: string , endDate: string ) => {
|
||||
} else {
|
||||
return "upcoming";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const renderShortDateWithYearFormat = (date: Date) => {
|
||||
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
const months = [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mar",
|
||||
"Apr",
|
||||
"May",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Oct",
|
||||
"Nov",
|
||||
"Dec",
|
||||
];
|
||||
const day = date.getDate();
|
||||
const month = months[date.getMonth()];
|
||||
const year = date.getFullYear();
|
||||
return isNaN(date.getTime()) ? "N/A" : ` ${month} ${day}, ${year}`;
|
||||
}
|
||||
};
|
||||
|
@ -115,7 +115,7 @@ const ProjectCycles: NextPage = () => {
|
||||
<h3 className="text-3xl font-semibold text-black">Current Cycle</h3>
|
||||
<div className="space-y-5">
|
||||
<CyclesList
|
||||
cycles={currentAndUpcomingCycles?.current_cycle ?? []}
|
||||
cycles={currentAndUpcomingCycles?.current_cycle}
|
||||
setCreateUpdateCycleModal={setCreateUpdateCycleModal}
|
||||
setSelectedCycle={setSelectedCycle}
|
||||
type="current"
|
||||
@ -132,7 +132,7 @@ const ProjectCycles: NextPage = () => {
|
||||
>
|
||||
<Tab
|
||||
className={({ selected }) =>
|
||||
`rounded-3xl border px-5 py-1.5 text-sm sm:px-7 sm:py-2 sm:text-base ${
|
||||
`rounded-3xl border px-5 py-1.5 text-sm outline-none sm:px-7 sm:py-2 sm:text-base ${
|
||||
selected
|
||||
? "border-theme bg-theme text-white"
|
||||
: "border-gray-300 bg-white hover:bg-hover-gray"
|
||||
@ -143,7 +143,7 @@ const ProjectCycles: NextPage = () => {
|
||||
</Tab>
|
||||
<Tab
|
||||
className={({ selected }) =>
|
||||
`rounded-3xl border px-5 py-1.5 text-sm sm:px-7 sm:py-2 sm:text-base ${
|
||||
`rounded-3xl border px-5 py-1.5 text-sm outline-none sm:px-7 sm:py-2 sm:text-base ${
|
||||
selected
|
||||
? "border-theme bg-theme text-white"
|
||||
: "border-gray-300 bg-white hover:bg-hover-gray"
|
||||
@ -154,7 +154,7 @@ const ProjectCycles: NextPage = () => {
|
||||
</Tab>
|
||||
<Tab
|
||||
className={({ selected }) =>
|
||||
`rounded-3xl border px-5 py-1.5 text-sm sm:px-7 sm:py-2 sm:text-base ${
|
||||
`rounded-3xl border px-5 py-1.5 text-sm outline-none sm:px-7 sm:py-2 sm:text-base ${
|
||||
selected
|
||||
? "border-theme bg-theme text-white"
|
||||
: "border-gray-300 bg-white hover:bg-hover-gray"
|
||||
@ -167,7 +167,7 @@ const ProjectCycles: NextPage = () => {
|
||||
<Tab.Panels>
|
||||
<Tab.Panel as="div" className="mt-8 space-y-5">
|
||||
<CyclesList
|
||||
cycles={currentAndUpcomingCycles?.upcoming_cycle ?? []}
|
||||
cycles={currentAndUpcomingCycles?.upcoming_cycle}
|
||||
setCreateUpdateCycleModal={setCreateUpdateCycleModal}
|
||||
setSelectedCycle={setSelectedCycle}
|
||||
type="upcoming"
|
||||
@ -181,10 +181,10 @@ const ProjectCycles: NextPage = () => {
|
||||
</Tab.Panel>
|
||||
<Tab.Panel as="div" className="mt-8 space-y-5">
|
||||
<CyclesList
|
||||
cycles={draftCycles?.draft_cycles ?? []}
|
||||
cycles={draftCycles?.draft_cycles}
|
||||
setCreateUpdateCycleModal={setCreateUpdateCycleModal}
|
||||
setSelectedCycle={setSelectedCycle}
|
||||
type="upcoming"
|
||||
type="draft"
|
||||
/>
|
||||
</Tab.Panel>
|
||||
</Tab.Panels>
|
||||
|
1
apps/app/types/cycles.d.ts
vendored
1
apps/app/types/cycles.d.ts
vendored
@ -25,7 +25,6 @@ export interface CurrentAndUpcomingCyclesResponse {
|
||||
upcoming_cycle: ICycle[];
|
||||
}
|
||||
|
||||
|
||||
export interface DraftCyclesResponse {
|
||||
draft_cycles: ICycle[];
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user