Packages now support an optional icon, a short tagline, and an "empfohlen"
flag, all editable in Admin > Pakete. The model page switches from a plain
stacked list to a responsive grid where the featured package gets a
highlighted border, badge, and bolder CTA — fixes the tiers looking
inconsistent when only some packages had bullet content.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
FAQ entries were previously hardcoded in the i18n dictionaries; they now live
in a new bilingual `faqs` table with full CRUD in the admin panel, a
reset-to-defaults action seeded with the current 6 questions, and the
homepage (incl. FAQPage JSON-LD) fetches them live per language. Export/
import was also extended to include FAQs and the full image gallery
(brands, projects, photos), which had been missing from that mechanism.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New Dashboard section shows users/sessions/pageviews for the last 7
days plus top pages, fetched server-side via the GA4 Data API using a
Google service account (JWT-bearer flow, no OAuth consent screen
needed) - the same approach WordPress plugins like MonsterInsights use
to surface GA stats inline instead of linking out to analytics.google.com.
Backend: GoogleAnalyticsReporting support class (signs its own JWT with
openssl, exchanges it for an access token, calls runReport - no need
for the full Google API PHP client library for one endpoint) and
AnalyticsController (settings show/update, report fetch). The service
account JSON key is written to a gitignored config file, mirroring the
existing db.php/setup.php pattern for environment-specific secrets.
Admin: new "Dashboard-Anbindung" section in Website Settings (Property
ID + service account JSON paste, independent save action). Dashboard
gracefully shows a setup hint when not yet configured instead of an
error.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a "Statistik" cookie category (alongside the existing external
media one), an admin-configurable Measurement ID field in Website
Settings, and a GoogleAnalytics component that only injects gtag.js
after the visitor has consented - mirrors the existing ExternalEmbed
pattern instead of loading tracking scripts unconditionally.
Also documents Google Analytics in the Datenschutzerklärung (new
section 8, remaining sections renumbered) since introducing a new
data processing purpose requires disclosure.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New admin panel "E-Mail" tab (Einstellungen -> E-Mail) lets the shop
owner configure their SMTP server, test the connection with a real
test email, set which address receives new-inquiry notifications, and
edit both the customer confirmation email and the shop notification
email as templates with {{placeholder}} variables.
Previously SMTP config only lived in a non-DB config.php file (with a
blank host, so mail sending was effectively off) and there was no
customer confirmation email at all - only a hardcoded owner
notification. ContactController now sends both emails using the
DB-configured (or config.php-fallback) settings; mail sending stays
best-effort so a contact form submission never fails because of it.
Backend: new Mailer support class (config resolution, PHPMailer setup,
placeholder rendering) and MailSettingsController (show/update/test),
following the existing WebsiteSettings/DatabaseSettings conventions
(password never returned in plaintext, empty password on save keeps
the existing one). New app_settings columns wired into Schema.php so
the "Datenbankstruktur aktualisieren" admin button picks them up on
existing installs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Mirrors the old site's Bildergalerie structure: a brand tile grid at
/galerie, project tiles within a brand, and a photo grid with a
lightbox for each project. Backed by new gallery_brands/gallery_projects/
gallery_photos tables and admin CRUD pages, gated by a new gallery.manage
permission.
Also fixes a real bug found while testing photo uploads: Apache's
mod_dir was redirecting POST /api/uploads to /api/uploads/ (a trailing
slash) because api/uploads/ exists as a real directory, silently
dropping the multipart body on every image upload across the whole
app. Fixed via DirectorySlash Off in .htaccess.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a Website settings tab (hero image, phone, WhatsApp, email) that
feeds the navbar, footer and contact page via a new SiteSettingsContext,
redesigns the navbar with a bigger centered logo plus phone/WhatsApp/shop
links, and adds a VIN/chassis-number field to the contact form, admin
inbox and notification email.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>