import { cache } from 'react'; import { use } from 'react'; import { cookies } from 'next/headers'; import { redirect } from 'next/navigation'; import { Fish, FileSignature, Building2 } from 'lucide-react'; import * as z from 'zod'; import { getSupabaseServerClient } from '@kit/supabase/server-client'; import { TeamAccountWorkspaceContextProvider } from '@kit/team-accounts/components'; import { NavigationConfigSchema } from '@kit/ui/navigation-schema'; import { Page, PageMobileNavigation, PageNavigation } from '@kit/ui/page'; import { SidebarProvider } from '@kit/ui/sidebar'; import { AppLogo } from '~/components/app-logo'; import { getTeamAccountSidebarConfig } from '~/config/team-account-navigation.config'; // local imports import { TeamAccountLayoutMobileNavigation } from './_components/team-account-layout-mobile-navigation'; import { TeamAccountLayoutSidebar } from './_components/team-account-layout-sidebar'; import { TeamAccountNavigationMenu } from './_components/team-account-navigation-menu'; import { loadTeamWorkspace } from './_lib/server/team-account-workspace.loader'; type TeamWorkspaceLayoutProps = React.PropsWithChildren<{ params: Promise<{ account: string }>; }>; function TeamWorkspaceLayout({ children, params }: TeamWorkspaceLayoutProps) { const account = use(params).account; const state = use(getLayoutState(account)); if (state.style === 'sidebar') { return {children}; } return {children}; } /** * Query account_settings.features for a given account slug. * Cached per-request so multiple calls don't hit the DB twice. */ const getAccountFeatures = cache(async (accountSlug: string) => { const client = getSupabaseServerClient(); const { data: accountData } = await client .from('accounts') .select('id') .eq('slug', accountSlug) .single(); if (!accountData) return {}; const { data: settings } = await client .from('account_settings') .select('features') .eq('account_id', accountData.id) .maybeSingle(); return (settings?.features as Record) ?? {}; }); /** * Inject per-account feature routes (e.g. Fischerei) into the parsed * navigation config. The entry is inserted right after "Veranstaltungen". */ function injectAccountFeatureRoutes( config: z.output, account: string, features: Record, ): z.output { if (!features.fischerei && !features.meetings && !features.verband) return config; const featureEntries: Array<{ label: string; path: string; Icon: React.ReactNode; }> = []; if (features.fischerei) { featureEntries.push({ label: 'common.routes.fischerei', path: `/home/${account}/fischerei`, Icon: , }); } if (features.meetings) { featureEntries.push({ label: 'common.routes.meetings', path: `/home/${account}/meetings`, Icon: , }); } if (features.verband) { featureEntries.push({ label: 'common.routes.verband', path: `/home/${account}/verband`, Icon: , }); } return { ...config, routes: config.routes.map((group) => { if (!('children' in group)) return group; const eventsIndex = group.children.findIndex( (child) => child.label === 'common.routes.events', ); if (eventsIndex === -1) return group; const newChildren = [...group.children]; newChildren.splice(eventsIndex + 1, 0, ...featureEntries); return { ...group, children: newChildren }; }), }; } async function SidebarLayout({ account, children, }: React.PropsWithChildren<{ account: string; }>) { const [data, state, features] = await Promise.all([ loadTeamWorkspace(account), getLayoutState(account), getAccountFeatures(account), ]); if (!data) { redirect('/'); } const baseConfig = getTeamAccountSidebarConfig(account); const config = injectAccountFeatureRoutes(baseConfig, account, features); const accounts = data.accounts.map(({ name, slug, picture_url }) => ({ label: name, value: slug, image: picture_url, })); return (
{children}
); } async function HeaderLayout({ account, children, }: React.PropsWithChildren<{ account: string; }>) { const [data, features] = await Promise.all([ loadTeamWorkspace(account), getAccountFeatures(account), ]); const baseConfig = getTeamAccountSidebarConfig(account); const config = injectAccountFeatureRoutes(baseConfig, account, features); return ( {children} ); } async function getLayoutState(account: string) { const cookieStore = await cookies(); const config = getTeamAccountSidebarConfig(account); const LayoutStyleSchema = z .enum(['sidebar', 'header', 'custom']) .default(config.style); const sidebarOpenCookie = cookieStore.get('sidebar_state'); const layoutCookie = cookieStore.get('layout-style'); const layoutStyle = LayoutStyleSchema.safeParse(layoutCookie?.value); const sidebarOpenCookieValue = sidebarOpenCookie ? sidebarOpenCookie.value === 'true' : !config.sidebarCollapsed; const style = layoutStyle.success ? layoutStyle.data : config.style; return { open: sidebarOpenCookieValue, style, }; } export default TeamWorkspaceLayout;