+
+
+
+
+ {routines.length} automation{routines.length === 1 ? "" : "s"}
+
+
+
+ {isShowingList && (
+
+ )}
-
-
- {routines.length} automation{routines.length === 1 ? "" : "s"}
-
-
-
-
-
-
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 () => {