Files
dashboard/plans/2026-02-28-vehicle-equipment-features.md
Matthias Hochmeister e2be29c712 refine vehicle freatures
2026-02-28 17:19:18 +01:00

28 KiB

Feuerwehr Dashboard — Vehicle & Equipment Feature Plan

Date: 2026-02-28 Author: Claude (brainstorm + analysis)


Feature Status Summary

# Feature Status Work Needed
1 Add vehicle DONE None
2 Edit vehicle DONE None
3 Remove vehicle BACKEND ONLY Frontend UI (delete button + confirmation dialog)
4 Permission visibility PARTIAL Hide restricted UI elements + frontend route guards
5 Remove info fields NOT DONE Remove 6 fields from form, detail, list view
6 Equipment system NOT IMPLEMENTED Full-stack (DB, backend, frontend)
7 Equipment on vehicle detail NOT IMPLEMENTED New tab on vehicle detail page
8 Equipment warnings on vehicle cards NOT IMPLEMENTED Warning badges on vehicle overview

Priority Order

Phase 1 — Quick Wins (vehicle cleanup)

  1. P1: Remove info fields — Remove Fahrgestellnr., Standort, Besatzung, Typ, Hersteller, Baujahr from frontend
  2. P2: Delete vehicle UI — Add delete button + confirmation dialog to FahrzeugDetail.tsx
  3. P3: Permission guards — Hide restricted features from unauthorized user groups

Phase 2 — Equipment Backend

  1. P4: Database migration — Create equipment tables (ausruestung, ausruestung_kategorien, ausruestung_wartungslog)
  2. P5: Backend model + service + controller + routes — Full backend CRUD for equipment

Phase 3 — Equipment Frontend

  1. P6: Equipment list page — Replace placeholder with full equipment management page
  2. P7: Equipment create/edit form — AusruestungForm.tsx
  3. P8: Equipment detail page — AusruestungDetail.tsx with tabs

Phase 4 — Vehicle-Equipment Integration

  1. P9: Vehicle detail equipment tab — Show assigned equipment in vehicle detail
  2. P10: Vehicle card warning badges — Show warnings when important equipment is not ready

Subagent Prompts

PROMPT 1: Remove Info Fields (P1)

You are a senior React/TypeScript developer working on a Feuerwehr (fire department) Dashboard.

TASK: Remove these 6 info fields from the frontend display:
- Fahrgestellnr. (fahrgestellnummer)
- Standort (standort)
- Besatzung (besatzung_soll)
- Typ (typ_schluessel)
- Hersteller (hersteller)
- Baujahr (baujahr)

FILES TO MODIFY:

1. /Users/matthias/work/feuerwehr_dashboard/frontend/src/pages/FahrzeugForm.tsx
   - Remove the TextField inputs for all 6 fields from the form
   - Remove them from the form state (formData) and any related onChange handlers
   - Keep the fields in the TypeScript types and backend — only remove from UI

2. /Users/matthias/work/feuerwehr_dashboard/frontend/src/pages/FahrzeugDetail.tsx
   - Remove the display rows for these 6 fields from the "Übersicht" tab data grid
   - They appear in the info section showing vehicle details

3. /Users/matthias/work/feuerwehr_dashboard/frontend/src/pages/Fahrzeuge.tsx
   - Remove besatzung_soll and baujahr from the VehicleCard component display
   - Remove hersteller from the search filter if it's included
   - Keep the TypeScript types intact — only remove visual display

IMPORTANT:
- Do NOT modify backend code, database, or TypeScript type definitions
- Do NOT remove the fields from API payloads — just stop displaying them
- Clean up any unused imports after removing the fields
- Verify the layout still looks good after removal (no empty gaps)
- Read each file first before making changes

PROMPT 2: Delete Vehicle UI (P2)

You are a senior React/TypeScript developer working on a Feuerwehr Dashboard.

TASK: Add a delete button with confirmation dialog to the vehicle detail page.

