diff --git a/docs/component_inventory.md b/docs/component_inventory.md new file mode 100644 index 0000000..f145a42 --- /dev/null +++ b/docs/component_inventory.md @@ -0,0 +1,210 @@ +# HMS Licht & Ton – Component Inventory + +## Overview +All components are built as Vue 3 single-file components (template + setup). In production (Nuxt 3), these map to `.vue` files in `components/` and `pages/` directories. + +## Shared Components + +### HmsLogo +- **Purpose:** SVG logo with white HMS text + orange (#EC6925) rectangular border +- **Props:** `size` (Number, default: 40) +- **States:** Default only (static SVG) +- **Accessibility:** `aria-label`, `role="img"` + +### SpeakerIcon +- **Purpose:** SVG loudspeaker/light icon for equipment grid +- **Props:** `type` (String: linearray, subwoofer, monitor, pointsource, column, movinghead) +- **States:** Default (gray), Hover (orange via parent CSS) +- **Accessibility:** `aria-hidden="true"` + +### AppHeader +- **Purpose:** Sticky navigation header with logo, desktop nav, mobile hamburger menu +- **States:** Default, Active nav item (`aria-current="page"`), Mobile menu open/closed +- **Accessibility:** `role="banner"`, `aria-label`, `aria-expanded`, `aria-controls` + +### AppFooter +- **Purpose:** 4-column footer with company info, navigation, contact, legal links +- **States:** Default only +- **Accessibility:** `role="contentinfo"` + +### ServiceCard +- **Purpose:** Service offering card with icon, title, description +- **Props:** `icon`, `title`, `description` +- **States:** Default, Hover (border highlight) + +### EquipmentCard +- **Purpose:** Equipment listing card with image, badge, name, description, add-to-cart button +- **Props:** `item` (Object: id, code, name, category, description, brand, image) +- **Emits:** `click`, `add-to-cart` +- **States:** Default, Hover (border + shadow + lift), Focus (keyboard), No image (placeholder) +- **Accessibility:** `role="article"`, `tabindex="0"`, `aria-label` on add button + +### LoadingSkeleton +- **Purpose:** Shimmer animation skeleton cards for loading states +- **Props:** `count` (Number, default: 6) +- **States:** Shimmer animation (infinite) +- **Accessibility:** `aria-live="polite"`, `aria-busy="true"` + +### EmptyState +- **Purpose:** Empty state with icon, title, message, optional CTA button +- **Props:** `icon`, `title`, `message`, `actionLabel` +- **Emits:** `action` +- **States:** Default +- **Accessibility:** `role="status"` + +### ErrorState +- **Purpose:** Error state with alert icon, title, message, retry button +- **Props:** `title`, `message` +- **Emits:** `retry` +- **States:** Default +- **Accessibility:** `role="alert"` + +### LegalContentPage +- **Purpose:** Reusable layout for static legal content pages (Impressum, Datenschutz, AGB) +- **Props:** `title` (String), `content` (String/HTML) +- **States:** loading (skeleton), content (rendered) +- **Accessibility:** `role="article"`, semantic heading structure h1 > h2 > h3 + +## Page Components + +### HomePage (`#/`) +- **Sections:** Hero (bg image + overlay), Speaker Grid (6 types), Über uns (stats + image), 8 Service Cards, Mietkatalog CTA +- **States:** Default (all content static) + +### ReferenzenPage (`#/referenzen`) +- **States:** Loading (skeleton), Loaded (gallery grid), Error (retry), Empty (no category results) +- **Features:** Category filter chips (Alle, Open-Air, Indoor, Rigging, Event), 9 real HMS photos + +### KontaktPage (`#/kontakt`) +- **Sections:** Büro/Lager/Öffnungszeiten cards, 2 Ansprechpartner cards, Contact form +- **Form States:** Default, Validation errors (inline), Submitting (spinner), Submitted (success message) +- **Validation:** Name*, Email* (regex), Message*, Privacy checkbox* + +### MietkatalogPage (`#/mietkatalog`) +- **States:** Loading (skeleton), Loaded (equipment grid), Error (retry), Empty (no search results) +- **Features:** Search input, Category filter chips (7 categories), Sort dropdown (name/brand/code), Result count + +### EquipmentDetailPage (`#/mietkatalog/:id`) +- **States:** Loading (skeleton), Loaded (detail view), Error (not found) +- **Sections:** Image/placeholder, Specs table, Rental form (date range + quantity), Related items grid +- **Form:** Mietbeginn, Mietende, Anzahl (stepper), Add to cart button + +### WarenkorbPage (`#/warenkorb`) +- **States:** Empty (no items), Cart with items +- **Features:** Item list with quantity steppers, remove buttons, rental dates display, CTA button → /mietanfrage +- **Note:** Warenkorb verwaltet nur Items (anzeigen, Mengen steuern, entfernen). Kein Formular für Kontaktdaten hier. +- **Accessibility:** Cart summary with live-updating totals, `aria-live="polite"` + +### MietanfragePage (`#/mietanfrage`) +- **Purpose:** Mietanfrage-Formular mit Veranstaltungsdaten + Kontaktdaten + Nachricht. Submit sendet an Rentman via POST /projectrequests + POST /projectrequests/{id}/projectrequestequipment. +- **Sections:** + 1. **Warenkorb-Übersicht:** Zeigt ausgewählte Artikel mit Mengen (read-only), Link zurück zum Warenkorb + 2. **Veranstaltungsdaten:** Name*, Datum von*, Datum bis*, Ort*, Anzahl Personen + 3. **Kontaktdaten:** Name*, Firma, E-Mail*, Telefon, Adresse (Straße, PLZ, Ort) + 4. **Nachricht:** Freitext-Nachrichtfeld + 5. **Submit:** "Mietanfrage absenden" Button +- **States:** + - `form` – Default, Formular leer oder mit Validierungs-Hinweisen + - `validation-error` – Inline-Validierungsfehler (Pflichtfelder markiert, Fehlermeldungen unter den Feldern) + - `submitting` – Spinner im Submit-Button, Felder disabled + - `success` – Erfolgsmeldung mit Referenz-Nummer, Weiter-Button ("Zurück zur Startseite") + - `error` – Fehlermeldung mit Retry-Button ("Erneut versuchen") +- **Empty Cart Handling:** Wenn Warenkorb leer → Hinweis "Keine Artikel ausgewählt" + Link zum Mietkatalog +- **Validation:** + - Veranstaltungsname: Pflicht, min 3 Zeichen + - Datum von/bis: Pflicht, von ≤ bis + - Ort: Pflicht + - Name: Pflicht, min 2 Zeichen + - E-Mail: Pflicht, Regex-Validierung + - Telefon: Optional, Format-Validierung + - Adresse: Optional (aber wenn PLZ angegeben, auch Ort erforderlich) +- **Accessibility:** `role="form"`, `aria-invalid` on error fields, `aria-describedby` for error messages, fieldset/legend for grouped sections + +### AdminPage (`#/admin`) +- **Purpose:** Admin-Login und post-login Dashboard mit Equipment-Sync-Trigger +- **States:** + - `login` – Login-Form (Username + Password), Fehler bei falschen Daten + - `idle` – Post-Login Dashboard, Sync-Status sichtbar, Sync-Button bereit + - `syncing` – Sync läuft, Progress-Indicator, Sync-Log live aktualisiert + - `sync-complete` – Sync abgeschlossen, Status aktualisiert (last sync date, article count), Erfolgsmeldung + - `sync-error` – Sync fehlgeschlagen, Fehlermeldung + Retry-Button, Sync-Log zeigt Fehlerdetails +- **Elements:** + - **Sync-Trigger Button:** "Equipment-Sync starten" (Primary, disabled während syncing) + - **Sync-Status:** Last sync date, article count, Sync-Status-Badge (idle/syncing/complete/error) + - **Sync-Log Viewer:** Scrollbarer Log-Bereich mit Zeitstempeln, kollabierbar +- **Login-Form States:** Default, Validation error (invalid credentials), Loading (spinner) +- **Accessibility:** `role="main"`, `aria-live="polite"` für Sync-Status, `aria-busy` während syncing + +### ImpressumPage (`#/impressum`) +- **Purpose:** Statische Impressum-Seite mit rechtlichen Angaben gemäß §5 TMG +- **Route:** `/impressum` +- **Content:** Firmenname, Vertretungsberechtigte, Anschrift, Kontakt, USt-IdNr., Verantwortlich für Inhalte +- **States:** loading (skeleton), content (rendered) +- **Component:** Verwendet `LegalContentPage` Layout +- **Accessibility:** Semantic heading structure h1 > h2 + +### DatenschutzPage (`#/datenschutz`) +- **Purpose:** Statische Datenschutz-Seite mit DSGVO-konformer Datenschutzerklärung +- **Route:** `/datenschutz` +- **Content:** Server-Logfiles, Cookies, Kontaktformular-Datenverarbeitung, Rentman-Datenverarbeitung, Nutzerrechte +- **States:** loading (skeleton), content (rendered) +- **Component:** Verwendet `LegalContentPage` Layout +- **Accessibility:** Semantic heading structure h1 > h2 > h3 + +### AGBVermietungPage (`#/agb-vermietung`) +- **Purpose:** Statische AGB-Seite für Vermietung +- **Route:** `/agb-vermietung` +- **Content:** Allgemeine Geschäftsbedingungen für Vermietung (vom User bereitgestellter Text) +- **States:** loading (skeleton), content (rendered) +- **Component:** Verwendet `LegalContentPage` Layout +- **Accessibility:** Semantic heading structure h1 > h2 + +### NotFoundPage (`#/404`) +- **States:** Default (404 message + home link) + +## UX State Coverage Summary + +| Page | Loading | Empty | Error | Partial | Success | Additional States | +|------|---------|-------|-------|---------|---------|------------------| +| Home | – | – | – | – | – | – | +| Referenzen | Skeleton | No category | Retry | Filtered | – | – | +| Kontakt | – | – | – | – | Form sent | validation-error | +| Mietkatalog | Skeleton | No results | Retry | Filtered | – | – | +| Detail | Skeleton | – | Not found | – | – | – | +| Warenkorb | – | Empty cart | – | – | – | – | +| Mietanfrage | – | Empty cart | Retry | – | Request sent | form, submitting, validation-error | +| Admin | Spinner | – | Login error | – | Logged in | idle, syncing, sync-complete, sync-error | +| Impressum | Skeleton | – | – | – | – | content | +| Datenschutz | Skeleton | – | – | – | – | content | +| AGB Vermietung | Skeleton | – | – | – | – | content | +| 404 | – | – | – | – | – | – | + +## Component States Documentation + +### Button States +- **Default:** `background: var(--color-accent); color: white` +- **Hover:** `background: var(--color-accent-hover)` +- **Active:** `transform: translateY(0)` +- **Disabled:** `background: var(--secondary); cursor: not-allowed` +- **Focus-visible:** `outline: 2px solid var(--color-accent); outline-offset: 2px` + +### Input States +- **Default:** `border: 1px solid var(--border-strong); background: var(--surface)` +- **Focus:** `border-color: var(--color-accent); box-shadow: 0 0 0 2px rgba(236,105,37,0.1)` +- **Error:** `border-color: var(--color-error)` +- **Disabled:** `background: var(--row); color: var(--secondary); cursor: not-allowed` + +### Card States +- **Default:** `border: 1px solid var(--border); box-shadow: var(--shadow-sm)` +- **Hover:** `border-color: var(--secondary); box-shadow: var(--shadow-lg)` + +### Chip States +- **Default:** `border: 1px solid var(--border-strong); background: var(--surface)` +- **Hover:** `border-color: var(--secondary)` +- **Active:** `background: var(--color-accent); color: white; border-color: var(--color-accent)` + +### Sync-Status Badge States (AdminPage) +- **Idle:** `background: var(--surface); color: var(--text-muted)` +- **Syncing:** `background: rgba(96, 165, 250, 0.08); color: var(--color-info); animation: pulse` +- **Complete:** `background: var(--color-success-bg); color: var(--color-success)` +- **Error:** `background: var(--color-error-bg); color: var(--color-error)`