Skip to content

Settings-Architektur

Setup- und Settings-Daten werden zentral geladen und anschließend über fokussierte Settings-Seiten bearbeitet.

Setup-Quelle

app/schemas/setup.ts definiert die Form des Setup-Payloads.

Raumtypen können dabei einer oder mehreren Raumtyp-Klassen zugeordnet sein; die UI und die Setup-API arbeiten dafür primär mit classIds, lesen aber ältere Einzelwerte weiterhin tolerant ein.

Dazu gehören:

  • Raumtypen und Raumtyp-Klassen
  • Zählertypen
  • Protokolltypen
  • Sektionsdefinitionen und Überschreibungen je Protokolltyp
  • Raumdetail-Komponenten und Materialien
  • Verantwortlichkeits-Scopes, Scope-Einstellungen und Verantwortlichkeits-Einträge
  • Vorlagen je Protokolltyp, inklusive Verweigerungsgründen für Signaturen über den Bereich Verweigerungsgründe
  • Felder für Mieterbestätigungen je Protokolltyp
  • Optionen für Kündigungsgründe
  • Feature-Flags
  • allgemeine Einstellungen
  • PDF-Einstellungen

Bei Raumdetail-Komponenten gelten inzwischen zusätzliche Fachregeln:

  • Komponenten sind gruppiert in general und electrical_installations
  • die Komponenten-Seite trennt beide Gruppen in eigenen Tabs und speichert die Sortierung je Gruppe separat; diese Reihenfolge steuert nur die PDF-Ausgabe, nicht die Erfassungsreihenfolge in der App
  • Elektroinstallationen werden über dieselbe Komponenten-Verwaltung gepflegt, nicht über eine eigene Settings-Seite
  • Elektroinstallationen stehen in jedem Raum ohne zusätzliche Raumklassen-Zuweisung zur Verfügung
  • Elektroinstallationen verwenden keine Materialien

Setup-Store

app/stores/setup.ts ist der zentrale Store für Konfigurations- und Lookup-Daten.

Im Code verifizierte Ladereihenfolge:

  1. Der App-Start triggert loadFromServer() über 01.appInit.client.ts
  2. Wenn /setup erfolgreich ist, wird das Payload angewendet und in Dexie gecacht
  3. Wenn der Server-Request fehlschlägt, versucht der Store die gecachte Tabelle setups
  4. Wenn kein Cache verfügbar ist, fällt der Store auf eingebaute Defaults zurück

Abgeleitete Setup-Zugriffe

Der Store stellt Helfer bereit, die in der gesamten App verwendet werden, darunter:

  • zusammengeführte Sektionskonfiguration je Protokolltyp
  • sortierte Mieterbestätigungsfelder
  • sortierte Kündigungsgrund-Optionen
  • Lookup-Helfer für Räume und Zähler
  • Template-, Verweigerungsgrund- und Mieterbestätigungs-Lookups je Protokolltyp

Für Raumdetail-Komponenten normalisiert und sortiert der Store zusätzlich groupKey und sortOrder, damit allgemeine Komponenten und Elektroinstallationen stabil und gruppenkonsistent ausgeliefert werden.

Verantwortlichkeiten werden über serverseitig gelieferte Scopes getrennt. Der Store stellt dafür getSelectableResponsibilityOptionsForScope(...), isResponsibilityRequired(...) und getDefaultResponsibilityIdForScope(...) bereit. Der genaue Vertrag ist in /responsibility-scopes beschrieben.

Settings-Routen