CONTEXT:
- Backend soft-delete endpoint already exists: DELETE /api/vehicles/:id (admin only)
- Frontend API method exists: vehiclesApi.delete(id) in /frontend/src/services/vehicles.ts
- Permission hook: usePermissions() returns { isAdmin, canChangeStatus }
- Delete should only be visible to admin users (isAdmin === true)

FILE TO MODIFY: /Users/matthias/work/feuerwehr_dashboard/frontend/src/pages/FahrzeugDetail.tsx

REQUIREMENTS:
1. Add a DELETE button (red/error color) next to the existing EDIT button in the header area
   - Only visible when isAdmin is true (same guard as edit button)
   - Use MUI DeleteOutline or Delete icon
   - Button variant: outlined, color: error

2. Add a CONFIRMATION DIALOG (MUI Dialog component):
   - Title: "Fahrzeug löschen"
   - Body: "Möchten Sie das Fahrzeug '{vehicle.bezeichnung}' wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden."
   - Cancel button: "Abbrechen" (default, autofocus)
   - Confirm button: "Löschen" (red/error color)

3. On confirm:
   - Call vehiclesApi.delete(id)
   - Show success notification (use the existing notification system/context)
   - Navigate to /fahrzeuge after successful deletion
   - Handle errors: show error notification if delete fails

4. State management:
   - useState for dialog open/close
   - useState for loading state during delete (disable buttons)

PATTERNS TO FOLLOW:
- Look at how the status change dialog works in the same file for patterns
- Use the existing useNotification() or similar notification hook
- Follow existing import patterns and code style
- Read the file first to understand the existing structure

PROMPT 3: Permission Guards (P3)

You are a senior React/TypeScript developer working on a Feuerwehr Dashboard.

TASK: Ensure features that require specific user groups are NOT VISIBLE to unauthorized users.

CONTEXT:
- usePermissions() hook at /frontend/src/hooks/usePermissions.ts returns { isAdmin, canChangeStatus, groups }
- Groups: dashboard_admin (full access), dashboard_fahrmeister (status + wartung)
- Backend already enforces permissions via requireGroups() middleware
- The goal is to also HIDE the UI elements so users don't see options they can't use

FILES TO CHECK AND MODIFY:

1. /Users/matthias/work/feuerwehr_dashboard/frontend/src/pages/Fahrzeuge.tsx
   - Verify: FAB "+" button only shows for isAdmin ✓ (already done)

2. /Users/matthias/work/feuerwehr_dashboard/frontend/src/pages/FahrzeugDetail.tsx
   - Verify: Edit button only shows for isAdmin ✓ (already done)
   - Verify: Status change controls only show for canChangeStatus
   - Verify: Wartungslog add form only shows for canChangeStatus
   - ADD: Delete button guard (if prompt P2 has been applied)

3. /Users/matthias/work/feuerwehr_dashboard/frontend/src/pages/FahrzeugForm.tsx
   - ADD: If a non-admin navigates directly to /fahrzeuge/neu or /fahrzeuge/:id/bearbeiten,
     show an "access denied" message or redirect to /fahrzeuge
   - Use usePermissions() to check isAdmin
   - Show a simple Card with "Keine Berechtigung" message and a back button

4. /Users/matthias/work/feuerwehr_dashboard/frontend/src/App.tsx
   - OPTIONAL: Consider wrapping admin-only routes with a permission-aware ProtectedRoute
   - If ProtectedRoute already supports group checks, use it
   - If not, the per-component check in FahrzeugForm.tsx is sufficient

5. Navigation / sidebar:
   - Check if there are navigation links that should be hidden for certain groups
   - If a sidebar/drawer component exists, check visibility of menu items

REQUIREMENTS:
- Read each file before modifying
- Do NOT change backend code
- Do NOT change the usePermissions hook signature (but you can add new computed properties)
- Use conditional rendering ({isAdmin && <Component />}) pattern consistently
- For route protection, prefer showing "Keine Berechtigung" over redirecting (less confusing UX)

