From a59b5f949be37c433b059ca3ae69946157a510cb Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 16 Jul 2026 17:03:40 +0000 Subject: [PATCH 01/10] fix(db): repair broken migration history for fresh deploys - 20260223_simplify_roles_admin_only failed on any fresh database: Postgres cannot cast a column default to a recreated enum type. Drop the default before the type swap and restore it after. - Add catch-up migration for Incident.mediationMinutes and the SystemConfig table, which existed in schema.prisma but were never captured in a migration (drift from a past db push). IF NOT EXISTS guards keep it safe on databases that already have them. Verified: prisma migrate deploy succeeds on a fresh database and prisma migrate diff reports no drift afterwards. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01XYKT7w58RCJasVqGUXNVkQ --- nav-admin-desktop-1280.png | Bin 97532 -> 0 bytes .../migration.sql | 9 +++++---- .../migration.sql | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) delete mode 100644 nav-admin-desktop-1280.png create mode 100644 prisma/migrations/20260716000000_add_mediation_minutes_and_system_config/migration.sql diff --git a/nav-admin-desktop-1280.png b/nav-admin-desktop-1280.png deleted file mode 100644 index 91c018953acc811450706d3318819dd1ed7f8c88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 97532 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�wsOAaWiQvYPcgevre1vCWqlRLlK#VCd8!u^RM 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�VGk;@m;=u>}* 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�nKZ!dn+tZIKU9N( zJJ`%O?}Z5d7Yiiciwq15JUVi|1xWwUN5zr+f#R_#K=_f*-4~VzOas2JOfg+M!(SE^ z6=48`gP>C8?}7wg4FLRJVBiXkuj-~@iVk4ge)_Z&`kd=UQNman+a-#M<9!R=$r0AD0Shet<8pL1E~HPYkp zY2IfC$o+1?9j*cLG{m942G{(^^@<8#4gmOQ zZmRP5K&W;simB=5wgVpc3J~c%`Q!TkYoGiNyZv+S>}ZkZ?fMG+maqL-?`{MSK$Fsz zbu(h%B`z)F*T?Ov$ZeP_5`c*?{zcXd}gW~5C3EM zHjfiPZ#Aek>?hBtFu0_k77=gcbW3ejGaAk*yJO(amY~M1obW!nIjxxD?Xwig%k(~P z?#ZOsbEJ82I(kQ3F+7T)hsg1hFHt;iD`Ub>nB6z~&S>l!WY}p8;aBb~t$7B{? zb6yIh%9#5Q?wPoaJh33P_o6SwN!2ea?gsacpL@5l2nf{R zBvhEwhxHmsBTnQjw4N|6ryuE+Ta9HQnRll@^H)%8s7#gcc@1`__DQgPz}~K*}xG>%NFcZN8H;2%W0xIp8Hi5PtMmcoP8i? zv-sKTYFAc#pvI)TIZF zN&%JdLz^8Ab8dbK;Fooc9-P Date: Thu, 16 Jul 2026 17:03:53 +0000 Subject: [PATCH 02/10] feat(transfers): execute the move when approving a transfer request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Approving a request previously only flipped status to APPROVED while the UI showed success — the resident stayed in the old unit and the captured target unit was discarded. Now, when a request names a target unit and the resident has an active placement, approval executes the transfer in one transaction: atomic PENDING claim, capacity check (first free non-ROOM spot), server-side blocking-conflict check, old placement ended as TRANSFERRED, new placement created with computed compatibility scores + pairwise assessments, spot and unit statuses updated, request marked COMPLETED, audit-logged as TRANSFER. Requests without a target unit stay approval-only and now link staff to the resident page to complete the move. Transfer-requests page: resident and unit references are real links, COMPLETED gets its own tab, APPROVED cards show a follow-up link, the duplicate full-table query is gone, and the review controls use the design-system .input/.btn classes. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01XYKT7w58RCJasVqGUXNVkQ --- .../transfer-requests/TransferActions.tsx | 35 ++- .../__tests__/TransferActions.test.tsx | 78 ++++-- src/app/(admin)/transfer-requests/page.tsx | 63 ++++- src/lib/actions/__tests__/transfers.test.ts | 246 +++++++++++++++++- src/lib/actions/transfers.ts | 227 +++++++++++++++- src/lib/constants/error-messages.ts | 2 + src/lib/constants/labels/ui.ts | 5 + 7 files changed, 608 insertions(+), 48 deletions(-) diff --git a/src/app/(admin)/transfer-requests/TransferActions.tsx b/src/app/(admin)/transfer-requests/TransferActions.tsx index 348f030c..58641ab1 100644 --- a/src/app/(admin)/transfer-requests/TransferActions.tsx +++ b/src/app/(admin)/transfer-requests/TransferActions.tsx @@ -1,19 +1,23 @@ 'use client' import { useState } from 'react' +import Link from 'next/link' import { useRouter } from 'next/navigation' -import { approveTransferRequest, denyTransferRequest } from '@/lib/actions/transfers' +import { approveTransferRequest, denyTransferRequest, type ReviewTransferResult } from '@/lib/actions/transfers' import { TRANSFER_ACTION_LABELS as L } from '@/lib/constants' interface TransferActionsProps { requestId: string + residentId: string + /** Whether the request names a target unit (approval then executes the move) */ + hasTargetUnit: boolean } -export function TransferActions({ requestId }: TransferActionsProps) { +export function TransferActions({ requestId, residentId, hasTargetUnit }: TransferActionsProps) { const router = useRouter() const [staffNotes, setStaffNotes] = useState('') const [loading, setLoading] = useState(false) - const [result, setResult] = useState<{ success: boolean; error?: string } | null>(null) + const [result, setResult] = useState(null) async function handleAction(action: 'approve' | 'deny') { setLoading(true) @@ -31,7 +35,19 @@ export function TransferActions({ requestId }: TransferActionsProps) { if (result?.success) { return ( -

{L.success}

+
+

+ {result.executed ? L.successExecuted : L.success} +

