- Updated the User model to implement Filament’s tenancy contracts - Seeded a ready-to-use demo tenant (user, tenant, active package, purchase) - Introduced a branded, translated 403 error page to replace the generic forbidden message for unauthorised admin hits - Removed the public “Register” links from the marketing header - hardened join event logic and improved error handling in the guest pwa.
112 lines
3.8 KiB
TypeScript
112 lines
3.8 KiB
TypeScript
import React from 'react';
|
|
import AppearanceToggleDropdown from '@/components/appearance-dropdown';
|
|
import { User } from 'lucide-react';
|
|
import { useEventData } from '../hooks/useEventData';
|
|
import { useOptionalEventStats } from '../context/EventStatsContext';
|
|
import { useOptionalGuestIdentity } from '../context/GuestIdentityContext';
|
|
import { SettingsSheet } from './settings-sheet';
|
|
|
|
export default function Header({ slug, title = '' }: { slug?: string; title?: string }) {
|
|
const statsContext = useOptionalEventStats();
|
|
const identity = useOptionalGuestIdentity();
|
|
|
|
if (!slug) {
|
|
const guestName = identity?.name && identity?.hydrated ? identity.name : null;
|
|
return (
|
|
<div className="sticky top-0 z-20 flex items-center justify-between border-b bg-white/70 px-4 py-2 backdrop-blur dark:bg-black/40">
|
|
<div className="flex flex-col">
|
|
<div className="font-semibold">{title}</div>
|
|
{guestName && (
|
|
<span className="text-xs text-muted-foreground">Hi {guestName}</span>
|
|
)}
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<AppearanceToggleDropdown />
|
|
<SettingsSheet />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const { event, status } = useEventData();
|
|
const guestName =
|
|
identity && identity.eventKey === slug && identity.hydrated && identity.name ? identity.name : null;
|
|
|
|
if (status === 'loading') {
|
|
return (
|
|
<div className="sticky top-0 z-20 flex items-center justify-between border-b bg-white/70 px-4 py-2 backdrop-blur dark:bg-black/40">
|
|
<div className="font-semibold">Lade Event...</div>
|
|
<div className="flex items-center gap-2">
|
|
<AppearanceToggleDropdown />
|
|
<SettingsSheet />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (status !== 'ready' || !event) {
|
|
return null;
|
|
}
|
|
|
|
const stats =
|
|
statsContext && statsContext.eventKey === slug ? statsContext : undefined;
|
|
|
|
const getEventAvatar = (event: any) => {
|
|
if (event.type?.icon) {
|
|
return (
|
|
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-pink-100 text-pink-600 text-xl">
|
|
{event.type.icon}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const getInitials = (name: string) => {
|
|
const words = name.split(' ');
|
|
if (words.length >= 2) {
|
|
return `${words[0][0]}${words[1][0]}`.toUpperCase();
|
|
}
|
|
return name.substring(0, 2).toUpperCase();
|
|
};
|
|
|
|
return (
|
|
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-pink-100 text-pink-600 font-semibold text-sm">
|
|
{getInitials(event.name)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<div className="sticky top-0 z-20 flex items-center justify-between border-b bg-white/70 px-4 py-2 backdrop-blur dark:bg-black/40">
|
|
<div className="flex items-center gap-3">
|
|
{getEventAvatar(event)}
|
|
<div className="flex flex-col">
|
|
<div className="font-semibold text-base">{event.name}</div>
|
|
{guestName && (
|
|
<span className="text-xs text-muted-foreground">Hi {guestName}</span>
|
|
)}
|
|
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
{stats && (
|
|
<>
|
|
<span className="flex items-center gap-1">
|
|
<User className="h-3 w-3" />
|
|
<span>{stats.onlineGuests} online</span>
|
|
</span>
|
|
<span className="text-muted-foreground">|</span>
|
|
<span className="flex items-center gap-1">
|
|
<span className="font-medium">{stats.tasksSolved}</span> Aufgaben geloest
|
|
</span>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<AppearanceToggleDropdown />
|
|
<SettingsSheet />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export {}
|