import { api, ApiRequestError } from './api'; export type ServerDataKey = | 'products-overview' | 'requirements' | 'version-plans' | 'dev-tasks' | 'test-cases' | 'bugs' | 'members' | 'task-categories' | 'task-worklogs' | 'work-activities' | 'xiaobao-risk-insights' | 'xiaobao-risk-snapshots' | 'xiaobao-warning-views' | 'overtime'; interface ServerDataResponse { key: ServerDataKey; value: T | null; version: string | null; } type ServerDataConflictBody = { code: 'APP_DATA_CONFLICT'; key: ServerDataKey; currentValue: T | null; currentVersion: string | null; }; const serverDataVersions = new Map(); export class ServerDataConflictError extends Error { constructor( public readonly key: ServerDataKey, public readonly currentValue: T | null, public readonly currentVersion: string | null, ) { super('Server data was changed by another user. Reload before saving again.'); this.name = 'ServerDataConflictError'; } } export async function loadServerData(key: ServerDataKey): Promise { const res = await api.get>(`/data/${key}`); serverDataVersions.set(key, res.version ?? null); return res.value; } export async function saveServerData(key: ServerDataKey, value: T): Promise { const payload: { value: T; version?: string | null } = { value }; if (serverDataVersions.has(key)) { payload.version = serverDataVersions.get(key) ?? null; } try { const res = await api.put>(`/data/${key}`, payload); serverDataVersions.set(key, res.version ?? null); } catch (error) { if ( error instanceof ApiRequestError && error.status === 409 && isServerDataConflictBody(error.body, key) ) { throw new ServerDataConflictError( error.body.key, error.body.currentValue, error.body.currentVersion, ); } throw error; } } function isServerDataConflictBody( body: unknown, key: ServerDataKey, ): body is ServerDataConflictBody { return ( typeof body === 'object' && body !== null && (body as { code?: unknown }).code === 'APP_DATA_CONFLICT' && (body as { key?: unknown }).key === key && ('currentVersion' in body ? typeof (body as { currentVersion?: unknown }).currentVersion === 'string' || (body as { currentVersion?: unknown }).currentVersion === null : true) ); }