- Create kanzlai.notes table (polymorphic FK with CHECK constraint,
partial indexes, RLS)
- Add Note model, NoteService (ListByParent, Create, Update, Delete),
and NoteHandler with endpoints: GET/POST /api/notes, PUT/DELETE /api/notes/{id}
- Add GET /api/deadlines/{deadlineID} detail endpoint
- Add GET /api/appointments/{id} detail endpoint
- Add GET /api/case-events/{id} detail endpoint (new CaseEventHandler)
- Fix dashboard query: add case_id to upcoming_deadlines SELECT,
add id and case_id to recent_activity SELECT
- Register all new routes in router.go
Comprehensive design covering:
- Dashboard interactivity (click-to-filter traffic lights, clickable timeline,
fixed quick actions, AI summary refresh)
- New detail pages (deadline, appointment, case event)
- Notes system with polymorphic table design
- Case detail URL-based tab navigation
- Breadcrumb navigation system
- Backend API additions and data model changes
- Phased implementation plan for coders
Prevents "M.forEach is not a function" crashes when API returns error
objects or unexpected shapes instead of arrays. Guards all useQuery
consumers with Array.isArray checks and safe defaults for object props.
Files fixed: DeadlineList, AppointmentList, TenantSwitcher,
DeadlineTrafficLights, UpcomingTimeline, CaseOverviewGrid,
AISummaryCard, TeamSettings, and all page-level components
(dashboard, cases, fristen, termine, ai/extract).
Frontend api.ts baseUrl is already "/api", so paths like
"/api/cases" produced "/api/api/cases". Stripped the redundant
prefix from all component calls. Rewrite destination correctly
adds /api/ back for the Go backend.
The middleware was intercepting API proxy requests and redirecting
to /login. API routes should pass through to the Go backend which
handles its own JWT auth.
Backend: PUT /api/tenants/{id}/settings endpoint for updating tenant
settings (JSONB merge). Frontend: /einstellungen page with CalDAV
config (URL, credentials, calendar path, sync toggle, interval),
manual sync button, live sync status display. /einstellungen/team
page with member list, invite-by-email, role management.
- Responsive sidebar: collapses on mobile with hamburger menu, slide-in animation
- Skeleton loaders: dashboard cards, case table, case detail page
- Empty states: friendly messages with icons for cases, deadlines, parties, documents
- Error states: retry button on dashboard, proper error message on case not found
- Form validation: inline error messages on case creation form
- German language: fix all missing umlauts (Zurück, wählen, Anhängig, Verfügung, etc.)
- Status labels: display German translations instead of raw status values
- Transitions: fade-in animations on page load, hover/transition-colors on all interactive elements
- Focus states: focus-visible ring for keyboard accessibility
- Mobile layout: stacking for filters, forms, tabs; horizontal scroll for tables
- Extraction results: card layout on mobile, table on desktop
- Missing types: add DashboardData, DeadlineSummary, CaseSummary, ExtractedDeadline etc.
- Fix QuickActions links to use correct routes (/cases/new, /ai/extract)
- Consistent input focus styles across all forms
Implements CalDAV sync using github.com/emersion/go-webdav:
- CalDAVService with background polling (configurable per-tenant interval)
- Push: deadlines -> VTODO, appointments -> VEVENT on create/update/delete
- Pull: periodic fetch from CalDAV, reconcile with local DB
- Conflict resolution: KanzlAI wins dates/status, CalDAV wins notes/description
- Conflicts logged as case_events with caldav_conflict type
- UID pattern: kanzlai-{deadline|appointment}-{uuid}@kanzlai.msbls.de
- CalDAV config per tenant in tenants.settings JSONB
Endpoints:
- POST /api/caldav/sync — trigger full sync for current tenant
- GET /api/caldav/status — last sync time, item counts, errors
8 unit tests for UID generation, parsing, path construction, config parsing.
- /termine page with list/calendar view toggle
- AppointmentList: date-grouped list with type/case filtering, summary cards
- AppointmentCalendar: month grid with colored type dots, clickable days/appointments
- AppointmentModal: create/edit/delete with case linking, type selection, location
- DocumentUpload: dropzone with multi-file support, upload via
POST /api/cases/{id}/documents, progress feedback with toast
- DocumentList: type badges, file size, upload date, download links,
delete with inline confirmation
- Integrated as Dokumente tab in case detail page with count badge
- Eagerly fetches document count for tab badge display
- Case list page (/cases) with search, status/type filters, status badges
- Case creation page (/cases/new) with reusable CaseForm component
- Case detail page (/cases/[id]) with tabs: Timeline, Deadlines, Documents, Parties
- CaseTimeline component for chronological case_events display
- PartyList component with inline party CRUD (add/delete)
- Updated sidebar navigation to route to /cases
Dashboard page at /dashboard with 5 components:
- DeadlineTrafficLights: RED/AMBER/GREEN cards with animated counts and pulse for overdue
- CaseOverviewGrid: active/new/closed case counts
- UpcomingTimeline: merged deadlines + appointments for next 7 days, grouped by day
- AISummaryCard: natural language summary generated from dashboard data
- QuickActions: shortcuts to create cases, deadlines, AI analysis, CalDAV sync
3-column responsive grid layout. Root / redirects to /dashboard.
Fetches from GET /api/dashboard with 60s auto-refresh via react-query.
- ExtractionForm: PDF dropzone (react-dropzone) + text textarea + case selector
- ExtractionResults: review table with edit/remove per row, confidence color-coding
- Page at /ai/extract: upload -> analyze -> review -> adopt deadlines to case
- Extended API client with postFormData for multipart uploads
- Added ExtractedDeadline and ExtractionResponse types
Add two Claude API-powered endpoints:
- POST /api/ai/extract-deadlines: accepts PDF upload or JSON text, extracts
legal deadlines using Claude tool_use for structured output
- POST /api/ai/summarize-case: generates AI summary from case events/deadlines,
caches result in cases.ai_summary
New files:
- internal/services/ai_service.go: AIService with Anthropic SDK integration
- internal/handlers/ai.go: HTTP handlers for both endpoints
- internal/services/ai_service_test.go: tool schema and serialization tests
Uses anthropic-sdk-go v1.27.1 with Claude Sonnet 4.5. AI service is optional —
endpoints only registered when ANTHROPIC_API_KEY is set.
GET /api/dashboard returns aggregated data:
- deadline_summary: overdue, due this/next week, ok counts
- case_summary: active, new this month, closed counts
- upcoming_deadlines: next 7 days with case info
- upcoming_appointments: next 7 days
- recent_activity: last 10 case events
Uses efficient CTE query for summaries. Also fixes duplicate
writeJSON/writeError declarations in appointments handler.
- Holiday service with German federal holidays, Easter calculation, DB loading
- Deadline calculator adapted from youpc.org (duration calc + non-working day adjustment)
- Deadline CRUD service (tenant-scoped: list, create, update, complete, delete)
- Deadline rule service (list, filter by proceeding type, hierarchical rule trees)
- HTTP handlers for all endpoints with tenant resolution via X-Tenant-ID header
- Router wired with all new endpoints under /api/
- Tests for holiday and calculator services (8 passing)
Tenant management:
- POST /api/tenants — create tenant (creator becomes owner)
- GET /api/tenants — list tenants for authenticated user
- GET /api/tenants/:id — tenant details with access check
- POST /api/tenants/:id/invite — invite user by email (owner/admin)
- DELETE /api/tenants/:id/members/:uid — remove member
- GET /api/tenants/:id/members — list members
New packages:
- internal/services/tenant_service.go — CRUD on tenants + user_tenants
- internal/handlers/tenant_handler.go — HTTP handlers with auth checks
- internal/auth/tenant_resolver.go — X-Tenant-ID header middleware,
defaults to user's first tenant for scoped routes
Authorization: owners/admins can invite and remove members. Cannot
remove the last owner. Users can remove themselves. TenantResolver
applies to resource routes (cases, deadlines, etc.) but not tenant
management routes.
- CaseService: list (paginated, filterable), get detail (with parties,
events, deadline count), create, update, soft-delete (archive)
- PartyService: list by case, create, update, delete
- Auto-create case_events on case creation, status change, party add,
and case archive
- Auth middleware now resolves tenant_id from user_tenants table
- All operations scoped to tenant_id from auth context
- AppointmentService with tenant-scoped List, GetByID, Create, Update, Delete
- List supports filtering by case_id, appointment_type, and date range (start_from/start_to)
- AppointmentHandler with JSON request/response handling and input validation
- Router wired up: GET/POST /api/appointments, PUT/DELETE /api/appointments/{id}