"use client";
import { useSession } from "next-auth/react";
import { usePathname, useRouter } from "next/navigation";
import type { Dispatch, ReactElement, ReactNode, SetStateAction } from "react";
import React, { cloneElement } from "react";
import { Toaster } from "sonner";
import { useRedirectToLoginIfUnauthenticated } from "@calcom/features/auth/lib/hooks/useRedirectToLoginIfUnauthenticated";
import { useRedirectToOnboardingIfNeeded } from "@calcom/features/auth/lib/hooks/useRedirectToOnboardingIfNeeded";
import { KBarContent, KBarRoot } from "@calcom/features/kbar/Kbar";
import TimezoneChangeDialog from "@calcom/features/settings/TimezoneChangeDialog";
import { useFormbricks } from "@calcom/lib/formbricks-client";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import classNames from "@calcom/ui/classNames";
import { Button } from "@calcom/ui/components/button";
import { ErrorBoundary } from "@calcom/ui/components/errorBoundary";
import { SkeletonText } from "@calcom/ui/components/skeleton";
import { SideBarContainer } from "./SideBar";
import { TopNavContainer } from "./TopNav";
import { BannerContainer } from "./banners/LayoutBanner";
import { useBanners } from "./banners/useBanners";
import { MobileNavigationContainer } from "./navigation/Navigation";
import { useAppTheme } from "./useAppTheme";
const Layout = (props: LayoutProps) => {
const { banners, bannersHeight } = useBanners();
const pathname = usePathname();
useFormbricks();
return (
<>
{banners && !props.isPlatformUser &&
}
{props.SidebarContainer ? (
cloneElement(props.SidebarContainer, { bannersHeight })
) : (
)}
>
);
};
type DrawerState = [isOpen: boolean, setDrawerOpen: Dispatch>];
export type LayoutProps = {
centered?: boolean;
title?: string;
description?: string;
heading?: ReactNode;
subtitle?: ReactNode;
headerClassName?: string;
children: ReactNode;
CTA?: ReactNode;
large?: boolean;
MobileNavigationContainer?: ReactNode;
SidebarContainer?: ReactElement;
TopNavContainer?: ReactNode;
drawerState?: DrawerState;
HeadingLeftIcon?: ReactNode;
backPath?: string | boolean; // renders back button to specified path
// use when content needs to expand with flex
flexChildrenContainer?: boolean;
isPublic?: boolean;
withoutMain?: boolean;
// Gives the ability to include actions to the right of the heading
actions?: JSX.Element;
beforeCTAactions?: JSX.Element;
afterHeading?: ReactNode;
smallHeading?: boolean;
isPlatformUser?: boolean;
};
const KBarWrapper = ({ children, withKBar = false }: { withKBar: boolean; children: React.ReactNode }) =>
withKBar ? (
{children}
) : (
<>{children}>
);
const PublicShell = (props: LayoutProps) => {
const { status } = useSession();
return (
);
};
export default function Shell(props: LayoutProps) {
// if a page is unauthed and isPublic is true, the redirect does not happen.
useRedirectToLoginIfUnauthenticated(props.isPublic);
useRedirectToOnboardingIfNeeded();
useAppTheme();
return !props.isPublic ? (
) : (
);
}
export function ShellMain(props: LayoutProps) {
const router = useRouter();
const { isLocaleReady } = useLocale();
return (
<>
{(props.heading || !!props.backPath) && (
{!!props.backPath && (
typeof props.backPath === "string" ? router.push(props.backPath as string) : router.back()
}
StartIcon="arrow-left"
aria-label="Go Back"
className="rounded-md ltr:mr-2 rtl:ml-2"
data-testid="go-back-button"
/>
)}
{props.heading && (
)}
)}
{props.afterHeading && <>{props.afterHeading}>}
{props.children}
>
);
}
function MainContainer({
isPlatformUser,
MobileNavigationContainer: MobileNavigationContainerProp = (
),
TopNavContainer: TopNavContainerProp = ,
...props
}: LayoutProps) {
return (
{/* show top navigation for md and smaller (tablet and phones) */}
{TopNavContainerProp}
{!props.withoutMain ? {props.children} : props.children}
{/* show bottom navigation for md and smaller (tablet and phones) on pages where back button doesn't exist */}
{!props.backPath ? MobileNavigationContainerProp : null}
);
}