Refine scheduled task dashboard UX (#135)
* add gated codex goals support * sync README SDK versions * bump claude agent sdk * add codex goals settings toggle * reuse status dashboard message and simplify settings actions * refine settings page UX * fix settings nav hash routing * add dashboard UX verification * refine dashboard settings and inbox UX * remove inbox top-level navigation * remove dashboard health top-level navigation * fix: allow runtime image attachment paths * feat: configure attachment allowlist dirs * fix: clean duplicate dashboard status messages * fix: poll dashboard duplicate cleanup between status updates * fix: delete dashboard duplicates on create * Refine settings IA with tabbed sections * Add read-only runtime inventory settings * Fix runtime inventory MCP JSON parsing * Add room skill settings inventory * Add room skill setting mutations * Apply room skill overrides to runner spawn * Refine scheduled task dashboard UX
This commit is contained in:
@@ -162,6 +162,8 @@ async function main() {
|
||||
},
|
||||
);
|
||||
|
||||
await runScheduledBoardScenario(browser, baseUrl);
|
||||
|
||||
await runScenario(
|
||||
'health route redirects to rooms and degraded state is conditional',
|
||||
browser,
|
||||
@@ -196,6 +198,29 @@ async function main() {
|
||||
}
|
||||
}
|
||||
|
||||
async function runScheduledBoardScenario(browser: Browser, baseUrl: string) {
|
||||
await runScenario(
|
||||
'scheduled board surfaces next task without empty lanes',
|
||||
browser,
|
||||
baseUrl,
|
||||
async (page) => {
|
||||
await page.goto(new URL('/#/scheduled', baseUrl).toString(), {
|
||||
waitUntil: 'networkidle',
|
||||
});
|
||||
|
||||
await assertVisible(page.locator('#scheduled .task-command-center'));
|
||||
await assertVisible(page.locator('#scheduled .task-create-form'));
|
||||
await assertVisible(page.getByText('Nightly cleanup').first());
|
||||
await assertVisible(page.getByText('*/15 * * * *').first());
|
||||
assert.equal(await page.locator('#scheduled .task-card').count(), 3);
|
||||
assert.equal(
|
||||
await page.locator('#scheduled .task-group-empty').count(),
|
||||
0,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async function startDashboardServer(): Promise<ViteDevServer> {
|
||||
const port = Number(process.env.DASHBOARD_UX_PORT ?? 5175);
|
||||
const server = await createServer({
|
||||
@@ -306,7 +331,7 @@ async function handleMockApi(route: Route, state: MockApiState) {
|
||||
}
|
||||
|
||||
if (method === 'GET' && url.pathname === '/api/tasks') {
|
||||
await fulfillJson(route, []);
|
||||
await fulfillJson(route, mockTasks());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -532,6 +557,52 @@ function mockStatusSnapshot() {
|
||||
};
|
||||
}
|
||||
|
||||
function mockTask(overrides: Record<string, unknown>) {
|
||||
return {
|
||||
agentType: 'codex',
|
||||
chatJid: 'room@example',
|
||||
ciMetadata: null,
|
||||
ciProvider: null,
|
||||
contextMode: 'group',
|
||||
createdAt: MOCK_TIME,
|
||||
groupFolder: 'ejclaw',
|
||||
id: 'task-fixture',
|
||||
isWatcher: false,
|
||||
lastResult: 'ok',
|
||||
lastRun: MOCK_TIME,
|
||||
nextRun: new Date(Date.now() + 30 * 60_000).toISOString(),
|
||||
promptLength: 28,
|
||||
promptPreview: 'Nightly cleanup',
|
||||
scheduleType: 'cron',
|
||||
scheduleValue: '*/15 * * * *',
|
||||
status: 'active',
|
||||
suspendedUntil: null,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function mockTasks() {
|
||||
return [
|
||||
mockTask({ id: 'task-nightly-cleanup' }),
|
||||
mockTask({
|
||||
ciProvider: 'github',
|
||||
id: 'task-ci-watch',
|
||||
isWatcher: true,
|
||||
promptPreview: 'Watch PR #133',
|
||||
scheduleType: 'interval',
|
||||
scheduleValue: '5m',
|
||||
}),
|
||||
mockTask({
|
||||
id: 'task-paused-report',
|
||||
nextRun: null,
|
||||
promptPreview: 'Weekly report',
|
||||
scheduleValue: '0 9 * * 1',
|
||||
status: 'paused',
|
||||
suspendedUntil: new Date(Date.now() + 2 * 3_600_000).toISOString(),
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
function runtimeSkill(name: string, description: string, skillPath: string) {
|
||||
return { name, description, path: skillPath };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user