Files
KanzlAI-mGMT/DESIGN-dashboard-redesign.md
m 0712d9a367 docs: design document for dashboard redesign + detail pages (t-kz-060)
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
2026-03-25 18:49:48 +01:00

26 KiB
Raw Blame History

Design: Dashboard Redesign + Detail Pages

Task: t-kz-060 Author: cronus (inventor) Date: 2026-03-25 Status: Design proposal


Problem Statement

The current dashboard is a read-only status board. Cards show counts but don't link anywhere. Timeline items are inert. Quick actions navigate to list pages rather than creation flows. There are no detail pages for individual events, deadlines, or appointments. Notes don't exist as a first-class entity. Case detail tabs use local state instead of URL segments, breaking deep linking and back navigation.

Design Principles

  1. Everything clickable goes somewhere — no dead-end UI
  2. Breadcrumb navigation — always know where you are, one click to go back
  3. German labels throughout — consistent with existing convention
  4. Mobile responsive — sidebar collapses, cards stack, touch targets >= 44px
  5. Information density over whitespace — law firm users want data, not decoration
  6. URL-driven state — tabs, filters, and views reflected in the URL for deep linking

Part 1: Dashboard Redesign

1.1 Traffic Light Cards → Click-to-Filter

Current: Three cards (Ueberfaellig / Diese Woche / Im Zeitplan) show counts. onFilter prop exists but is never wired up in dashboard/page.tsx.

Proposed:

Clicking a traffic light card navigates to /fristen?status={filter}:

Card Navigation Target
Ueberfaellig (red) /fristen?status=overdue
Diese Woche (amber) /fristen?status=this_week
Im Zeitplan (green) /fristen?status=ok

Implementation:

  • Replace onFilter callback with next/link navigation using href
  • DeadlineTrafficLights becomes a pure link-based component (no callback needed)
  • /fristen page reads searchParams.status and pre-applies the filter
  • The DeadlineList component already supports status filtering — just needs to read from URL

Changes:

  • DeadlineTrafficLights.tsx: Replace <button onClick> with <Link href="/fristen?status=...">
  • fristen/page.tsx: Read searchParams and pass as initial filter to DeadlineList
  • DeadlineList.tsx: Accept initialStatus prop from URL params

1.2 Case Overview Grid → Click-to-Filter

Current: Three static metrics (Aktive Akten / Neu / Abgeschlossen). No links.

Proposed:

Card Navigation Target
Aktive Akten /cases?status=active
Neu (Monat) /cases?status=active&since=month
Abgeschlossen /cases?status=closed

Implementation:

  • Wrap each metric row in <Link> to /cases with appropriate query params
  • Cases list page already has filtering — needs to read URL params on mount
  • Add visual hover state (arrow icon on hover, background highlight)

Changes:

  • CaseOverviewGrid.tsx: Each row becomes a <Link> with hover arrow
  • cases/page.tsx: Read searchParams for initial filter state

1.3 Timeline Items → Click-to-Navigate

Current: Timeline entries show deadline/appointment info but are not clickable. No link to the parent case or the item itself.

Proposed:

Each timeline entry becomes a clickable row:

  • Deadline entries: Click navigates to /fristen/{id} (new deadline detail page)
  • Appointment entries: Click navigates to /termine/{id} (new appointment detail page)
  • Case reference (Az. / case_number): Secondary click target linking to /cases/{case_id}

Visual changes:

  • Add cursor-pointer and hover state (hover:bg-neutral-100 transition)
  • Add a small chevron-right icon on the right edge
  • Case number becomes a subtle underlined link (click stops propagation)