PROMPT 4: Equipment Database Migration (P4)

You are a senior PostgreSQL developer working on a Feuerwehr Dashboard backend.

TASK: Create the database migration for the equipment management system.

CONTEXT:
- Existing migrations are at /Users/matthias/work/feuerwehr_dashboard/backend/src/database/migrations/
- Latest migration is 010_simplify_wartungslog_art.sql
- New migration should be 011_create_ausruestung.sql
- The database uses uuid_generate_v4() for PKs (uuid-ossp extension already enabled)
- Existing pattern: update_updated_at_column() trigger function already exists
- Vehicles table: fahrzeuge (with soft-delete via deleted_at)

CREATE FILE: /Users/matthias/work/feuerwehr_dashboard/backend/src/database/migrations/011_create_ausruestung.sql

SCHEMA:

1. TABLE ausruestung_kategorien:
   - id UUID PK DEFAULT uuid_generate_v4()
   - name VARCHAR(100) NOT NULL UNIQUE
   - kurzname VARCHAR(30) NOT NULL UNIQUE
   - sortierung INTEGER NOT NULL DEFAULT 0
   - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()

2. TABLE ausruestung:
   - id UUID PK DEFAULT uuid_generate_v4()
   - bezeichnung VARCHAR(200) NOT NULL
   - kategorie_id UUID NOT NULL FK → ausruestung_kategorien(id)
   - seriennummer VARCHAR(100)
   - inventarnummer VARCHAR(50)
   - hersteller VARCHAR(150)
   - baujahr INTEGER CHECK (1950-2100)
   - status VARCHAR(30) NOT NULL DEFAULT 'einsatzbereit' CHECK IN ('einsatzbereit','beschaedigt','in_wartung','ausser_dienst')
   - status_bemerkung TEXT
   - ist_wichtig BOOLEAN NOT NULL DEFAULT FALSE
   - fahrzeug_id UUID FK → fahrzeuge(id) ON DELETE SET NULL (nullable)
   - standort VARCHAR(150) NOT NULL DEFAULT 'Lager'
   - pruef_intervall_monate INTEGER CHECK > 0
   - letzte_pruefung_am DATE
   - naechste_pruefung_am DATE
   - bemerkung TEXT
   - deleted_at TIMESTAMPTZ
   - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
   - updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()

3. TABLE ausruestung_wartungslog:
   - id UUID PK DEFAULT uuid_generate_v4()
   - ausruestung_id UUID NOT NULL FK → ausruestung(id) ON DELETE CASCADE
   - datum DATE NOT NULL
   - art VARCHAR(30) NOT NULL CHECK IN ('Prüfung','Reparatur','Sonstiges')
   - beschreibung TEXT NOT NULL
   - ergebnis VARCHAR(30) CHECK IN ('bestanden','bestanden_mit_maengeln','nicht_bestanden')
   - kosten DECIMAL(8,2) CHECK >= 0
   - pruefende_stelle VARCHAR(150)
   - dokument_url VARCHAR(500)
   - erfasst_von UUID FK → users(id) ON DELETE SET NULL
   - created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()

4. VIEW ausruestung_mit_pruefstatus:
   - Join ausruestung + kategorien + fahrzeuge (LEFT JOIN)
   - Compute pruefung_tage_bis_faelligkeit
   - Filter WHERE deleted_at IS NULL

5. INDEXES: status, kategorie, fahrzeug, active (partial WHERE deleted_at IS NULL), pruefung, wichtig (partial composite)

6. TRIGGER: update_updated_at_column on ausruestung

7. SEED DATA for categories:
   Atemschutzgeräte (PA), Pumpen (Pumpe), Schläuche (SL), Leitern (Leiter),
   Rettungsgeräte (RG), Messgeräte (MG), PSA (PSA), Kommunikation (Funk),
   Beleuchtung (Licht), Sonstige (Sonst.)

