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;