Data changes:

  • UpcomingDeadline needs case_id field (currently missing from the dashboard query — the backend model has it but the SQL join doesn't select it)
  • UpcomingAppointment already has case_id

Backend change:

  • dashboard_service.go line 112: Add d.case_id to the upcoming deadlines SELECT
  • DashboardService.UpcomingDeadline struct: Add CaseID uuid.UUID field
  • Frontend UpcomingDeadline type: Already has case_id (it's in types.ts but the backend doesn't send it)

1.4 Quick Actions → Proper Navigation

Current: "Frist eintragen" goes to /fristen (list page), not a creation flow. "CalDAV Sync" goes to /einstellungen.

Proposed:

Action Current Target New Target
Neue Akte /cases/new /cases/new (keep)
Frist eintragen /fristen /fristen/neu (new creation page)
Neuer Termin (missing) /termine/neu (new creation page)
AI Analyse /ai/extract /ai/extract (keep)

Replace "CalDAV Sync" with "Neuer Termin" — CalDAV sync is a settings function, not a daily quick action. Creating an appointment is something a secretary does multiple times per day.

Changes:

  • QuickActions.tsx: Update hrefs, swap CalDAV for appointment creation
  • Create /fristen/neu/page.tsx — standalone deadline creation form (select case, fill fields)
  • Create /termine/neu/page.tsx — standalone appointment creation form

1.5 AI Summary Card → Refresh Button

Current: Rule-based summary text, no refresh mechanism. Card regenerates on page load but not on demand.

Proposed:

  • Add a small refresh icon button (RefreshCw) in the card header, next to "KI-Zusammenfassung"
  • Clicking it calls refetch() on the dashboard query (passed as prop)
  • Show a brief spinning animation during refetch
  • If/when real AI summarization is wired up, this button triggers POST /api/ai/summarize-dashboard (future endpoint)

Changes:

  • AISummaryCard.tsx: Accept onRefresh prop, add button with spinning state
  • dashboard/page.tsx: Pass refetch to AISummaryCard

1.6 Dashboard Layout: Add Recent Activity Section

Current: The backend returns recent_activity (last 10 case events) but the frontend ignores it entirely.

Proposed:

  • Add a "Letzte Aktivitaet" section below the timeline, full width
  • Shows the 10 most recent case events in a compact list
  • Each row: event icon (by type) | title | case number (linked) | relative time
  • Clicking a row navigates to the case event detail page /cases/{case_id}/ereignisse/{event_id}

Changes:

  • New component: RecentActivityList.tsx in components/dashboard/
  • dashboard/page.tsx: Add section below the main grid
  • Add RecentActivity type to types.ts (needs case_id and event_id fields from backend)
  • Backend: Add case_id and id to the recent activity query

Part 2: New Pages

2.1 Deadline Detail Page — /fristen/{id}

Route: src/app/(app)/fristen/[id]/page.tsx

Layout:

Breadcrumb: Dashboard > Fristen > {deadline.title}
+---------------------------------------------------------+
| [Status Badge]  {deadline.title}            [Erledigen] |
| Fällig: 28. März 2026                                   |
+---------------------------------------------------------+
| Akte: Az. 2024/001 — Müller v. Schmidt  [→ Zur Akte]   |
| Quelle: Berechnet (R.118 RoP)                          |
| Ursprüngliches Datum: 25. März 2026                     |
| Warnungsdatum: 21. März 2026                            |
+---------------------------------------------------------+
| Notizen                                      [Bearbeiten]|
| Fristverlängerung beantragt am 20.03.         |
+---------------------------------------------------------+
| Verlauf                                                  |
| ○ Erstellt am 15.03.2026                                |
| ○ Warnung gesendet am 21.03.2026                        |
+---------------------------------------------------------+

Data requirements:

  • GET /api/deadlines/{id} — new endpoint returning full deadline with case info
  • Returns: Deadline + associated case (number, title, id) + notes

Sections:

  1. Header: Status badge (Offen/Erledigt/Ueberfaellig), title, "Erledigen" action button
  2. Due date: Large, with relative time ("in 3 Tagen" / "vor 2 Tagen ueberfaellig")
  3. Context panel: Parent case (linked), source (manual/calculated/caldav), rule reference, original vs adjusted date
  4. Notes section: Free-text notes (existing notes field on deadline), inline edit
  5. Activity log: Timeline of changes to this deadline (future: from case_events filtered by deadline)

Backend additions:

  • GET /api/deadlines/{id} — new handler returning single deadline with case join
  • Handler: deadlines.go add Get method
  • Service: deadline_service.go add GetByID with case join

2.2 Appointment Detail Page — /termine/{id}

Route: src/app/(app)/termine/[id]/page.tsx

Layout:

Breadcrumb: Dashboard > Termine > {appointment.title}
+---------------------------------------------------------+
| {appointment.title}                    [Bearbeiten] [X] |
| Typ: Verhandlung                                        |
+---------------------------------------------------------+
| Datum: 28. März 2026, 10:00  12:00 Uhr                |
| Ort: UPC München, Saal 3                                |
+---------------------------------------------------------+
| Akte: Az. 2024/001 — Müller v. Schmidt  [→ Zur Akte]   |
+---------------------------------------------------------+
| Beschreibung                                             |
| Erste mündliche Verhandlung...                           |
+---------------------------------------------------------+
| Notizen                                         [+ Neu] |
| ○ 25.03. — Mandant über Termin informiert               |
| ○ 24.03. — Schriftsatz vorbereitet                      |
+---------------------------------------------------------+

Data requirements:

  • GET /api/appointments/{id} — new endpoint returning single appointment with case info
  • Notes: Uses new notes table (see Part 3)

Backend additions:

  • GET /api/appointments/{id} — new handler
  • Handler: appointments.go add Get method
  • Service: appointment_service.go add GetByID with optional case join

2.3 Case Event Detail Page — /cases/{id}/ereignisse/{eventId}

Route: src/app/(app)/cases/[id]/ereignisse/[eventId]/page.tsx

Layout:

Breadcrumb: Akten > Az. 2024/001 > Verlauf > {event.title}
+---------------------------------------------------------+
| [Event Type Icon]  {event.title}                        |
| 25. März 2026, 14:30                                    |
+---------------------------------------------------------+
| Beschreibung                                             |
| Statusänderung: aktiv → geschlossen                     |
+---------------------------------------------------------+
| Metadaten                                                |
| Erstellt von: max.mustermann@kanzlei.de                 |
| Typ: status_changed                                     |
+---------------------------------------------------------+
| Notizen                                         [+ Neu] |
| (keine Notizen)                                          |
+---------------------------------------------------------+

Data requirements:

  • GET /api/case-events/{id} — new endpoint
  • Notes: Uses new notes table

Backend additions:

  • New handler: case_events.go with Get method
  • New service method: CaseEventService.GetByID
  • Or extend existing case handler to include event fetching

2.4 Standalone Deadline Creation — /fristen/neu

Route: src/app/(app)/fristen/neu/page.tsx

Layout:

Breadcrumb: Fristen > Neue Frist
+---------------------------------------------------------+
| Neue Frist anlegen                                       |
+---------------------------------------------------------+
| Akte*:        [Dropdown: Aktenauswahl]                  |
| Bezeichnung*: [________________________]                |
| Beschreibung: [________________________]                |
| Fällig am*:   [Datumsauswahl]                           |
| Warnung am:   [Datumsauswahl]                           |
| Notizen:      [Textarea]                                |
+---------------------------------------------------------+
|                              [Abbrechen]  [Frist anlegen]|
+---------------------------------------------------------+

Reuses existing deadline creation logic but as a standalone page rather than requiring the user to first navigate to a case. Case is selected via dropdown.

2.5 Standalone Appointment Creation — /termine/neu

Route: src/app/(app)/termine/neu/page.tsx

Same pattern as deadline creation. Reuses AppointmentModal fields but as a full page form. Appointment can optionally be linked to a case.

2.6 Case Detail Tabs → URL Segments

Current: Tabs use useState<TabKey> — no URL change, no deep linking, no browser back.

Proposed route structure:

/cases/{id}              → redirects to /cases/{id}/verlauf
/cases/{id}/verlauf      → Timeline tab
/cases/{id}/fristen      → Deadlines tab
/cases/{id}/dokumente    → Documents tab
/cases/{id}/parteien     → Parties tab
/cases/{id}/notizen      → Notes tab (new)

Implementation approach:

Use Next.js nested layouts with a shared layout for the case header + tab bar:

src/app/(app)/cases/[id]/
  layout.tsx              # Case header + tab navigation
  page.tsx                # Redirect to ./verlauf
  verlauf/page.tsx        # Timeline
  fristen/page.tsx        # Deadlines
  dokumente/page.tsx      # Documents
  parteien/page.tsx       # Parties
  notizen/page.tsx        # Notes (new)

The layout.tsx fetches case data and renders the header + tab bar. Each child page renders its tab content. The active tab is determined by the current pathname.

Benefits:

  • Deep linking: /cases/abc123/fristen opens directly to the deadlines tab
  • Browser back button works between tabs
  • Each tab can have its own loading state
  • Bookmarkable

Part 3: Notes System

3.1 Data Model

Notes are a polymorphic entity — they can be attached to cases, deadlines, appointments, or case events.

New table: kanzlai.notes

CREATE TABLE kanzlai.notes (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id   UUID NOT NULL REFERENCES kanzlai.tenants(id),

    -- Polymorphic parent reference (exactly one must be set)
    case_id         UUID REFERENCES kanzlai.cases(id) ON DELETE CASCADE,
    deadline_id     UUID REFERENCES kanzlai.deadlines(id) ON DELETE CASCADE,
    appointment_id  UUID REFERENCES kanzlai.appointments(id) ON DELETE CASCADE,
    case_event_id   UUID REFERENCES kanzlai.case_events(id) ON DELETE CASCADE,

    content     TEXT NOT NULL,
    created_by  UUID,           -- auth.users reference
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now(),

    -- Ensure exactly one parent is set
    CONSTRAINT notes_single_parent CHECK (
        (CASE WHEN case_id IS NOT NULL THEN 1 ELSE 0 END +
         CASE WHEN deadline_id IS NOT NULL THEN 1 ELSE 0 END +
         CASE WHEN appointment_id IS NOT NULL THEN 1 ELSE 0 END +
         CASE WHEN case_event_id IS NOT NULL THEN 1 ELSE 0 END) = 1
    )
);

-- Indexes for efficient lookup by parent
CREATE INDEX idx_notes_case ON kanzlai.notes(tenant_id, case_id) WHERE case_id IS NOT NULL;
CREATE INDEX idx_notes_deadline ON kanzlai.notes(tenant_id, deadline_id) WHERE deadline_id IS NOT NULL;
CREATE INDEX idx_notes_appointment ON kanzlai.notes(tenant_id, appointment_id) WHERE appointment_id IS NOT NULL;
CREATE INDEX idx_notes_case_event ON kanzlai.notes(tenant_id, case_event_id) WHERE case_event_id IS NOT NULL;

-- RLS
ALTER TABLE kanzlai.notes ENABLE ROW LEVEL SECURITY;
CREATE POLICY notes_tenant_isolation ON kanzlai.notes
    USING (tenant_id IN (
        SELECT tenant_id FROM kanzlai.user_tenants WHERE user_id = auth.uid()
    ));

3.2 Why Polymorphic Table vs Separate Tables

Considered alternatives:

  1. Separate notes per entity (case_notes, deadline_notes, etc.) — More tables, duplicated logic, harder to search across all notes.
  2. Generic entity_type + entity_id pattern — Loses FK constraints, can't cascade delete, harder to query with joins.
  3. Polymorphic with nullable FKs (chosen) — FK constraints maintained, cascade deletes work, partial indexes keep queries fast, single service/handler. The CHECK constraint ensures data integrity.

3.3 Backend Model & API

Go model:

type Note struct {
    ID            uuid.UUID  `db:"id" json:"id"`
    TenantID      uuid.UUID  `db:"tenant_id" json:"tenant_id"`
    CaseID        *uuid.UUID `db:"case_id" json:"case_id,omitempty"`
    DeadlineID    *uuid.UUID `db:"deadline_id" json:"deadline_id,omitempty"`
    AppointmentID *uuid.UUID `db:"appointment_id" json:"appointment_id,omitempty"`
    CaseEventID   *uuid.UUID `db:"case_event_id" json:"case_event_id,omitempty"`
    Content       string     `db:"content" json:"content"`
    CreatedBy     *uuid.UUID `db:"created_by" json:"created_by,omitempty"`
    CreatedAt     time.Time  `db:"created_at" json:"created_at"`
    UpdatedAt     time.Time  `db:"updated_at" json:"updated_at"`
}

API endpoints:

GET    /api/notes?case_id={id}            # List notes for a case
GET    /api/notes?deadline_id={id}        # List notes for a deadline
GET    /api/notes?appointment_id={id}     # List notes for an appointment
GET    /api/notes?case_event_id={id}      # List notes for a case event
POST   /api/notes                         # Create note (body includes parent ID)
PUT    /api/notes/{id}                    # Update note content
DELETE /api/notes/{id}                    # Delete note

Single endpoint with query parameter filtering — simpler than nested routes, works uniformly across all parent types.

Service methods:

type NoteService struct { db *sqlx.DB }

func (s *NoteService) ListByParent(ctx, tenantID, parentType, parentID) ([]Note, error)
func (s *NoteService) Create(ctx, tenantID, note) (*Note, error)
func (s *NoteService) Update(ctx, tenantID, noteID, content) (*Note, error)
func (s *NoteService) Delete(ctx, tenantID, noteID) error

3.4 Notes UI Component

Reusable <NotesList> component used on every detail page:

+------------------------------------------------------------+
| Notizen                                            [+ Neu] |
+------------------------------------------------------------+
| m@kanzlei.de · 25. Mär 2026, 14:30                [X][E] |
| Fristverlängerung beim Gericht beantragt.                  |
+------------------------------------------------------------+
| m@kanzlei.de · 24. Mär 2026, 10:15                [X][E] |
| Mandant telefonisch über Sachstand informiert.             |
+------------------------------------------------------------+

Props:

interface NotesListProps {
  parentType: "case" | "deadline" | "appointment" | "case_event";
  parentId: string;
}

Features:

  • Fetches notes via GET /api/notes?{parentType}_id={parentId}
  • "Neu" button opens inline textarea (not a modal — faster for quick notes)
  • Each note shows: author, timestamp, content, edit/delete buttons
  • Edit is inline (textarea replaces content)
  • Optimistic updates via react-query mutation + invalidation
  • Empty state: "Keine Notizen vorhanden. Klicken Sie +, um eine Notiz hinzuzufügen."

3.5 Migration from deadlines.notes Field

The existing deadlines.notes text field should be migrated:

  1. For each deadline with a non-null notes value, create a corresponding row in the notes table with deadline_id set
  2. Drop the deadlines.notes column after migration
  3. This can be a one-time SQL migration script

Part 4: Breadcrumb Navigation

4.1 Breadcrumb Component

New shared component: src/components/layout/Breadcrumb.tsx

interface BreadcrumbItem {
  label: string;
  href?: string;  // omit for current page (last item)
}

function Breadcrumb({ items }: { items: BreadcrumbItem[] }) {
  // Renders: Home > Parent > Current
  // Each item with href is a Link, last item is plain text
}

Placement: At the top of every page, inside the main content area (not in the layout — different pages have different breadcrumbs).

4.2 Breadcrumb Patterns

Page Breadcrumb
Dashboard Dashboard
Fristen Dashboard > Fristen
Fristen Detail Dashboard > Fristen > {title}
Fristen Neu Dashboard > Fristen > Neue Frist
Termine Dashboard > Termine
Termine Detail Dashboard > Termine > {title}
Termine Neu Dashboard > Termine > Neuer Termin
Akten Dashboard > Akten
Akte Detail Dashboard > Akten > {case_number}
Akte > Fristen Dashboard > Akten > {case_number} > Fristen
Akte > Notizen Dashboard > Akten > {case_number} > Notizen
Ereignis Detail Dashboard > Akten > {case_number} > Verlauf > {title}
Einstellungen Dashboard > Einstellungen
AI Analyse Dashboard > AI Analyse

Part 5: Summary of Backend Changes

New Endpoints

Method Path Handler Purpose
GET /api/deadlines/{id} deadlineH.Get Single deadline with case context
GET /api/appointments/{id} apptH.Get Single appointment with case context
GET /api/case-events/{id} eventH.Get Single case event
GET /api/notes noteH.List List notes (filtered by parent)
POST /api/notes noteH.Create Create note
PUT /api/notes/{id} noteH.Update Update note
DELETE /api/notes/{id} noteH.Delete Delete note

Modified Endpoints

Endpoint Change
GET /api/dashboard Add case_id, id to recent_activity; add case_id to upcoming_deadlines query

New Files

File Purpose
backend/internal/models/note.go Note model
backend/internal/services/note_service.go Note CRUD service
backend/internal/handlers/notes.go Note HTTP handlers
backend/internal/handlers/case_events.go Case event detail handler

Database Migration

  1. Create kanzlai.notes table with polymorphic FK pattern
  2. Migrate existing deadlines.notes data
  3. Drop deadlines.notes column

Part 6: Summary of Frontend Changes

New Files

File Purpose
src/app/(app)/fristen/[id]/page.tsx Deadline detail page
src/app/(app)/fristen/neu/page.tsx Standalone deadline creation
src/app/(app)/termine/[id]/page.tsx Appointment detail page
src/app/(app)/termine/neu/page.tsx Standalone appointment creation
src/app/(app)/cases/[id]/layout.tsx Case detail shared layout (header + tabs)
src/app/(app)/cases/[id]/verlauf/page.tsx Case timeline tab
src/app/(app)/cases/[id]/fristen/page.tsx Case deadlines tab
src/app/(app)/cases/[id]/dokumente/page.tsx Case documents tab
src/app/(app)/cases/[id]/parteien/page.tsx Case parties tab
src/app/(app)/cases/[id]/notizen/page.tsx Case notes tab (new)
src/app/(app)/cases/[id]/ereignisse/[eventId]/page.tsx Case event detail
src/components/layout/Breadcrumb.tsx Reusable breadcrumb
src/components/notes/NotesList.tsx Reusable notes list + inline creation
src/components/dashboard/RecentActivityList.tsx Recent activity feed

Modified Files

File Change
src/components/dashboard/DeadlineTrafficLights.tsx Buttons → Links with navigation
src/components/dashboard/CaseOverviewGrid.tsx Static metrics → clickable links
src/components/dashboard/UpcomingTimeline.tsx Items → clickable with navigation
src/components/dashboard/AISummaryCard.tsx Add refresh button
src/components/dashboard/QuickActions.tsx Fix targets, swap CalDAV for Termin
src/app/(app)/dashboard/page.tsx Wire navigation, add RecentActivity section
src/app/(app)/fristen/page.tsx Read URL params for initial filter
src/app/(app)/cases/page.tsx Read URL params for initial filter
src/app/(app)/cases/[id]/page.tsx Refactor into layout + nested routes
src/lib/types.ts Add Note, RecentActivity types; update UpcomingDeadline

Types to Add

export interface Note {
  id: string;
  tenant_id: string;
  case_id?: string;
  deadline_id?: string;
  appointment_id?: string;
  case_event_id?: string;
  content: string;
  created_by?: string;
  created_at: string;
  updated_at: string;
}

export interface RecentActivity {
  id: string;
  event_type?: string;
  title: string;
  case_id: string;
  case_number: string;
  event_date?: string;
  created_at: string;
}

Part 7: Implementation Plan

Recommended order for a coder to implement:

Phase A: Backend Foundation (can be done in parallel)

  1. Create notes table migration + model + service + handler
  2. Add GET /api/deadlines/{id} endpoint
  3. Add GET /api/appointments/{id} endpoint
  4. Add GET /api/case-events/{id} endpoint
  5. Fix dashboard query to include case_id in upcoming deadlines and recent activity

Phase B: Frontend — Dashboard Interactivity

  1. Create Breadcrumb component
  2. Make traffic light cards clickable (Links)
  3. Make case overview grid clickable (Links)
  4. Make timeline items clickable (Links)
  5. Fix quick actions (swap CalDAV for Termin, update hrefs)
  6. Add refresh button to AI Summary card
  7. Add RecentActivityList component + wire to dashboard

Phase C: Frontend — New Detail Pages

  1. Deadline detail page (/fristen/[id])
  2. Appointment detail page (/termine/[id])
  3. Case event detail page (/cases/[id]/ereignisse/[eventId])
  4. Standalone deadline creation (/fristen/neu)
  5. Standalone appointment creation (/termine/neu)

Phase D: Frontend — Case Detail Refactor

  1. Extract case header + tabs into layout.tsx
  2. Create sub-route pages (verlauf, fristen, dokumente, parteien)
  3. Add notes tab
  4. Wire NotesList component into all detail pages

Phase E: Polish

  1. URL filter params on /fristen and /cases pages
  2. Breadcrumbs on all pages
  3. Mobile responsive testing
  4. Migration of existing deadlines.notes data

Appendix: What This Design Does NOT Cover

  • Real AI-powered summary (currently rule-based — kept as-is with refresh button)
  • Notification system (toast-based alerts for approaching deadlines)
  • Audit log / change history per entity
  • Batch operations (mark multiple deadlines complete)
  • Print views

These are separate features that can be designed independently.