Follow the exact SQL style of existing migrations (read 005_create_fahrzeuge.sql for reference).

PROMPT 5: Equipment Backend (P5)

You are a senior Node.js/TypeScript developer working on a Feuerwehr Dashboard backend.

TASK: Create the complete backend for equipment management (model, service, controller, routes).

CONTEXT:
- Express 5 + TypeScript + PostgreSQL (raw SQL via pg pool, no ORM)
- Read existing vehicle implementation as the pattern to follow:
  - Model: /backend/src/models/vehicle.model.ts
  - Service: /backend/src/services/vehicle.service.ts
  - Controller: /backend/src/controllers/vehicle.controller.ts
  - Routes: /backend/src/routes/vehicle.routes.ts
- Database tables: ausruestung, ausruestung_kategorien, ausruestung_wartungslog (created by migration 011)
- View: ausruestung_mit_pruefstatus

FILES TO CREATE:

1. /backend/src/models/equipment.model.ts
   - Enums: AusruestungStatus, AusruestungWartungslogArt
   - Interfaces: AusruestungKategorie, Ausruestung, AusruestungListItem, AusruestungDetail,
     AusruestungWartungslog, EquipmentStats, VehicleEquipmentWarning
   - DTOs: CreateAusruestungData, UpdateAusruestungData, CreateAusruestungWartungslogData

2. /backend/src/services/equipment.service.ts
   Methods:
   - getAllEquipment() → SELECT from view ausruestung_mit_pruefstatus
   - getEquipmentById(id) → detail + wartungslog
   - getEquipmentByVehicle(fahrzeugId) → equipment assigned to vehicle
   - getCategories() → all categories ordered by sortierung
   - createEquipment(data, createdBy) → INSERT into ausruestung
   - updateEquipment(id, data, updatedBy) → dynamic PATCH
   - deleteEquipment(id, deletedBy) → SET deleted_at
   - updateStatus(id, status, bemerkung, updatedBy)
   - addWartungslog(equipmentId, data, createdBy)
   - getEquipmentStats() → counts by status + inspection alerts
   - getVehicleWarnings() → important items not einsatzbereit, grouped by fahrzeug
   - getUpcomingInspections(daysAhead)

3. /backend/src/controllers/equipment.controller.ts
   - Zod validation schemas (CreateAusruestungSchema, UpdateAusruestungSchema, etc.)
   - Request handlers matching service methods
   - Standard { success: true, data: ... } response envelope
   - Error handling with try/catch and appropriate HTTP status codes

4. /backend/src/routes/equipment.routes.ts
   - GET / (list), GET /stats, GET /alerts, GET /categories
   - GET /vehicle-warnings, GET /vehicle/:fahrzeugId
   - GET /:id (detail)
   - POST / (create, requireGroups admin+fahrmeister)
   - PATCH /:id (update, requireGroups admin+fahrmeister)
   - PATCH /:id/status (status change, requireGroups admin+fahrmeister)
   - POST /:id/wartung (add log, requireGroups admin+fahrmeister)
   - DELETE /:id (soft-delete, requireGroups admin only)

5. MODIFY /backend/src/app.ts
   - Add: import equipmentRoutes from './routes/equipment.routes'
   - Add: app.use('/api/equipment', equipmentRoutes)

IMPORTANT:
- Follow EXACT patterns from vehicle implementation (pool.query, error handling, logging)
- Use the database pool import from existing config
- Use authenticate and requireGroups middleware from existing middleware
- Read the vehicle files first to understand all patterns before writing

PROMPT 6: Equipment Frontend Types + API Service (P6a)

You are a senior React/TypeScript developer working on a Feuerwehr Dashboard.

TASK: Create the frontend TypeScript types and API service for equipment management.

CONTEXT:
- Follow the exact patterns from vehicle types and service:
  - Types: /frontend/src/types/vehicle.types.ts
  - Service: /frontend/src/services/vehicles.ts
  - API client: /frontend/src/services/api.ts (Axios instance)

