Appearance
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
generalundelectrical_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:
- Der App-Start triggert
loadFromServer()über01.appInit.client.ts - Wenn
/setuperfolgreich ist, wird das Payload angewendet und in Dexie gecacht - Wenn der Server-Request fehlschlägt, versucht der Store die gecachte Tabelle
setups - 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.vuesettings/global/rooms.vuesettings/global/sections/[protocolType].vuesettings/global/templates/[protocolType].vuesettings/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ählerimportautoSaveProtocolChanges: lokale Opt-in-Option, die Editor-Änderungen nach kurzer Inaktivität automatisch über den normalen Server-Save synchronisiertshowAutoSaveProtocolChangesToast: optionale Erfolgs-Meldung nach automatischem SpeichernsectionOrder: lokal gespeicherte Reihenfolge bevorzugter ProtokollsektionenautoScrollAccordion: lokale Scroll-Option für die Accordion-AnsichtprotocolSectionLayout: Umschaltung zwischenaccordionundsidebarim Protokoll-EditorshowSectionTextsInForm: lokale Option, die pro Protokolltyp konfigurierte Texte vor und nach Sektionen auch im Formular anzeigtanimationsDisabled: globale Abschaltung von Bewegungen wie Seitenübergängen, Bereichswechseln und sanftem ScrollencolorMode: lokale Anzeigeoptionsystem,lightoderdarkenableImageEditor: 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-Freischaltungapp/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-BilderimageMaxHeightMm: maximale Bildhöhe in MillimeternimageCaptionEnabled: 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:
- Mutation über
useSetupApi()an das Backend senden - 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:
SettingsListEditorfür Header, Actions, Liste und Empty-StateAppModalfür Add/Edit-DialogeSettingsEditorDialogFooterfü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 > Verantwortlichkeitenje Scope eine Zuständigkeit anlegen, bearbeiten, deaktivieren und löschen - In
Einstellungen > Verantwortlichkeiteneine Zuständigkeit aus einem anderen Scope übernehmen und danach unabhängig bearbeiten - In
Einstellungen > VerantwortlichkeitenPflichtfeld und Standard-Zuständigkeit je Scope ändern - In
Einstellungen > Räume > Raumklasseneine Klasse anlegen, bearbeiten und Raumdetail-Komponenten zuordnen - In
Einstellungen > Abrechnung > Dienstleistereinen 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.