- Introduced `resolveDashboardGuild` to handle guild resolution for both managed and owner-panel bypass scenarios. - Updated `GuildLayout` and `WelcomePage` components to utilize the new guild resolution logic, improving access handling. - Enhanced `DashboardShell` to display an informational banner for users accessing guilds via the owner-panel bypass. - Added localization support for owner bypass messages in English and German. - Updated authentication logic to allow owner-panel users to access guilds without Discord Manage Guild permission.
85 lines
3.4 KiB
TypeScript
85 lines
3.4 KiB
TypeScript
'use client';
|
|
|
|
import type { DashboardGuild, SessionUser } from '@nexumi/shared';
|
|
import { Menu } from 'lucide-react';
|
|
import { useState, type ReactNode } from 'react';
|
|
import { DashboardCommandPalette } from '@/components/layout/dashboard-command-palette';
|
|
import { HashScroll } from '@/components/layout/field-anchor';
|
|
import { ServerSwitcher } from '@/components/layout/server-switcher';
|
|
import { Sidebar, SidebarNav } from '@/components/layout/sidebar';
|
|
import { UserMenu } from '@/components/layout/user-menu';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Sheet, SheetContent, SheetTitle, SheetTrigger } from '@/components/ui/sheet';
|
|
import { useTranslations } from '@/components/locale-provider';
|
|
|
|
interface DashboardShellProps {
|
|
guildId: string;
|
|
currentGuild: DashboardGuild;
|
|
otherGuilds: DashboardGuild[];
|
|
user: SessionUser;
|
|
showOwnerLink?: boolean;
|
|
/** True when an Owner-panel user opened a guild they do not manage on Discord. */
|
|
ownerBypass?: boolean;
|
|
children: ReactNode;
|
|
}
|
|
|
|
export function DashboardShell({
|
|
guildId,
|
|
currentGuild,
|
|
otherGuilds,
|
|
user,
|
|
showOwnerLink = false,
|
|
ownerBypass = false,
|
|
children
|
|
}: DashboardShellProps) {
|
|
const t = useTranslations();
|
|
const [mobileOpen, setMobileOpen] = useState(false);
|
|
|
|
return (
|
|
<div className="flex min-h-screen bg-background">
|
|
<HashScroll />
|
|
<Sidebar guildId={guildId} />
|
|
<div className="flex min-w-0 flex-1 flex-col">
|
|
<header className="sticky top-0 z-40 flex h-14 items-center gap-3 border-b border-border bg-background/80 px-4 backdrop-blur-md supports-[backdrop-filter]:bg-background/70 sm:px-6">
|
|
<Sheet open={mobileOpen} onOpenChange={setMobileOpen}>
|
|
<SheetTrigger asChild>
|
|
<Button variant="ghost" size="icon" className="lg:hidden" aria-label={t('search.openNav')}>
|
|
<Menu className="size-5" />
|
|
</Button>
|
|
</SheetTrigger>
|
|
<SheetContent side="left" className="p-0">
|
|
<SheetTitle className="sr-only">{t('nav.dashboard')}</SheetTitle>
|
|
<SidebarNav guildId={guildId} onNavigate={() => setMobileOpen(false)} />
|
|
</SheetContent>
|
|
</Sheet>
|
|
|
|
<ServerSwitcher currentGuild={currentGuild} otherGuilds={otherGuilds} />
|
|
<div className="ml-auto flex min-w-0 items-center gap-3">
|
|
<div className="min-w-0 flex-1 sm:flex-initial">
|
|
<DashboardCommandPalette guildId={guildId} />
|
|
</div>
|
|
<UserMenu user={user} showOwnerLink={showOwnerLink} />
|
|
</div>
|
|
</header>
|
|
<main className="flex-1 px-4 py-6 sm:px-6 sm:py-8">
|
|
<div className="mx-auto w-full max-w-[1200px] space-y-4">
|
|
{ownerBypass ? (
|
|
<div className="rounded-md border border-amber-500/40 bg-amber-500/10 px-4 py-3 text-sm text-amber-100">
|
|
<p className="font-medium">{t('dashboard.ownerBypass.title')}</p>
|
|
<p className="mt-1 text-amber-100/80">{t('dashboard.ownerBypass.description')}</p>
|
|
<a
|
|
href="/owner/guilds"
|
|
className="mt-2 inline-block text-sm font-medium text-amber-50 underline underline-offset-4 hover:text-white"
|
|
>
|
|
{t('dashboard.ownerBypass.backToOwner')}
|
|
</a>
|
|
</div>
|
|
) : null}
|
|
{children}
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|