FILES TO CREATE:

1. /frontend/src/types/equipment.types.ts
   - AusruestungStatus enum (einsatzbereit, beschaedigt, in_wartung, ausser_dienst)
   - AusruestungKategorie interface
   - AusruestungListItem interface (from view, includes kategorie_name, fahrzeug_bezeichnung, pruefung_tage_bis_faelligkeit)
   - AusruestungDetail interface (extends with wartungslog array)
   - AusruestungWartungslog interface
   - EquipmentStats interface
   - VehicleEquipmentWarning interface
   - Create/Update payload types
   - All Date fields as string (JSON serialization)

2. /frontend/src/services/equipment.ts
   Methods (using api instance and unwrap pattern from vehicles.ts):
   - getAll() → AusruestungListItem[]
   - getById(id) → AusruestungDetail
   - getByVehicle(fahrzeugId) → AusruestungListItem[]
   - getCategories() → AusruestungKategorie[]
   - getStats() → EquipmentStats
   - getVehicleWarnings() → VehicleEquipmentWarning[]
   - getAlerts(daysAhead) → InspectionAlert-like array
   - create(payload) → AusruestungDetail
   - update(id, payload) → AusruestungDetail
   - delete(id) → void
   - updateStatus(id, payload) → void
   - addWartungslog(id, payload) → AusruestungWartungslog

Read the vehicle files first to match patterns exactly.

PROMPT 7: Equipment List Page (P6b)

You are a senior React/TypeScript developer building a Feuerwehr Dashboard with React 18 + MUI 5 + TypeScript.

TASK: Replace the placeholder Ausruestung.tsx with a full equipment list page.

CONTEXT:
- Existing placeholder: /frontend/src/pages/Ausruestung.tsx (just shows "coming soon")
- Follow the pattern of /frontend/src/pages/Fahrzeuge.tsx (vehicle list with cards, search, filters)
- API service: equipmentApi from /frontend/src/services/equipment.ts
- Types: from /frontend/src/types/equipment.types.ts
- Permissions: usePermissions() → { isAdmin, canChangeStatus, canManageEquipment }
- Layout: DashboardLayout wrapper
- Routing: clicking an item navigates to /ausruestung/:id

REPLACE FILE: /frontend/src/pages/Ausruestung.tsx

REQUIREMENTS:

1. Stats bar at top:
   - Total equipment count, einsatzbereit count, beschädigt count, prüfung fällig count
   - Use Chip or small stat cards

2. Filters:
   - Search field (search across bezeichnung, seriennummer, inventarnummer, hersteller)
   - Category dropdown (populated from equipmentApi.getCategories())
   - Status dropdown (all 4 statuses)
   - Checkbox: "Nur wichtige" (filter ist_wichtig)
   - Checkbox: "Prüfung fällig" (filter pruefung_tage_bis_faelligkeit <= 30)

3. Table/List view (use MUI Table or Card grid matching Fahrzeuge.tsx pattern):
   - Columns: Bezeichnung, Kategorie, Seriennr., Fahrzeug/Standort, Status (chip), Nächste Prüfung
   - Status chips with color coding:
     - einsatzbereit → success (green)
     - beschaedigt → error (red)
     - in_wartung → warning (orange)
     - ausser_dienst → default (grey)
   - Prüfung overdue → red text/chip
   - Click row → navigate to /ausruestung/:id
   - Important items: show star/flag icon

4. FAB button to add new equipment:
   - Only visible to canManageEquipment users
   - Navigates to /ausruestung/neu

5. Loading state with Skeleton/CircularProgress
6. Error state with retry button
7. Empty state message

Read Fahrzeuge.tsx first to match the exact code style, imports, and patterns.

PROMPT 8: Equipment Form Page (P7)

You are a senior React/TypeScript developer building a Feuerwehr Dashboard.

TASK: Create the equipment create/edit form page.

