- Added public gallery API with token-expiry enforcement, branding payload, cursor pagination, and per-photo download stream (app/Http/Controllers/Api/EventPublicController.php:1, routes/api.php:16). 410 is returned when the package gallery duration has lapsed.

- Served the guest PWA at /g/{token} and introduced a mobile-friendly gallery page with lazy-loaded thumbnails, themed colors, lightbox, and download links plus new gallery data client (resources/js/guest/pages/PublicGalleryPage.tsx:1, resources/js/guest/services/galleryApi.ts:1, resources/js/guest/router.tsx:1). Added i18n strings for the public gallery experience (resources/js/guest/i18n/messages.ts:1).
- Ensured checkout step changes snap back to the progress bar on mobile via smooth scroll anchoring (resources/ js/pages/marketing/checkout/CheckoutWizard.tsx:1).
- Enabled tenant admins to export all approved event photos through a new download action that streams a ZIP archive, with translations and routing in place (app/Http/Controllers/Tenant/EventPhotoArchiveController.php:1, app/Filament/Resources/EventResource.php:1, routes/web.php:1, resources/lang/de/admin.php:1, resources/lang/en/admin.php:1).
This commit is contained in:
Codex Agent
2025-10-17 23:24:06 +02:00
parent 5817270c35
commit ae9b9160ac
20 changed files with 1410 additions and 3 deletions

View File

@@ -1,4 +1,4 @@
import React, { useMemo } from "react";
import React, { useMemo, useRef, useEffect } from "react";
import { useTranslation } from 'react-i18next';
import { Steps } from "@/components/ui/Steps";
import { Button } from "@/components/ui/button";
@@ -54,6 +54,8 @@ const baseStepConfig: { id: CheckoutStepId; titleKey: string; descriptionKey: st
const WizardBody: React.FC<{ stripePublishableKey: string; paypalClientId: string; privacyHtml: string }> = ({ stripePublishableKey, paypalClientId, privacyHtml }) => {
const { t } = useTranslation('marketing');
const { currentStep, nextStep, previousStep } = useCheckoutWizard();
const progressRef = useRef<HTMLDivElement | null>(null);
const hasMountedRef = useRef(false);
const stepConfig = useMemo(() =>
baseStepConfig.map(step => ({
@@ -73,9 +75,29 @@ const WizardBody: React.FC<{ stripePublishableKey: string; paypalClientId: strin
return (currentIndex / (stepConfig.length - 1)) * 100;
}, [currentIndex, stepConfig]);
useEffect(() => {
if (typeof window === 'undefined' || !progressRef.current) {
return;
}
if (!hasMountedRef.current) {
hasMountedRef.current = true;
return;
}
const element = progressRef.current;
const rect = element.getBoundingClientRect();
const scrollTop = window.scrollY + rect.top - 16; // slightly above the progress bar
window.scrollTo({
top: Math.max(scrollTop, 0),
behavior: 'smooth',
});
}, [currentStep]);
return (
<div className="space-y-8">
<div className="space-y-4">
<div ref={progressRef} className="space-y-4">
<Progress value={progress} />
<Steps steps={stepConfig} currentStep={currentIndex >= 0 ? currentIndex : 0} />
</div>