feat(HAI-004): add settings modal with GET/PUT API endpoints

- Extend config schema and store helpers to support settings persistence
- Add GET/PUT /settings REST API endpoints in dashboard routes
- Create SettingsModal component with frontend API wiring
- Add settings trigger button to Header and integrate into App
- Add CSS styles for settings modal and header actions
This commit is contained in:
Dustin Byrne
2026-03-25 20:06:02 -04:00
9 changed files with 212 additions and 10 deletions

View File

@@ -1,4 +1,4 @@
export { COLUMNS, COLUMN_LABELS, COLUMN_DESCRIPTIONS, VALID_TRANSITIONS } from "./types.js";
export type { Column, Task, TaskCreateInput, TaskDetail, BoardConfig, MergeResult } from "./types.js";
export { COLUMNS, COLUMN_LABELS, COLUMN_DESCRIPTIONS, VALID_TRANSITIONS, DEFAULT_SETTINGS } from "./types.js";
export type { Column, Task, TaskCreateInput, TaskDetail, BoardConfig, MergeResult, Settings } from "./types.js";
export { TaskStore } from "./store.js";
export { canTransition, getValidTransitions, resolveDependencyOrder } from "./board.js";

View File

@@ -3,8 +3,8 @@ import { execSync } from "node:child_process";
import { mkdir, readFile, writeFile, readdir } from "node:fs/promises";
import { join, sep } from "node:path";
import { existsSync, watch, type FSWatcher } from "node:fs";
import type { Task, TaskDetail, TaskCreateInput, BoardConfig, Column, MergeResult } from "./types.js";
import { VALID_TRANSITIONS } from "./types.js";
import type { Task, TaskDetail, TaskCreateInput, BoardConfig, Column, MergeResult, Settings } from "./types.js";
import { VALID_TRANSITIONS, DEFAULT_SETTINGS } from "./types.js";
export interface TaskStoreEvents {
"task:created": [task: Task];
@@ -44,6 +44,20 @@ export class TaskStore extends EventEmitter<TaskStoreEvents> {
}
}
async getSettings(): Promise<Settings> {
const config = await this.readConfig();
return { ...DEFAULT_SETTINGS, ...config.settings };
}
async updateSettings(patch: Partial<Settings>): Promise<Settings> {
const config = await this.readConfig();
const current = { ...DEFAULT_SETTINGS, ...config.settings };
const updated = { ...current, ...patch };
config.settings = updated;
await this.writeConfig(config);
return updated;
}
private async readConfig(): Promise<BoardConfig> {
const data = await readFile(this.configPath, "utf-8");
return JSON.parse(data);

View File

@@ -24,8 +24,19 @@ export interface TaskCreateInput {
dependencies?: string[];
}
export interface Settings {
maxConcurrent: number;
pollIntervalMs: number;
}
export const DEFAULT_SETTINGS: Settings = {
maxConcurrent: 2,
pollIntervalMs: 15000,
};
export interface BoardConfig {
nextId: number;
settings?: Settings;
}
export interface MergeResult {