CONTEXT:
- Follow the exact pattern of /frontend/src/pages/FahrzeugForm.tsx
- Route: /ausruestung/neu (create) and /ausruestung/:id/bearbeiten (edit)
- API: equipmentApi from /frontend/src/services/equipment.ts
- Types from /frontend/src/types/equipment.types.ts
- Permission: only canManageEquipment users should see this

CREATE FILE: /frontend/src/pages/AusruestungForm.tsx

FORM FIELDS:
- bezeichnung (required, TextField)
- kategorie_id (required, Select dropdown from equipmentApi.getCategories())
- seriennummer (TextField)
- inventarnummer (TextField)
- hersteller (TextField)
- baujahr (number input, 1950-2100)
- status (Select: einsatzbereit, beschaedigt, in_wartung, ausser_dienst)
- status_bemerkung (TextField multiline)
- ist_wichtig (Checkbox/Switch with label "Wichtiges Gerät (Warnung auf Fahrzeugkarte)")
- fahrzeug_id (Select dropdown, populated from vehiclesApi.getAll(), show "Kein Fahrzeug" option)
- standort (TextField, shown when no fahrzeug_id selected)
- pruef_intervall_monate (number input)
- letzte_pruefung_am (date input)
- naechste_pruefung_am (date input)
- bemerkung (TextField multiline)

BEHAVIOR:
- Create mode: empty form, POST on submit
- Edit mode: fetch equipment by id, pre-populate all fields, PATCH on submit
- Permission check: show "Keine Berechtigung" if not canManageEquipment
- Navigate back to /ausruestung or /ausruestung/:id on success
- Validation: bezeichnung required, kategorie_id required
- Loading states during fetch and submit

Read FahrzeugForm.tsx first to match patterns exactly.

PROMPT 9: Equipment Detail Page (P8)

You are a senior React/TypeScript developer building a Feuerwehr Dashboard.

TASK: Create the equipment detail page with tabs.

CONTEXT:
- Follow the pattern of /frontend/src/pages/FahrzeugDetail.tsx
- Route: /ausruestung/:id
- API: equipmentApi from /frontend/src/services/equipment.ts

CREATE FILE: /frontend/src/pages/AusruestungDetail.tsx

STRUCTURE (2 tabs):

Tab 1 — Übersicht:
- Header with bezeichnung, edit button (canManageEquipment), delete button (isAdmin)
- Status panel with current status chip + change button (canManageEquipment)
- Status change dialog (select new status + bemerkung)
- Data grid showing: Kategorie, Seriennummer, Inventarnummer, Hersteller, Baujahr,
  Fahrzeug (link to /fahrzeuge/:fahrzeug_id), Standort, Wichtig flag,
  Prüfintervall, Letzte Prüfung, Nächste Prüfung (with days-until color coding)
- Delete confirmation dialog (same pattern as vehicle delete from P2)

Tab 2 — Wartung:
- Timeline/list of wartungslog entries
- Each entry shows: datum, art (chip), beschreibung, ergebnis (chip), kosten, pruefende_stelle
- Add wartungslog form (canManageEquipment only):
  - datum (date), art (select), beschreibung (text), ergebnis (select optional),
    kosten (number optional), pruefende_stelle (text optional)
- Sorted by date DESC

PATTERNS:
- Read FahrzeugDetail.tsx first and follow its exact structure
- Use same notification pattern for success/error
- Use useParams() for id, useNavigate() for navigation
- Loading/error states

PROMPT 10: Vehicle-Equipment Integration (P9 + P10)

You are a senior React/TypeScript developer working on a Feuerwehr Dashboard.

TASK: Integrate equipment into the vehicle pages.

CONTEXT:
- Equipment API: equipmentApi from /frontend/src/services/equipment.ts
- Equipment types from /frontend/src/types/equipment.types.ts
- Vehicle pages to modify:
  - /frontend/src/pages/FahrzeugDetail.tsx (add equipment tab)
  - /frontend/src/pages/Fahrzeuge.tsx (add warning badges on cards)