+ {!result.executed && ( +

+ {L.completeTransferHint}{' '} + + {L.completeTransferLink} + +

+ )} +
) } @@ -41,26 +57,27 @@ export function TransferActions({ requestId }: TransferActionsProps) { value={staffNotes} onChange={(e) => setStaffNotes(e.target.value)} placeholder={L.notesPlaceholder} + aria-label={L.notesPlaceholder} rows={2} - className="w-full rounded-lg border border-ui-border-strong px-3 py-2 text-sm focus:border-aoz-primary focus:ring-1 focus:ring-aoz-primary resize-none" + className="input resize-none" /> {result?.error && (

{result.error}

)} -
+
diff --git a/src/app/(admin)/transfer-requests/__tests__/TransferActions.test.tsx b/src/app/(admin)/transfer-requests/__tests__/TransferActions.test.tsx index f43202ab..a894ece0 100644 --- a/src/app/(admin)/transfer-requests/__tests__/TransferActions.test.tsx +++ b/src/app/(admin)/transfer-requests/__tests__/TransferActions.test.tsx @@ -20,17 +20,34 @@ jest.mock('@/lib/constants', () => ({ TRANSFER_ACTION_LABELS: { notesPlaceholder: 'Notiz (optional)', approve: 'Genehmigen', + approveAndTransfer: 'Genehmigen & verlegen', deny: 'Ablehnen', processing: 'Wird bearbeitet...', success: 'Erfolgreich bearbeitet', + successExecuted: 'Genehmigt — Verlegung wurde durchgeführt', + successApprovedOnly: 'Genehmigt — Verlegung noch nicht durchgeführt', + completeTransferHint: 'Zieleinheit über die Bewohnerseite wählen und Verlegung abschliessen:', + completeTransferLink: 'Verlegung durchführen', }, })) // --- Helpers --- -function mockSuccess() { - mockApprove.mockResolvedValue({ success: true }) - mockDeny.mockResolvedValue({ success: true }) +function renderActions( + props: Partial<{ requestId: string; residentId: string; hasTargetUnit: boolean }> = {} +) { + return render( + + ) +} + +function mockSuccess(executed = false) { + mockApprove.mockResolvedValue({ success: true, executed }) + mockDeny.mockResolvedValue({ success: true, executed: false }) } function mockError(message = 'Fehler aufgetreten') { @@ -46,17 +63,23 @@ describe('TransferActions', () => { // ── Rendering ─────────────────────────────────────────────────────────── it('renders the notes textarea, approve and deny buttons', () => { - render() + renderActions() expect(screen.getByPlaceholderText('Notiz (optional)')).toBeInTheDocument() expect(screen.getByRole('button', { name: 'Genehmigen' })).toBeInTheDocument() expect(screen.getByRole('button', { name: 'Ablehnen' })).toBeInTheDocument() }) + it('labels the approve button "Genehmigen & verlegen" when a target unit is set', () => { + renderActions({ hasTargetUnit: true }) + expect(screen.getByRole('button', { name: 'Genehmigen & verlegen' })).toBeInTheDocument() + expect(screen.queryByRole('button', { name: 'Genehmigen' })).not.toBeInTheDocument() + }) + // ── Approve flow ──────────────────────────────────────────────────────── it('calls approveTransferRequest with requestId when approve is clicked', async () => { mockSuccess() - render() + renderActions({ requestId: 'req-abc' }) fireEvent.click(screen.getByRole('button', { name: 'Genehmigen' })) @@ -66,7 +89,7 @@ describe('TransferActions', () => { it('includes staffNotes in the approve call when typed', async () => { mockSuccess() - render() + renderActions({ requestId: 'req-abc' }) fireEvent.change(screen.getByPlaceholderText('Notiz (optional)'), { target: { value: 'Genehmigt nach Rücksprache' }, @@ -82,7 +105,7 @@ describe('TransferActions', () => { it('shows success message after successful approve', async () => { mockSuccess() - render() + renderActions() fireEvent.click(screen.getByRole('button', { name: 'Genehmigen' })) @@ -93,9 +116,34 @@ describe('TransferActions', () => { expect(screen.queryByRole('button', { name: 'Genehmigen' })).not.toBeInTheDocument() }) + it('shows executed message and no follow-up link when the transfer was executed', async () => { + mockSuccess(true) + renderActions({ hasTargetUnit: true }) + + fireEvent.click(screen.getByRole('button', { name: 'Genehmigen & verlegen' })) + + await waitFor(() => { + expect(screen.getByRole('status')).toHaveTextContent( + 'Genehmigt — Verlegung wurde durchgeführt' + ) + }) + expect(screen.queryByRole('link', { name: 'Verlegung durchführen' })).not.toBeInTheDocument() + }) + + it('shows a link to complete the transfer when approval did not execute it', async () => { + mockSuccess(false) + renderActions({ residentId: 'res-42' }) + + fireEvent.click(screen.getByRole('button', { name: 'Genehmigen' })) + + await waitFor(() => screen.getByRole('status')) + const link = screen.getByRole('link', { name: 'Verlegung durchführen' }) + expect(link).toHaveAttribute('href', '/residents/res-42') + }) + it('calls router.refresh() after successful approve', async () => { mockSuccess() - render() + renderActions() fireEvent.click(screen.getByRole('button', { name: 'Genehmigen' })) @@ -106,7 +154,7 @@ describe('TransferActions', () => { it('calls denyTransferRequest with requestId when deny is clicked', async () => { mockSuccess() - render() + renderActions({ requestId: 'req-xyz' }) fireEvent.click(screen.getByRole('button', { name: 'Ablehnen' })) @@ -116,7 +164,7 @@ describe('TransferActions', () => { it('shows success message after successful deny', async () => { mockSuccess() - render() + renderActions() fireEvent.click(screen.getByRole('button', { name: 'Ablehnen' })) @@ -127,7 +175,7 @@ describe('TransferActions', () => { it('calls router.refresh() after successful deny', async () => { mockSuccess() - render() + renderActions() fireEvent.click(screen.getByRole('button', { name: 'Ablehnen' })) @@ -140,7 +188,7 @@ describe('TransferActions', () => { let resolve: (v: { success: boolean }) => void mockApprove.mockReturnValue(new Promise((r) => { resolve = r })) - render() + renderActions() fireEvent.click(screen.getByRole('button', { name: 'Genehmigen' })) await waitFor(() => { @@ -156,7 +204,7 @@ describe('TransferActions', () => { it('shows error alert and keeps the form visible on failure', async () => { mockError('Anfrage bereits bearbeitet') - render() + renderActions() fireEvent.click(screen.getByRole('button', { name: 'Genehmigen' })) @@ -169,7 +217,7 @@ describe('TransferActions', () => { it('does NOT call router.refresh() on failure', async () => { mockError() - render() + renderActions() fireEvent.click(screen.getByRole('button', { name: 'Genehmigen' })) @@ -180,7 +228,7 @@ describe('TransferActions', () => { it('clears a previous error when a new action is started', async () => { // First attempt fails mockApprove.mockResolvedValueOnce({ success: false, error: 'Erster Fehler' }) - render() + renderActions() fireEvent.click(screen.getByRole('button', { name: 'Genehmigen' })) await waitFor(() => screen.getByRole('alert')) diff --git a/src/app/(admin)/transfer-requests/page.tsx b/src/app/(admin)/transfer-requests/page.tsx index e47010ae..ef381e71 100644 --- a/src/app/(admin)/transfer-requests/page.tsx +++ b/src/app/(admin)/transfer-requests/page.tsx @@ -1,4 +1,5 @@ import type { Metadata } from 'next' +import Link from 'next/link' import { getTransferRequests } from '@/lib/actions/transfers' import { TabLink } from '@/components/ui/Tabs' import { PageHeader } from '@/components/ui/Page' @@ -17,15 +18,15 @@ export default async function TransferRequestsPage({ searchParams }: Props) { const params = await searchParams const statusFilter = params.status || 'PENDING' - const [requests, allRequests] = await Promise.all([ - getTransferRequests(statusFilter === 'ALL' ? undefined : statusFilter), - getTransferRequests(), - ]) + const allRequests = await getTransferRequests() + const requests = + statusFilter === 'ALL' ? allRequests : allRequests.filter((r) => r.status === statusFilter) const counts = { all: allRequests.length, pending: allRequests.filter(r => r.status === 'PENDING').length, approved: allRequests.filter(r => r.status === 'APPROVED').length, denied: allRequests.filter(r => r.status === 'DENIED').length, + completed: allRequests.filter(r => r.status === 'COMPLETED').length, } return ( @@ -49,6 +50,12 @@ export default async function TransferRequestsPage({ searchParams }: Props) { count={counts.approved} active={statusFilter === 'APPROVED'} /> +

- {request.resident.code} + + {request.resident.code} +

{STATUS_LABEL[request.status] || request.status} @@ -125,10 +138,26 @@ function TransferRequestCard({ request }: { request: TransferRequestData }) {

{formatRelativeDate(request.createdAt)} {request.currentPlacement?.housingUnit && ( - <> · {TRANSFER_ACTION_LABELS.fromUnit} {request.currentPlacement.housingUnit.code} + <> + {' · '}{TRANSFER_ACTION_LABELS.fromUnit}{' '} + + {request.currentPlacement.housingUnit.code} + + )} {request.targetUnit && ( - <> · {TRANSFER_ACTION_LABELS.toUnit} {request.targetUnit.code} + <> + {' · '}{TRANSFER_ACTION_LABELS.toUnit}{' '} + + {request.targetUnit.code} + + )}

@@ -143,7 +172,25 @@ function TransferRequestCard({ request }: { request: TransferRequestData }) { )} {request.status === 'PENDING' && ( - + + )} + + {request.status === 'APPROVED' && ( +
+

+ {TRANSFER_ACTION_LABELS.completeTransferHint}{' '} + + {TRANSFER_ACTION_LABELS.completeTransferLink} + +

+
)}
) diff --git a/src/lib/actions/__tests__/transfers.test.ts b/src/lib/actions/__tests__/transfers.test.ts index bae91dcd..29db84f1 100644 --- a/src/lib/actions/__tests__/transfers.test.ts +++ b/src/lib/actions/__tests__/transfers.test.ts @@ -1,9 +1,11 @@ /** * Unit tests for transfer request server actions * - * Tests getTransferRequests (read-only, no auth guard), - * approveTransferRequest and denyTransferRequest (both require auth, - * update status atomically, create audit log). + * Tests getTransferRequests (read-only), denyTransferRequest (status flip + + * audit log), and approveTransferRequest — which now EXECUTES the move when + * the request names a target unit (end old placement, create new placement, + * spot + unit status updates, request → COMPLETED) and falls back to a pure + * APPROVED status flip when no target unit is set. */ import { prisma } from '@/lib/db' @@ -26,6 +28,7 @@ jest.mock('@/lib/db', () => ({ findUnique: jest.fn(), updateMany: jest.fn(), }, + $transaction: jest.fn(), }, })) @@ -52,10 +55,88 @@ jest.mock('@/lib/logger', () => ({ }, })) +const mockCalculateApartmentFit = jest.fn() +jest.mock('@/lib/compatibility', () => ({ + calculateCompatibility: jest.fn().mockReturnValue({ overall: 80, strengths: [], concerns: [] }), + saveBidirectionalAssessment: jest.fn(), +})) +jest.mock('@/lib/compatibility/convert', () => ({ + toResidentProfile: jest.fn((r: unknown) => r), +})) +jest.mock('@/lib/compatibility/aggregate', () => ({ + calculateApartmentProfile: jest.fn(() => ({})), + calculateApartmentFit: (...args: unknown[]) => mockCalculateApartmentFit(...args), +})) +jest.mock('@/lib/compatibility/placement-scores', () => ({ + calculateAverageScores: jest.fn(() => ({ + compatibilityScore: 75, + lifestyleScore: 75, + socialScore: 75, + practicalScore: 75, + riskScore: 75, + })), +})) + const mockPrisma = prisma as jest.Mocked +/** Transaction client mock handed to the $transaction callback */ +function makeTx() { + return { + transferRequest: { + updateMany: jest.fn().mockResolvedValue({ count: 1 }), + update: jest.fn().mockResolvedValue({}), + }, + placement: { + findFirst: jest.fn().mockResolvedValue({ + id: 'pl-1', + residentId: 'res-1', + housingUnitId: 'unit-old', + spotId: 'spot-old', + status: 'ACTIVE', + }), + findMany: jest.fn().mockResolvedValue([]), + update: jest.fn().mockResolvedValue({}), + create: jest.fn().mockResolvedValue({ id: 'pl-2' }), + }, + placementSpot: { + findFirst: jest.fn().mockResolvedValue({ id: 'spot-new' }), + update: jest.fn().mockResolvedValue({}), + }, + resident: { + findUnique: jest.fn().mockResolvedValue({ id: 'res-1' }), + }, + housingUnit: { + findUnique: jest + .fn() + .mockResolvedValueOnce({ id: 'unit-old', status: 'FULL' }) + .mockResolvedValueOnce({ id: 'unit-new', spots: [{ id: 'spot-x' }] }), + update: jest.fn().mockResolvedValue({}), + }, + } +} + +function mockTransactionWith(tx: ReturnType) { + ;(mockPrisma.$transaction as jest.Mock).mockImplementation( + async (cb: (tx: unknown) => Promise) => cb(tx) + ) +} + +const pendingWithTarget = { + id: 'clxxxxxxxxxxxxxxxxx0001', + status: 'PENDING', + residentId: 'res-1', + targetUnitId: 'unit-new', + currentPlacementId: 'pl-1', +} + +const pendingWithoutTarget = { + ...pendingWithTarget, + targetUnitId: null, +} + beforeEach(() => { jest.clearAllMocks() + mockCalculateApartmentFit.mockReturnValue({ fitScore: 80, conflicts: [] }) }) // ============================================================================= @@ -80,6 +161,7 @@ describe('getTransferRequests', () => { currentPlacement: { select: { id: true, + status: true, housingUnit: { select: { id: true, code: true, address: true } }, }, }, @@ -111,18 +193,20 @@ describe('getTransferRequests', () => { }) // ============================================================================= -// approveTransferRequest +// approveTransferRequest — approval-only path (no target unit) // ============================================================================= -describe('approveTransferRequest', () => { +describe('approveTransferRequest without target unit', () => { const validInput = { requestId: 'clxxxxxxxxxxxxxxxxx0001', staffNotes: 'Genehmigt' } - it('approves a pending request atomically and returns success', async () => { + it('approves a pending request atomically and returns success without executing', async () => { + ;(mockPrisma.transferRequest.findUnique as jest.Mock).mockResolvedValue(pendingWithoutTarget) ;(mockPrisma.transferRequest.updateMany as jest.Mock).mockResolvedValue({ count: 1 }) const result = await approveTransferRequest(validInput) - expect(result).toEqual({ success: true }) + expect(result).toEqual({ success: true, executed: false }) + expect(mockPrisma.$transaction).not.toHaveBeenCalled() expect(mockPrisma.transferRequest.updateMany).toHaveBeenCalledWith({ where: { id: 'clxxxxxxxxxxxxxxxxx0001', status: 'PENDING' }, @@ -136,6 +220,7 @@ describe('approveTransferRequest', () => { }) it('calls logAudit with userId on approval', async () => { + ;(mockPrisma.transferRequest.findUnique as jest.Mock).mockResolvedValue(pendingWithoutTarget) ;(mockPrisma.transferRequest.updateMany as jest.Mock).mockResolvedValue({ count: 1 }) await approveTransferRequest(validInput) @@ -150,7 +235,6 @@ describe('approveTransferRequest', () => { }) it('returns error when request is not found', async () => { - ;(mockPrisma.transferRequest.updateMany as jest.Mock).mockResolvedValue({ count: 0 }) ;(mockPrisma.transferRequest.findUnique as jest.Mock).mockResolvedValue(null) const result = await approveTransferRequest(validInput) @@ -160,9 +244,9 @@ describe('approveTransferRequest', () => { }) it('returns error when request has already been reviewed', async () => { - ;(mockPrisma.transferRequest.updateMany as jest.Mock).mockResolvedValue({ count: 0 }) ;(mockPrisma.transferRequest.findUnique as jest.Mock).mockResolvedValue({ - id: 'clxxxxxxxxxxxxxxxxx0001', + ...pendingWithoutTarget, + status: 'APPROVED', }) const result = await approveTransferRequest(validInput) @@ -172,6 +256,7 @@ describe('approveTransferRequest', () => { }) it('returns generic error when prisma fails', async () => { + ;(mockPrisma.transferRequest.findUnique as jest.Mock).mockResolvedValue(pendingWithoutTarget) ;(mockPrisma.transferRequest.updateMany as jest.Mock).mockRejectedValue( new Error('DB error') ) @@ -182,6 +267,145 @@ describe('approveTransferRequest', () => { }) }) +// ============================================================================= +// approveTransferRequest — executing path (target unit set) +// ============================================================================= + +describe('approveTransferRequest with target unit', () => { + const validInput = { requestId: 'clxxxxxxxxxxxxxxxxx0001', staffNotes: 'Passt' } + + it('executes the transfer and marks the request COMPLETED', async () => { + ;(mockPrisma.transferRequest.findUnique as jest.Mock).mockResolvedValue(pendingWithTarget) + const tx = makeTx() + mockTransactionWith(tx) + + const result = await approveTransferRequest(validInput) + + expect(result).toEqual({ success: true, executed: true }) + + // Atomic claim inside the transaction + expect(tx.transferRequest.updateMany).toHaveBeenCalledWith({ + where: { id: pendingWithTarget.id, status: 'PENDING' }, + data: expect.objectContaining({ status: 'APPROVED', reviewedBy: 'staff-1' }), + }) + // Old placement ended as TRANSFERRED + expect(tx.placement.update).toHaveBeenCalledWith({ + where: { id: 'pl-1' }, + data: expect.objectContaining({ status: 'TRANSFERRED', endReason: 'REQUEST' }), + }) + // Old spot freed, new spot occupied + expect(tx.placementSpot.update).toHaveBeenCalledWith({ + where: { id: 'spot-old' }, + data: { status: 'AVAILABLE' }, + }) + expect(tx.placementSpot.update).toHaveBeenCalledWith({ + where: { id: 'spot-new' }, + data: { status: 'OCCUPIED' }, + }) + // New placement created in the target unit with server-computed scores + expect(tx.placement.create).toHaveBeenCalledWith({ + data: expect.objectContaining({ + residentId: 'res-1', + housingUnitId: 'unit-new', + spotId: 'spot-new', + status: 'ACTIVE', + compatibilityScore: 75, + }), + }) + // Formerly-full old unit reopened + expect(tx.housingUnit.update).toHaveBeenCalledWith({ + where: { id: 'unit-old' }, + data: { status: 'AVAILABLE' }, + }) + // Request finished, not merely approved + expect(tx.transferRequest.update).toHaveBeenCalledWith({ + where: { id: pendingWithTarget.id }, + data: { status: 'COMPLETED' }, + }) + expect(logAudit).toHaveBeenCalledWith( + expect.objectContaining({ + action: 'TRANSFER', + entity: 'TRANSFER_REQUEST', + changes: expect.objectContaining({ + status: 'COMPLETED', + fromHousingUnitId: 'unit-old', + toHousingUnitId: 'unit-new', + }), + }) + ) + }) + + it('fails when the target unit has no free spot', async () => { + ;(mockPrisma.transferRequest.findUnique as jest.Mock).mockResolvedValue(pendingWithTarget) + const tx = makeTx() + tx.placementSpot.findFirst.mockResolvedValue(null) + mockTransactionWith(tx) + + const result = await approveTransferRequest(validInput) + + expect(result).toEqual({ success: false, error: ERROR_MESSAGES.TRANSFER_TARGET_NO_SPOT }) + expect(tx.placement.create).not.toHaveBeenCalled() + expect(logAudit).not.toHaveBeenCalled() + }) + + it('fails with a blocking-conflict message when the target unit is incompatible', async () => { + ;(mockPrisma.transferRequest.findUnique as jest.Mock).mockResolvedValue(pendingWithTarget) + mockCalculateApartmentFit.mockReturnValue({ + fitScore: 10, + conflicts: [{ severity: 'BLOCKING', message: 'Raucher in Nichtraucher-Einheit' }], + }) + const tx = makeTx() + mockTransactionWith(tx) + + const result = await approveTransferRequest(validInput) + + expect(result.success).toBe(false) + expect(result.error).toContain('Verlegung blockiert') + expect(result.error).toContain('Raucher in Nichtraucher-Einheit') + expect(tx.placement.create).not.toHaveBeenCalled() + }) + + it('fails when the resident already lives in the target unit', async () => { + ;(mockPrisma.transferRequest.findUnique as jest.Mock).mockResolvedValue(pendingWithTarget) + const tx = makeTx() + tx.placement.findFirst.mockResolvedValue({ + id: 'pl-1', + residentId: 'res-1', + housingUnitId: 'unit-new', + spotId: 'spot-old', + status: 'ACTIVE', + }) + mockTransactionWith(tx) + + const result = await approveTransferRequest(validInput) + + expect(result).toEqual({ success: false, error: ERROR_MESSAGES.TRANSFER_SAME_UNIT }) + }) + + it('falls back to approval-only when the resident has no active placement', async () => { + ;(mockPrisma.transferRequest.findUnique as jest.Mock).mockResolvedValue(pendingWithTarget) + const tx = makeTx() + tx.placement.findFirst.mockResolvedValue(null) + mockTransactionWith(tx) + + const result = await approveTransferRequest(validInput) + + expect(result).toEqual({ + success: false, + error: ERROR_MESSAGES.TRANSFER_REQUEST_NO_PLACEMENT, + }) + }) + + it('returns generic transfer error on unexpected failure', async () => { + ;(mockPrisma.transferRequest.findUnique as jest.Mock).mockResolvedValue(pendingWithTarget) + ;(mockPrisma.$transaction as jest.Mock).mockRejectedValue(new Error('DB down')) + + const result = await approveTransferRequest(validInput) + + expect(result).toEqual({ success: false, error: ERROR_MESSAGES.TRANSFER_ERROR }) + }) +}) + // ============================================================================= // denyTransferRequest // ============================================================================= @@ -194,7 +418,7 @@ describe('denyTransferRequest', () => { const result = await denyTransferRequest(validInput) - expect(result).toEqual({ success: true }) + expect(result).toEqual({ success: true, executed: false }) expect(mockPrisma.transferRequest.updateMany).toHaveBeenCalledWith({ where: { id: 'clxxxxxxxxxxxxxxxxx0002', status: 'PENDING' }, diff --git a/src/lib/actions/transfers.ts b/src/lib/actions/transfers.ts index 33d4a9a4..c6bcf31d 100644 --- a/src/lib/actions/transfers.ts +++ b/src/lib/actions/transfers.ts @@ -6,8 +6,19 @@ import { logAudit } from '@/lib/audit' import { logger } from '@/lib/logger' import { ERROR_MESSAGES } from '@/lib/constants/error-messages' import { requireStaffAuth } from '@/lib/auth' +import { calculateCompatibility, saveBidirectionalAssessment } from '@/lib/compatibility' +import { toResidentProfile } from '@/lib/compatibility/convert' +import { calculateApartmentProfile, calculateApartmentFit } from '@/lib/compatibility/aggregate' +import { calculateAverageScores } from '@/lib/compatibility/placement-scores' import type { ReviewTransferRequestInput } from '@/lib/validation/transfer' +export interface ReviewTransferResult { + success: boolean + /** true when the approval also executed the move into the target unit */ + executed?: boolean + error?: string +} + export async function getTransferRequests(status?: string) { await requireStaffAuth() const where = status ? { status: status as 'PENDING' | 'APPROVED' | 'DENIED' | 'COMPLETED' | 'CANCELLED' } : {} @@ -19,6 +30,7 @@ export async function getTransferRequests(status?: string) { currentPlacement: { select: { id: true, + status: true, housingUnit: { select: { id: true, code: true, address: true } }, }, }, @@ -31,7 +43,7 @@ export async function getTransferRequests(status?: string) { async function reviewTransferRequest( input: ReviewTransferRequestInput, decision: 'APPROVED' | 'DENIED' -): Promise<{ success: boolean; error?: string }> { +): Promise { const user = await requireStaffAuth() try { @@ -69,7 +81,7 @@ async function reviewTransferRequest( }) revalidatePath('/transfer-requests') - return { success: true } + return { success: true, executed: false } } catch (error) { logger.errorWithCause(`Failed to ${decision.toLowerCase()} transfer request`, error, { requestId: input.requestId, @@ -78,10 +90,215 @@ async function reviewTransferRequest( } } -export async function approveTransferRequest(input: ReviewTransferRequestInput) { - return reviewTransferRequest(input, 'APPROVED') +/** + * Approve a transfer request. When the request names a target unit and the + * resident still has an active placement, the approval also EXECUTES the move + * (end old placement, create new one with server-computed scores, update spot + * and unit statuses) and marks the request COMPLETED — all in one transaction. + * Without a target unit the request is only marked APPROVED and staff complete + * the move from the resident page. + */ +export async function approveTransferRequest(input: ReviewTransferRequestInput): Promise { + const user = await requireStaffAuth() + + let request + try { + request = await prisma.transferRequest.findUnique({ + where: { id: input.requestId }, + select: { id: true, status: true, residentId: true, targetUnitId: true, currentPlacementId: true }, + }) + } catch (error) { + logger.errorWithCause('Failed to load transfer request', error, { requestId: input.requestId }) + return { success: false, error: ERROR_MESSAGES.TRANSFER_REQUEST_REVIEW_ERROR } + } + + if (!request) { + return { success: false, error: ERROR_MESSAGES.TRANSFER_REQUEST_NOT_FOUND } + } + if (request.status !== 'PENDING') { + return { success: false, error: 'Anfrage wurde bereits bearbeitet' } + } + + // No target unit named, or no placement context → approve only. + if (!request.targetUnitId) { + return reviewTransferRequest(input, 'APPROVED') + } + + const targetUnitId = request.targetUnitId + let fromHousingUnitId: string | null = null + try { + await prisma.$transaction(async (tx) => { + // Atomic claim: flip PENDING → APPROVED inside the transaction so a + // concurrent reviewer can't process the same request. Rolls back to + // PENDING automatically if any later step fails. + const claimed = await tx.transferRequest.updateMany({ + where: { id: request.id, status: 'PENDING' }, + data: { + status: 'APPROVED', + staffNotes: input.staffNotes, + reviewedBy: user.id, + reviewedAt: new Date(), + }, + }) + if (claimed.count === 0) { + throw new Error('Anfrage wurde bereits bearbeitet') + } + + // Current active placement (from the request, with fallback lookup). + const currentPlacement = request.currentPlacementId + ? await tx.placement.findFirst({ + where: { id: request.currentPlacementId, status: 'ACTIVE' }, + }) + : await tx.placement.findFirst({ + where: { residentId: request.residentId, status: 'ACTIVE' }, + }) + if (!currentPlacement) { + throw new Error(ERROR_MESSAGES.TRANSFER_REQUEST_NO_PLACEMENT) + } + fromHousingUnitId = currentPlacement.housingUnitId + + if (currentPlacement.housingUnitId === targetUnitId) { + throw new Error(ERROR_MESSAGES.TRANSFER_SAME_UNIT) + } + + // Free spot in the target unit (ROOM spots are containers, not assignable). + const targetSpot = await tx.placementSpot.findFirst({ + where: { housingUnitId: targetUnitId, status: 'AVAILABLE', type: { not: 'ROOM' } }, + orderBy: { code: 'asc' }, + }) + if (!targetSpot) { + throw new Error(ERROR_MESSAGES.TRANSFER_TARGET_NO_SPOT) + } + + // Server-side blocking-conflict check against the target unit's residents. + const resident = await tx.resident.findUnique({ where: { id: request.residentId } }) + if (!resident) { + throw new Error(ERROR_MESSAGES.RESIDENT_NOT_FOUND) + } + const targetPlacements = await tx.placement.findMany({ + where: { housingUnitId: targetUnitId, status: 'ACTIVE' }, + include: { resident: true }, + }) + const residentProfile = toResidentProfile(resident) + const targetProfiles = targetPlacements.map((p) => toResidentProfile(p.resident)) + const apartmentFit = calculateApartmentFit(residentProfile, calculateApartmentProfile(targetProfiles)) + const blocking = apartmentFit.conflicts.filter((c) => c.severity === 'BLOCKING') + if (blocking.length > 0) { + throw new Error(`Verlegung blockiert: ${blocking.map((c) => c.message).join('; ')}`) + } + + // End the old placement as a resident-requested transfer. + await tx.placement.update({ + where: { id: currentPlacement.id }, + data: { + status: 'TRANSFERRED', + endDate: new Date(), + endReason: 'REQUEST', + outcomeNotes: input.staffNotes || undefined, + }, + }) + if (currentPlacement.spotId) { + await tx.placementSpot.update({ + where: { id: currentPlacement.spotId }, + data: { status: 'AVAILABLE' }, + }) + } + + // Scores + pairwise assessments for the new constellation. + const { compatibilityScore, lifestyleScore, socialScore, practicalScore, riskScore } = + calculateAverageScores(resident, targetPlacements) + for (const existing of targetPlacements) { + const score = calculateCompatibility(residentProfile, toResidentProfile(existing.resident)) + await saveBidirectionalAssessment(tx, request.residentId, existing.residentId, score) + } + + await tx.placement.create({ + data: { + residentId: request.residentId, + housingUnitId: targetUnitId, + spotId: targetSpot.id, + startDate: new Date(), + status: 'ACTIVE', + compatibilityScore, + lifestyleScore, + socialScore, + practicalScore, + riskScore, + placementNotes: `Verlegung auf Wunsch des Bewohners (Anfrage genehmigt). ${input.staffNotes || ''}`.trim(), + }, + }) + await tx.placementSpot.update({ + where: { id: targetSpot.id }, + data: { status: 'OCCUPIED' }, + }) + + // Unit statuses: old unit may reopen, new unit may fill up. + const oldUnit = await tx.housingUnit.findUnique({ where: { id: currentPlacement.housingUnitId } }) + if (oldUnit?.status === 'FULL') { + await tx.housingUnit.update({ + where: { id: currentPlacement.housingUnitId }, + data: { status: 'AVAILABLE' }, + }) + } + const newUnit = await tx.housingUnit.findUnique({ + where: { id: targetUnitId }, + include: { spots: { where: { status: 'AVAILABLE', type: { not: 'ROOM' } } } }, + }) + if (newUnit && newUnit.spots.length === 0) { + await tx.housingUnit.update({ + where: { id: targetUnitId }, + data: { status: 'FULL' }, + }) + } + + // The move happened — the request is done, not merely approved. + await tx.transferRequest.update({ + where: { id: request.id }, + data: { status: 'COMPLETED' }, + }) + }) + } catch (error) { + const knownMessages = [ + 'bereits bearbeitet', + ERROR_MESSAGES.TRANSFER_REQUEST_NO_PLACEMENT, + ERROR_MESSAGES.TRANSFER_SAME_UNIT, + ERROR_MESSAGES.TRANSFER_TARGET_NO_SPOT, + ERROR_MESSAGES.RESIDENT_NOT_FOUND, + 'Verlegung blockiert', + ] + if (error instanceof Error && knownMessages.some((m) => error.message.includes(m))) { + return { success: false, error: error.message } + } + logger.errorWithCause('Failed to execute approved transfer', error, { + requestId: input.requestId, + targetUnitId, + }) + return { success: false, error: ERROR_MESSAGES.TRANSFER_ERROR } + } + + await logAudit({ + action: 'TRANSFER', + entity: 'TRANSFER_REQUEST', + entityId: request.id, + userId: user.id, + changes: { + status: 'COMPLETED', + residentId: request.residentId, + fromHousingUnitId, + toHousingUnitId: targetUnitId, + staffNotes: input.staffNotes, + }, + }) + + revalidatePath('/transfer-requests') + revalidatePath(`/residents/${request.residentId}`) + revalidatePath('/placements') + if (fromHousingUnitId) revalidatePath(`/housing/${fromHousingUnitId}`) + revalidatePath(`/housing/${targetUnitId}`) + + return { success: true, executed: true } } -export async function denyTransferRequest(input: ReviewTransferRequestInput) { +export async function denyTransferRequest(input: ReviewTransferRequestInput): Promise { return reviewTransferRequest(input, 'DENIED') } diff --git a/src/lib/constants/error-messages.ts b/src/lib/constants/error-messages.ts index 1d1686c6..f3a9a76a 100644 --- a/src/lib/constants/error-messages.ts +++ b/src/lib/constants/error-messages.ts @@ -104,6 +104,8 @@ export const ERROR_MESSAGES = { TRANSFER_REQUEST_NO_PLACEMENT: 'Keine aktive Platzierung vorhanden', TRANSFER_REQUEST_CREATE_ERROR: 'Fehler beim Erstellen der Verlegungsanfrage', TRANSFER_REQUEST_REVIEW_ERROR: 'Fehler beim Bearbeiten der Verlegungsanfrage', + TRANSFER_TARGET_NO_SPOT: 'Die Zieleinheit hat keinen freien Platz', + TRANSFER_SAME_UNIT: 'Bewohner wohnt bereits in der Zieleinheit', // ─── Portal / Registration ───────────────────────────────────── PREFERENCES_SAVE_ERROR: 'Einstellungen konnten nicht gespeichert werden', diff --git a/src/lib/constants/labels/ui.ts b/src/lib/constants/labels/ui.ts index 38b13c32..0b57b4f9 100644 --- a/src/lib/constants/labels/ui.ts +++ b/src/lib/constants/labels/ui.ts @@ -188,9 +188,14 @@ export const SATISFACTION_SURVEY_LABELS = { export const TRANSFER_ACTION_LABELS = { notesPlaceholder: 'Notiz (optional)', approve: 'Genehmigen', + approveAndTransfer: 'Genehmigen & verlegen', deny: 'Ablehnen', processing: 'Wird bearbeitet...', success: 'Erfolgreich bearbeitet', + successExecuted: 'Genehmigt — Verlegung wurde durchgeführt', + successApprovedOnly: 'Genehmigt — Verlegung noch nicht durchgeführt', + completeTransferHint: 'Zieleinheit über die Bewohnerseite wählen und Verlegung abschliessen:', + completeTransferLink: 'Verlegung durchführen', emptyPending: 'Keine offenen Verlegungsanfragen', emptyOther: 'Keine Verlegungsanfragen in dieser Kategorie', noteLabel: 'Notiz:', From 84bc62680ded76f74487747d40952698a4b8949c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 16 Jul 2026 17:04:03 +0000 Subject: [PATCH 03/10] feat(matching): progressive disclosure on match cards Lead each card with what the placement decision needs: fit score with tier label (score-legend tooltip), occupancy, top strengths, blocking concerns, high-risk warning and safeguard notes. Unit history, the historical success box, the per-roommate breakdown and minor concerns move behind a native
expander. Also drop a stray tracked screenshot and ignore future nav-*.png captures. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01XYKT7w58RCJasVqGUXNVkQ --- .gitignore | 1 + src/components/matching/MatchCard.tsx | 275 +++++++++++------- .../matching/__tests__/MatchCard.test.tsx | 32 ++ src/lib/constants/labels/matching.ts | 2 + 4 files changed, 197 insertions(+), 113 deletions(-) diff --git a/.gitignore b/.gitignore index f2ed4c34..8c0e0656 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ tests/.auth/ /before-*.png /after-*.png /final-*.png +/nav-*.png # next.js /.next/ diff --git a/src/components/matching/MatchCard.tsx b/src/components/matching/MatchCard.tsx index ff3c13e0..534a8806 100644 --- a/src/components/matching/MatchCard.tsx +++ b/src/components/matching/MatchCard.tsx @@ -4,7 +4,7 @@ import type { ApartmentConflict } from '@/lib/compatibility/types' import type { MatchResult, CompatibilityDetail } from '@/lib/matching/types' import { placeResident } from '@/lib/actions/matching' import { MATCHING_LABELS } from '@/lib/constants' -import { getScoreColorClass } from '@/lib/utils' +import { getScoreColorClass, getScoreLabel } from '@/lib/utils' import { DISPLAY_LIMITS } from '@/lib/config/thresholds' import { SCORE_TOKENS } from '@/lib/config/ui-tokens' import { SpotSelection } from './SpotSelection' @@ -16,6 +16,12 @@ interface Props { rank?: number } +/** + * Match card with progressive disclosure: the headline carries only what the + * decision needs (score + tier, occupancy, top reasons, hard warnings, place + * action); history, per-roommate breakdown and minor concerns live behind a + * native
expander so staff can compare units at a glance. + */ export function MatchCard({ match, resident, rank }: Props) { const occupancy = match.unit.placements.length const realSuccessData = match.realSuccessData @@ -39,12 +45,23 @@ export function MatchCard({ match, resident, rank }: Props) { (l: string) => roommateLanguages.includes(l) ) - const hasBlockingIssues = match.unitConcerns.some((c: string) => + const isBlockingConcern = (c: string) => c.includes('Rollstuhl') || c.includes('Erdgeschoss') - ) + const blockingUnitConcerns = match.unitConcerns.filter(isBlockingConcern) + const otherUnitConcerns = match.unitConcerns.filter((c) => !isBlockingConcern(c)) + const hasBlockingIssues = blockingUnitConcerns.length > 0 + + const fitScore = match.apartmentFit?.fitScore ?? null + const highRisk = + match.unitMetrics && + (match.unitMetrics.riskLevel === 'HIGH' || match.unitMetrics.riskLevel === 'CRITICAL') + + const detailCount = + allConcerns.length + otherUnitConcerns.length + match.unit.placements.length return (
+ {/* ── Headline: identity + score ─────────────────────────────── */}
@@ -62,8 +79,21 @@ export function MatchCard({ match, resident, rank }: Props) {

{match.unit.address}

-
-

+

+ {fitScore !== null && ( +

+ + {fitScore}% + + + {getScoreLabel(fitScore)} + +

+ )} +

{occupancy}/{match.unit.totalBeds} belegt

{occupancy === 0 && ( @@ -72,69 +102,7 @@ export function MatchCard({ match, resident, rank }: Props) {
- {match.unitMetrics && ( -
-
-
- - {MATCHING_LABELS.history} {match.unitMetrics.label} - - {match.unitMetrics.incidentFreeMonths > 0 && ( - - ({match.unitMetrics.incidentFreeMonths}M konfliktfrei) - - )} -
- - {match.unitMetrics.conflictRate.toFixed(1)}/Monat Ø - -
- {match.unitMetrics.riskLevel === 'HIGH' || match.unitMetrics.riskLevel === 'CRITICAL' ? ( -

- ⚠️ {match.unitMetrics.recentConflicts} Konflikte letzte 30 Tage -

- ) : null} -
- )} - - {match.apartmentProfile && ( - - )} - - {match.apartmentFit && !match.apartmentProfile.isEmpty && ( -
- {realSuccessData && realSuccessData.totalPlacements > 0 ? ( - <> -
- - 📊 {MATCHING_LABELS.historicalData} ({realSuccessData.totalPlacements} {MATCHING_LABELS.placements}): - - - {realSuccessData.successRate}% {MATCHING_LABELS.successRate} - -
-

- {realSuccessData.successfulPlacements}/{realSuccessData.totalPlacements} {MATCHING_LABELS.placementsWithSimilar} ({match.apartmentFit.fitScore}% {MATCHING_LABELS.plusMinusTen}) {MATCHING_LABELS.successRateDesc(realSuccessData.successRate)} -

- - ) : ( -
- 📊 {MATCHING_LABELS.noHistoricalData} ({match.apartmentFit.fitScore}% {MATCHING_LABELS.plusMinusTen}) {MATCHING_LABELS.available}. -
- )} -
- )} - + {/* ── Headline: decision-critical signals ────────────────────── */} {(allStrengths.length > 0 || sharedLanguages.length > 0 || occupancy === 0) && (
{occupancy === 0 && ( @@ -153,55 +121,21 @@ export function MatchCard({ match, resident, rank }: Props) {
)} - {match.unit.placements.length > 0 && ( + {blockingUnitConcerns.length > 0 && (
-

{MATCHING_LABELS.currentResidents}

-
- {match.unit.placements.map((p) => { - const detail = match.compatibilityDetails.find( - (d: CompatibilityDetail) => d.resident.id === p.resident.id - ) - const hasSharedLang = (resident.languages || []).some( - (l: string) => (p.resident.languages || []).includes(l) - ) - const concernCount = detail?.score.concerns?.length || 0 - return ( -
- {p.resident.code} - 0 ? 'text-status-warning-text' : 'text-status-success-text'}> - {hasSharedLang ? '✓ Sprache' : '✗ Sprache'} - {concernCount > 0 && ` · ${concernCount} ${MATCHING_LABELS.concerns}`} - -
- ) - })} -
-
- )} - - {match.unitConcerns.length > 0 && ( -
- {match.unitConcerns.map((concern: string, i: number) => ( -

+ {blockingUnitConcerns.map((concern: string, i: number) => ( +

⚠️ {concern}

))}
)} - {allConcerns.length > 0 && ( -
- {allConcerns.slice(0, DISPLAY_LIMITS.matchConcerns).map((concern, i) => ( -

⚠️ {concern}

- ))} - {allConcerns.length > DISPLAY_LIMITS.matchConcerns && ( -

{MATCHING_LABELS.moreConcerns(allConcerns.length - DISPLAY_LIMITS.matchConcerns)}

- )} -
+ {highRisk && match.unitMetrics && ( +

+ ⚠️ {MATCHING_LABELS.history} {match.unitMetrics.label} — {match.unitMetrics.recentConflicts}{' '} + {MATCHING_LABELS.conflictsLast30Days} +

)} {match.safeguardWarnings.length > 0 && ( @@ -217,6 +151,121 @@ export function MatchCard({ match, resident, rank }: Props) {
)} + {/* ── Details: history, breakdowns, minor concerns ───────────── */} +
+ + {MATCHING_LABELS.showDetails} + {detailCount > 0 ? ` (${detailCount})` : ''} + +
+ {match.unitMetrics && ( +
+
+
+ + {MATCHING_LABELS.history} {match.unitMetrics.label} + + {match.unitMetrics.incidentFreeMonths > 0 && ( + + ({match.unitMetrics.incidentFreeMonths}M konfliktfrei) + + )} +
+ + {match.unitMetrics.conflictRate.toFixed(1)}/Monat Ø + +
+
+ )} + + {match.apartmentProfile && ( + + )} + + {match.apartmentFit && !match.apartmentProfile.isEmpty && ( +
+ {realSuccessData && realSuccessData.totalPlacements > 0 ? ( + <> +
+ + 📊 {MATCHING_LABELS.historicalData} ({realSuccessData.totalPlacements} {MATCHING_LABELS.placements}): + + + {realSuccessData.successRate}% {MATCHING_LABELS.successRate} + +
+

+ {realSuccessData.successfulPlacements}/{realSuccessData.totalPlacements} {MATCHING_LABELS.placementsWithSimilar} ({match.apartmentFit.fitScore}% {MATCHING_LABELS.plusMinusTen}) {MATCHING_LABELS.successRateDesc(realSuccessData.successRate)} +

+ + ) : ( +
+ 📊 {MATCHING_LABELS.noHistoricalData} ({match.apartmentFit.fitScore}% {MATCHING_LABELS.plusMinusTen}) {MATCHING_LABELS.available}. +
+ )} +
+ )} + + {match.unit.placements.length > 0 && ( +
+

{MATCHING_LABELS.currentResidents}

+
+ {match.unit.placements.map((p) => { + const detail = match.compatibilityDetails.find( + (d: CompatibilityDetail) => d.resident.id === p.resident.id + ) + const hasSharedLang = (resident.languages || []).some( + (l: string) => (p.resident.languages || []).includes(l) + ) + const concernCount = detail?.score.concerns?.length || 0 + return ( +
+ {p.resident.code} + 0 ? 'text-status-warning-text' : 'text-status-success-text'}> + {hasSharedLang ? '✓ Sprache' : '✗ Sprache'} + {concernCount > 0 && ` · ${concernCount} ${MATCHING_LABELS.concerns}`} + +
+ ) + })} +
+
+ )} + + {otherUnitConcerns.length > 0 && ( +
+ {otherUnitConcerns.map((concern: string, i: number) => ( +

+ ⚠️ {concern} +

+ ))} +
+ )} + + {allConcerns.length > 0 && ( +
+ {allConcerns.slice(0, DISPLAY_LIMITS.matchConcerns).map((concern, i) => ( +

⚠️ {concern}

+ ))} + {allConcerns.length > DISPLAY_LIMITS.matchConcerns && ( +

{MATCHING_LABELS.moreConcerns(allConcerns.length - DISPLAY_LIMITS.matchConcerns)}

+ )} +
+ )} +
+
+ + {/* ── Action ─────────────────────────────────────────────────── */} {match.unit.spots && match.unit.spots.length > 0 && ( c.severity === 'BLOCKING' ) || false - const fitScore = match.apartmentFit?.fitScore || 100 + const actionFitScore = match.apartmentFit?.fitScore || 100 return (
- + diff --git a/src/components/matching/__tests__/MatchCard.test.tsx b/src/components/matching/__tests__/MatchCard.test.tsx index ebc7dd7f..392c3753 100644 --- a/src/components/matching/__tests__/MatchCard.test.tsx +++ b/src/components/matching/__tests__/MatchCard.test.tsx @@ -79,6 +79,8 @@ jest.mock('@/lib/constants', () => ({ blocked: 'Blockiert', place: 'Platzieren', placeLowCompat: 'Platzieren (niedrige Kompatibilität)', + showDetails: 'Details anzeigen', + scoreLegend: '80+ Sehr gut · 60–79 Gut · 40–59 Mittel · 20–39 Niedrig · 0–19 Kritisch', }, getLabel: (labels: Record, key: string) => labels[key] || key, })) @@ -91,6 +93,13 @@ jest.mock('@/lib/utils', () => ({ if (score >= 20) return 'text-score-low-text' return 'text-score-critical-text' }, + getScoreLabel: (score: number) => { + if (score >= 80) return 'Sehr gut' + if (score >= 60) return 'Gut' + if (score >= 40) return 'Mittel' + if (score >= 20) return 'Niedrig' + return 'Kritisch' + }, })) jest.mock('../HeadToHeadComparison', () => ({ @@ -248,6 +257,29 @@ describe('MatchCard', () => { }) }) + describe('headline score', () => { + it('shows the fit score with tier label in the headline', () => { + render() + + // fixture fitScore is 85 → excellent tier + expect(screen.getByText('85%')).toBeInTheDocument() + expect(screen.getByText('Sehr gut')).toBeInTheDocument() + }) + + it('exposes the score-tier legend as a tooltip on the score', () => { + render() + + const score = screen.getByText('85%').closest('[title]') + expect(score).toHaveAttribute('title', expect.stringContaining('Sehr gut')) + }) + + it('renders a details expander for secondary information', () => { + render() + + expect(screen.getByText(/Details anzeigen/)).toBeInTheDocument() + }) + }) + describe('rank badges', () => { it('shows "Top Empfehlung" for rank 1', () => { render() diff --git a/src/lib/constants/labels/matching.ts b/src/lib/constants/labels/matching.ts index b247b8b4..89b92182 100644 --- a/src/lib/constants/labels/matching.ts +++ b/src/lib/constants/labels/matching.ts @@ -38,6 +38,8 @@ export const MATCHING_LABELS = { moreConcerns: (count: number) => `+${count} weitere Bedenken`, blocked: 'Blockiert', place: 'Platzieren', + showDetails: 'Details anzeigen', + scoreLegend: '80+ Sehr gut · 60–79 Gut · 40–59 Mittel · 20–39 Niedrig · 0–19 Kritisch', placeLowCompat: 'Platzieren (niedrige Kompatibilität)', // Spot selection noSpotsAvailable: 'Keine freien Plätze verfügbar', From d60f8b6d25ece858d0f34affe38ab854e57d907b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 16 Jul 2026 17:11:28 +0000 Subject: [PATCH 04/10] feat(portal): close feedback loops and lower literacy barriers - Preferences round-trip: roommate-preference answers (age range, cultural preference, free-text wishes) are now serialized in a stable labeled format, parsed back into the form on revisit, and legacy free-form values are preserved in the wishes field instead of dropped. - Dashboard shows the resident's open transfer request (status card linking to /portal/transfer), mirroring the reports card. - Rating scales get emoji end anchors, a word label for the selected value and per-option aria-labels instead of bare 1-5 numbers. - Emergency guidance unified across footer, report and help pages: 112 first everywhere, then Polizei 117 / Rettungsdienst 144, AOZ emergency vs general line clearly separated. - Tone fixes: portal error copy now uses du form; preferences success message says the AOZ team reviews changes. - Login page: simpler copy plus hints telling residents (du form) their code is on the welcome letter and staff where codes come from. - Sticky safe-area submit bars on the report and create-chore forms. - Removed dead PORTAL_LABELS.login/.landing blocks (unused register flow). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01XYKT7w58RCJasVqGUXNVkQ --- .../api/portal/__tests__/preferences.test.ts | 48 ++++++ src/app/api/portal/preferences/route.ts | 15 +- src/app/login/page.tsx | 3 +- src/app/portal/help/page.tsx | 25 ++- src/app/portal/page.tsx | 18 +++ .../portal/preferences/PreferencesForm.tsx | 32 +++- src/app/portal/report/ReportForm.tsx | 25 +-- src/components/portal/CreateChoreForm.tsx | 17 +- .../portal/PortalTransferStatusCard.tsx | 51 ++++++ .../__tests__/roommate-preferences.test.ts | 126 +++++++++++++++ src/components/portal/roommate-preferences.ts | 145 ++++++++++++++++++ src/lib/constants/labels/auth.ts | 12 +- src/lib/constants/labels/portal.ts | 95 +++++------- 13 files changed, 507 insertions(+), 105 deletions(-) create mode 100644 src/components/portal/PortalTransferStatusCard.tsx create mode 100644 src/components/portal/__tests__/roommate-preferences.test.ts create mode 100644 src/components/portal/roommate-preferences.ts diff --git a/src/app/api/portal/__tests__/preferences.test.ts b/src/app/api/portal/__tests__/preferences.test.ts index f1c035ed..38aff10a 100644 --- a/src/app/api/portal/__tests__/preferences.test.ts +++ b/src/app/api/portal/__tests__/preferences.test.ts @@ -43,6 +43,7 @@ jest.mock('@/lib/logger', () => ({ // --- Import after mocks --- import { POST } from '../preferences/route' +import { parseRoommatePreferences } from '@/components/portal/roommate-preferences' // --- Helpers --- @@ -159,6 +160,53 @@ describe('POST /api/portal/preferences', () => { expect(updateCall.data.roommatePreferences).toContain('Ruhige Person') }) + test('serializes roommatePreferences in the parseable labeled-line format', async () => { + const resident = { id: 'res-3', code: 'RES-003' } + mockCookieGet.mockReturnValue({ value: 'RES-003' }) + mockFindUnique.mockResolvedValue(resident) + mockUpdate.mockResolvedValue(resident) + + const prefsWithRoommate = { + ...VALID_PREFS, + preferredAgeRange: 'SIMILAR', + culturalPreference: 'SAME_REGION', + additionalPreferences: 'Ich arbeite Nachtschicht', + } + + const req = createPreferencesRequest(prefsWithRoommate) + const res = await POST(req) + + expect(res.status).toBe(200) + + const updateCall = mockUpdate.mock.calls[0][0] + // Human-readable German labels, one labeled line per part + expect(updateCall.data.roommatePreferences).toBe( + 'Altersgruppe: Ähnliches Alter wie ich\n' + + 'Kultur: Aus meiner Region\n' + + 'Wünsche: Ich arbeite Nachtschicht' + ) + // Stored text must parse back to the submitted form values (round-trip) + expect(parseRoommatePreferences(updateCall.data.roommatePreferences)).toEqual({ + preferredAgeRange: 'SIMILAR', + culturalPreference: 'SAME_REGION', + additionalPreferences: 'Ich arbeite Nachtschicht', + }) + }) + + test('stores null roommatePreferences when all optional fields are empty', async () => { + const resident = { id: 'res-4', code: 'RES-004' } + mockCookieGet.mockReturnValue({ value: 'RES-004' }) + mockFindUnique.mockResolvedValue(resident) + mockUpdate.mockResolvedValue(resident) + + const req = createPreferencesRequest(VALID_PREFS) + const res = await POST(req) + + expect(res.status).toBe(200) + const updateCall = mockUpdate.mock.calls[0][0] + expect(updateCall.data.roommatePreferences).toBeNull() + }) + test('returns 400 on validation error (invalid sleepSchedule)', async () => { mockCookieGet.mockReturnValue({ value: 'RES-001' }) mockFindUnique.mockResolvedValue({ id: 'res-1', code: 'RES-001' }) diff --git a/src/app/api/portal/preferences/route.ts b/src/app/api/portal/preferences/route.ts index e37121c8..e97b3f7c 100644 --- a/src/app/api/portal/preferences/route.ts +++ b/src/app/api/portal/preferences/route.ts @@ -5,6 +5,7 @@ import { portalPreferencesSchema, validateFormData, ValidationError } from '@/li import { logger } from '@/lib/logger' import { ERROR_MESSAGES } from '@/lib/constants/error-messages' import { getResidentCookie } from '@/lib/portal-auth' +import { serializeRoommatePreferences } from '@/components/portal/roommate-preferences' export async function POST(request: NextRequest) { const residentCode = await getResidentCookie() @@ -32,12 +33,14 @@ export async function POST(request: NextRequest) { return NextResponse.json({ success: false, error: ERROR_MESSAGES.INVALID_INPUT }, { status: 400 }) } - // Build roommate preferences text (stored in dedicated column, NOT notes) - const roommatePrefsText = [ - data.preferredAgeRange ? `Altersgruppe: ${data.preferredAgeRange}` : null, - data.culturalPreference ? `Kultur: ${data.culturalPreference}` : null, - data.additionalPreferences || null, - ].filter(Boolean).join('. ') || null + // Build roommate preferences text (stored in dedicated column, NOT notes). + // Serialized as labeled German lines so staff can read it and the portal + // form can parse it back into its fields (round-trip). + const roommatePrefsText = serializeRoommatePreferences({ + preferredAgeRange: data.preferredAgeRange, + culturalPreference: data.culturalPreference, + additionalPreferences: data.additionalPreferences, + }) try { await prisma.resident.update({ diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index 8588e5ad..25253306 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -163,7 +163,8 @@ function LoginForm() { autoFocus={!searchParams.get('code')} className="input font-mono text-center text-lg tracking-wider" /> -

{LOGIN_LABELS.codeHint}

+

{LOGIN_LABELS.residentHint}

+

{LOGIN_LABELS.staffHint}

@@ -273,6 +293,7 @@ export function PreferencesForm({ resident, languageOptions, dietOptions }: Prop defaultValue={resident.privacyNeed} lowLabel={(RESIDENT_FACTORS.privacyNeed as ScaleFactorDef).lowLabel} highLabel={(RESIDENT_FACTORS.privacyNeed as ScaleFactorDef).highLabel} + wordLabels={P.scale.privacyNeed} /> @@ -387,7 +408,7 @@ export function PreferencesForm({ resident, languageOptions, dietOptions }: Prop
- {(RESIDENT_FACTORS.ageRange as EnumFactorDef).options.map((opt) => ( @@ -400,7 +421,7 @@ export function PreferencesForm({ resident, languageOptions, dietOptions }: Prop
- @@ -412,6 +433,7 @@ export function PreferencesForm({ resident, languageOptions, dietOptions }: Prop