2026-06-17 16:02:04 -04:00
|
|
|
const BASE = '/api'
|
|
|
|
|
|
|
|
|
|
async function request(method, path, body) {
|
|
|
|
|
const res = await fetch(`${BASE}${path}`, {
|
|
|
|
|
method,
|
|
|
|
|
headers: body !== undefined ? { 'Content-Type': 'application/json' } : {},
|
|
|
|
|
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
|
|
|
})
|
|
|
|
|
if (!res.ok) {
|
|
|
|
|
const err = await res.json().catch(() => ({ detail: res.statusText }))
|
|
|
|
|
throw new Error(err.detail || `HTTP ${res.status}`)
|
|
|
|
|
}
|
|
|
|
|
return res.json()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const api = {
|
|
|
|
|
// Health
|
|
|
|
|
health: () => request('GET', '/health'),
|
|
|
|
|
|
|
|
|
|
// Settings
|
|
|
|
|
getSettings: () => request('GET', '/settings'),
|
|
|
|
|
saveSettings: (settings) => request('POST', '/settings', { settings }),
|
2026-07-23 14:02:14 -04:00
|
|
|
restartAndClearData: () => request('POST', '/settings/restart'),
|
2026-06-17 16:02:04 -04:00
|
|
|
|
|
|
|
|
// Holidays
|
|
|
|
|
getHolidays: () => request('GET', '/holidays'),
|
|
|
|
|
saveHolidays: (dates) => request('POST', '/holidays', { dates }),
|
|
|
|
|
|
|
|
|
|
// Tests
|
2026-07-29 11:08:17 -04:00
|
|
|
loadCsv: (csv_paths, target_dir) => request('POST', '/tests/load', { csv_paths, target_dir }),
|
2026-06-17 16:02:04 -04:00
|
|
|
|
|
|
|
|
// Schedule
|
|
|
|
|
compileSchedule: (opts) => request('POST', '/schedule/compile', opts),
|
2026-07-20 14:16:56 -04:00
|
|
|
getScheduleWeek: (start, version = null) => {
|
|
|
|
|
const params = new URLSearchParams({ start })
|
|
|
|
|
if (version !== null && version !== undefined) {
|
|
|
|
|
params.set('version', String(version))
|
|
|
|
|
}
|
|
|
|
|
return request('GET', `/schedule/week?${params.toString()}`)
|
|
|
|
|
},
|
2026-06-25 11:31:20 -04:00
|
|
|
getRerunTests: () => request('GET', '/tests/rerun'),
|
2026-06-17 16:02:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Transform the flat items array from GET /api/schedule/week into the
|
|
|
|
|
// { "YYYY-MM-DD": { shift1: [], shift2: [], shift3: [] } } shape the Calendar expects.
|
|
|
|
|
export function groupScheduleItems(items = []) {
|
|
|
|
|
const out = {}
|
|
|
|
|
for (const item of items) {
|
|
|
|
|
const key = item.scheduled_date
|
|
|
|
|
if (!out[key]) out[key] = { shift1: [], shift2: [], shift3: [] }
|
|
|
|
|
const shiftKey = `shift${item.shift_index}`
|
|
|
|
|
out[key][shiftKey].push(item)
|
|
|
|
|
}
|
|
|
|
|
return out
|
|
|
|
|
}
|