diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 5ae97d0..78e565a 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -580,3 +580,21 @@ export async function updateEvent(id: number, payload: EventUpdatePayload): Prom export async function deleteEvent(id: number): Promise { return apiDelete(`/api/events/${id}`); } + +// ─── API Keys ───────────────────────────────────────────────────────────────── + +export interface ApiKeyEntry { + id: number + name: string + scope: string + key_prefix: string + last_used_at: string | null +} + +export const listApiKeys = () => + apiGet<{ api_keys: ApiKeyEntry[] }>('/api/api-keys').then(r => r.api_keys) + +export const createApiKey = (name: string, scope: 'read' | 'write') => + apiPost<{ key: string; api_key: ApiKeyEntry }>('/api/api-keys', { name, scope }) + +export const revokeApiKey = (id: number) => apiDelete(`/api/api-keys/${id}`) diff --git a/frontend/src/composables/useBackgroundRefresh.ts b/frontend/src/composables/useBackgroundRefresh.ts new file mode 100644 index 0000000..9aa2912 --- /dev/null +++ b/frontend/src/composables/useBackgroundRefresh.ts @@ -0,0 +1,29 @@ +import { onMounted, onUnmounted } from 'vue' + +/** + * Runs `refreshFn` on a recurring interval while the page is visible. + * Safe to use in any component — timer is cleared on unmount. + * + * @param refreshFn Called each tick. Should be silent (no loading state changes). + * @param intervalMs Polling interval in milliseconds. + * @param canRun Optional guard — refresh is skipped when this returns false. + */ +export function useBackgroundRefresh( + refreshFn: () => void, + intervalMs: number, + canRun?: () => boolean, +): void { + let timer: ReturnType | null = null + + onMounted(() => { + timer = setInterval(() => { + if (document.hidden) return + if (canRun && !canRun()) return + refreshFn() + }, intervalMs) + }) + + onUnmounted(() => { + if (timer !== null) clearInterval(timer) + }) +} diff --git a/frontend/src/utils/palette.ts b/frontend/src/utils/palette.ts new file mode 100644 index 0000000..e04101d --- /dev/null +++ b/frontend/src/utils/palette.ts @@ -0,0 +1,13 @@ +/** Cyclic color palette for milestone progress bars. */ +export const MILESTONE_PALETTE = [ + 'var(--color-primary)', + 'var(--color-success, #22c55e)', + '#c98a00', + '#8b5cf6', + 'var(--color-danger, #ef4444)', + '#06b6d4', +] + +export function milestoneColor(index: number): string { + return MILESTONE_PALETTE[index % MILESTONE_PALETTE.length] +} diff --git a/frontend/src/views/BriefingView.vue b/frontend/src/views/BriefingView.vue index 63c236d..d3c0fce 100644 --- a/frontend/src/views/BriefingView.vue +++ b/frontend/src/views/BriefingView.vue @@ -1,5 +1,6 @@ diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue index 40664c3..ca79eda 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -1,6 +1,8 @@