Sammelrelease nach Feedback aus der Test-Installation. Sieben unabhängige
Probleme — vom Form-Breaker über Übersetzungs-Lücken bis Log-Hygiene.
- fix(jobs): Einsatz manuell erfassen brach das HTML-Attribut, sobald
Customer-Namen ein " enthielten (@json in x-data=""). Auf json_encode
via {{ }} umgestellt (HTML-escaped). Alpine initialisiert wieder,
Anlegen-Button funktioniert.
- feat(i18n): BrandedTranslator injiziert :app_name automatisch in alle
__()-Aufrufe. Hilfetexte, Mails, Update-Strings rendern jetzt überall
korrekt 'Schneespur' bzw. 'Wintertrace' statt rohes :app_name.
- feat(dsgvo): EN-Default-Vorlage default-template-en.blade.php mit
UK/EU GDPR-Wording (Art. 6(1)(f), ICO-Hinweis, Subject Rights).
Controller laden locale-aware mit Fallback auf DE. Placeholder-Helper
kennt DE + EN Tokens.
- ui(settings): Alle 8 Settings-Karten haben jetzt Icons, nicht nur
Module. Markup auf array-driven Loop entrümpelt.
- chore(modules): Example-Modul boot()-gated via EXAMPLE_MODULE_ENABLED
env-Var (default false). Aus Release-ZIP komplett entfernt. Bestehende
Installs mit altem example/-Ordner laden es nicht mehr automatisch.
- chore(logging): ModuleManager Discovery/Boot-Logs auf debug
runtergesetzt (waren info → fluteten laravel.log bei jedem Request).
Defaults auf daily-Rotation mit 14d Retention + LOG_LEVEL=warning für
Production.
- fix(install): Hardcoded deutsche Fehlermeldung im InstallerController
durch __('install.preflight_has_failures') ersetzt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- bootstrap/app.php: restore default Laravel exception logging. The
diagnostic reportable() callback no longer returns false unconditionally
it only suppresses default reporting when a reporter actually handled the
exception, so storage/logs/laravel.log shows errors again on fresh installs.
- Customer object creation: fix 500 when notify_recipients is empty (NOT NULL
violation). Reconcile drift across migration/validation/form/lang: the field
is now treated consistently as an enum (customer|object|both) matching the
notification consumers; form uses a <select> instead of free-text input;
validation tightened via in: rule; coercion in prepareForValidation keeps
the DB invariant intact when the field is empty or missing.
- config/app.php: version is now read from the VERSION file at runtime.
The previously hardcoded '1.0.0' caused footer, settings, and dashboard to
show a stale version after every release. VERSION is now the single source
of truth for display.
- Module catalog UI: fix render crash (htmlspecialchars on i18n category dict)
and disappearing modules on 304 Not Modified responses. SchneespurModuleClient
now has a normalizeModule() adapter that bridges server-side field naming
(current_version, image_url, i18n category dict) to the internal shape used
by controller and views. The catalog body is cached in state, so 304
responses replay the cached catalog instead of falling back to the
semantically wrong "installed" list.
- Module installer: strip common top-level prefix from module ZIPs to prevent
modules/<slug>/<slug>/ double-nesting. The installer now detects whether all
ZIP entries share one wrapper folder and strips it during extraction; flat
ZIPs continue to work unchanged. Path-traversal validation runs on the
original entry names before the strip, so the security guarantee is intact.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Reverts the schneespur/ subdirectory restructure (b8e426b)
- Restores package.json and vite.config.js (needed for npm build, were
removed in an earlier cleanup before the restructure)
- Updates public/build/ assets with current Vite output (new content hashes)
- Move all application code into schneespur/ subdirectory for cleaner
GitHub presentation (README, LICENSE, INSTALL guides stay in root)
- Fix German Umlaut encoding in INSTALL.de.md and README.md
(ae→ä, oe→ö, ue→ü throughout)
- ZIP download structure remains flat (code in root) for easy deployment