- Facility: TravelCostMode (Pauschale/ProKilometer) + TravelCostPerKm, alongside the existing flat rate; fixes FacilityService.UpdateAsync silently dropping all Konditionen fields on update. - New EmployeeFacilityDistance (Mitarbeiter x Einrichtung -> km) with full office-side CRUD in omsorgapp, plus a self-service endpoint/UI so field staff can maintain their own commute distance via OMSORG Connect (new ModuleType.EmployeeFacilityDistances, Own-scope, no Facilities access needed). - Roles can now be deleted (blocked with a 409 while still assigned to a user). - Fix employeesApi.js missing the Date-object conversion for dateOfBirth/entryDate/ exitDate that crashed employee creation whenever a date was filled in; add a clear/remove control for those date fields. - Requirements: flesh out FR-REC-3 with a staged Akquise cadence, add FR-REC-5/6 for CRM feature scope and success metrics. - Regenerate api-client-ts and api-client-php for all of the above. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
22 KiB
CLAUDE.md — omsorgapp (OMSORG Desktop)
Gilt zusätzlich zur Root-CLAUDE.md. Dies ist das React/Vite-Browser-Projekt für Büromitarbeiter (Sabina, Malik, Sabrina, Sascha) — kein Electron mehr (Umstieg 2026-08-10, siehe "Login + Session" unten für den Grund).
Stack & Struktur
-
React 18 + Vite, reine Browser-SPA (kein Electron, kein Node-Zugriff aus dem Renderer), reines JSX (kein TS).
-
src/api/— Adapterschicht zuomsorgCore(HTTP), ersetzt das frühereelectron/backend/*.cjs+electron/main.cjs/preload.cjs-IPC 1:1 als reines Browser-JS:config.js(einzige Stelle mit der Backend-URL, Defaulthttp://localhost:5245, überschreibbar per Vite-EnvVITE_OMSORG_CORE_URL),apiClientHelpers.js(configFor/callApi-Wrapper um den generiertenomsorgcore-client-ts-Client, setztcredentials:"include"für die HttpOnly-Refresh-Cookie),session.js(Access-Token nur als Modul-Variable im Speicher, Silent-Refresh-Timer,withAuthRetry,onSessionChanged-Listener — Browser-Ersatz für den früheren Session-State im Electron-Hauptprozess, siehe "Login + Session" unten),authApi.js(login/refresh/logout/me/... gegen/api/auth/*),genericApi.js(Ersatz für den früherenapi:get/api:post-IPC-Proxy, für Ressourcen ohne eigene<kategorie>Api.js-Datei, aktuell/api/admin/sessions*//api/admin/email/*), je eine<kategorie>Api.js-Datei pro Ressource (employeesApi.js,facilitiesApi.js,facilityContactsApi.js,facilityQualificationRatesApi.js,employeeFacilityDistancesApi.js,ordersApi.js,absencesApi.js,timeEntriesApi.js,usersApi.js,rolesApi.js,auditLogApi.js,valueListsApi.js,trashApi.js,contractsApi.js,documentsApi.js, analog zu den Controllern inomsorgCore),index.js(buildOmsorgApi()— baut das komplettewindow.omsorg-Objekt zusammen, wrapped jede Ressourcenfunktion mitsession.withAuthRetry). -
Login + Session: Der Refresh-Token liegt als HttpOnly-Secure-Cookie (von
omsorgCoresAuthControllergesetzt,Path=/api/auth) beim Server — nie per JS lesbar, kein Electron-safeStoragemehr nötig, dafür brauchtomsorgCoreCORS mitAllowCredentials()(sieheomsorgCore/CLAUDE.md, Abschnitt "Auth-Flow"). Der Access-Token lebt nur als Modul-Variable insrc/api/session.js(verschwindet bei Tab-Reload/-Schließen) —session.bootstrapSession()läuft beim App-Start (src/main.jsx) und stellt die Session über die noch gültige Cookie perPOST /api/auth/refresh(ohne Body, die Cookie geht automatisch mit) wieder her, damit ein Reload nicht zum erneuten Login zwingt. Silent Refresh läuft zusätzlich per Timer (kurz vor Ablauf) und reaktiv bei 401.window.omsorg.auth(auth.login,auth.logout,auth.getSession,auth.onSessionChanged) bildet dieselbe Oberfläche wie früher über IPC ab, nur als direkte Funktionsaufrufe. -
src/main.jsx— Renderer-Einstiegspunkt, setztwindow.omsorg = buildOmsorgApi()(aussrc/api/index.js, ersetzt das früherecontextBridge.exposeInMainWorld), wrapptAppinAuthProvider(src/app/AuthContext.jsx) und rendert in#root. -
src/app/AuthContext.jsx— React-Context umwindow.omsorg.auth, stelltuseAuth()mit{ isAuthenticated, user, isLoading, login, logout }bereit. -
src/app/app.jsx— gated zuerst auf Auth (isLoading→ Ladehinweis,!isAuthenticated→src/modules/auth/LoginPage.jsx), danach Top-Level-Router: hältactivePageals lokalen State (kein Routing-Framework), switcht zwischen Modulen. Noch nicht implementierte Module rendernPlaceholderPage. -
src/layouts/AppLayout.jsx— Grundlayout:Sidebar+Header+main.page-content. -
src/components/— geteilte UI:Sidebar,Header,OmsorgCard, undcomponents/ui/(OmsorgButton,OmsorgBadge,OmsorgStatCard). -
src/modules/<modulname>/— ein Ordner pro Fachmodul, z. B.modules/home/(HomePage, HomeStats, ContractWidget),modules/employees/(EmployeesPage, EmployeeDetailPanel, EmployeeTabs),modules/facilities/(FacilitiesPage, FacilityDetailPanel, FacilityForm, FacilityContactsList/FacilityContactForm für die Ansprechpartner-Unterliste (FR-EIN-2), FacilityQualificationRatesList/FacilityQualificationRateForm für die qualifikationsabhängigen Preise (FR-EIN-4), EmployeeFacilityDistancesList/EmployeeFacilityDistanceForm für die kilometerbasierte Fahrtkostenabrechnung (nur sichtbar, wennfacility.travelCostMode === "ProKilometer") — Referenz war 1:1modules/employees/, ohne Tabs, da noch keine weiteren Unterobjekte wie Dokumente/Verträge existieren),modules/debug/(DebugSessionsPage.jsx),modules/settings/(SettingsPage.jsx,UsersPanel.jsx,ResetUserPasswordDialog.jsx,RolesPanel.jsx,RolePermissionMatrix.jsx,UserOverridesPanel.jsx,StatusManagementPanel.jsx,permissionOptions.js). Neue Module folgen diesem Muster: eigener Ordner untersrc/modules/, Einstiegskomponente<Modul>Page.jsx. -
Benutzer-, Rollen- & Rechteverwaltung (
modules/settings/): Admin-UI unter dem Sidebar-Tab "Einstellungen" — sichtbar, sobaldViewauf mindestens einem vonUsers/UserManagement/Configurationvorliegt (navPermissions.js,SETTINGS_MODULES).SettingsPage.jsxfiltert vier mögliche Tabs jeweils einzeln nach ihrem Modul (hasPermission(tab.module, "View")), ein Nutzer sieht also nur die Tabs, für die er tatsächlich berechtigt ist — kein pauschales Alles-oder-nichts mehr (Hintergrund/Historie:omsorgCore/CLAUDE.md, Abschnitt "Rechtesystem", "Drei getrennte Admin-Rechte"):UsersPanel.jsx(ModulUsers) — Benutzerkonten sehen, Rolle ändern, aktivieren/deaktivieren (window.omsorg.users.{list,update}), Passwort zurücksetzen überResetUserPasswordDialog.jsx(window.omsorg.users.resetPassword, gleiches Invite/Direct-Formular wie beim Account-Anlegen inmodules/employees/CreateUserAccountDialog.jsx).RolesPanel.jsx/RolePermissionMatrix.jsx(ModulUserManagement) — Rollen anlegen viawindow.omsorg.roles.create, Auswahl öffnet die Checkbox-MatrixModuleType×PermissionAction, speichert überwindow.omsorg.roles.updatePermissions.UserOverridesPanel.jsx(ModulUserManagement) — Nutzerauswahl überwindow.omsorg.users.list(braucht dafür zusätzlichUsers/View, sieheomsorgCore/CLAUDE.md), individuelleUserPermissionOverride-Ausnahmen je Nutzer viawindow.omsorg.users.{listPermissionOverrides,addPermissionOverride,deletePermissionOverride}.StatusManagementPanel.jsx(ModulConfiguration) — Mitarbeiterstatus, Beschäftigungsart, CRM-Status, Einrichtungstyp, Vertragstyp/-status, Auftragsstatus als admin-editierbare Auswahllisten überwindow.omsorg.valueLists.*; Löschen eines Werts wird serverseitig verweigert, solange er noch irgendwo gesetzt ist — Details:omsorgCore/CLAUDE.md, Abschnitt "Konfigurierbare Auswahllisten".
ModuleType/PermissionAction/PermissionEffect-Werte + deutsche Labels sind inpermissionOptions.jshart codiert (kein gemeinsames Enum-Modul zwischen Backend und Frontend, analognavPermissions.js). Backend-Details:omsorgCore/CLAUDE.md, Abschnitt "Rechtesystem". -
Audit-Log (
modules/auditLog/AuditLogPage.jsx): rein lesende, paginierte Liste (OmsorgPagination,PAGE_SIZE = 50) überwindow.omsorg.auditLog.list({ page, pageSize })→src/api/auditLogApi.js→GET /api/audit-loginomsorgCore. Keine Bearbeiten-/Löschen-Aktionen (Audit-Einträge sind unveränderlich, es gibt serverseitig keine entsprechenden Endpoints). ImSidebar.jsx-Menü nur sichtbar, wennhasPermission("AuditLog", "View")(navPermissions.js,NAV_MODULES["Audit-Log"] = "AuditLog") — per Default nur die Rolle Geschäftsführung (Backend-Details:omsorgCore/CLAUDE.md, Abschnitt "Audit-Log").ModuleType.AuditLogist zusätzlich inmodules/settings/permissionOptions.jssMODULE_OPTIONSeingetragen, damit Geschäftsführung die Berechtigung über die Rollen-Rechte-Matrix auch anderen Rollen zuweisen kann. -
Debug-Sicht (
modules/debug/DebugSessionsPage.jsx): listet aktive Sessions (GET /api/admin/sessionsüber den generischenapi:get-Proxy,src/api/genericApi.js), erlaubt Einzel- und Gesamt-Widerruf (POST /api/admin/sessions/{id}/revoke,POST /api/admin/sessions/revoke-all) — invalidiert serverseitig sofort alle betroffenen Access-Tokens (Details:omsorgCore/CLAUDE.mdAbschnitt "Session-Killswitch"). ImSidebar.jsx-Menü nur sichtbar, wennuseAuth().hasPermission("UserManagement", "View")— dieselbe granulare Rechteprüfung, die serverseitig überRequirePermission(ModuleType.UserManagement, ...)aufAdminSessionsControllererzwungen wird (kein Rollennamen-Vergleich mehr, siehe "Rechtesystem im Client" unten). -
Rechtesystem im Client: Rechte kommen nicht aus dem JWT (das trägt nur
sub/name/role/sst).src/api/session.jslädt nach jedem Login/Refresh zusätzlichGET /api/auth/me(src/api/authApi.js#me) und ersetztsession.userdurch{ username, role, permissions }—permissionsist die vom Backend aufgelöste Liste ausPermissionService.GetGrantedPermissionsAsync(Rollen-Default + Overrides, je{ module, action, scope }).AuthContext.jsxstellt daraufhasPermission(module, action)bereit; UI-Komponenten prüfen darüber, nie überuser.roledirekt. ZusätzlichgetScope(module, action)(liefert"All"/"Own"/null) für rein kosmetische UI-Anpassungen bei Own-Scope (z. B. Suchfeld ausblenden) — die eigentliche "nur eigene Daten"-Durchsetzung passiert serverseitig (omsorgCore/CLAUDE.md, Abschnitt "Datenebenen-Scope"), aktuell für die Module Mitarbeiter/Verträge/Abwesenheiten relevant. Admin-Verwaltung dieser dritten Matrix-Dimension:modules/settings/RolePermissionMatrix.jsx(3-Zustands-Auswahl je Zelle fürEmployees/Contracts/Absences, sonst weiterhin Checkbox) undUserOverridesPanel.jsx(Scope-Auswahl im Override-Formular), Optionen/Labels inpermissionOptions.js(SCOPE_OPTIONS,SCOPE_CAPABLE_MODULES). -
Verbindliche Regel — UI folgt den Rechten, für jedes Modul: Jeder Sidebar-Tab und jede Aktion (Anlegen/Bearbeiten/Löschen/...) muss über
hasPermission(module, action)gegated werden — das ist kein Sonderfall für Mitarbeiter, sondern das Standardmuster für jedes neue Modul (mindestensViewfürs Sichtbarsein des Tabs,Create/Edit/... für einzelne Aktionen darin). Referenzimplementierung:src/modules/employees/EmployeesPage.jsx(canCreate = hasPermission("Employees", "Create")) undsrc/modules/employees/EmployeeDetailPanel.jsx(canEdit = hasPermission("Employees", "Edit")).- Die Zuordnung Sidebar-Tab →
ModuleTypesteht zentral insrc/app/navPermissions.js(NAV_MODULES) und wird sowohl vonSidebar.jsx(blendet nicht erlaubte Tabs aus) als auch vonapp.jsx(fällt auf"Home"zurück, falls der aktive Tab durch eine Rechteänderung nicht mehr erlaubt ist) genutzt — neue Zuordnungen nur dort eintragen, nicht duplizieren. Zwei Tabs hängen nicht an einem einzelnen Modul, sondern an einer Liste (isNavItemVisibleprüft dafürArray.some(...)statt eines einzelnen Lookups): "Papierkorb" (TRASH_MODULES, sichtbar beiRecoverauf irgendeinem Objekt mit Soft-Delete) und "Einstellungen" (SETTINGS_MODULES = [Users, UserManagement, Configuration], sichtbar beiViewauf irgendeinem der drei — welcher Tab innerhalb der Seite dann tatsächlich erscheint, entscheidetSettingsPage.jsxseparat pro Tab, siehe oben). - Aktuelles Mapping: Mitarbeiter→
Employees, Kunden→Facilities, Disposition→Orders, Abwesenheiten→Absences, Zeiterfassung→TimeEntries, Rechnungen→Invoices, Controlling→Controlling, Debug→UserManagement, Audit-Log→AuditLog, Einstellungen→ sieheSETTINGS_MODULESoben. Home hat kein Modul und ist immer sichtbar. Kalkulation und Fahrzeuge sind reinePlaceholderPage-Stubs ohne Fachlogik und haben (noch) kein passendesModuleType— bewusst ungegated, bis ein echtes Modul dahintersteht; dann Eintrag innavPermissions.jsergänzen (ggf. mit neuemModuleType-Wert inomsorgCore, additiv, keine Migration nötig).
- Die Zuordnung Sidebar-Tab →
-
src/style.css— einziges Stylesheet, kein CSS-Framework.
Wichtige Startbefehle
npm install
npm run dev # Vite-Devserver, http://127.0.0.1:5173 im Browser öffnen
omsorgCore muss dafür separat laufen (siehe omsorgCore/CLAUDE.md) und dessen Cors:AllowedOrigins http://127.0.0.1:5173 enthalten (per Default in appsettings.Development.json gesetzt).
Aktueller Ist-Stand (siehe auch REQUIREMENTS.md)
- Vollständig:
HomePage,EmployeesPage(Grundgerüst; Daten kommen übersrc/api/employeesApi.jsecht ausomsorgCore, keine hartcodierten Beispieldaten mehr für dieses Modul),FacilitiesPage(Sidebar-Tab "Kunden",ModuleType.Facilities; Grundgerüst analogEmployeesPage, Daten übersrc/api/facilitiesApi.jsecht ausomsorgCore; deckt Stammdaten und eine Ansprechpartner-Liste je Einrichtung ab (FacilityContactsListimFacilityDetailPanel, übersrc/api/facilityContactsApi.jsgegen/api/facilities/{id}/contacts, Anlegen/Bearbeiten, kein Löschen); CRM-Pipeline (FR-EIN-3) und Konditionen (FR-EIN-4, "Konditionen"-Fieldset inFacilityForm.jsx+FacilityQualificationRatesListimFacilityDetailPanel, übersrc/api/facilityQualificationRatesApi.jsgegen/api/facilities/{id}/qualification-rates) sind jetzt ebenfalls umgesetzt — Fahrtkosten (seit 2026-08-10) haben zwei Abrechnungsarten:travelCostMode"Pauschale" (FeldtravelCostRate) oder "ProKilometer" (FeldtravelCostPerKm, EUR/km); im Pro-Kilometer-Modus zeigtFacilityDetailPanelzusätzlichEmployeeFacilityDistancesList(Entfernung je Mitarbeiter in km, übersrc/api/employeeFacilityDistancesApi.jsgegen/api/facilities/{id}/employee-distances— eigene 1:n-Unterressource, weil jeder Mitarbeiter von einem anderen Wohnort anfährt, sieheomsorgCore/CLAUDE.md"Konditionen einer Einrichtung"). FR-EIN-5 (konsolidierte Historie) bleibt offen), Login-Screen + persistente Session gegenomsorgCore,SettingsPage(Benutzerübersicht + Rollen-Rechte-Matrix + User-Permission-Overrides + Status-Verwaltung, siehe "Benutzer-, Rollen- & Rechteverwaltung" oben),AuditLogPage(siehe "Audit-Log" oben),OrdersPage(Sidebar-Tab "Disposition",ModuleType.Orders; FR-EM-1, Grundgerüst 1:1 analogFacilitiesPage, Daten übersrc/api/ordersApi.jsecht ausomsorgCore, volles CRUD (OrdersPage/OrderDetailPanel/Create-/EditOrderDialog/OrderForm.jsx); Pflichtfelder Einrichtung/Ansprechpartner/Zeitraum/Qualifikation/Schichtart/Anzahl Mitarbeiter/Konditionen/Priorität abgedeckt — Ansprechpartner-Dropdown lädt beim Wechsel der Einrichtung deren Kontakte perwindow.omsorg.facilityContacts.list(facilityId)nach; Qualifikation/Schichtart/Priorität/Status als admin-editierbareValueLists überuseValueListItems; Statusfeld ist nur im Bearbeiten-Formular sichtbar und zeigt dabei nur die laut/api/value-lists/OrderStatus/transitionserlaubten Zielstatus, analog zum CRM-Status-Dropdown bei Facilities). FR-EM-2 (Dashboard-Sichtbarkeit der Statuspipeline):modules/home/OrderStatusWidget.jsxinHomePage.jsx, listet die aktuelle Auftragsanzahl jeOrderStatus-Wert (client-seitig auswindow.omsorg.orders.list({ pageSize: 200 })aggregiert, kein eigener Stats-Endpoint), rechtegegated überhasPermission("Orders","View"), UI-Muster 1:1 vonFollowUpWidget.jsxübernommen.AbsencesPage(Sidebar-Tab "Abwesenheiten",ModuleType.Absences; Datenbasis für FR-CON-1/FR-EM-3, Backend-DetailsomsorgCore/CLAUDE.md"Abwesenheits-/Urlaubs-/Krankmeldungsanträge"): Liste+Filter (Status/Art) +AbsenceDetailPanelmit Genehmigen/Ablehnen-Aktionen (hasPermission("Absences","Approve"),window.omsorg.absences.decide(id, { status, adminNote })) und Bearbeiten (hasPermission("Absences","Edit"),EditAbsenceDialog.jsx/AbsenceForm.jsxnach demOrderForm.jsx-Muster,window.omsorg.absences.update(id, payload)) — Bearbeiten-Button nur sichtbar, solange der Status noch der initiale ist (statusItems.find(i => i.isInitial)?.valueaususeValueListItems("AbsenceStatus")— nicht der Literal"Eingereicht", umbenennbar über die Status-Verwaltung, ohne dass diese Komponente angefasst werden muss; serverseitig ohnehin erzwungen, sieheomsorgCore/CLAUDE.md). Genehmigen/Ablehnen ist bewusst jederzeit möglich, nicht nur solange der Antrag noch im initialen Status ist — derDecide-Endpoint hat serverseitig keine Statusprüfung (anders alsUpdate), damit eine versehentliche Entscheidung korrigierbar bleibt; die UI zeigt bei bereits entschiedenen Anträgen zusätzlich einen Hinweistext ("Bereits entschieden (...) — hier lässt sich die Entscheidung bei Bedarf noch ändern"). Bewusst kein Anlegen-Dialog hier, Anträge stellt ausschließlich der Außendienst überomsorgWeb/mitarbeiter-app(pages/urlaubsantrag.php),omsorgappprüft/bearbeitet/entscheidet nur.TimeEntriesPage(Sidebar-Tab "Zeiterfassung",ModuleType.TimeEntries; FR-ZE-1/FR-ZE-2, Backend-DetailsomsorgCore/CLAUDE.md"Zeiterfassung"): Liste+Statusfilter +TimeEntryDetailPanelmit dynamischen Büro-Entscheidungs-Buttons (hasPermission("TimeEntries","Approve"), auswindow.omsorg.valueLists.listTransitions("TimeEntryStatus")gefiltert aufrequiresApproval===true-Kanten ab dem aktuellen Status,window.omsorg.timeEntries.decide(id, { statusId, adminNote })— Muster 1:1 vonOrderForm.jsxs Statuswechsel-Filterung übernommen, nicht vonAbsenceDetailPanel, da hier eine echte Mehrstufen-Pipeline statt einer binären Entscheidung vorliegt) und Bearbeiten (hasPermission("TimeEntries","Edit"),EditTimeEntryDialog.jsx/TimeEntryForm.jsx,window.omsorg.timeEntries.update(id, payload)) — Bearbeiten-Button nur sichtbar, solangetimeEntry.isEditableByOwner(direkt aus derTimeEntryResponse, nicht per Statuswert-Vergleich). Bewusst kein Anlegen-Dialog und keinstatusId-Feld im Bearbeiten-Formular — Erfassung passiert inomsorgWeb/mitarbeiter-app(pages/stundenerfassung.php), Statuswechsel laufen ausschließlich übersubmit(dort) bzw.decide(hier). - Papierkorb (
modules/trash/TrashPage.jsx): tab-basierte Liste, ein Eintrag inTABSpro Objekt mit Soft-Delete (window.omsorg.trash.{list,restore}<Objekt>), inzwischenemployees/facilities/contracts/orders/facilityContacts/facilityQualificationRates/employeeFacilityDistances/absences/timeEntries— jeder Tab nur sichtbar mithasPermission(tab.module, "Recover"). Neue Objekte mit Soft-Delete: Eintrag hier UND innavPermissions.jssTRASH_MODULESergänzen (beide nötig, siehe oben). - Dokumente (FR-MA-3,
modules/employees/DocumentsList.jsx/UploadDocumentDialog.jsx/EditDocumentDialog.jsx): eigener Tab in der Personalakte (EmployeeDetailPanel.jsx), nur sichtbar mithasPermission("Documents","View")(EmployeeTabs.jsxbekommt dafür einenhiddenTabIds-Prop). Liste gruppiert nach Kategorie (admin-editierbareDocumentCategory-Auswahlliste, sieheomsorgCore/CLAUDE.md), Buttons Hochladen/Bearbeiten/Herunterladen/Löschen je nachDocuments-Rechten. Datei-Upload läuft über einen normalen<input type="file">; die Bytes gehen alsArrayBuffer(ausfile.arrayBuffer()) direkt ansrc/api/documentsApi.js#uploadDocument, das daraus einBlobfür den generiertenDocumentsApi-Multipart-Aufruf baut. Download läuft bewusst NICHT über den generierten Client (apiDocumentsIdDownloadGetRawist alsVoidApiResponsegeneriert, verwirft den Response-Body) —documentsApi.downloadDocumentmacht dafür einen direktenfetchmitAuthorization-Header und liefert einenBlobzurück;src/api/index.jssdocuments.downloaderzeugt darausURL.createObjectURL(blob)+ einen unsichtbaren<a download>-Klick (Browser-natives Herunterladen statt Electronsdialog.showSaveDialog),documents.view(fürDocumentViewerDialog.jsx) reicht denselben Blob als<iframe src={objectUrl}>(PDF) bzw.<img>weiter. "Bearbeiten" ändert nur Metadaten (Kategorie/Beschreibung/Dateiname) überPUT /api/documents/{id}, kein erneuter Datei-Upload. - Alle anderen Menüpunkte (Kalkulation, Fahrzeuge, Rechnungen, Controlling) sind reine
PlaceholderPage-Platzhalter inapp.jsx. - Auth, Mitarbeiter, Kunden/Einrichtungen, Aufträge/Disposition, Abwesenheiten und Zeiterfassung sprechen bereits gegen
omsorgCore(siehesrc/api/employeesApi.js/facilitiesApi.js/ordersApi.js/absencesApi.js/timeEntriesApi.js, zusammengefügt insrc/api/index.jssemployees/facilities/orders/absences/timeEntries-Namespaces). Die übrigen Fachmodule (Kalkulation, Fahrzeuge, Rechnungen, Controlling) haben noch gar keine Datenhaltung — weder lokal noch überomsorgCore— sondern sind reinePlaceholderPage-Stubs. Nächster Schritt: weitere Fachmodule nach und nach nach demselben Muster (src/api/<kategorie>Api.js+omsorgCore-Endpunkte) umsetzen, analog zum Mitarbeiter-/Kunden-/Auftrags-Vorbild. - Keine Verbindung zu
omsorgWeb/MySQL — Mitarbeiterdaten hier sind komplett getrennt von denen in OMSORG Connect. Nicht durch neue Kopplungen/Workarounds "beheben"; die eigentliche Lösung ist die gemeinsame Datenbasis inomsorgCore. Abwesenheitsanträge sind der erste Datentyp, der diese Trennung nicht mehr hat:omsorgWeb/mitarbeiter-applegt sie direkt gegenomsorgCorean,omsorgappliest/entscheidet dieselben Datensätze — kein separater Sync, keine zweite Quelle.
Konventionen
- Neue Module: Ordner
src/modules/<name>/<Name>Page.jsx+ Unterkomponenten, inapp.jsx-Switch eintragen, inSidebar.jsxverlinken. - UI-Bausteine aus
src/components/ui/wiederverwenden statt neue Button/Badge/Card-Varianten zu bauen. - Jeglicher Backend-Datenzugriff nur über die
window.omsorg-Brücke aussrc/api/index.js, nie direktesfetch/omsorgcore-client-tsin Modul-Komponenten. - Neue
omsorgCore-Endpunkte: pro Endpunkt-Kategorie eine eigene Dateisrc/api/<kategorie>Api.js(z. B. künftiginvoicesApi.jsfür denInvoicesController), die ausschließlichapiClientHelpers.jssconfigFor/callApinutzt und Routen/Feldnamen genau dieser einen Ressource kapselt — spiegelt die Controller-Aufteilung inomsorgCore1:1.src/api/index.jsimportiert nur diese<kategorie>Api.js-Dateien und wrapped sie mitsession.withAuthRetry, nieapiClientHelpers.jsdirekt in einer Modul-Komponente. Solange eine Ressource noch keine eigeneApi.js-Datei hat, läuft sie übergenericApi.jsswindow.omsorg.api.get/.post; sobald die Datei existiert, bekommt sie einen eigenen Namespace nach demselben Muster wieemployees/orders/etc. Grund: Backend soll austauschbar bleiben, ohne den React-Teil anzufassen (siehe Adapterschicht oben).