diff --git a/hifi-src/src/assets/leistungen/hero-highlight.webp b/hifi-src/src/assets/leistungen/hero-highlight.webp new file mode 100644 index 0000000..08c32b0 Binary files /dev/null and b/hifi-src/src/assets/leistungen/hero-highlight.webp differ diff --git a/hifi-src/src/assets/photos/gallery-amp-purple.webp b/hifi-src/src/assets/photos/gallery-amp-purple.webp new file mode 100644 index 0000000..0335f0a Binary files /dev/null and b/hifi-src/src/assets/photos/gallery-amp-purple.webp differ diff --git a/hifi-src/src/assets/photos/gallery-dash.webp b/hifi-src/src/assets/photos/gallery-dash.webp new file mode 100644 index 0000000..696d41a Binary files /dev/null and b/hifi-src/src/assets/photos/gallery-dash.webp differ diff --git a/hifi-src/src/assets/photos/gallery-dash2.webp b/hifi-src/src/assets/photos/gallery-dash2.webp new file mode 100644 index 0000000..8a85301 Binary files /dev/null and b/hifi-src/src/assets/photos/gallery-dash2.webp differ diff --git a/hifi-src/src/assets/photos/gallery-rear.webp b/hifi-src/src/assets/photos/gallery-rear.webp new file mode 100644 index 0000000..fb4a10c Binary files /dev/null and b/hifi-src/src/assets/photos/gallery-rear.webp differ diff --git a/hifi-src/src/assets/photos/gallery-speaker.webp b/hifi-src/src/assets/photos/gallery-speaker.webp new file mode 100644 index 0000000..33ed1ea Binary files /dev/null and b/hifi-src/src/assets/photos/gallery-speaker.webp differ diff --git a/hifi-src/src/assets/photos/gallery-subs.webp b/hifi-src/src/assets/photos/gallery-subs.webp new file mode 100644 index 0000000..8241c9d Binary files /dev/null and b/hifi-src/src/assets/photos/gallery-subs.webp differ diff --git a/hifi-src/src/assets/photos/hero-trunk.webp b/hifi-src/src/assets/photos/hero-trunk.webp new file mode 100644 index 0000000..c07a723 Binary files /dev/null and b/hifi-src/src/assets/photos/hero-trunk.webp differ diff --git a/hifi-src/src/components/Footer.jsx b/hifi-src/src/components/Footer.jsx index 57bc03d..88f6eca 100644 --- a/hifi-src/src/components/Footer.jsx +++ b/hifi-src/src/components/Footer.jsx @@ -66,10 +66,10 @@ export default function Footer() {

{t('footer.contactHeading')}

- {phone} + {phone}

- {contactEmail} + {contactEmail}

{ api .get('/auth/me') - .then(setUser) + .then((data) => setUser(data.authenticated === false ? null : data)) .catch(() => setUser(null)) .finally(() => setLoading(false)); }, []); @@ -36,7 +36,7 @@ export function AuthProvider({ children }) { const refreshUser = async () => { const data = await api.get('/auth/me'); - setUser(data); + setUser(data.authenticated === false ? null : data); }; const hasPermission = (permission) => diff --git a/hifi-src/src/pages/public/ContactPage.jsx b/hifi-src/src/pages/public/ContactPage.jsx index b6fce9b..883ebe1 100644 --- a/hifi-src/src/pages/public/ContactPage.jsx +++ b/hifi-src/src/pages/public/ContactPage.jsx @@ -212,11 +212,11 @@ export default function ContactPage() {
{t('contactPage.phone')}
-
{phone}
+
{phone}
{t('contactPage.email')}
-
{contactEmail}
+
{contactEmail}
{t('contactPage.hours')}
diff --git a/hifi-src/src/pages/public/Home.jsx b/hifi-src/src/pages/public/Home.jsx index 3800495..2845435 100644 --- a/hifi-src/src/pages/public/Home.jsx +++ b/hifi-src/src/pages/public/Home.jsx @@ -8,14 +8,14 @@ import StarRating from '../../components/StarRating.jsx'; import TestimonialSlider from '../../components/TestimonialSlider.jsx'; import { useSiteSettings } from '../../context/SiteSettingsContext.jsx'; import { useLanguage } from '../../context/LanguageContext.jsx'; -import defaultHeroImage from '../../assets/photos/gallery-dash2.jpg'; -import galleryAmp from '../../assets/photos/gallery-amp-purple.jpg'; -import gallerySpeaker from '../../assets/photos/gallery-speaker.jpg'; -import gallerySubs from '../../assets/photos/gallery-subs.jpg'; -import galleryDash from '../../assets/photos/gallery-dash.jpg'; -import galleryTrunk from '../../assets/photos/hero-trunk.jpg'; -import galleryRear from '../../assets/photos/gallery-rear.jpg'; -import heroHighlight from '../../assets/leistungen/hero-highlight.jpg'; +import defaultHeroImage from '../../assets/photos/gallery-dash2.webp'; +import galleryAmp from '../../assets/photos/gallery-amp-purple.webp'; +import gallerySpeaker from '../../assets/photos/gallery-speaker.webp'; +import gallerySubs from '../../assets/photos/gallery-subs.webp'; +import galleryDash from '../../assets/photos/gallery-dash.webp'; +import galleryTrunk from '../../assets/photos/hero-trunk.webp'; +import galleryRear from '../../assets/photos/gallery-rear.webp'; +import heroHighlight from '../../assets/leistungen/hero-highlight.webp'; import youtubeVideo from '../../assets/videos/youtube-hintergrund.mp4'; const YOUTUBE_URL = 'https://www.youtube.com/@hifiplanet2812'; @@ -23,7 +23,18 @@ const YOUTUBE_VIDEO_ID = 'ostj2mKDVUc'; const brands = ['Focal', 'Helix', 'Ground Zero', 'Mosconi', 'Hifonics', 'Audison']; -const galleryImages = [gallerySubs, galleryAmp, gallerySpeaker, galleryDash, galleryRear, galleryTrunk]; +// Native Pixelmasse der Bilder unten - nur fuer die width/height-Attribute auf , +// damit der Browser vor dem Laden Platz reserviert (verhindert Layout-Sprung). Die +// tatsaechliche Anzeigegroesse wird weiterhin komplett per CSS (aspect-square etc.) +// bestimmt, diese Werte muessen also nicht exakt zum gerenderten Seitenverhaeltnis passen. +const galleryImages = [ + { src: gallerySubs, width: 900, height: 600 }, + { src: galleryAmp, width: 900, height: 600 }, + { src: gallerySpeaker, width: 900, height: 1200 }, + { src: galleryDash, width: 900, height: 814 }, + { src: galleryRear, width: 900, height: 1200 }, + { src: galleryTrunk, width: 1920, height: 1280 }, +]; export default function Home() { const { hero_image_path: heroImagePath } = useSiteSettings(); @@ -33,7 +44,7 @@ export default function Home() { const stats = t('home.stats'); const steps = t('home.steps'); const galleryAlts = t('home.galleryAlts'); - const gallery = galleryImages.map((src, i) => ({ src, alt: galleryAlts[i] })); + const gallery = galleryImages.map((img, i) => ({ ...img, alt: galleryAlts[i] })); const testimonials = t('home.testimonials'); const faqs = t('home.faqs'); @@ -221,7 +232,14 @@ export default function Home() {
- {t('home.craftImageAlt')} + {t('home.craftImageAlt')}

{t('home.craftEyebrow')}

@@ -241,7 +259,14 @@ export default function Home() {

- {t('home.adviceImageAlt')} + {t('home.adviceImageAlt')}
@@ -257,7 +282,14 @@ export default function Home() {
{gallery.map((img, i) => ( - {img.alt} + {img.alt} ))}
@@ -289,7 +321,14 @@ export default function Home() {
- {t('home.moreImageAlt')} + {t('home.moreImageAlt')}

{t('home.moreTitle')}

diff --git a/hifi/.htaccess b/hifi/.htaccess index 4efb384..52d6f9c 100644 --- a/hifi/.htaccess +++ b/hifi/.htaccess @@ -1,6 +1,20 @@ RewriteEngine On RewriteBase / +# Lang-lebiger Cache fuer gehashte Build-Assets (Vite haengt bei jeder Aenderung einen +# neuen Hash an den Dateinamen) und hochgeladene Bilder (bekommen beim Upload einen +# zufaelligen Dateinamen) - beide aendern sich unter derselben URL nie, "immutable" ist +# hier also sicher. Ueber mod_headers statt mod_expires, da Letzteres nicht ueberall +# aktiviert ist. + + + Header set Cache-Control "public, max-age=31536000, immutable" + + + Header set Cache-Control "public, max-age=31536000, immutable" + + + # Ohne dies leitet Apache (mod_dir) Anfragen an /api/uploads mit einem 301 auf # /api/uploads/ um, bevor mod_rewrite greifen kann - weil api/uploads/ als # echtes Verzeichnis existiert. Bei einem POST mit Datei-Upload geht dabei der diff --git a/hifi/api/src/Controllers/AuthController.php b/hifi/api/src/Controllers/AuthController.php index a28b72a..8eb294e 100644 --- a/hifi/api/src/Controllers/AuthController.php +++ b/hifi/api/src/Controllers/AuthController.php @@ -154,8 +154,12 @@ class AuthController public static function me(): void { + // Bewusst kein 401 hier: /auth/me wird auf JEDER oeffentlichen Seite aufgerufen, + // um den Login-Status zu pruefen - "nicht angemeldet" ist der Normalfall fuer + // Besucher, kein Fehler. Ein 401 erzeugt in der Browser-Konsole/DevTools trotz + // JS-seitigem catch() einen sichtbaren Netzwerk-Fehler (bemaengelt u.a. Lighthouse). if (empty($_SESSION['admin_id'])) { - Http::error('Nicht angemeldet', 401); + Http::send(['authenticated' => false]); } Http::send(self::userPayload((int) $_SESSION['admin_id'], $_SESSION['admin_username'])); }