Settings-Seiten sind schmale Wrapper um fokussierte Editor-Komponenten unter app/pages/settings/** und app/components/Settings/**.

Wiederkehrende Seitenköpfe werden über app/components/Settings/SettingsPageSection.vue gebündelt, damit die Routen nur noch definePageMeta(...), die passende Titelkonfiguration und die eigentliche Editor-Komponente verdrahten.

Beispiele:

  • settings/global/general.vue
  • settings/global/rooms.vue
  • settings/global/sections/[protocolType].vue
  • settings/global/templates/[protocolType].vue
  • settings/global/tenant-confirmation.vue

Lokale Anwendungseinstellungen

Persönliche Anwendungseinstellungen liegen nicht im Setup-Payload, sondern im lokalen Settings-Store app/stores/settings.ts. Der Store persistiert über VueUse useLocalStorage(...) unter dem Key app-settings und merged neue Default-Werte tolerant nach.

Aktuell gehören dazu:

  • autoImportMeters: lokale Option für den Zählerimport
  • autoSaveProtocolChanges: lokale Opt-in-Option, die Editor-Änderungen nach kurzer Inaktivität automatisch über den normalen Server-Save synchronisiert
  • showAutoSaveProtocolChangesToast: optionale Erfolgs-Meldung nach automatischem Speichern
  • sectionOrder: lokal gespeicherte Reihenfolge bevorzugter Protokollsektionen
  • autoScrollAccordion: lokale Scroll-Option für die Accordion-Ansicht
  • protocolSectionLayout: Umschaltung zwischen accordion und sidebar im Protokoll-Editor
  • showSectionTextsInForm: lokale Option, die pro Protokolltyp konfigurierte Texte vor und nach Sektionen auch im Formular anzeigt
  • animationsDisabled: globale Abschaltung von Bewegungen wie Seitenübergängen, Bereichswechseln und sanftem Scrollen
  • colorMode: lokale Anzeigeoption system, light oder dark
  • enableImageEditor: lokale Freischaltung des Bildeditors in Upload-Panels

Die lokalen Anwendungseinstellungen sind auf zwei App-Settings-Seiten verteilt:

  • app/pages/settings/app/general.vue: Zählerimport, Auto-Save und Bildeditor-Freischaltung
  • app/pages/settings/app/appearance.vue: Sprache, Farbmodus, Editor-Layout, Sektionsreihenfolge, Sektionshinweise, Animationen und zurückgesetzte Hinweise

Reset setzt den lokalen Store wieder auf DEFAULT_SETTINGS zurück und betrifft nicht die serverseitigen Setup-Daten.

autoSaveProtocolChanges ist standardmäßig deaktiviert. Wenn die Option aktiv ist, startet der Editor nach kurzer Inaktivität einen Save über denselben Acceptance-Speicherpfad wie der manuelle Button. Erfolgs-Toasts bleiben standardmäßig aus und können über die eingerückte Unteroption showAutoSaveProtocolChangesToast bewusst eingeschaltet werden. Offline-Zustände, Serverfehler, gesperrte Protokolle und Konfliktzustände wie local_changes_present oder server_data_changed werden nicht automatisch überschrieben; der sichtbare Local-only-/Fehlerhinweis bleibt dann maßgeblich.

Die Layout-Einstellung protocolSectionLayout wird in app/pages/index.vue ausgewertet. Bei sidebar rendert der Editor MainSectionSidebar.vue, bei accordion weiterhin MainAccordion.vue.

Die Einstellung showSectionTextsInForm nutzt die gleichen zusammengeführten Sektionskonfigurationen wie die PDF-Ausgabe und rendert die gespeicherten HTML-Texte als Hinweisblöcke innerhalb von SectionWrapper.vue.

Die Animations-Einstellung animationsDisabled wird global in app/app.vue und zusätzlich in AppPageTransition.vue berücksichtigt, damit CSS-Bewegungen und Vue-Transitions gleichzeitig abgeschaltet werden.

colorMode wird in app/app.vue auf document.documentElement angewendet. Im Modus system folgt die App prefers-color-scheme; bei light oder dark wird die entsprechende Klasse direkt gesetzt und color-scheme synchronisiert.

enableImageEditor schaltet nur die Bildeditor-Aktion in Upload-Panels frei. Der Upload-, Sync- und PDF-Pfad bleibt auch bei deaktiviertem Editor funktionsfähig; vorhandene Annotationen bleiben im Bilddatensatz erhalten.

Die Seite appearance.vue verwaltet zusätzlich den lokalen Key dismissed-hints. Der Reset dort betrifft nur ausgeblendete Hinweisboxen, nicht app-settings und nicht das serverseitige Setup.

API-Ebene

app/composables/api/useSetupApi.ts stellt die Server-API bereit, die von Settings-Editoren verwendet wird.

Implementierte Mutationsbereiche:

  • room types
  • room type classes and room class component assignments
  • room detail components
  • room detail materials
  • responsibilities
  • responsibility settings per scope
  • billing providers
  • global templates
  • tenant confirmation fields
  • tenant confirmation field order
  • template and tenant confirmation copy actions between protocol types
  • general settings
  • PDF settings

Die globalen PDF-Einstellungen enthalten neben Logo- und Headerfarben auch Bildlayout-Optionen:

  • imageColumnCount: automatische oder feste Spaltenzahl für PDF-Bilder
  • imageMaxHeightMm: maximale Bildhöhe in Millimetern
  • imageCaptionEnabled: steuert, ob Bildbeschreibungen als Beschriftung ausgegeben werden

Die Bilder-Einstellungen werden in SettingsGlobalPdf.vue im Tab Bilder bearbeitet. Die Vorschau rendert über SettingsPdfImagePreview.vue eine generische pdfmake-Vorschau mit mehreren Beispielbildern und Seitenwechsel, damit Änderungen am Layout vor der Kundenansicht sichtbar geprüft werden können.

Bei room detail components umfasst die Mutation inzwischen auch Gruppenwechsel und persistierte Sortierung. Der Mock-Server liefert diese Komponenten ebenfalls bereits gruppiert und nach sortOrder stabil zurück.

Update-Muster

Die meisten Settings-Editoren folgen diesem Muster:

  1. Mutation über useSetupApi() an das Backend senden
  2. den bereits geladenen Setup-Store lokal patchen

Wenn ein Editor das übliche Listen- und Modal-Layout verwendet, wird die Vue-Hülle über app/components/Settings/SettingsCrudShell.vue geteilt. Diese Komponente bündelt:

  • SettingsListEditor für Header, Actions, Liste und Empty-State
  • AppModal für Add/Edit-Dialoge
  • SettingsEditorDialogFooter für das Standard-Save/Cancel-Verhalten

Für einfache Setup-Editoren mit Add/Edit-Modal wird der wiederkehrende Controller-Teil zusätzlich über app/composables/settings/useSetupCrudController.ts geteilt. Der Controller besitzt die lokale Modal- und Loading-State-Maschine, öffnet Add- und Edit-Dialoge, ruft Create/Update/Delete-Mutationen auf und zeigt die Standard-Toasts für Speichern, Anlegen, Löschen und Fehler.

Die jeweilige Settings-Komponente bleibt verantwortlich für:

  • Formularzustand und Validierung
  • Mapping vom Formularzustand zum API-Payload
  • Mapping der API-Antwort in den useSetupStore()
  • fachliche Sonderfälle wie Zusatzkonfigurationen, Suchfilter oder abweichende Dialoginhalte

SettingsBillingProviders.vue, SettingsResponsibilities.vue und SettingsRoomClasses.vue folgen diesem Muster. Neue einfache Setup-Editoren sollten denselben Controller verwenden, statt isModalOpen, editingItem, isLoading, openAddModal(), openEditModal(), handleSave() und Standard-Toast-Handling erneut lokal zu duplizieren.

Manuelle Prüfung für dieses Muster:

  • In Einstellungen > Verantwortlichkeiten je Scope eine Zuständigkeit anlegen, bearbeiten, deaktivieren und löschen
  • In Einstellungen > Verantwortlichkeiten eine Zuständigkeit aus einem anderen Scope übernehmen und danach unabhängig bearbeiten
  • In Einstellungen > Verantwortlichkeiten Pflichtfeld und Standard-Zuständigkeit je Scope ändern
  • In Einstellungen > Räume > Raumklassen eine Klasse anlegen, bearbeiten und Raumdetail-Komponenten zuordnen
  • In Einstellungen > Abrechnung > Dienstleister einen Dienstleister anlegen, bearbeiten, deaktivieren und löschen
  • Bei Dienstleistern zusätzlich Kundennummer, E-Mail-Liste, Zählertyp-Zuordnung und Protokolltyp-Regeln prüfen
  • Nach jedem Speichern prüfen, dass der Dialog schließt, ein Erfolg-Toast erscheint und die Liste ohne kompletten Reload aktualisiert ist
  • Bei leerem Pflichtfeld prüfen, dass Speichern deaktiviert bleibt oder keine Mutation ausgelöst wird

Sonderfälle wie verschachtelte Dialoge oder stark abweichende Abläufe können weiterhin direkt SettingsListEditor oder AppModal verwenden.

Sektions- und Vorlagen-Seiten bilden den Protokolltyp als dynamisches Routensegment ab, zum Beispiel /settings/global/sections/1 und /settings/global/templates/1. Dadurch öffnet die Settings-Sidebar beim Wechsel aus dem Protokoll-Editor wieder den passenden Protokolltyp, ohne interne UI-Zustände in der Query mitzuschleppen. Interne Settings-Auswahlen wie der aktuell bearbeitete Vorlagen-Bereich bleiben lokaler UI-Zustand. Der Vorlagen-Editor verwaltet neben wiederverwendbaren Sektionstexten auch Signatur-Verweigerungsgründe über den Bereich Verweigerungsgründe; diese Einträge werden in der Signaturseite pro Protokolltyp als Grundauswahl verwendet. Die Vorlagen-Navigation führt Protokolltypen als Unterpunkte, sodass die Seite selbst nur noch den zu bearbeitenden Bereich auswählt. Die Seitenbeschreibung nennt den aktuell geöffneten Protokolltyp konkret und erklärt, dass die Texte in den passenden Sektionen verwendet werden. Der Copy-Dialog wählt den aktuellen Protokolltyp beim Öffnen als Ziel aus und zeigt je aktuell gewähltem Bereich an, wie viele Einträge vorhanden sind. Der Vorlagen-Copy-Dialog führt fehlende Einträge zusammen und lässt vorhandene Ziel-Vorlagen stehen; Mieterbestätigungen ersetzen Zielkonfigurationen weiterhin bewusst vollständig. Der gemeinsame Dialog- und Toast-Flow dafür liegt in app/composables/settings/useProtocolTypeCopyController.ts.

Wenn ein Settings-Editor zusätzliche Fachregeln hat, sollten diese nicht in der SFC verteilt werden. SettingsTenantConfirmation.vue zeigt das aktuelle Muster: Regel- und Formlogik liegt in app/composables/settings/useTenantConfirmationFieldForm.ts, während Listen- und Dialog-UI in kleine Subkomponenten getrennt sind und die Hauptkomponente nur CRUD und Reorder koordiniert.

Die wichtigste Ausnahme ist der Sektionseditor.

SettingsSectionEditor.vue bündelt Sektionskonfigurations-Updates und kann neue Sektionen anlegen. Nach dem Speichern erzwingt er einen vollständigen Setup-Reload statt nur lokale Patches.