feat: personal equipment tracking, order assignment, purge fix, widget consolidation

- Migration 084: new persoenliche_ausruestung table with catalog link, user
  assignment, soft delete; adds zuweisung_typ/ausruestung_id/persoenlich_id
  columns to ausruestung_anfrage_positionen; seeds feature group + 5 permissions

- Fix user data purge: table was shop_anfragen, renamed to ausruestung_anfragen
  in mig 046 — caused full transaction rollback. Also keep mitglieder_profile
  row but NULL FDISK-synced fields (dienstgrad, geburtsdatum, etc.) instead of
  deleting the profile

- Personal equipment CRUD: backend service/controller/routes at
  /api/persoenliche-ausruestung; frontend page with DataTable, user filter,
  catalog Autocomplete, FAB create dialog; widget in Status group; sidebar
  entry (Checkroom icon); card in MitgliedDetail Tab 0

- Ausruestungsanfrage item assignment: when a request reaches erledigt,
  auto-opens ItemAssignmentDialog listing all delivered positions; each item
  can be assigned as general equipment (vehicle/storage), personal item (user,
  prefilled with requester), or not tracked; POST /requests/:id/assign backend

- StatCard refactored to use WidgetCard as outer shell for consistent header
  styling across all dashboard widget templates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Matthias Hochmeister
2026-04-13 19:19:35 +02:00
parent b477e5dbe0
commit 1215e9ea70
23 changed files with 1700 additions and 63 deletions

View File

@@ -111,6 +111,9 @@ export interface AusruestungAnfragePosition {
eigenschaften?: AusruestungPositionEigenschaft[];
ist_ersatz: boolean;
altes_geraet_zurueckgegeben: boolean;
zuweisung_typ?: 'ausruestung' | 'persoenlich' | 'keine' | null;
zuweisung_ausruestung_id?: string | null;
zuweisung_persoenlich_id?: string | null;
}
export interface AusruestungAnfrageFormItem {

View File

@@ -0,0 +1,65 @@
// Personal Equipment (Persönliche Ausrüstung) — Frontend Types
export type PersoenlicheAusruestungZustand = 'gut' | 'beschaedigt' | 'abgaengig' | 'verloren';
export const ZUSTAND_LABELS: Record<PersoenlicheAusruestungZustand, string> = {
gut: 'Gut',
beschaedigt: 'Beschädigt',
abgaengig: 'Abgängig',
verloren: 'Verloren',
};
export const ZUSTAND_COLORS: Record<PersoenlicheAusruestungZustand, 'success' | 'warning' | 'error' | 'default'> = {
gut: 'success',
beschaedigt: 'warning',
abgaengig: 'error',
verloren: 'default',
};
export interface PersoenlicheAusruestung {
id: string;
bezeichnung: string;
kategorie?: string;
artikel_id?: number;
artikel_bezeichnung?: string;
user_id?: string;
user_display_name?: string;
benutzer_name?: string;
groesse?: string;
seriennummer?: string;
inventarnummer?: string;
anschaffung_datum?: string;
zustand: PersoenlicheAusruestungZustand;
notizen?: string;
anfrage_id?: number;
erstellt_am: string;
aktualisiert_am: string;
}
export interface CreatePersoenlicheAusruestungPayload {
bezeichnung: string;
kategorie?: string;
artikel_id?: number;
user_id?: string;
benutzer_name?: string;
groesse?: string;
seriennummer?: string;
inventarnummer?: string;
anschaffung_datum?: string;
zustand?: PersoenlicheAusruestungZustand;
notizen?: string;
}
export interface UpdatePersoenlicheAusruestungPayload {
bezeichnung?: string;
kategorie?: string | null;
artikel_id?: number | null;
user_id?: string | null;
benutzer_name?: string | null;
groesse?: string | null;
seriennummer?: string | null;
inventarnummer?: string | null;
anschaffung_datum?: string | null;
zustand?: PersoenlicheAusruestungZustand;
notizen?: string | null;
}