Bugfixes in dashboard and calendar

This commit is contained in:
2026-06-14 19:02:20 +02:00
parent 566595984f
commit d556ce0f6f
6 changed files with 699 additions and 31 deletions
+299
View File
@@ -0,0 +1,299 @@
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import * as api from './api'
import type { RawParentalLeaveFetch, RawWorkspaceOverview } from './api'
describe('API response mappers (snake_case → camelCase)', () => {
let fetchMock: ReturnType<typeof vi.fn>
beforeEach(() => {
fetchMock = vi.fn()
globalThis.fetch = fetchMock as any
})
afterEach(() => {
vi.clearAllMocks()
})
describe('getWorkspaceOverview', () => {
it('maps snake_case workspace overview to camelCase', async () => {
const rawResponse: RawWorkspaceOverview = {
workspace_id: 'ws1',
child_id: 'child1',
parent_projections: [
{
personal_data_uuid: 'pd1',
child_id: 'child1',
birth_date: '2024-01-01',
fourth_birthday: '2028-01-01',
total_days: 480,
days_used: 100,
planned_days_before_b4: 150,
projected_left_at_b4: 230,
savable_after_b4: 96,
expiring_at_b4: 134,
},
],
combined_days_used: 100,
combined_days_left: 380,
combined_expiring_at_b4: 134,
}
fetchMock.mockResolvedValue({
ok: true,
status: 200,
json: async () => rawResponse,
})
const result = await api.getWorkspaceOverview('ws1', 'child1')
expect(result.workspaceId).toBe('ws1')
expect(result.childId).toBe('child1')
expect(result.combinedDaysUsed).toBe(100)
expect(result.combinedDaysLeft).toBe(380)
expect(result.combinedExpiringAtB4).toBe(134)
expect(result.parentProjections.length).toBe(1)
expect(result.parentProjections[0]!.personalDataUuid).toBe('pd1')
expect(result.parentProjections[0]!.fourthBirthday).toBe('2028-01-01')
expect(result.parentProjections[0]!.plannedDaysBeforeB4).toBe(150)
expect(result.parentProjections[0]!.projectedLeftAtB4).toBe(230)
expect(result.parentProjections[0]!.savableAfterB4).toBe(96)
expect(result.parentProjections[0]!.expiringAtB4).toBe(134)
})
})
describe('getParentalLeave', () => {
it('maps snake_case parental leave fetch to camelCase with nested tierData', async () => {
const rawResponse: RawParentalLeaveFetch = {
id: 'pf1',
personal_data_uuid: 'pd1',
workspace_id: 'ws1',
child_id: 'child1',
source: 'manual',
fetched_at: '2026-06-14T17:09:47',
total_days: 480,
days_used: 100,
days_left: 380,
reserved_days_total: 90,
reserved_days_left: 80,
transferable_days_left: 100,
dubbeldagar_left: 60,
tier_data: {
sjukpenning_total: 390,
sjukpenning_used: 50,
sjukpenning_left: 340,
lagsta_niva_total: 90,
lagsta_niva_used: 10,
lagsta_niva_left: 80,
},
created_at: '2026-06-14T17:09:47',
updated_at: '2026-06-14T17:09:47',
}
fetchMock.mockResolvedValue({
ok: true,
status: 200,
json: async () => rawResponse,
})
const result = await api.getParentalLeave('pd1', 'child1')
expect(result.personalDataUuid).toBe('pd1')
expect(result.workspaceId).toBe('ws1')
expect(result.childId).toBe('child1')
expect(result.fetchedAt).toBe('2026-06-14T17:09:47')
expect(result.totalDays).toBe(480)
expect(result.daysUsed).toBe(100)
expect(result.daysLeft).toBe(380)
expect(result.reservedDaysTotal).toBe(90)
expect(result.reservedDaysLeft).toBe(80)
expect(result.transferableDaysLeft).toBe(100)
expect(result.dubbeldagarLeft).toBe(60)
expect(result.createdAt).toBe('2026-06-14T17:09:47')
expect(result.updatedAt).toBe('2026-06-14T17:09:47')
expect(result.tierData!.sjukpenningTotal).toBe(390)
expect(result.tierData!.sjukpenningUsed).toBe(50)
expect(result.tierData!.sjukpenningLeft).toBe(340)
expect(result.tierData!.lagstaNivaTotal).toBe(90)
expect(result.tierData!.lagstaNivaUsed).toBe(10)
expect(result.tierData!.lagstaNivaLeft).toBe(80)
})
})
describe('getPlannedDays', () => {
it('maps snake_case planned days to camelCase', async () => {
const rawResponse = [
{
id: 'pd1',
workspace_id: 'ws1',
child_id: 'child1',
parent_personal_data_uuid: 'pp1',
date: '2026-06-15',
day_type: 'sjukpenning' as const,
is_dubbeldag: false,
created_at: '2026-06-14T17:09:47',
updated_at: '2026-06-14T17:09:47',
},
{
id: 'pd2',
workspace_id: 'ws1',
child_id: 'child1',
parent_personal_data_uuid: 'pp1',
date: '2026-06-16',
day_type: 'vacation' as const,
is_dubbeldag: true,
created_at: '2026-06-14T17:09:47',
updated_at: '2026-06-14T17:09:47',
},
]
fetchMock.mockResolvedValue({
ok: true,
status: 200,
json: async () => rawResponse,
})
const result = await api.getPlannedDays('child1')
expect(result).toHaveLength(2)
expect(result[0]!.workspaceId).toBe('ws1')
expect(result[0]!.childId).toBe('child1')
expect(result[0]!.parentPersonalDataUuid).toBe('pp1')
expect(result[0]!.dayType).toBe('sjukpenning')
expect(result[0]!.isDubbeldag).toBe(false)
expect(result[1]!.dayType).toBe('vacation')
expect(result[1]!.isDubbeldag).toBe(true)
})
})
describe('getBreakPeriods', () => {
it('maps snake_case break periods to camelCase', async () => {
const rawResponse = [
{
id: 'bp1',
workspace_id: 'ws1',
name: 'Summer 2026',
start_date: '2026-06-15',
end_date: '2026-08-31',
target_weeks: 12,
created_at: '2026-06-14T17:09:47',
updated_at: '2026-06-14T17:09:47',
},
]
fetchMock.mockResolvedValue({
ok: true,
status: 200,
json: async () => rawResponse,
})
const result = await api.getBreakPeriods('ws1')
expect(result).toHaveLength(1)
expect(result[0]!.workspaceId).toBe('ws1')
expect(result[0]!.name).toBe('Summer 2026')
expect(result[0]!.startDate).toBe('2026-06-15')
expect(result[0]!.endDate).toBe('2026-08-31')
expect(result[0]!.targetWeeks).toBe(12)
})
})
describe('getVacationDaysByWorkspace', () => {
it('maps snake_case vacation days to camelCase', async () => {
const rawResponse = [
{
id: 'vd1',
personal_data_uuid: 'pd1',
workspace_id: 'ws1',
year: 2026,
total_days: 25,
saved_days_in: 5,
created_at: '2026-06-14T17:09:47',
updated_at: '2026-06-14T17:09:47',
},
]
fetchMock.mockResolvedValue({
ok: true,
status: 200,
json: async () => rawResponse,
})
const result = await api.getVacationDaysByWorkspace('ws1')
expect(result).toHaveLength(1)
expect(result[0]!.personalDataUuid).toBe('pd1')
expect(result[0]!.workspaceId).toBe('ws1')
expect(result[0]!.year).toBe(2026)
expect(result[0]!.totalDays).toBe(25)
expect(result[0]!.savedDaysIn).toBe(5)
})
})
describe('createBreakPeriod', () => {
it('sends camelCase request and maps snake_case response to camelCase', async () => {
const rawResponse = {
id: 'bp1',
workspace_id: 'ws1',
name: 'Summer 2026',
start_date: '2026-06-15',
end_date: '2026-08-31',
target_weeks: 12,
created_at: '2026-06-14T17:09:47',
updated_at: '2026-06-14T17:09:47',
}
fetchMock.mockResolvedValue({
ok: true,
status: 200,
json: async () => rawResponse,
})
const result = await api.createBreakPeriod('ws1', {
startDate: '2026-06-15',
endDate: '2026-08-31',
name: 'Summer 2026',
targetWeeks: 12,
})
const calls = (fetchMock.mock?.calls ?? []) as any[][]
if (calls.length === 0) throw new Error('No fetch calls made')
const [, options] = calls[0]!
const bodyStr = options.body
const body = JSON.parse(bodyStr)
expect(body.start_date).toBe('2026-06-15')
expect(body.end_date).toBe('2026-08-31')
expect(body.target_weeks).toBe(12)
expect(result.startDate).toBe('2026-06-15')
expect(result.endDate).toBe('2026-08-31')
expect(result.targetWeeks).toBe(12)
})
})
describe('getWorkspace', () => {
it('maps snake_case workspace to camelCase', async () => {
const rawResponse = {
id: 'ws1',
name: 'My Workspace',
planning_goal: 'max_time' as const,
created_at: '2026-06-14T17:09:47',
}
fetchMock.mockResolvedValue({
ok: true,
status: 200,
json: async () => rawResponse,
})
const result = await api.getWorkspace('ws1')
expect(result.id).toBe('ws1')
expect(result.name).toBe('My Workspace')
expect(result.planningGoal).toBe('max_time')
expect(result.createdAt).toBe('2026-06-14T17:09:47')
})
})
})