Files
scheduler/frontend/src/api.js
T

93 lines
3.1 KiB
JavaScript
Raw Normal View History

2026-06-17 16:02:04 -04:00
const BASE = '/api'
2026-08-03 10:50:58 -04:00
function buildAuthHeaders() {
const token = sessionStorage.getItem('auth_token')
return token ? { Authorization: `Bearer ${token}` } : {}
}
2026-06-17 16:02:04 -04:00
async function request(method, path, body) {
const res = await fetch(`${BASE}${path}`, {
method,
2026-08-03 10:50:58 -04:00
headers: {
...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),
...buildAuthHeaders(),
},
2026-06-17 16:02:04 -04:00
body: body !== undefined ? JSON.stringify(body) : undefined,
})
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: res.statusText }))
2026-08-03 10:50:58 -04:00
const error = new Error(err.detail || `HTTP ${res.status}`)
error.status = res.status
throw error
2026-06-17 16:02:04 -04:00
}
return res.json()
}
export const api = {
// Health
health: () => request('GET', '/health'),
2026-08-03 10:50:58 -04:00
// Auth
login: (username, password) => request('POST', '/auth/login', { username, password }),
me: () => request('GET', '/auth/me'),
listUsers: () => request('GET', '/users'),
createUser: (username, password, role) => request('POST', '/users', { username, password, role }),
deleteUser: (username) => request('DELETE', `/users/${encodeURIComponent(username)}`),
2026-06-17 16:02:04 -04:00
// 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
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-07-29 11:26:54 -04:00
exportWindow: async (windowId) => {
const params = new URLSearchParams({ window_id: windowId })
2026-08-03 10:50:58 -04:00
const res = await fetch(`${BASE}/schedule/export?${params}`, {
headers: {
...buildAuthHeaders(),
},
})
2026-07-29 11:26:54 -04:00
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: res.statusText }))
2026-08-03 10:50:58 -04:00
const error = new Error(err.detail || `HTTP ${res.status}`)
error.status = res.status
throw error
2026-07-29 11:26:54 -04:00
}
const blob = await res.blob()
const disposition = res.headers.get('Content-Disposition') ?? ''
const match = disposition.match(/filename="([^"]+)"/)
const filename = match ? match[1] : 'tests.zip'
return { blob, filename }
},
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
}