platz zu begrenzt im aufnahmemodus - vollbildmodus möglich? Menü und Kopfleiste ausblenden? Bild aus eigener galerie auswählen - Upload schlägt fehl (zu groß? evtl fehlende Rechte - aber browser hat rechte auf bilder und dateien!) hochgeladene bilder tauchen in der galerie nicht beim filter "Meine Bilder" auf - fotos werden auch nicht gezählt in den stats und achievements zeigen keinen fortschriftt. geteilte fotos: ruft man den Link auf, bekommt man die meldung "Link abgelaufen" der im startbildschirm gewählte name mit Umlauten (Sören) ist nach erneutem aufruf der pwa ohne umlaut (Sren). Aufgabenseite verbessert (Zwischenstand)
167 lines
6.1 KiB
TypeScript
167 lines
6.1 KiB
TypeScript
import React from 'react';
|
|
import { NavLink, useParams, useLocation, Link } from 'react-router-dom';
|
|
import { CheckSquare, GalleryHorizontal, Home, Trophy, Camera } from 'lucide-react';
|
|
import { useEventData } from '../hooks/useEventData';
|
|
import { useTranslation } from '../i18n/useTranslation';
|
|
import { useEventBranding } from '../context/EventBrandingContext';
|
|
|
|
function TabLink({
|
|
to,
|
|
children,
|
|
isActive,
|
|
accentColor,
|
|
radius,
|
|
style,
|
|
compact = false,
|
|
}: {
|
|
to: string;
|
|
children: React.ReactNode;
|
|
isActive: boolean;
|
|
accentColor: string;
|
|
radius: number;
|
|
style?: React.CSSProperties;
|
|
compact?: boolean;
|
|
}) {
|
|
const activeStyle = isActive
|
|
? {
|
|
background: `linear-gradient(135deg, ${accentColor}, ${accentColor}cc)`,
|
|
color: '#ffffff',
|
|
boxShadow: `0 12px 30px ${accentColor}33`,
|
|
borderRadius: radius,
|
|
...style,
|
|
}
|
|
: { borderRadius: radius, ...style };
|
|
|
|
return (
|
|
<NavLink
|
|
to={to}
|
|
className={`
|
|
flex ${compact ? 'h-10 text-[10px]' : 'h-14 text-xs'} flex-col items-center justify-center gap-1 rounded-lg border border-transparent p-2 font-medium transition-all duration-200 ease-out
|
|
touch-manipulation backdrop-blur-md
|
|
${isActive ? 'scale-[1.04]' : 'text-white/70 hover:text-white'}
|
|
`}
|
|
style={activeStyle}
|
|
>
|
|
{children}
|
|
</NavLink>
|
|
);
|
|
}
|
|
|
|
export default function BottomNav() {
|
|
const { token } = useParams();
|
|
const location = useLocation();
|
|
const { event, status } = useEventData();
|
|
const { t } = useTranslation();
|
|
const { branding } = useEventBranding();
|
|
const radius = branding.buttons?.radius ?? 12;
|
|
const buttonStyle = branding.buttons?.style ?? 'filled';
|
|
const linkColor = branding.buttons?.linkColor ?? branding.secondaryColor;
|
|
const surface = branding.palette?.surface ?? branding.backgroundColor;
|
|
|
|
const isReady = status === 'ready' && !!event;
|
|
|
|
if (!token || !isReady) return null;
|
|
|
|
const base = `/e/${encodeURIComponent(token)}`;
|
|
const currentPath = location.pathname;
|
|
|
|
const labels = {
|
|
home: t('navigation.home'),
|
|
tasks: t('navigation.tasks'),
|
|
achievements: t('navigation.achievements'),
|
|
gallery: t('navigation.gallery'),
|
|
upload: t('home.actions.items.upload.label'),
|
|
};
|
|
|
|
const isHomeActive = currentPath === base || currentPath === `/${token}`;
|
|
const isTasksActive = currentPath.startsWith(`${base}/tasks`);
|
|
const isAchievementsActive = currentPath.startsWith(`${base}/achievements`);
|
|
const isGalleryActive = currentPath.startsWith(`${base}/gallery`) || currentPath.startsWith(`${base}/photos`);
|
|
const isUploadActive = currentPath.startsWith(`${base}/upload`);
|
|
|
|
const compact = isUploadActive;
|
|
|
|
return (
|
|
<div
|
|
className={`guest-bottom-nav fixed inset-x-0 bottom-0 z-30 border-t border-white/20 bg-gradient-to-t from-black/40 via-black/20 to-transparent px-4 shadow-xl backdrop-blur-2xl transition-all duration-200 dark:border-white/10 dark:from-gray-950/90 dark:via-gray-900/70 dark:to-gray-900/35 ${
|
|
compact ? 'pb-1 pt-1 translate-y-3' : 'pb-3 pt-2'
|
|
}`}
|
|
>
|
|
<div className="mx-auto flex max-w-lg items-center gap-3">
|
|
<div className="flex flex-1 justify-evenly gap-2">
|
|
<TabLink
|
|
to={`${base}`}
|
|
isActive={isHomeActive}
|
|
accentColor={branding.primaryColor}
|
|
radius={radius}
|
|
compact={compact}
|
|
style={buttonStyle === 'outline' ? { background: 'transparent', color: linkColor, border: `1px solid ${linkColor}` } : undefined}
|
|
>
|
|
<div className="flex flex-col items-center gap-1">
|
|
<Home className="h-5 w-5" aria-hidden />
|
|
<span>{labels.home}</span>
|
|
</div>
|
|
</TabLink>
|
|
<TabLink
|
|
to={`${base}/tasks`}
|
|
isActive={isTasksActive}
|
|
accentColor={branding.primaryColor}
|
|
radius={radius}
|
|
compact={compact}
|
|
style={buttonStyle === 'outline' ? { background: 'transparent', color: linkColor, border: `1px solid ${linkColor}` } : undefined}
|
|
>
|
|
<div className="flex flex-col items-center gap-1">
|
|
<CheckSquare className="h-5 w-5" aria-hidden />
|
|
<span>{labels.tasks}</span>
|
|
</div>
|
|
</TabLink>
|
|
</div>
|
|
|
|
<Link
|
|
to={`${base}/upload`}
|
|
aria-label={labels.upload}
|
|
className={`relative flex ${compact ? 'h-12 w-12' : 'h-16 w-16'} items-center justify-center rounded-full text-white shadow-2xl transition ${
|
|
isUploadActive ? 'scale-105' : 'hover:scale-105'
|
|
}`}
|
|
style={{
|
|
background: `radial-gradient(circle at 20% 20%, ${branding.secondaryColor}, ${branding.primaryColor})`,
|
|
boxShadow: `0 20px 35px ${branding.primaryColor}44`,
|
|
borderRadius: radius,
|
|
}}
|
|
>
|
|
<Camera className="h-6 w-6" aria-hidden />
|
|
</Link>
|
|
|
|
<div className="flex flex-1 justify-evenly gap-2">
|
|
<TabLink
|
|
to={`${base}/achievements`}
|
|
isActive={isAchievementsActive}
|
|
accentColor={branding.primaryColor}
|
|
radius={radius}
|
|
style={buttonStyle === 'outline' ? { background: 'transparent', color: linkColor, border: `1px solid ${linkColor}` } : undefined}
|
|
compact={compact}
|
|
>
|
|
<div className="flex flex-col items-center gap-1">
|
|
<Trophy className="h-5 w-5" aria-hidden />
|
|
<span>{labels.achievements}</span>
|
|
</div>
|
|
</TabLink>
|
|
<TabLink
|
|
to={`${base}/gallery`}
|
|
isActive={isGalleryActive}
|
|
accentColor={branding.primaryColor}
|
|
radius={radius}
|
|
style={buttonStyle === 'outline' ? { background: 'transparent', color: linkColor, border: `1px solid ${linkColor}` } : undefined}
|
|
compact={compact}
|
|
>
|
|
<div className="flex flex-col items-center gap-1">
|
|
<GalleryHorizontal className="h-5 w-5" aria-hidden />
|
|
<span>{labels.gallery}</span>
|
|
</div>
|
|
</TabLink>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|