Files
AkeshanandClaude Opus 4.8 d8b6772148 Interim-UI-Handover: 55 Vorschau-Screens fuer David (AD-28)
Uebergabe der Dev-Interim-Screens aus HiveGrid-Cloud an den Wireframes-Vault.
INDEX + 7 Domaenen-Seiten (CRM, Leads, HR, Files/Tasks, Audit/Notifications,
Integrations, Auth/RBAC) mit je Screen: D-*-Ziel, Archetyp, Route, VM,
benoetigte ~/ui-Elemente, Actions, States. Layout bleibt Davids freie Hand.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 17:35:12 +02:00

280 lines
22 KiB
Markdown

# P17 · HR-Hub (AK-P17a/b → D-HR)
> Interim-UI-Referenz (AD-28) für den HR-Hub. Alle Screens rein präsentational (VM als Prop, Route besitzt
> Loader/Fetcher/Actions). **Layout = Davids freie Hand** — unten nur ELEMENTE/DATEN/ACTIONS. `~/ui` ist das
> gesetzte Baukasten-Inventar; `data-ui-id`-Marker existieren für Interim-Snapshots. Route-URLs: App-Host,
> Ordner `routes/app.$tenant/` → `/{tenant}/hr/…`.
### HrHubPage · D-HR „HR-Hub-Start" (A1/A6)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrHubPage.tsx`
- **Route(n):** `/{tenant}/hr` (`routes/app.$tenant/hr._index.tsx`)
- **Daten (VM):** kein `@hivegrid`-VM — lokale Props `moduleActive: boolean`, `links: HrHubLink[]` (`{ labelKey, href }`); Loader gated Connector+Permission und liefert die Quick-Link-Liste
- **Benötigte UI-Elemente (KEIN Layout):** Card, EmptyState · Icons: FeatherArrowRight, FeatherLock
- **Interaktionen (Actions):** keine Callbacks — Links sind `<a href>` (Navigations-Kacheln)
- **States:** Locked (`!moduleActive` → EmptyState mit Lock) / Empty (leere Link-Liste rendert leere Grid) / Permission-Denied via Loader-Gate (404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte:
- [ ] Neue UI/UX-Entscheidungen (Hub-Widgets Ankündigungen/Pflichten/Geburtstage kommen laut Header erst als Folge-Nachzug):
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrDirectoryPage · D-HR „MA-Verzeichnis" (A2)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrDirectoryPage.tsx`
- **Route(n):** `/{tenant}/hr/directory` (`routes/app.$tenant/hr.directory._index.tsx`)
- **Daten (VM):** `HrDirectoryVM` — `packages/view-models/src/hr/hr-directory.vm.ts` (nur nicht-sensible Anzeigefelder; Notfallkontakt NUR im Detail)
- **Benötigte UI-Elemente (KEIN Layout):** Avatar, EmptyState, IconButton, Table · Icons: FeatherMail
- **Interaktionen (Actions):** keine Action-Callbacks — Name-Link → Detail, `mailto:`-IconButton
- **States:** Empty (`rows.length===0` → EmptyState) / Loading (`status!=="ready"` → leeres Div) / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte:
- [ ] Neue UI/UX-Entscheidungen:
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrDirectoryDetailPage · D-HR „MA-Detail" (A3)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrDirectoryDetailPage.tsx`
- **Route(n):** `/{tenant}/hr/directory/{userId}` (`routes/app.$tenant/hr.directory.$userId.tsx`)
- **Daten (VM):** kein VM-Typ-Import — flache Props (`name`, `email`, `position`, `team`, `isSelf`, `canRevealEmergency`, `canEditEmergency`, `hasEmergencyContact`, `revealedContact`, `revealing`, `saving`, `savingEmergency`); Domain in `hr-directory.vm.ts`
- **Benötigte UI-Elemente (KEIN Layout):** Breadcrumbs, Button, Card, Dialog, TextInput · Icons: —
- **Interaktionen (Actions):** `onSaveProfile({position,team})`, `onRevealEmergency()`, `onSetEmergency({name,phone,relation})`
- **States:** Self-Edit vs. Read-only (`isSelf`) / Emergency: kein Kontakt / gespeichert-nicht-enthüllbar / enthüllt / Reveal-Button / Loading via `revealing`/`saving`/`savingEmergency` / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte (On-Demand-Reveal Notfallkontakt, serverseitig auditiert):
- [ ] Neue UI/UX-Entscheidungen:
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrLeavePage · D-HR „Urlaub" (A2 + A4/A5)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrLeavePage.tsx`
- **Route(n):** `/{tenant}/hr/leave` (`routes/app.$tenant/hr.leave._index.tsx`), URL-Tabs `?tab=own|approvals|team`
- **Daten (VM):** `HrLeaveOwnVM`, `HrLeaveApproverVM`, `HrVacationRequestKind` — `packages/view-models/src/hr/hr-leave.vm.ts`; plus lokaler `HrLeaveTeamAbsence`
- **Benötigte UI-Elemente (KEIN Layout):** Alert, BadgeFeedback, Button, Card, Dialog, EmptyState, Select, Tabs, TextArea, TextInput, `DateField` (tasks) · Icons: FeatherPlus
- **Interaktionen (Actions):** `onSubmit({requestKind,approverUserId,startDate,endDate,note})`, `onWithdraw(requestId)`, `onReply(requestId,reply)`, `onApprove(requestId)`, `onReject(requestId)`, `onClarify(requestId,question)`
- **States:** Empty je Tab (own/approvals/team → EmptyState) / Loading (`status!=="ready"` → null) / Konflikt-Warnung (Alert bei `hasConflict`) / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte (Overlap-Konflikt als Inline-Warnung):
- [ ] Neue UI/UX-Entscheidungen (Approver-Auswahl aktuell freies TextInput = userId):
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrSickPage · D-HR „Krank" (A2 + A4)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrSickPage.tsx`
- **Route(n):** `/{tenant}/hr/sick` (`routes/app.$tenant/hr.sick._index.tsx`), Tabs `own|office`
- **Daten (VM):** `HrSickLeaveOwnVM`, `HrSickLeaveOfficeVM`, `HrSickLeaveRow` — `packages/view-models/src/hr/hr-sickleave.vm.ts`
- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, Dialog, EmptyState, Tabs, TextInput, `DateField` (tasks) · Icons: FeatherPlus
- **Interaktionen (Actions):** `onReport({startDate,expectedReturnDate})`, `onReportFor({employeeUserId,startDate})`, `onAttach(reportId,certificateFileId)`, `onRevealCertificate(reportId)`, `onClose(reportId,returnDate)`
- **States:** Empty je Tab (own/office → EmptyState) / AU-Zertifikat nie gerendert, nur Reveal-Action / Loading (Rows via `?? []`) / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte (Zertifikat sensibel, reveal-on-demand):
- [ ] Neue UI/UX-Entscheidungen (Attach = File-ID-TextInput; echte Datei-Auswahl offen):
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrRequestsPage · D-HR „Anträge" (A2 + A4/A5)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrRequestsPage.tsx`
- **Route(n):** `/{tenant}/hr/requests` (`routes/app.$tenant/hr.requests._index.tsx`), Tabs `own|approvals`
- **Daten (VM):** `HrRequestOwnVM`, `HrRequestApproverVM`, `HrRequestKind` — `packages/view-models/src/hr/hr-request.vm.ts`; plus lokaler `HrRequestTemplateOption`
- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, Dialog, EmptyState, Select, Tabs, TextArea, TextInput · Icons: FeatherPlus
- **Interaktionen (Actions):** `onSubmit({templateId,requestKind,approverUserId,title,details})`, `onWithdraw(requestId)`, `onApprove(requestId)`, `onReject(requestId)`, `onClarify(requestId,question)`
- **States:** Empty je Tab (own/approvals → EmptyState) / Loading (Rows via `?? []`) / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte (optionaler Vorlagen-Prefill):
- [ ] Neue UI/UX-Entscheidungen:
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrPayslipsPage · D-HR „Lohnabrechnungen" (A2)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrPayslipsPage.tsx`
- **Route(n):** `/{tenant}/hr/payslips` (`routes/app.$tenant/hr.payslips._index.tsx`)
- **Daten (VM):** `HrPayslipOwnVM` — `packages/view-models/src/hr/hr-payslip.vm.ts`
- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, EmptyState, Table · Icons: FeatherExternalLink
- **Interaktionen (Actions):** `onOpen(payslipId)` (Öffnen serverseitig auditiert, Route öffnet File)
- **States:** Empty (`rows.length===0` → EmptyState) / Loading (`status!=="ready"` → leeres Div) / Unread-Badge / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte:
- [ ] Neue UI/UX-Entscheidungen:
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrMyDocumentsPage · D-HR „Eigene Dokumente" (A2)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrMyDocumentsPage.tsx`
- **Route(n):** `/{tenant}/hr/my-documents` (`routes/app.$tenant/hr.my-documents.tsx`)
- **Daten (VM):** `HrDocumentListVM`, `HrDocumentKind` — `packages/view-models/src/hr/hr-document.vm.ts`; plus injizierter `upload: HrDocumentUploadState` (idle/uploading/done/error)
- **Benötigte UI-Elemente (KEIN Layout):** Alert, BadgeFeedback, Button, Dialog, EmptyState, Select, Table, TextInput; native `input[type=date]` + `input[type=file]` (bewusste Ausnahme) · Icons: FeatherUpload
- **Interaktionen (Actions):** `onArchive(documentId)`, `onUpload({file,title,documentKind,validUntilIso})`, `onResetUpload()`
- **States:** Empty (`rows.length===0` → EmptyState) / No-Results (Kind-Filter leer → EmptyState `no-results`) / Loading (`status!=="ready"` → leeres Div) / Upload uploading/error (Alert) / Read-only-Badge (Office-Docs) / Expired-Badge / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte (Kind-Filter client-seitig, Office-Docs read-only):
- [ ] Neue UI/UX-Entscheidungen (Upload-Progress-Darstellung):
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrAnnouncementsFeedPage · D-HR „Ankündigungen" (A2)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrAnnouncementsFeedPage.tsx`
- **Route(n):** `/{tenant}/hr/announcements` (`routes/app.$tenant/hr.announcements._index.tsx`), Filter `?unread=1`
- **Daten (VM):** `HrAnnouncementFeedVM` — `packages/view-models/src/hr/hr-announcement.vm.ts` (pinned first)
- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Card, EmptyState, LinkButton, Tabs · Icons: FeatherPin
- **Interaktionen (Actions):** keine Callbacks — Zeilen-Link → Detail, „Verwalten"-Link (`manageHref`, bei `canManage`)
- **States:** Empty (`rows.length===0` → EmptyState) / Loading (`status!=="ready"` → leeres Div) / Ack-Required-Badge / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte (pinned zuerst, Unread-Filter):
- [ ] Neue UI/UX-Entscheidungen:
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrAnnouncementDetailPage · D-HR „Ankündigung Detail" (A3)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrAnnouncementDetailPage.tsx`
- **Route(n):** `/{tenant}/hr/announcements/{id}` (`routes/app.$tenant/hr.announcements.$id.tsx`)
- **Daten (VM):** `HrAnnouncementDetailVM` — `packages/view-models/src/hr/hr-announcement.vm.ts` (Markdown-Body, Reaktionen, Kommentare 1 Ebene, Read-Receipts)
- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, Table, TextInput · Icons: — (Emoji-Reaktionen 👍❤️🎉👀 als Literale)
- **Interaktionen (Actions):** `onAck()`, `onReact(reaction)`, `onComment(body)`
- **States:** Loading (`status!=="ready"` → leeres Div) / Kommentare-Empty (`commentEmptyKey`) / Ack-Zustand acknowledged vs. Button / Read-Receipt-Tabelle nur bei `canManage` / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte (Markdown wird aktuell `whitespace-pre-wrap` roh gerendert):
- [ ] Neue UI/UX-Entscheidungen (Reaktions-Set fix vier Emojis):
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrLocationsPage · D-HR „Standorte" (A2 + A5)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrLocationsPage.tsx`
- **Route(n):** `/{tenant}/hr/locations` (`routes/app.$tenant/hr.locations._index.tsx`)
- **Daten (VM):** `HrLocationListVM` — `packages/view-models/src/hr/hr-location.vm.ts`
- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Checkbox, Dialog, EmptyState, Table, TextInput · Icons: FeatherPlus
- **Interaktionen (Actions):** `onCreate({name,city,isDefault})`, `onSetDefault(locationId)`, `onArchive(locationId)`
- **States:** Empty (`rows.length===0` → EmptyState) / Loading (`status!=="ready"` → leeres Div) / Default-Badge / Aktionen nur bei `canManage` / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte:
- [ ] Neue UI/UX-Entscheidungen:
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrPasswordSafeListPage · D-HR „Passwort-Safe" (A2 + A5)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrPasswordSafeListPage.tsx`
- **Route(n):** `/{tenant}/hr/password-safe` (`routes/app.$tenant/hr.password-safe._index.tsx`)
- **Daten (VM):** `VaultListVM` — `packages/view-models/src/hr/hr-password-safe.vm.ts` (nur zugriffsberechtigte Vaults, keine Secrets)
- **Benötigte UI-Elemente (KEIN Layout):** Button, Card, Dialog, EmptyState, TextInput · Icons: FeatherPlus
- **Interaktionen (Actions):** `onCreateVault({name,description})`
- **States:** Empty (`rows.length===0` → EmptyState) / Loading (`status!=="ready"` → leeres Div) / Create nur bei `canCreate` / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte:
- [ ] Neue UI/UX-Entscheidungen:
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrVaultDetailPage · D-HR „Passwort-Safe Detail" (A3 + reveal)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrVaultDetailPage.tsx`
- **Route(n):** `/{tenant}/hr/password-safe/{vaultId}` (`routes/app.$tenant/hr.password-safe.$vaultId._index.tsx`); Action-only: `…$vaultId.reveal.tsx`, `…$vaultId.export.tsx`
- **Daten (VM):** `VaultDetailVM`, `EntryRow` — `packages/view-models/src/hr/hr-password-safe.vm.ts` (nur `hasUsername/hasPassword/hasNotes`-Flags, `copyAutoClearMs`)
- **Benötigte UI-Elemente (KEIN Layout):** Breadcrumbs, Button, Card, CopyToClipboardButton, Dialog, EmptyState, LinkButton, Select, Tag_Label, TextInput · Icons: FeatherPlus
- **Interaktionen (Actions):** `onReveal(entryId,field): Promise<string|null>`, `onCreateEntry({title,url,username,password,notes})`, `onArchiveEntry(entryId)`, `onGrantAccess({targetUserId,permissionLevel})`
- **States:** Empty (`rows.length===0` → EmptyState) / Loading (`status!=="ready"` → leeres Div) / Secret pro Feld reveal-on-demand → copy → Clipboard-Wipe nach 30s (DoD #3) / Aktionen gated via `canWrite`/`canManageAccess` / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte (SECURITY: Werte nur per audited reveal, Clipboard-Auto-Clear):
- [ ] Neue UI/UX-Entscheidungen (Copy-Cleared-Hinweis, Zugriff via userId-TextInput):
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrSurveysListPage · D-HR „Umfragen" (A2 + A5)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrSurveysListPage.tsx`
- **Route(n):** `/{tenant}/hr/surveys` (`routes/app.$tenant/hr.surveys._index.tsx`)
- **Daten (VM):** `HrSurveyListVM` — `packages/view-models/src/hr/hr-survey.vm.ts`
- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, Checkbox, Dialog, EmptyState, LinkButton, Select, TextInput · Icons: FeatherPlus
- **Interaktionen (Actions):** `onCreate({title,description,isAnonymous,resultVisibility})`, `onSetStatus(surveyId,"open"|"close")`, `onArchive(surveyId)`
- **States:** Empty (`rows.length===0` → EmptyState) / Loading (Rows via `?? []`) / Status-/Anonymous-Badges / Teilnehmen/Ergebnisse/Edit gated (`hasSubmitted`,`canViewResults`,`canManage`) / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte:
- [ ] Neue UI/UX-Entscheidungen:
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrSurveyParticipationPage · D-HR „Umfrage Teilnahme" (A3/A4)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrSurveyParticipationPage.tsx`
- **Route(n):** `/{tenant}/hr/surveys/{id}` (`routes/app.$tenant/hr.surveys.$id.tsx`)
- **Daten (VM):** `HrSurveyParticipationVM`, `HrSurveyItem`, `HrSurveyItemType` — `packages/view-models/src/hr/hr-survey.vm.ts`
- **Benötigte UI-Elemente (KEIN Layout):** Alert, Button, Card, Checkbox, Dialog, Select, TextInput; native `input[type=date]` (sanktionierte Ausnahme) · Icons: FeatherPlus
- **Interaktionen (Actions):** `onSubmit(answers: Record<string,string>)`, `onAddItem({itemType,prompt,options,required})`, `onRemoveItem(itemId)`
- **States:** Loading (`status!=="ready"` → leeres Div) / Anonymous-Hinweis (Alert) / bereits abgegeben (Alert success) / offen → Formular / geschlossen (Alert neutral) / Item-Editor nur bei `canManage` / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte (Item-Typen text/rating_5/nps/choice/boolean/date):
- [ ] Neue UI/UX-Entscheidungen (Antwort-Feld-Rendering je Typ):
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrSurveyResultsPage · D-HR „Umfrage Ergebnisse" (A3)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrSurveyResultsPage.tsx`
- **Route(n):** `/{tenant}/hr/surveys/{id}/results` (`routes/app.$tenant/hr.surveys.$id.results.tsx`)
- **Daten (VM):** `HrSurveyResultsVM` — `packages/view-models/src/hr/hr-survey.vm.ts` (Verteilung + Durchschnitt + Freitext)
- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Card, EmptyState, Progress · Icons: —
- **Interaktionen (Actions):** keine (read-only)
- **States:** Empty (`submissionCount===0` → EmptyState) / Loading (`status!=="ready"` → leeres Div) / Anonymous-Badge / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte (Interim nutzt Progress-Balken; „richer charts" = expliziter Nachzug lt. Header):
- [ ] Neue UI/UX-Entscheidungen:
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrAdminAnnouncementsPage · D-HR „Ankündigungen verwalten" (A2 + A4/A5)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrAdminAnnouncementsPage.tsx`
- **Route(n):** `/{tenant}/hr/admin/announcements` (`routes/app.$tenant/hr.admin.announcements._index.tsx`)
- **Daten (VM):** kein `@hivegrid`-VM — lokale Props `rows: AdminAnnouncementRow[]`, `audiences: string[]`
- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, Checkbox, Dialog, EmptyState, Select, TextArea, TextInput, `DateField` (tasks) · Icons: FeatherPin, FeatherPlus
- **Interaktionen (Actions):** `onCreate({title,body,audienceKind,audienceRef,requiresAck,pinned,publishAt,expiresAt})`, `onArchive(announcementId)`
- **States:** Empty (`rows.length===0` → EmptyState) / Loading (—, Rows als Prop) / Ack-/Pin-/Audience-Badges / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte (Audience, Ack, Pin, Schedule/Expiry):
- [ ] Neue UI/UX-Entscheidungen:
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrAdminPasswordSafePage · D-HR „Passwort-Safe-Verwaltung" (A2/A6)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrAdminPasswordSafePage.tsx`
- **Route(n):** `/{tenant}/hr/admin/password-safe` (`routes/app.$tenant/hr.admin.password-safe._index.tsx`)
- **Daten (VM):** `AdminVaultListVM` — `packages/view-models/src/hr/hr-password-safe.vm.ts` (alle Tenant-Vaults, Entry-/Access-Counts, NIE Inhalte)
- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, EmptyState, Table · Icons: —
- **Interaktionen (Actions):** `onArchiveVault(vaultId)`
- **States:** Empty (`rows.length===0` → EmptyState) / Loading (`status!=="ready"` → leeres Div) / Active/Archived-Badge / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte (Oversight ohne Content-Zugriff):
- [ ] Neue UI/UX-Entscheidungen:
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrAdminPayslipsPage · D-HR „Lohnabrechnungen verwalten" (A2 + A4)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrAdminPayslipsPage.tsx`
- **Route(n):** `/{tenant}/hr/admin/payslips` (`routes/app.$tenant/hr.admin.payslips._index.tsx`)
- **Daten (VM):** kein `@hivegrid`-VM — lokale Props `rows: PayslipAdminRow[]`, `preview: PayslipPreviewRow[]` (confidence hard/soft/none), `period: {year,month}`
- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, EmptyState, Table, TextArea, TextInput · Icons: —
- **Interaktionen (Actions):** `onMatch({periodYear,periodMonth,files})`, `onConfirm({fileId,employeeUserId,periodYear,periodMonth})`
- **States:** Empty (existing `rows.length===0` → EmptyState) / Preview-Block nur bei `preview.length>0` / Loading (Props) / Confidence-Badges / Read/Unread-Badge / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte (Bulk-Auto-Match-Preview + Per-Row-Confirm):
- [ ] Neue UI/UX-Entscheidungen (Files-Input als Freitext-Hint, Assignee als userId-TextInput):
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrAdminRequestTemplatesPage · D-HR „Antragsvorlagen" (A2 + A5)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrAdminRequestTemplatesPage.tsx`
- **Route(n):** `/{tenant}/hr/admin/request-templates` (`routes/app.$tenant/hr.admin.request-templates._index.tsx`)
- **Daten (VM):** `HrRequestTemplateListVM`, `HrRequestKind` — `packages/view-models/src/hr/hr-request.vm.ts`
- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, Dialog, EmptyState, Select, TextInput · Icons: FeatherPlus
- **Interaktionen (Actions):** `onCreate({requestKind,defaultApproverUserId,title,description})`, `onToggle(templateId,isActive)`, `onArchive(templateId)`
- **States:** Empty (`rows.length===0` → EmptyState) / Loading (Rows via `?? []`) / Inactive-Badge / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte:
- [ ] Neue UI/UX-Entscheidungen:
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev:
### HrAdminUploadRemindersPage · D-HR „Upload-Erinnerungen" (A2 + A5)
- **Interim-Screen:** `apps/web/app/components/screens/hr/HrAdminUploadRemindersPage.tsx`
- **Route(n):** `/{tenant}/hr/admin/upload-reminders` (`routes/app.$tenant/hr.admin.upload-reminders._index.tsx`)
- **Daten (VM):** `HrReminderListVM` — `packages/view-models/src/hr/hr-upload-reminder.vm.ts`; `HrReminderCadenceKind` aus `packages/view-models/src/hr/reminder-cadence.ts`; plus Props `documentKinds`, `audiences`
- **Benötigte UI-Elemente (KEIN Layout):** BadgeFeedback, Button, Card, Checkbox, Dialog, EmptyState, Select, TextInput, `DateField` (tasks) · Icons: FeatherPlus
- **Interaktionen (Actions):** `onCreate({title,documentKind,cadenceKind,intervalMonths,audienceKind,dueAt,mandatory,escalate})`, `onSetStatus(reminderId,"active"|"paused")`, `onArchive(reminderId)`
- **States:** Empty (`rows.length===0` → EmptyState) / Loading (Rows via `?? []`) / Status-Badge active/paused / Permission-Denied (Loader/404)
- **Davids Delta (offen):**
- [ ] Überschriebene/geänderte Spec-Punkte (Cadence once/yearly/every_n_months/on_expiry; Dispatch im Worker-Cron):
- [ ] Neue UI/UX-Entscheidungen:
- [ ] Subframe-Screen-Link(s):
- [ ] Offene Fragen an Dev: