mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Refactor ShowSchedules component for improved layout and user interaction
- Replaced the table layout with a grid layout for better visual organization of schedules. - Enhanced the display of schedule details, including status badges and command information. - Updated action buttons for running schedules and managing them with improved icons and tooltips. - Streamlined the code for better readability and maintainability.
This commit is contained in:
@@ -1,15 +1,14 @@
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHead,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "@/components/ui/table";
|
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
import { HandleSchedules } from "./handle-schedules";
|
import { HandleSchedules } from "./handle-schedules";
|
||||||
import { Clock, Play, Terminal, Trash2 } from "lucide-react";
|
import {
|
||||||
|
Clock,
|
||||||
|
Play,
|
||||||
|
Terminal,
|
||||||
|
Trash2,
|
||||||
|
ClipboardList,
|
||||||
|
Loader2,
|
||||||
|
} from "lucide-react";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
@@ -33,9 +32,10 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ShowSchedules = ({ applicationId }: Props) => {
|
export const ShowSchedules = ({ applicationId }: Props) => {
|
||||||
const { data: schedules } = api.schedule.list.useQuery({
|
const { data: schedules, isLoading: isLoadingSchedules } =
|
||||||
applicationId,
|
api.schedule.list.useQuery({
|
||||||
});
|
applicationId,
|
||||||
|
});
|
||||||
const utils = api.useUtils();
|
const utils = api.useUtils();
|
||||||
|
|
||||||
const { mutateAsync: deleteSchedule, isLoading: isDeleting } =
|
const { mutateAsync: deleteSchedule, isLoading: isDeleting } =
|
||||||
@@ -47,7 +47,7 @@ export const ShowSchedules = ({ applicationId }: Props) => {
|
|||||||
return (
|
return (
|
||||||
<Card className="border px-4 shadow-none bg-transparent">
|
<Card className="border px-4 shadow-none bg-transparent">
|
||||||
<CardHeader className="px-0">
|
<CardHeader className="px-0">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<CardTitle className="text-xl font-bold flex items-center gap-2">
|
<CardTitle className="text-xl font-bold flex items-center gap-2">
|
||||||
Scheduled Tasks
|
Scheduled Tasks
|
||||||
@@ -63,138 +63,145 @@ export const ShowSchedules = ({ applicationId }: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="px-0">
|
<CardContent className="px-0">
|
||||||
{schedules && schedules.length > 0 ? (
|
{isLoadingSchedules ? (
|
||||||
<div className="rounded-lg border">
|
<div className="flex gap-4 min-h-[35vh] w-full items-center justify-center text-center mx-auto">
|
||||||
<Table>
|
<Loader2 className="size-4 text-muted-foreground/70 transition-colors animate-spin self-center" />
|
||||||
<TableHeader>
|
<span className="text-sm text-muted-foreground/70">
|
||||||
<TableRow>
|
Loading scheduled tasks...
|
||||||
<TableHead>Task Name</TableHead>
|
</span>
|
||||||
<TableHead>Schedule</TableHead>
|
</div>
|
||||||
<TableHead>Shell</TableHead>
|
) : schedules && schedules.length > 0 ? (
|
||||||
<TableHead>Command</TableHead>
|
<div className="grid xl:grid-cols-2 gap-4 grid-cols-1 h-full">
|
||||||
<TableHead>Status</TableHead>
|
{schedules.map((schedule) => {
|
||||||
<TableHead className="text-right">Actions</TableHead>
|
const application = schedule.application;
|
||||||
</TableRow>
|
const deployments = schedule.deployments;
|
||||||
</TableHeader>
|
return (
|
||||||
<TableBody>
|
<div
|
||||||
{schedules.map((schedule) => {
|
key={schedule.scheduleId}
|
||||||
const application = schedule.application;
|
className=" flex items-center justify-between rounded-lg border p-3 transition-colors bg-muted/50"
|
||||||
const deployments = schedule.deployments;
|
>
|
||||||
return (
|
<div className="flex items-start gap-3">
|
||||||
<TableRow key={schedule.scheduleId}>
|
<div className="flex h-9 w-9 items-center justify-center rounded-full bg-primary/5">
|
||||||
<TableCell className="font-medium">
|
<Clock className="size-4 text-primary/70" />
|
||||||
{schedule.name}
|
</div>
|
||||||
</TableCell>
|
<div className="space-y-1.5">
|
||||||
<TableCell>
|
<div className="flex items-center gap-2">
|
||||||
<Badge variant="secondary" className="font-mono">
|
<h3 className="text-sm font-medium leading-none">
|
||||||
{schedule.cronExpression}
|
{schedule.name}
|
||||||
</Badge>
|
</h3>
|
||||||
</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<Badge variant="secondary" className="font-mono">
|
|
||||||
{schedule.shellType}
|
|
||||||
</Badge>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Terminal className="w-4 h-4 text-muted-foreground" />
|
|
||||||
<code className="bg-muted px-2 py-1 rounded text-sm">
|
|
||||||
{schedule.command}
|
|
||||||
</code>
|
|
||||||
</div>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>
|
|
||||||
<Badge
|
<Badge
|
||||||
variant={schedule.enabled ? "default" : "secondary"}
|
variant={schedule.enabled ? "default" : "secondary"}
|
||||||
|
className="text-[10px] px-1 py-0"
|
||||||
>
|
>
|
||||||
{schedule.enabled ? "Enabled" : "Disabled"}
|
{schedule.enabled ? "Enabled" : "Disabled"}
|
||||||
</Badge>
|
</Badge>
|
||||||
</TableCell>
|
</div>
|
||||||
<TableCell className="text-right">
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
||||||
<div className="flex justify-end gap-2">
|
<Badge
|
||||||
<ShowSchedulesLogs
|
variant="outline"
|
||||||
deployments={deployments || []}
|
className="font-mono text-[10px] bg-transparent"
|
||||||
serverId={application.serverId || undefined}
|
>
|
||||||
/>
|
{schedule.cronExpression}
|
||||||
|
</Badge>
|
||||||
|
<span className="text-xs text-muted-foreground/50">
|
||||||
|
•
|
||||||
|
</span>
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="font-mono text-[10px] bg-transparent"
|
||||||
|
>
|
||||||
|
{schedule.shellType}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Terminal className="size-3.5 text-muted-foreground/70" />
|
||||||
|
<code className="font-mono text-[10px] text-muted-foreground/70">
|
||||||
|
{schedule.command}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<TooltipProvider delayDuration={0}>
|
<div className="flex items-center gap-1.5">
|
||||||
<Tooltip>
|
<ShowSchedulesLogs
|
||||||
<TooltipTrigger asChild>
|
deployments={deployments || []}
|
||||||
<Button
|
serverId={application.serverId || undefined}
|
||||||
type="button"
|
>
|
||||||
variant="ghost"
|
<Button variant="ghost" size="icon">
|
||||||
isLoading={isLoading}
|
<ClipboardList className="size-4 transition-colors " />
|
||||||
onClick={async () => {
|
</Button>
|
||||||
await runManually({
|
</ShowSchedulesLogs>
|
||||||
scheduleId: schedule.scheduleId,
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
toast.success(
|
|
||||||
"Schedule run successfully",
|
|
||||||
);
|
|
||||||
utils.schedule.list.invalidate({
|
|
||||||
applicationId,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log(error);
|
|
||||||
toast.error(
|
|
||||||
`Error running schedule: ${error}`,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Play className="size-4" />
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
Run Manual Schedule
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
|
|
||||||
<HandleSchedules
|
<TooltipProvider delayDuration={0}>
|
||||||
scheduleId={schedule.scheduleId}
|
<Tooltip>
|
||||||
applicationId={applicationId}
|
<TooltipTrigger asChild>
|
||||||
/>
|
<Button
|
||||||
|
type="button"
|
||||||
<DialogAction
|
variant="ghost"
|
||||||
title="Delete Schedule"
|
size="icon"
|
||||||
description="Are you sure you want to delete this schedule?"
|
isLoading={isLoading}
|
||||||
type="destructive"
|
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
await deleteSchedule({
|
await runManually({
|
||||||
scheduleId: schedule.scheduleId,
|
scheduleId: schedule.scheduleId,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
toast.success("Schedule run successfully");
|
||||||
utils.schedule.list.invalidate({
|
utils.schedule.list.invalidate({
|
||||||
applicationId,
|
applicationId,
|
||||||
});
|
});
|
||||||
toast.success(
|
|
||||||
"Schedule deleted successfully",
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((error) => {
|
||||||
toast.error("Error deleting schedule");
|
console.log(error);
|
||||||
|
toast.error(
|
||||||
|
`Error running schedule: ${error}`,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button
|
<Play className="size-4 transition-colors" />
|
||||||
variant="ghost"
|
</Button>
|
||||||
size="icon"
|
</TooltipTrigger>
|
||||||
className="group hover:bg-red-500/10 "
|
<TooltipContent>Run Manual Schedule</TooltipContent>
|
||||||
isLoading={isDeleting}
|
</Tooltip>
|
||||||
>
|
</TooltipProvider>
|
||||||
<Trash2 className="size-4 text-primary group-hover:text-red-500" />
|
|
||||||
</Button>
|
<HandleSchedules
|
||||||
</DialogAction>
|
scheduleId={schedule.scheduleId}
|
||||||
</div>
|
applicationId={applicationId}
|
||||||
</TableCell>
|
/>
|
||||||
</TableRow>
|
|
||||||
);
|
<DialogAction
|
||||||
})}
|
title="Delete Schedule"
|
||||||
</TableBody>
|
description="Are you sure you want to delete this schedule?"
|
||||||
</Table>
|
type="destructive"
|
||||||
|
onClick={async () => {
|
||||||
|
await deleteSchedule({
|
||||||
|
scheduleId: schedule.scheduleId,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
utils.schedule.list.invalidate({
|
||||||
|
applicationId,
|
||||||
|
});
|
||||||
|
toast.success("Schedule deleted successfully");
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
toast.error("Error deleting schedule");
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="group hover:bg-red-500/10 "
|
||||||
|
isLoading={isDeleting}
|
||||||
|
>
|
||||||
|
<Trash2 className="size-4 text-primary group-hover:text-red-500" />
|
||||||
|
</Button>
|
||||||
|
</DialogAction>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex flex-col gap-2 items-center justify-center py-12 border rounded-lg">
|
<div className="flex flex-col gap-2 items-center justify-center py-12 border rounded-lg">
|
||||||
|
|||||||
Reference in New Issue
Block a user