dak.c2s/docs/plans/2026-02-26-report-viewer-implementation.md
CCS Admin dbb5afaeb9 feat: add inline report viewer with 5-tab sheet display
- Backend: GET /reports/{id}/data endpoint returns stored report JSON
- Frontend: ReportViewer component renders all 5 Excel sheets as tabs
  (KW gesamt, Fachgebiete, Gutachten, Therapieänderungen, ICD onko)
- ReportsPage: clickable rows with inline expansion to view reports
- Empty KW rows filtered, summary row at bottom, German labels
- Download button still available alongside inline view

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 22:57:35 +00:00

6 KiB

Report Viewer (Inline-Expansion) Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Berichte in der Berichteseite als Inline-Expansion mit 5 Tabs (wie Excel-Sheets) anzeigen, statt nur als Download.

Architecture: Neuer Backend-Endpoint GET /reports/{id}/data liefert gespeichertes report_data-JSON. Frontend rendert mit Inline-Expansion und 5-Tab-Viewer-Komponente. Daten bereits in DB vorhanden, kein Neuberechnen nötig.

Tech Stack: FastAPI, SQLAlchemy, React 19, TanStack Query v5, shadcn/ui (Tabs, Table), Tailwind CSS 4


Task 1: Backend — GET /reports/{id}/data Endpoint

Files:

  • Modify: backend/app/api/reports.py (nach dem /download/{report_id} Endpoint, ca. Zeile 211)

Step 1: Endpoint implementieren

In backend/app/api/reports.py nach dem download_report-Endpoint einfügen:

@router.get("/{report_id}/data")
def get_report_data(
    report_id: int,
    db: Session = Depends(get_db),
    user: User = Depends(get_current_user),
):
    """Return the stored report_data JSON for a given report.

    Accessible to both admin and dak_mitarbeiter users.
    """
    report = db.query(WeeklyReport).filter(WeeklyReport.id == report_id).first()
    if not report:
        raise HTTPException(status.HTTP_404_NOT_FOUND, "Report not found")
    if not report.report_data:
        raise HTTPException(status.HTTP_404_NOT_FOUND, "Report data not available")
    return report.report_data

WICHTIG: Dieser Endpoint MUSS vor dem bestehenden @router.delete("/delete") und @router.get("/list") stehen — aber er hat den Pfad /{report_id}/data mit einem Path-Parameter. Da FastAPI Routen in der Reihenfolge prüft, und /list und /delete statisch sind (und vor /{report_id} definiert sind), gibt es kein Routing-Konflikt. Einfach nach download_report (Zeile ~211) einfügen.

Step 2: Testen

Manuell (oder cURL): curl -H "Authorization: Bearer <token>" http://localhost:8000/api/reports/1/data

Step 3: Commit

git add backend/app/api/reports.py
git commit -m "feat: add GET /reports/{id}/data endpoint for report viewer"

Task 2: Frontend — useReportData Hook

Files:

  • Modify: frontend/src/hooks/useReports.ts

Step 1: Hook hinzufügen

Am Ende von frontend/src/hooks/useReports.ts einfügen:

export function useReportData(reportId: number | null) {
  return useQuery({
    queryKey: ['report-data', reportId],
    queryFn: () =>
      api.get<Record<string, unknown>>(`/reports/${reportId}/data`).then(r => r.data),
    enabled: reportId !== null,
  })
}

Step 2: Build prüfen

cd frontend && pnpm build

Step 3: Commit

git add frontend/src/hooks/useReports.ts
git commit -m "feat: add useReportData hook for fetching report JSON"

Task 3: Frontend — ReportViewer-Komponente

Files:

  • Create: frontend/src/components/ReportViewer.tsx

Step 1: Komponente erstellen

Die Komponente empfängt data: Record<string, unknown> (das report_data-JSON) und rendert 5 Tabs.

Datenstruktur (aus report_service.py):

  • data.sheet1{ summary: {...}, weekly: [{kw, erstberatungen, unterlagen, ablehnungen, keine_rm, gutachten}, ...] }
  • data.sheet2{ weekly: [{kw, onko: {anzahl, gutachten, keine_rm}, kardio: {...}, ...}, ...] }
  • data.sheet3{ weekly: [{kw, gesamt: {gutachten, alternative, bestaetigung}, onko: {...}, ...}, ...] }
  • data.sheet4{ weekly: [{kw, gutachten, ta_ja, ta_nein, diagnosekorrektur, unterversorgung, uebertherapie}, ...] }
  • data.sheet5{ icd_codes: [{icd, count}, ...] }

Implementiere die Komponente mit:

  • shadcn/ui Tabs, TabsList, TabsTrigger, TabsContent
  • shadcn/ui Table, TableHeader, TableRow, TableHead, TableBody, TableCell
  • Leere KW-Zeilen (alle Werte 0) ausfiltern
  • Summenzeile am Ende (fett)
  • Deutsche Spalten-Header
  • Fallgruppen-Labels: { onko: 'Onkologie', kardio: 'Kardiologie', intensiv: 'Intensivmedizin', galle: 'Gallenblase', sd: 'Schilddrüse' }

Sheet 2 und 3 haben verschachtelte Spalten (pro Fallgruppe). Nutze grouped headers mit colSpan.

Step 2: Build prüfen

pnpm build

Step 3: Commit

git add frontend/src/components/ReportViewer.tsx
git commit -m "feat: add ReportViewer component with 5-tab sheet display"

Task 4: Frontend — ReportsPage mit Inline-Expansion

Files:

  • Modify: frontend/src/pages/ReportsPage.tsx

Step 1: Expansion-State und Klick-Handler

Änderungen:

  1. Import useReportData und ReportViewer
  2. Neuer State: const [expandedId, setExpandedId] = useState<number | null>(null)
  3. useReportData(expandedId) aufrufen
  4. Tabellenzeile klickbar machen: onClick={() => setExpandedId(expandedId === r.id ? null : r.id)}
  5. Cursor-Pointer auf der Zeile: className="cursor-pointer hover:bg-muted/50"
  6. Nach jeder <TableRow> eine bedingte Expansion-Zeile:
{expandedId === r.id && (
  <TableRow>
    <TableCell colSpan={isAdmin ? 6 : 5} className="p-0">
      {reportDataLoading ? (
        <div className="p-6"><Skeleton className="h-64 w-full" /></div>
      ) : reportData ? (
        <div className="p-4 border-t bg-muted/20">
          <ReportViewer data={reportData} />
        </div>
      ) : (
        <div className="p-6 text-center text-muted-foreground">
          Keine Berichtsdaten verfügbar.
        </div>
      )}
    </TableCell>
  </TableRow>
)}

Step 2: Build und Tests prüfen

pnpm build && pnpm test

Step 3: Commit

git add frontend/src/pages/ReportsPage.tsx
git commit -m "feat: add inline report viewer expansion to ReportsPage"

Task 5: Verifikation und finaler Commit

Step 1: Build

cd /home/frontend/dak_c2s/frontend && pnpm build

Step 2: Tests

pnpm test

Alle 128+ Tests müssen bestehen.

Step 3: Finaler Commit (falls noch unstaged changes)

git add -A && git commit -m "feat: complete report viewer inline expansion"