diff --git a/packages/dashboard/app/components/ScheduledTasksModal.tsx b/packages/dashboard/app/components/ScheduledTasksModal.tsx index 0541cba8b..e417d20d3 100644 --- a/packages/dashboard/app/components/ScheduledTasksModal.tsx +++ b/packages/dashboard/app/components/ScheduledTasksModal.tsx @@ -254,30 +254,18 @@ export function ScheduledTasksModal({ onClose, addToast, projectId }: ScheduledT return (
-
-
-
- -

Automations

-
-
- {isShowingList && ( - - )} - -
+
+
+ +

Automations

+ +
-
+
+
+ + + {routines.length} automation{routines.length === 1 ? "" : "s"} + +
+
+ {isShowingList && ( + + )}
-
- - {routines.length} automation{routines.length === 1 ? "" : "s"} -
-
- -
- -
+
{renderContent()}
diff --git a/packages/dashboard/app/components/ScriptsModal.css b/packages/dashboard/app/components/ScriptsModal.css index 0fd8d099b..3c5b7a6b9 100644 --- a/packages/dashboard/app/components/ScriptsModal.css +++ b/packages/dashboard/app/components/ScriptsModal.css @@ -57,48 +57,43 @@ /* ── Scheduled Tasks ──────────────────────────────────────────────── */ -/* Automation count summary in scheduled tasks modal */ -.scheduling-summary { - display: inline-flex; - align-items: center; - gap: var(--space-xs); - padding: var(--space-sm) var(--space-lg); - font-size: var(--text-xs, 12px); - color: var(--text-muted); - border-bottom: 1px solid var(--border); -} - -.schedule-modal-content { - padding: var(--space-lg) 20px; - overflow-y: auto; - max-height: 70vh; -} - -/* Scheduling modal header */ -.scheduling-modal-header { - display: flex; - flex-direction: column; - align-items: stretch; - gap: var(--space-sm); -} - -.scheduling-header-main-row { +/* Scheduling toolbar below modal header */ +.scheduling-toolbar { display: flex; align-items: center; justify-content: space-between; gap: var(--space-md); + padding: var(--space-sm) var(--modal-padding, var(--space-lg)); + border-bottom: 1px solid var(--border); + background: color-mix(in srgb, var(--text) 10%, transparent); } -.scheduling-header-main-row .detail-title-row { +.scheduling-toolbar-left { + display: flex; + align-items: center; + gap: var(--space-md); min-width: 0; } -.scheduling-header-scope-row { +.scheduling-toolbar-right { display: flex; align-items: center; - justify-content: flex-start; - padding-top: var(--space-sm); - border-top: 1px solid var(--border); + gap: var(--space-sm); +} + +.scheduling-count { + display: inline-flex; + align-items: center; + gap: var(--space-xs); + font-size: var(--text-xs, 12px); + color: var(--text-muted); + white-space: nowrap; +} + +.schedule-modal-content { + padding: var(--space-lg) var(--space-xl); + overflow-y: auto; + max-height: 70vh; } /* Scheduling scope selector */ @@ -815,18 +810,19 @@ padding-right: var(--space-lg); } - .scheduling-header-main-row { - align-items: flex-start; - } - - .scheduling-modal-header .modal-header-actions { + .scheduling-toolbar { flex-wrap: wrap; - justify-content: flex-end; gap: var(--space-sm); } - .scheduling-header-scope-row { - padding-top: var(--space-sm); + .scheduling-toolbar-left { + flex: 1 1 100%; + flex-wrap: wrap; + } + + .scheduling-toolbar-right { + flex: 1 1 100%; + justify-content: flex-end; } .scheduling-scope-selector { @@ -838,10 +834,6 @@ flex: 1; justify-content: center; } - - .detail-tabs { - padding: 0 var(--space-lg); - } } /* ── Routines ──────────────────────────────────────────────────── */ diff --git a/packages/dashboard/app/components/__tests__/ScheduledTasksModal.test.tsx b/packages/dashboard/app/components/__tests__/ScheduledTasksModal.test.tsx index dea5905b0..752a9a473 100644 --- a/packages/dashboard/app/components/__tests__/ScheduledTasksModal.test.tsx +++ b/packages/dashboard/app/components/__tests__/ScheduledTasksModal.test.tsx @@ -109,11 +109,12 @@ describe("ScheduledTasksModal", () => { expect(screen.getByText("Automations")).toBeDefined(); expect(screen.getByRole("dialog").getAttribute("aria-labelledby")).toBe("schedules-modal-title"); + expect(screen.getByRole("button", { name: "Close" })).toBeDefined(); await waitFor(() => { expect(screen.getByText("No automations yet")).toBeDefined(); }); expect(screen.getByText("Create your first automation")).toBeDefined(); - expect(screen.getByText("Routines")).toBeDefined(); + expect(screen.getByText("0 automations")).toBeDefined(); expect(mockFetchAutomations).not.toHaveBeenCalled(); }); @@ -131,7 +132,7 @@ describe("ScheduledTasksModal", () => { expect(screen.getByText("New Automation")).toBeDefined(); }); - it("renders scope controls in a dedicated header row", async () => { + it("renders scope controls in the toolbar below the modal header", async () => { mockFetchRoutines.mockResolvedValue([makeRoutine({ name: "Scoped Routine" })]); const { container } = render(); @@ -139,15 +140,21 @@ describe("ScheduledTasksModal", () => { expect(screen.getByText("Scoped Routine")).toBeDefined(); }); - const headerActions = container.querySelector(".scheduling-header-main-row .modal-header-actions"); - const scopeRow = container.querySelector(".scheduling-header-scope-row"); + const header = container.querySelector(".modal-header"); + const toolbar = container.querySelector(".scheduling-toolbar"); + const toolbarLeft = container.querySelector(".scheduling-toolbar-left"); + const toolbarRight = container.querySelector(".scheduling-toolbar-right"); const scopeSelector = container.querySelector(".scheduling-scope-selector"); + const newAutomationButton = screen.getByRole("button", { name: /new automation/i }); - expect(headerActions).toBeTruthy(); - expect(scopeRow).toBeTruthy(); + expect(header).toBeTruthy(); + expect(toolbar).toBeTruthy(); + expect(toolbarLeft).toBeTruthy(); + expect(toolbarRight).toBeTruthy(); expect(scopeSelector).toBeTruthy(); - expect(scopeRow?.contains(scopeSelector as Node)).toBe(true); - expect(headerActions?.contains(scopeSelector as Node)).toBe(false); + expect(toolbarLeft?.contains(scopeSelector as Node)).toBe(true); + expect(header?.contains(scopeSelector as Node)).toBe(false); + expect(toolbarRight?.contains(newAutomationButton)).toBe(true); }); it("uses routine APIs with global scope by default", async () => {