PART A — Vehicle Detail Equipment Tab:

MODIFY: /frontend/src/pages/FahrzeugDetail.tsx

1. Add a new tab "Ausrüstung" between existing tabs (after the overview/maintenance tabs)
2. Tab label: "Ausrüstung" with a count badge showing number of assigned items
3. Tab content:
   - Fetch equipment via equipmentApi.getByVehicle(vehicleId)
   - Show table/list of assigned equipment:
     - Bezeichnung, Kategorie, Status (chip), Nächste Prüfung
     - Status chips with color coding (green/red/orange/grey)
     - Important items marked with star icon
     - Click navigates to /ausruestung/:id
   - Empty state: "Keine Ausrüstung zugewiesen"
   - Link to /ausruestung page

PART B — Vehicle Card Warning Badges:

MODIFY: /frontend/src/pages/Fahrzeuge.tsx

1. On mount, fetch equipmentApi.getVehicleWarnings() alongside the vehicle list
2. Group warnings by fahrzeug_id into a Map
3. In VehicleCard component, below existing inspection badges:
   - If vehicle has warnings, show a Chip:
     - "1 Ausrüstung beschädigt" (red/error color) or
     - "2 Ausrüstung nicht bereit" (orange/warning color)
   - Tooltip showing individual item names
4. Only show if warnings array for that vehicle is non-empty

