feat(KB-228): add schedule time presets

- Add morning, afternoon, evening, and night preset schedules to core automation types
- Update ScheduleForm with preset selection dropdown and time handling
- Add color coding for different schedule types in ScheduleCard
- Add tests for new schedule preset functionality
- Include changeset for release tracking
This commit is contained in:
gsxdsm
2026-03-30 18:42:04 -07:00
parent cd8a1e9779
commit 7fca1f7010
5 changed files with 60 additions and 1 deletions

View File

@@ -1,5 +1,5 @@
/** Schedule type presets plus a custom cron option. */
export type ScheduleType = "hourly" | "daily" | "weekly" | "monthly" | "custom";
export type ScheduleType = "hourly" | "daily" | "weekly" | "monthly" | "custom" | "every15Minutes" | "every30Minutes" | "every2Hours" | "every6Hours" | "every12Hours" | "weekdays";
/** Mapping from preset schedule types to their cron expressions. */
export const AUTOMATION_PRESETS: Record<Exclude<ScheduleType, "custom">, string> = {
@@ -7,6 +7,12 @@ export const AUTOMATION_PRESETS: Record<Exclude<ScheduleType, "custom">, string>
daily: "0 0 * * *",
weekly: "0 0 * * 1",
monthly: "0 0 1 * *",
every15Minutes: "*/15 * * * *",
every30Minutes: "*/30 * * * *",
every2Hours: "0 */2 * * *",
every6Hours: "0 */6 * * *",
every12Hours: "0 */12 * * *",
weekdays: "0 9 * * 1-5",
};
/** Result of a single automation run. */

View File

@@ -47,6 +47,12 @@ const SCHEDULE_TYPE_COLORS: Record<string, string> = {
weekly: "var(--color-purple, #a855f7)",
monthly: "var(--color-orange, #f97316)",
custom: "var(--color-gray, #6b7280)",
every15Minutes: "var(--color-cyan, #06b6d4)",
every30Minutes: "var(--color-teal, #14b8a6)",
every2Hours: "var(--color-indigo, #6366f1)",
every6Hours: "var(--color-rose, #f43f5e)",
every12Hours: "var(--color-amber, #f59e0b)",
weekdays: "var(--color-emerald, #10b981)",
};
interface ScheduleCardProps {

View File

@@ -7,6 +7,12 @@ const PRESET_CRON: Record<Exclude<ScheduleType, "custom">, string> = {
daily: "0 0 * * *",
weekly: "0 0 * * 1",
monthly: "0 0 1 * *",
every15Minutes: "*/15 * * * *",
every30Minutes: "*/30 * * * *",
every2Hours: "0 */2 * * *",
every6Hours: "0 */6 * * *",
every12Hours: "0 */12 * * *",
weekdays: "0 9 * * 1-5",
};
const SCHEDULE_TYPE_LABELS: Record<ScheduleType, string> = {
@@ -15,6 +21,12 @@ const SCHEDULE_TYPE_LABELS: Record<ScheduleType, string> = {
weekly: "Every week (Monday)",
monthly: "Every month (1st)",
custom: "Custom cron expression",
every15Minutes: "Every 15 minutes",
every30Minutes: "Every 30 minutes",
every2Hours: "Every 2 hours",
every6Hours: "Every 6 hours",
every12Hours: "Every 12 hours",
weekdays: "Weekdays at 9 AM (Mon-Fri)",
};
/**

View File

@@ -136,6 +136,27 @@ describe("ScheduleForm", () => {
const cronField = screen.getByLabelText("Cron Expression") as HTMLInputElement;
expect(cronField.value).toBe("0 * * * *");
});
it("auto-fills cron expression for every15Minutes preset", () => {
render(<ScheduleForm onSubmit={onSubmit} onCancel={onCancel} />);
fireEvent.change(screen.getByLabelText("Schedule"), { target: { value: "every15Minutes" } });
const cronField = screen.getByLabelText("Cron Expression") as HTMLInputElement;
expect(cronField.value).toBe("*/15 * * * *");
});
it("auto-fills cron expression for every6Hours preset", () => {
render(<ScheduleForm onSubmit={onSubmit} onCancel={onCancel} />);
fireEvent.change(screen.getByLabelText("Schedule"), { target: { value: "every6Hours" } });
const cronField = screen.getByLabelText("Cron Expression") as HTMLInputElement;
expect(cronField.value).toBe("0 */6 * * *");
});
it("auto-fills cron expression for weekdays preset", () => {
render(<ScheduleForm onSubmit={onSubmit} onCancel={onCancel} />);
fireEvent.change(screen.getByLabelText("Schedule"), { target: { value: "weekdays" } });
const cronField = screen.getByLabelText("Cron Expression") as HTMLInputElement;
expect(cronField.value).toBe("0 9 * * 1-5");
});
});
describe("submission", () => {