MVP frontend+backend

This commit is contained in:
2026-06-14 09:58:30 +02:00
parent d9539f23cc
commit 772d853e37
56 changed files with 5099 additions and 207 deletions
+130 -17
View File
@@ -17,6 +17,7 @@ import type {
VacationDays,
VacationDaysCreateRequest,
WorkspaceCreateRequest,
WorkspaceUpdateRequest,
WorkspaceOverview,
} from '@/types'
import type { Workspace, BreakPeriod, Tip, PlannedLeaveDay } from '@/types'
@@ -47,10 +48,17 @@ async function apiFetch<T>(
message: response.statusText,
}
try {
const body = (await response.json()) as { detail?: string; code?: string; message?: string }
const body = (await response.json()) as {
detail?: string | Array<{ msg?: string }>
code?: string
message?: string
}
const detail = Array.isArray(body.detail)
? body.detail.map((e) => e.msg ?? JSON.stringify(e)).join('; ')
: body.detail
error = {
code: body.code ?? String(response.status),
message: body.detail ?? body.message ?? response.statusText,
message: detail ?? body.message ?? response.statusText,
}
} catch {
// response body wasn't JSON — use status text
@@ -91,6 +99,30 @@ export async function refreshToken(): Promise<TokenResponse> {
return apiFetch('/auth/refresh', { method: 'POST' })
}
export interface UserContext {
personal_data_uuid: string
workspace_id: string
parent_name: string
children: Array<{ id: string; name: string; birth_date: string }>
}
export async function getUserContext(): Promise<UserContext> {
return apiFetch('/auth/context')
}
export interface CreateChildRequest {
workspace_id: string
name: string
birth_date: string
}
export async function createChild(req: CreateChildRequest): Promise<{ id: string; name: string }> {
return apiFetch('/children', {
method: 'POST',
body: JSON.stringify(req),
})
}
// ---------------------------------------------------------------------------
// Workspace
// ---------------------------------------------------------------------------
@@ -103,6 +135,19 @@ export async function getWorkspace(workspaceId: string): Promise<Workspace> {
return apiFetch(`/workspace/${workspaceId}`)
}
export async function updateWorkspace(
workspaceId: string,
req: WorkspaceUpdateRequest,
): Promise<Workspace> {
return apiFetch(`/workspace/${workspaceId}`, {
method: 'PUT',
body: JSON.stringify({
name: req.name,
planning_goal: req.planningGoal,
}),
})
}
// ---------------------------------------------------------------------------
// Vacation days (reference endpoint group — mirror this pattern for others)
// ---------------------------------------------------------------------------
@@ -160,21 +205,55 @@ export async function getWorkspaceOverview(
return apiFetch(`/analytics/${workspaceId}/overview?child_id=${childId}`)
}
// ---------------------------------------------------------------------------
// Settings
// ---------------------------------------------------------------------------
interface ParentSettingsUpdate {
salary_type?: string | undefined
employment_status?: string | undefined
}
export async function updateParentSettings(
personalDataUuid: string,
payload: ParentSettingsUpdate,
): Promise<{ uuid: string; name: string; salary_type: string; employment_status: string }> {
return apiFetch(`/settings/parent/${personalDataUuid}`, {
method: 'PUT',
body: JSON.stringify(payload),
})
}
interface ChildSettingsUpdate {
birth_date?: string
preschool_start_date?: string
}
export async function updateChildSettings(
childId: string,
payload: ChildSettingsUpdate,
): Promise<{ id: string; name: string; birth_date: string }> {
return apiFetch(`/settings/child/${childId}`, {
method: 'PUT',
body: JSON.stringify(payload),
})
}
// ---------------------------------------------------------------------------
// Break Periods
// ---------------------------------------------------------------------------
export async function getBreakPeriods(workspaceId: string): Promise<BreakPeriod[]> {
return apiFetch(`/workspaces/${workspaceId}/break-periods`)
return apiFetch(`/break-periods/${workspaceId}`)
}
export async function createBreakPeriod(
workspaceId: string,
payload: { startDate: string; endDate: string; name: string; targetWeeks: number },
): Promise<BreakPeriod> {
return apiFetch(`/workspaces/${workspaceId}/break-periods`, {
return apiFetch(`/break-periods`, {
method: 'POST',
body: JSON.stringify(payload),
body: JSON.stringify({ ...payload, workspace_id: workspaceId }),
})
}
@@ -187,40 +266,74 @@ export async function deleteBreakPeriod(id: string): Promise<void> {
// ---------------------------------------------------------------------------
export async function getTips(workspaceId: string): Promise<Tip[]> {
return apiFetch(`/workspaces/${workspaceId}/tips`)
return apiFetch(`/tips/${workspaceId}`)
}
// ---------------------------------------------------------------------------
// Parental Leave Fetch
// ---------------------------------------------------------------------------
export async function getParentalLeave(workspaceId: string): Promise<ParentalLeaveFetch> {
return apiFetch(`/workspaces/${workspaceId}/parental-leave`)
export async function getParentalLeave(
personalDataUuid: string,
childId: string,
): Promise<ParentalLeaveFetch> {
return apiFetch(`/parental-leave/${personalDataUuid}/${childId}`)
}
// ---------------------------------------------------------------------------
// Planned Days
// ---------------------------------------------------------------------------
export async function getPlannedDays(
workspaceId: string,
childId?: string,
): Promise<PlannedLeaveDay[]> {
const query = childId ? `?childId=${childId}` : ''
return apiFetch(`/workspaces/${workspaceId}/planned-days${query}`)
export async function getPlannedDays(childId?: string): Promise<PlannedLeaveDay[]> {
const query = childId ? `?child_id=${childId}` : ''
return apiFetch(`/planned-days${query}`)
}
export async function createPlannedDay(
workspaceId: string,
childId: string,
payload: { date: string; leaveType: string },
): Promise<PlannedLeaveDay> {
return apiFetch(`/workspaces/${workspaceId}/planned-days`, {
return apiFetch(`/planned-days`, {
method: 'POST',
body: JSON.stringify({ ...payload, childId }),
body: JSON.stringify({ ...payload, child_id: childId }),
})
}
export async function deletePlannedDay(id: string): Promise<void> {
return apiFetch(`/planned-days/${id}`, { method: 'DELETE' })
}
// ---------------------------------------------------------------------------
// FK Scraper (dev / authenticated users only — requires scraper_enabled=true)
// ---------------------------------------------------------------------------
interface ScraperStartRequest {
workspace_id?: string
personal_data_uuid?: string
child_id?: string
}
interface ScraperStartResponse {
session_id: string
status: string
workspace_id: string
personal_data_uuid: string
child_id: string
}
export async function startScraperSession(
payload: ScraperStartRequest,
): Promise<ScraperStartResponse> {
return apiFetch('/fetch-data/start', { method: 'POST', body: JSON.stringify(payload) })
}
export async function pollScraperSession(sessionId: string): Promise<unknown> {
return apiFetch(`/fetch-data/poll?session_id=${encodeURIComponent(sessionId)}`)
}
export async function cancelScraperSession(sessionId: string): Promise<void> {
return apiFetch('/fetch-data/cancel', {
method: 'POST',
body: JSON.stringify({ session_id: sessionId }),
})
}