IMPORTANT:
- Read both files completely before making changes
- Don't break existing tab indexing in FahrzeugDetail
- Handle loading states gracefully (don't block vehicle loading)
- Equipment fetch failures should not break the vehicle page (catch errors silently)

PROMPT 11: Routing Updates (included in P6b-P9)

You are a senior React/TypeScript developer.

TASK: Add equipment routes to the React Router configuration.

MODIFY: /Users/matthias/work/feuerwehr_dashboard/frontend/src/App.tsx

ADD these routes inside the existing <Routes>, near the existing /ausruestung route:

- /ausruestung → Ausruestung (already exists, will use new component)
- /ausruestung/neu → AusruestungForm (new)
- /ausruestung/:id/bearbeiten → AusruestungForm (new)
- /ausruestung/:id → AusruestungDetail (new)

ALSO ADD:
- /ausruestung/neu BEFORE /ausruestung/:id (so "neu" isn't matched as an :id)

Add imports for AusruestungDetail and AusruestungForm at the top of the file.
Wrap each route in <ProtectedRoute> following the existing pattern.

ALSO MODIFY: /frontend/src/hooks/usePermissions.ts
- Add: canManageEquipment: groups.includes('dashboard_admin') || groups.includes('dashboard_fahrmeister')

Step-by-Step Instructions for Matthias

Phase 1: Vehicle Cleanup (no backend changes, no Docker needed)

Step 1.1 — Remove info fields

  • Run subagent with Prompt 1
  • Files changed: FahrzeugForm.tsx, FahrzeugDetail.tsx, Fahrzeuge.tsx
  • Test: Open vehicle form/detail in browser, verify fields are gone
  • Commit: feat: remove Fahrgestellnr, Standort, Besatzung, Typ, Hersteller, Baujahr from vehicle UI

Step 1.2 — Add delete vehicle UI

  • Run subagent with Prompt 2
  • Files changed: FahrzeugDetail.tsx
  • Test: Log in as admin, open vehicle detail, click delete, verify dialog, cancel, then test actual delete
  • Commit: feat: add delete button with confirmation dialog to vehicle detail page

Step 1.3 — Permission guards

  • Run subagent with Prompt 3
  • Files changed: FahrzeugDetail.tsx, FahrzeugForm.tsx, possibly App.tsx
  • Test: Log in as non-admin user, verify add/edit/delete buttons are hidden, try direct URL to /fahrzeuge/neu
  • Commit: feat: hide restricted vehicle features from unauthorized user groups

Phase 2: Equipment Backend (requires Docker/PostgreSQL for migration)

Step 2.1 — Create database migration

  • Run subagent with Prompt 4
  • File created: backend/src/database/migrations/011_create_ausruestung.sql
  • Action needed: Run migration against your PostgreSQL database
    # Connect to your database and run:
    psql -U <user> -d <database> -f backend/src/database/migrations/011_create_ausruestung.sql
    
  • Verify: Check that tables ausruestung, ausruestung_kategorien, ausruestung_wartungslog exist
  • Verify: Check that view ausruestung_mit_pruefstatus works
  • Verify: Check that seed categories were inserted
  • Commit: feat: add equipment management database schema (migration 011)

Step 2.2 — Create backend model + service + controller + routes

  • Run subagent with Prompt 5
  • Files created: equipment.model.ts, equipment.service.ts, equipment.controller.ts, equipment.routes.ts
  • File modified: app.ts (add route registration)
  • Test: Start backend, test endpoints with curl/Postman:
    # Get categories
    curl -H "Authorization: Bearer <token>" http://localhost:3000/api/equipment/categories
    # Create equipment
    curl -X POST -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
      -d '{"bezeichnung":"Test Gerät","kategorie_id":"<uuid>"}' \
      http://localhost:3000/api/equipment
    
  • Commit: feat: add equipment management backend (model, service, controller, routes)

Phase 3: Equipment Frontend (no backend changes)

Step 3.1 — Create frontend types + API service

  • Run subagent with Prompt 6
  • Files created: equipment.types.ts, equipment.ts (service)
  • Commit: feat: add equipment TypeScript types and API service

Step 3.2 — Create equipment list page

  • Run subagent with Prompt 7
  • File replaced: Ausruestung.tsx
  • Test: Navigate to /ausruestung, verify list loads (empty state initially)
  • Commit: feat: replace equipment placeholder with full list page

Step 3.3 — Create equipment form

  • Run subagent with Prompt 8
  • File created: AusruestungForm.tsx
  • Test: Navigate to /ausruestung/neu, fill form, submit
  • Commit: feat: add equipment create/edit form page

Step 3.4 — Create equipment detail page

  • Run subagent with Prompt 9
  • File created: AusruestungDetail.tsx
  • Test: Click on equipment in list, verify detail page with tabs
  • Commit: feat: add equipment detail page with tabs

Step 3.5 — Update routing + permissions

  • Run subagent with Prompt 11
  • Files modified: App.tsx, usePermissions.ts
  • Test: Navigate between equipment pages, verify routing works
  • Commit: feat: add equipment routes and canManageEquipment permission

Phase 4: Integration (modifies vehicle pages)

Step 4.1 — Vehicle-equipment integration

  • Run subagent with Prompt 10
  • Files modified: FahrzeugDetail.tsx, Fahrzeuge.tsx
  • Test:
    1. Assign equipment to a vehicle (via equipment form, set fahrzeug_id)
    2. Open vehicle detail → verify "Ausrüstung" tab shows the items
    3. Set an important equipment item to "beschädigt"
    4. Open vehicle list → verify warning badge appears on that vehicle's card
  • Commit: feat: add equipment tab to vehicle detail and warning badges to vehicle cards

Final Verification Checklist

  • Vehicle add works (admin only)
  • Vehicle edit works (admin only)
  • Vehicle delete works with confirmation (admin only)
  • Non-admin users cannot see add/edit/delete buttons
  • Non-admin users get "Keine Berechtigung" when accessing restricted routes directly
  • Removed fields (Fahrgestellnr, Standort, Besatzung, Typ, Hersteller, Baujahr) not visible
  • Equipment categories load correctly
  • Equipment CRUD works (create, read, update, soft-delete)
  • Equipment list page with filters works
  • Equipment detail page with tabs works
  • Equipment wartungslog can be added
  • Vehicle detail shows equipment tab with assigned items
  • Vehicle cards show warning badges for non-ready important equipment
  • Permission guards work: equipment management restricted to admin + fahrmeister