mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Add 'enabled' field to schedule management
- Introduced a new boolean field `enabled` in the `schedule` schema to indicate the active status of schedules. - Updated the `HandleSchedules` component to include a toggle switch for enabling/disabling schedules. - Enhanced the `ShowSchedules` component to display the status of each schedule with a badge indicating whether it is enabled or disabled. - Added a new API mutation to run schedules manually, ensuring proper error handling for non-existent schedules. - Updated database schema to reflect the new `enabled` field with a default value of true.
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
|||||||
TooltipProvider,
|
TooltipProvider,
|
||||||
TooltipTrigger,
|
TooltipTrigger,
|
||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
|
import { Switch } from "@/components/ui/switch";
|
||||||
|
|
||||||
const commonCronExpressions = [
|
const commonCronExpressions = [
|
||||||
{ label: "Every minute", value: "* * * * *" },
|
{ label: "Every minute", value: "* * * * *" },
|
||||||
@@ -42,6 +43,7 @@ const formSchema = z.object({
|
|||||||
name: z.string().min(1, "Name is required"),
|
name: z.string().min(1, "Name is required"),
|
||||||
cronExpression: z.string().min(1, "Cron expression is required"),
|
cronExpression: z.string().min(1, "Cron expression is required"),
|
||||||
command: z.string().min(1, "Command is required"),
|
command: z.string().min(1, "Command is required"),
|
||||||
|
enabled: z.boolean().default(true),
|
||||||
});
|
});
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -68,6 +70,7 @@ export const HandleSchedules = ({
|
|||||||
name: "",
|
name: "",
|
||||||
cronExpression: "",
|
cronExpression: "",
|
||||||
command: "",
|
command: "",
|
||||||
|
enabled: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -205,6 +208,21 @@ export const HandleSchedules = ({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="enabled"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel className="flex items-center gap-2">
|
||||||
|
<Switch
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
Enabled
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<Button type="submit" disabled={isLoading} className="w-full">
|
<Button type="submit" disabled={isLoading} className="w-full">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -18,7 +18,13 @@ import { api } from "@/utils/api";
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { HandleSchedules } from "./handle-schedules";
|
import { HandleSchedules } from "./handle-schedules";
|
||||||
import { PlusCircle, Clock, Terminal, Trash2, Edit2 } from "lucide-react";
|
import { PlusCircle, Clock, Terminal, Trash2, Edit2 } from "lucide-react";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/components/ui/card";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -54,11 +60,16 @@ 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">
|
||||||
<CardTitle className="text-xl font-bold flex items-center gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Clock className="size-4 text-muted-foreground" />
|
<CardTitle className="text-xl font-bold flex items-center gap-2">
|
||||||
Scheduled Tasks
|
Scheduled Tasks
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Schedule tasks to run automatically at specified intervals.
|
||||||
|
</CardDescription>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
<Button className="gap-2">
|
<Button className="gap-2">
|
||||||
@@ -91,6 +102,7 @@ export const ShowSchedules = ({ applicationId }: Props) => {
|
|||||||
<TableHead>Task Name</TableHead>
|
<TableHead>Task Name</TableHead>
|
||||||
<TableHead>Schedule</TableHead>
|
<TableHead>Schedule</TableHead>
|
||||||
<TableHead>Command</TableHead>
|
<TableHead>Command</TableHead>
|
||||||
|
<TableHead>Status</TableHead>
|
||||||
<TableHead className="text-right">Actions</TableHead>
|
<TableHead className="text-right">Actions</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
@@ -113,6 +125,13 @@ export const ShowSchedules = ({ applicationId }: Props) => {
|
|||||||
</code>
|
</code>
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Badge
|
||||||
|
variant={schedule.enabled ? "default" : "secondary"}
|
||||||
|
>
|
||||||
|
{schedule.enabled ? "Enabled" : "Disabled"}
|
||||||
|
</Badge>
|
||||||
|
</TableCell>
|
||||||
<TableCell className="text-right">
|
<TableCell className="text-right">
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end gap-2">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
1
apps/dokploy/drizzle/0090_colossal_azazel.sql
Normal file
1
apps/dokploy/drizzle/0090_colossal_azazel.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE "schedule" ADD COLUMN "enabled" boolean DEFAULT true NOT NULL;
|
||||||
5496
apps/dokploy/drizzle/meta/0090_snapshot.json
Normal file
5496
apps/dokploy/drizzle/meta/0090_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -631,6 +631,13 @@
|
|||||||
"when": 1746178027816,
|
"when": 1746178027816,
|
||||||
"tag": "0089_fearless_morlun",
|
"tag": "0089_fearless_morlun",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 90,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1746178996842,
|
||||||
|
"tag": "0090_colossal_azazel",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -80,6 +80,24 @@ export const scheduleRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return schedule;
|
||||||
|
}),
|
||||||
|
|
||||||
|
runManually: protectedProcedure
|
||||||
|
.input(z.object({ scheduleId: z.string() }))
|
||||||
|
.mutation(async ({ ctx, input }) => {
|
||||||
|
const schedule = await ctx.db
|
||||||
|
.select()
|
||||||
|
.from(schedules)
|
||||||
|
.where(eq(schedules.scheduleId, input.scheduleId));
|
||||||
|
|
||||||
|
if (!schedule) {
|
||||||
|
throw new TRPCError({
|
||||||
|
code: "NOT_FOUND",
|
||||||
|
message: "Schedule not found",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return schedule;
|
return schedule;
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { relations } from "drizzle-orm";
|
import { relations } from "drizzle-orm";
|
||||||
import { pgTable, text } from "drizzle-orm/pg-core";
|
import { boolean, pgTable, text } from "drizzle-orm/pg-core";
|
||||||
import { createInsertSchema } from "drizzle-zod";
|
import { createInsertSchema } from "drizzle-zod";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
@@ -18,6 +18,7 @@ export const schedules = pgTable("schedule", {
|
|||||||
.references(() => applications.applicationId, {
|
.references(() => applications.applicationId, {
|
||||||
onDelete: "cascade",
|
onDelete: "cascade",
|
||||||
}),
|
}),
|
||||||
|
enabled: boolean("enabled").notNull().default(true),
|
||||||
createdAt: text("createdAt")
|
createdAt: text("createdAt")
|
||||||
.notNull()
|
.notNull()
|
||||||
.$defaultFn(() => new Date().toISOString()),
|
.$defaultFn(() => new Date().toISOString()),
|
||||||
|
|||||||
Reference in New Issue
Block a user