feat: SAC 0.6.0 user edit UI, login to dashboard, daily report timer sync

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-01 09:55:12 +10:00
parent 56c469ddbd
commit 8de5d14cfb
17 changed files with 449 additions and 56 deletions
+56 -1
View File
@@ -51,7 +51,7 @@ export async function apiFetch<T>(path: string, init: RequestInit = {}): Promise
}
if (!res.ok) {
const text = await res.text();
throw new Error(text || res.statusText);
throw new Error(parseApiError(text) || res.statusText);
}
return res.json() as Promise<T>;
}
@@ -72,6 +72,61 @@ export function fetchMe(): Promise<MeResponse> {
return apiFetch<MeResponse>("/api/v1/auth/me");
}
export function parseApiError(text: string): string {
if (!text) return "";
try {
const data = JSON.parse(text) as { detail?: string | Array<{ msg?: string }> };
if (typeof data.detail === "string") return data.detail;
if (Array.isArray(data.detail)) {
return data.detail.map((item) => item.msg ?? String(item)).join("; ");
}
} catch {
/* plain text */
}
return text;
}
export interface UserSummary {
id: number;
username: string;
role: string;
is_active: boolean;
created_at: string;
}
export interface UserListResponse {
items: UserSummary[];
}
export interface UpdateUserPayload {
username?: string;
role?: string;
password?: string;
is_active?: boolean;
}
export function fetchUsers(): Promise<UserListResponse> {
return apiFetch<UserListResponse>("/api/v1/users");
}
export function createUser(payload: {
username: string;
password: string;
role: string;
}): Promise<UserSummary> {
return apiFetch<UserSummary>("/api/v1/users", {
method: "POST",
body: JSON.stringify(payload),
});
}
export function updateUser(userId: number, payload: UpdateUserPayload): Promise<UserSummary> {
return apiFetch<UserSummary>(`/api/v1/users/${userId}`, {
method: "PATCH",
body: JSON.stringify(payload),
});
}
export interface EventSummary {
id: number;
event_id: string;