mirror of
https://github.com/multica-ai/multica.git
synced 2026-08-12 10:59:06 +02:00
Replace separate CreateAutopilotDialog / EditAutopilotDialog with a single
shared <AutopilotDialog mode="create"|"edit"> that mirrors the issue create
modal — dynamic sizing, expand/collapse, richtext Prompt, pill toolbar.
- Tiptap ContentEditor replaces plain textarea for Prompt; detail page
renders description via ReadonlyContent for visual parity.
- Pills: Agent, Priority, Execution Mode, Schedule (Popover hosting
TriggerConfigSection). 0/1/N trigger strategy: add on 0, edit inline on
1, disabled with tooltip on 2+ (power users edit in detail page).
- Exposes priority + execution_mode at creation time (backend always
supported them; old UI only offered them in Edit).
- parseCronExpression reverse-parses stored cron back to TriggerConfig for
Edit-time prefill (with round-trip tests).
- PillButton extracted to packages/views/common for reuse across modals.
- DialogContent uses showCloseButton={false} so the shared header renders
the Maximize + Close buttons next to the Rocket-prefixed breadcrumb.
- Conditional mount at call sites (`{open && <AutopilotDialog/>}`) keeps
state fresh on each open.
- Schedule dirty detection compares cron+timezone payloads vs mount
snapshot, and edit-mode submits against a snapshotted trigger id so
concurrent WS refetches can't mis-target the update.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
69 lines
2.4 KiB
TypeScript
69 lines
2.4 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import {
|
|
parseCronExpression,
|
|
toCronExpression,
|
|
getDefaultTriggerConfig,
|
|
} from "./trigger-config";
|
|
|
|
describe("parseCronExpression", () => {
|
|
it("round-trips hourly", () => {
|
|
const cfg = { ...getDefaultTriggerConfig(), frequency: "hourly" as const, time: "00:15" };
|
|
const cron = toCronExpression(cfg);
|
|
const parsed = parseCronExpression(cron, "UTC");
|
|
expect(parsed.frequency).toBe("hourly");
|
|
});
|
|
|
|
it("round-trips daily at 09:30", () => {
|
|
const cfg = { ...getDefaultTriggerConfig(), frequency: "daily" as const, time: "09:30" };
|
|
const cron = toCronExpression(cfg);
|
|
const parsed = parseCronExpression(cron, "UTC");
|
|
expect(parsed.frequency).toBe("daily");
|
|
expect(parsed.time).toBe("09:30");
|
|
});
|
|
|
|
it("recognises weekdays pattern", () => {
|
|
const parsed = parseCronExpression("0 9 * * 1-5", "UTC");
|
|
expect(parsed.frequency).toBe("weekdays");
|
|
expect(parsed.time).toBe("09:00");
|
|
});
|
|
|
|
it("recognises weekly with multiple days", () => {
|
|
const parsed = parseCronExpression("0 9 * * 1,3,5", "UTC");
|
|
expect(parsed.frequency).toBe("weekly");
|
|
expect(parsed.daysOfWeek).toEqual([1, 3, 5]);
|
|
expect(parsed.time).toBe("09:00");
|
|
});
|
|
|
|
it("falls back to custom for non-matching pattern", () => {
|
|
const parsed = parseCronExpression("*/15 * * * *", "UTC");
|
|
expect(parsed.frequency).toBe("custom");
|
|
expect(parsed.cronExpression).toBe("*/15 * * * *");
|
|
});
|
|
|
|
it("falls back to custom for malformed input", () => {
|
|
const parsed = parseCronExpression("not a cron", "UTC");
|
|
expect(parsed.frequency).toBe("custom");
|
|
});
|
|
|
|
it("preserves provided timezone", () => {
|
|
const parsed = parseCronExpression("0 9 * * *", "Asia/Shanghai");
|
|
expect(parsed.timezone).toBe("Asia/Shanghai");
|
|
});
|
|
|
|
it("rejects out-of-range minute", () => {
|
|
expect(parseCronExpression("60 * * * *", "UTC").frequency).toBe("custom");
|
|
});
|
|
|
|
it("rejects out-of-range hour", () => {
|
|
expect(parseCronExpression("0 24 * * *", "UTC").frequency).toBe("custom");
|
|
});
|
|
|
|
it("round-trips weekly preserving daysOfWeek", () => {
|
|
const cfg = { ...getDefaultTriggerConfig(), frequency: "weekly" as const, time: "14:45", daysOfWeek: [0, 2, 6] };
|
|
const parsed = parseCronExpression(toCronExpression(cfg), "UTC");
|
|
expect(parsed.frequency).toBe("weekly");
|
|
expect(parsed.time).toBe("14:45");
|
|
expect(parsed.daysOfWeek).toEqual([0, 2, 6]);
|
|
});
|
|
});
|