fix(schedules): track loading state per scheduler

This commit is contained in:
HarikrishnanD
2025-10-16 12:17:45 +05:30
parent ceb4cc453e
commit 819bb6ca14

View File

@@ -7,6 +7,7 @@ import {
Trash2, Trash2,
} from "lucide-react"; } from "lucide-react";
import { toast } from "sonner"; import { toast } from "sonner";
import { useState } from "react";
import { DialogAction } from "@/components/shared/dialog-action"; import { DialogAction } from "@/components/shared/dialog-action";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@@ -33,6 +34,7 @@ interface Props {
} }
export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => { export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => {
const [runningSchedules, setRunningSchedules] = useState<Set<string>>(new Set());
const { const {
data: schedules, data: schedules,
isLoading: isLoadingSchedules, isLoading: isLoadingSchedules,
@@ -46,15 +48,29 @@ export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => {
enabled: !!id, enabled: !!id,
}, },
); );
const utils = api.useUtils(); const utils = api.useUtils();
const { mutateAsync: deleteSchedule, isLoading: isDeleting } = const { mutateAsync: deleteSchedule, isLoading: isDeleting } =
api.schedule.delete.useMutation(); api.schedule.delete.useMutation();
const { mutateAsync: runManually } =
const { mutateAsync: runManually, isLoading } =
api.schedule.runManually.useMutation(); api.schedule.runManually.useMutation();
const handleRunManually = async (scheduleId: string) => {
setRunningSchedules((prev) => new Set(prev).add(scheduleId));
try {
await runManually({ scheduleId });
toast.success("Schedule run successfully");
await refetchSchedules();
} catch {
toast.error("Error running schedule");
} finally {
setRunningSchedules((prev) => {
const newSet = new Set(prev);
newSet.delete(scheduleId);
return newSet;
});
}
};
return ( return (
<Card className="border px-6 shadow-none bg-transparent h-full min-h-[50vh]"> <Card className="border px-6 shadow-none bg-transparent h-full min-h-[50vh]">
<CardHeader className="px-0"> <CardHeader className="px-0">
@@ -67,7 +83,6 @@ export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => {
Schedule tasks to run automatically at specified intervals. Schedule tasks to run automatically at specified intervals.
</CardDescription> </CardDescription>
</div> </div>
{schedules && schedules.length > 0 && ( {schedules && schedules.length > 0 && (
<HandleSchedules id={id} scheduleType={scheduleType} /> <HandleSchedules id={id} scheduleType={scheduleType} />
)} )}
@@ -75,7 +90,7 @@ export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => {
</CardHeader> </CardHeader>
<CardContent className="px-0"> <CardContent className="px-0">
{isLoadingSchedules ? ( {isLoadingSchedules ? (
<div className="flex gap-4 w-full items-center justify-center text-center mx-auto min-h-[45vh]"> <div className="flex gap-4 w-full items-center justify-center text-center mx-auto min-h-[45vh]">
<Loader2 className="size-4 text-muted-foreground/70 transition-colors animate-spin self-center" /> <Loader2 className="size-4 text-muted-foreground/70 transition-colors animate-spin self-center" />
<span className="text-sm text-muted-foreground/70"> <span className="text-sm text-muted-foreground/70">
Loading scheduled tasks... Loading scheduled tasks...
@@ -141,7 +156,6 @@ export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => {
)} )}
</div> </div>
</div> </div>
<div className="flex items-center gap-0.5 md:gap-1.5"> <div className="flex items-center gap-0.5 md:gap-1.5">
<ShowDeploymentsModal <ShowDeploymentsModal
id={schedule.scheduleId} id={schedule.scheduleId}
@@ -149,10 +163,9 @@ export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => {
serverId={serverId || undefined} serverId={serverId || undefined}
> >
<Button variant="ghost" size="icon"> <Button variant="ghost" size="icon">
<ClipboardList className="size-4 transition-colors " /> <ClipboardList className="size-4 transition-colors" />
</Button> </Button>
</ShowDeploymentsModal> </ShowDeploymentsModal>
<TooltipProvider delayDuration={0}> <TooltipProvider delayDuration={0}>
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
@@ -160,37 +173,24 @@ export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => {
type="button" type="button"
variant="ghost" variant="ghost"
size="icon" size="icon"
isLoading={isLoading} disabled={runningSchedules.has(schedule.scheduleId)}
onClick={async () => { onClick={() => handleRunManually(schedule.scheduleId)}
toast.success("Schedule run successfully");
await runManually({
scheduleId: schedule.scheduleId,
})
.then(async () => {
await new Promise((resolve) =>
setTimeout(resolve, 1500),
);
refetchSchedules();
})
.catch(() => {
toast.error("Error running schedule");
});
}}
> >
<Play className="size-4 transition-colors" /> {runningSchedules.has(schedule.scheduleId) ? (
<Loader2 className="size-4 animate-spin" />
) : (
<Play className="size-4 transition-colors" />
)}
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent>Run Manual Schedule</TooltipContent> <TooltipContent>Run Manual Schedule</TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
<HandleSchedules <HandleSchedules
scheduleId={schedule.scheduleId} scheduleId={schedule.scheduleId}
id={id} id={id}
scheduleType={scheduleType} scheduleType={scheduleType}
/> />
<DialogAction <DialogAction
title="Delete Schedule" title="Delete Schedule"
description="Are you sure you want to delete this schedule?" description="Are you sure you want to delete this schedule?"
@@ -214,8 +214,8 @@ export const ShowSchedules = ({ id, scheduleType = "application" }: Props) => {
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
className="group hover:bg-red-500/10 " className="group hover:bg-red-500/10"
isLoading={isDeleting} disabled={isDeleting}
> >
<Trash2 className="size-4 text-primary group-hover:text-red-500" /> <Trash2 className="size-4 text-primary group-hover:text-red-500" />
</Button> </Button>