Show the partner's name, and count in the unit that was asked for
A partner added during onboarding appeared as "Parent 2" on the overview until you navigated away and back: the members list had been fetched before that partner existed and nothing refetched when the wizard finished. The fallback was doing its job; it was covering for stale data. A period drawn on the calendar is measured in days, but a shortfall was reported in the weeks the engine had rounded it to, so seven days came back as three weeks. It now reports what was actually asked for.
This commit is contained in:
@@ -80,6 +80,10 @@ export const calendar = {
|
||||
'{name} ({start}–{end}): none of the {weeksWished} week(s) requested could be funded — {shortfallDays} day(s) short',
|
||||
'calendar.toastShortfallPartial':
|
||||
'{name} ({start}–{end}): only {weeksFunded} of {weeksWished} week(s) funded — {shortfallDays} day(s) short',
|
||||
'calendar.toastShortfallNoneDays':
|
||||
'{name} ({start}–{end}): none of the {daysWished} day(s) requested could be funded — {shortfallDays} day(s) short',
|
||||
'calendar.toastShortfallPartialDays':
|
||||
'{name} ({start}–{end}): only {daysFunded} of {daysWished} day(s) funded — {shortfallDays} day(s) short',
|
||||
'calendar.undoButton': 'Undo',
|
||||
'calendar.undoError': 'Failed to undo the change',
|
||||
'calendar.dismissToastAria': 'Dismiss the message',
|
||||
|
||||
@@ -80,6 +80,10 @@ export const calendar = {
|
||||
'{name} ({start}–{end}): inget av de {weeksWished} önskade veckorna kunde finansieras — {shortfallDays} dag(ar) saknas',
|
||||
'calendar.toastShortfallPartial':
|
||||
'{name} ({start}–{end}): endast {weeksFunded} av {weeksWished} veckor finansierades — {shortfallDays} dag(ar) saknas',
|
||||
'calendar.toastShortfallNoneDays':
|
||||
'{name} ({start}–{end}): inget av de {daysWished} önskade dagarna kunde finansieras — {shortfallDays} dag(ar) saknas',
|
||||
'calendar.toastShortfallPartialDays':
|
||||
'{name} ({start}–{end}): endast {daysFunded} av {daysWished} dagar finansierades — {shortfallDays} dag(ar) saknas',
|
||||
'calendar.undoButton': 'Ångra',
|
||||
'calendar.undoError': 'Det gick inte att ångra ändringen',
|
||||
'calendar.dismissToastAria': 'Stäng meddelandet',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import { Dashboard } from './Dashboard'
|
||||
@@ -159,6 +159,49 @@ describe('Dashboard', () => {
|
||||
expect(screen.queryByTestId('dashboard')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('refetches workspace members once onboarding hands off (children goes from empty to non-empty)', async () => {
|
||||
// Regression: a couple's partner is added mid-onboarding via a direct
|
||||
// `api.addPartner` call, invisible to this component's own
|
||||
// `useWorkspaceMembers` instance (fetched once, on mount, back when
|
||||
// there was only one member). `Onboarding` only ever refreshes
|
||||
// `WorkspaceContext` — flipping `children` from empty to non-empty is
|
||||
// the only externally-observable signal this component gets that the
|
||||
// member list it already fetched might now be stale.
|
||||
const refreshMembers = vi.fn().mockResolvedValue(undefined)
|
||||
vi.mocked(hooks.useWorkspaceMembers).mockReturnValue(
|
||||
makeWorkspaceMembersResult({ refresh: refreshMembers, data: [] }),
|
||||
)
|
||||
const soloWorkspace = makeWorkspaceContextValue({
|
||||
workspace: { id: 'ws-1', name: 'Test Workspace', planningMode: 'couple', createdAt: '2026-01-01' },
|
||||
workspaceId: 'ws-1',
|
||||
children: [],
|
||||
selectedChildId: null,
|
||||
personalDataUuid: 'pd-1',
|
||||
parentName: 'Test Parent',
|
||||
})
|
||||
vi.mocked(useWorkspace).mockReturnValue(soloWorkspace)
|
||||
|
||||
const { rerender } = renderWithRouter(<Dashboard />)
|
||||
expect(screen.getByTestId('onboarding-page')).toBeInTheDocument()
|
||||
expect(refreshMembers).not.toHaveBeenCalled()
|
||||
|
||||
// Onboarding finishes: `WorkspaceContext.children` flips non-empty.
|
||||
vi.mocked(useWorkspace).mockReturnValue(
|
||||
makeWorkspaceContextValue({
|
||||
...soloWorkspace,
|
||||
children: [{ id: 'c-1', name: 'Child 1', birth_date: '2026-01-01' }],
|
||||
selectedChildId: 'c-1',
|
||||
})
|
||||
)
|
||||
rerender(
|
||||
<BrowserRouter>
|
||||
<Dashboard />
|
||||
</BrowserRouter>,
|
||||
)
|
||||
|
||||
await waitFor(() => expect(refreshMembers).toHaveBeenCalledTimes(1))
|
||||
})
|
||||
|
||||
describe('estimated-figures badge', () => {
|
||||
it('is hidden once real parental-leave balances have been fetched (the default mock)', () => {
|
||||
renderWithRouter(<Dashboard />)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactNode, useState } from 'react'
|
||||
import { ReactNode, useEffect, useRef, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useWorkspace } from '@/context'
|
||||
import {
|
||||
@@ -47,7 +47,7 @@ export function Dashboard(): ReactNode {
|
||||
)
|
||||
const { data: plannedDays, isLoading: pdLoading } = usePlannedDays(selectedChildId ?? undefined)
|
||||
const { data: parentalLeave, refresh: refreshParentalLeave } = useParentalLeave(personalDataUuid, selectedChildId ?? null)
|
||||
const { data: members, isLoading: membersLoading } = useWorkspaceMembers(workspace?.id ?? null)
|
||||
const { data: members, isLoading: membersLoading, refresh: refreshMembers } = useWorkspaceMembers(workspace?.id ?? null)
|
||||
const { data: tips, dismiss: dismissTip } = useOptimizationTips(workspace?.id ?? null)
|
||||
const scraper = useScraperFetch()
|
||||
|
||||
@@ -56,6 +56,23 @@ export function Dashboard(): ReactNode {
|
||||
const [isManualFetchSaving, setIsManualFetchSaving] = useState(false)
|
||||
const [manualFetchError, setManualFetchError] = useState<string | null>(null)
|
||||
|
||||
// `useWorkspaceMembers` fetches once per `workspace.id`, which never
|
||||
// changes across onboarding — so a couple's partner, added mid-wizard via
|
||||
// `Onboarding.tsx`'s own direct `api.addPartner` call, is invisible to
|
||||
// this hook's already-fetched `data` until something explicitly refetches
|
||||
// it. `Onboarding` only refreshes `WorkspaceContext` (which flips
|
||||
// `children` from empty to non-empty) when it finishes — it has no way to
|
||||
// reach into this component's `useWorkspaceMembers` instance. Watching
|
||||
// that same empty-to-non-empty transition here is what tells this
|
||||
// component "onboarding just finished, the members list might be stale."
|
||||
const wasAwaitingFirstChildRef = useRef(children.length === 0)
|
||||
useEffect(() => {
|
||||
if (wasAwaitingFirstChildRef.current && children.length > 0) {
|
||||
void refreshMembers()
|
||||
}
|
||||
wasAwaitingFirstChildRef.current = children.length === 0
|
||||
}, [children.length, refreshMembers])
|
||||
|
||||
const isLoading = wsLoading || analyticsLoading || pdLoading
|
||||
|
||||
if (isLoading) {
|
||||
|
||||
Reference in New Issue
Block a user