fix: use getLayout to persist layout state across pages

This commit is contained in:
Henry
2025-07-18 16:48:45 +01:00
parent c58c8c753c
commit 076fe1d922
10 changed files with 94 additions and 56 deletions

View File

@@ -10,6 +10,7 @@ import { authClient } from "@kan/auth/client";
import { useClickOutside } from "~/hooks/useClickOutside"; import { useClickOutside } from "~/hooks/useClickOutside";
import { useTheme } from "~/providers/theme"; import { useTheme } from "~/providers/theme";
import { WorkspaceProvider } from "~/providers/workspace";
import SideNavigation from "./SideNavigation"; import SideNavigation from "./SideNavigation";
interface DashboardProps { interface DashboardProps {
@@ -18,6 +19,20 @@ interface DashboardProps {
hasRightPanel?: boolean; hasRightPanel?: boolean;
} }
export function getDashboardLayout(
page: React.ReactElement,
rightPanel?: React.ReactNode,
hasRightPanel = false,
) {
return (
<WorkspaceProvider>
<Dashboard rightPanel={rightPanel} hasRightPanel={hasRightPanel}>
{page}
</Dashboard>
</WorkspaceProvider>
);
}
export default function Dashboard({ export default function Dashboard({
children, children,
rightPanel, rightPanel,
@@ -26,6 +41,7 @@ export default function Dashboard({
const theme = useTheme(); const theme = useTheme();
const { data: session, isPending: sessionLoading } = authClient.useSession(); const { data: session, isPending: sessionLoading } = authClient.useSession();
const [isSideNavOpen, setIsSideNavOpen] = useState(false); const [isSideNavOpen, setIsSideNavOpen] = useState(false);
const [isRightPanelOpen, setIsRightPanelOpen] = useState(false); const [isRightPanelOpen, setIsRightPanelOpen] = useState(false);

View File

@@ -88,7 +88,7 @@ export default function SideNavigation({
<> <>
<nav <nav
className={twMerge( className={twMerge(
"flex h-full flex-col justify-between bg-light-100 pr-3 transition-all duration-300 dark:bg-dark-100", "flex h-full flex-col justify-between bg-light-100 pr-3 dark:bg-dark-100",
isCollapsed ? "w-auto" : "w-64", isCollapsed ? "w-auto" : "w-64",
)} )}
> >
@@ -96,7 +96,7 @@ export default function SideNavigation({
<div className="flex h-14 items-center justify-between pb-[18px] pt-1.5"> <div className="flex h-14 items-center justify-between pb-[18px] pt-1.5">
{!isCollapsed && ( {!isCollapsed && (
<Link href="/" className="block"> <Link href="/" className="block">
<h1 className="pl-2 text-lg font-bold tracking-tight text-neutral-700 dark:text-dark-950"> <h1 className="pl-2 text-lg font-bold tracking-tight text-neutral-700 dark:text-dark-1000">
kan.bn kan.bn
</h1> </h1>
</Link> </Link>

View File

@@ -1,8 +1,9 @@
import "~/styles/globals.css"; import "~/styles/globals.css";
import "~/utils/i18n"; import "~/utils/i18n";
import type { Viewport } from "next"; import type { NextPage, Viewport } from "next";
import type { AppType } from "next/app"; import type { AppProps, AppType } from "next/app";
import type { ReactElement, ReactNode } from "react";
import { Plus_Jakarta_Sans } from "next/font/google"; import { Plus_Jakarta_Sans } from "next/font/google";
import Script from "next/script"; import Script from "next/script";
import { env } from "next-runtime-env"; import { env } from "next-runtime-env";
@@ -34,7 +35,15 @@ export const viewport: Viewport = {
userScalable: false, userScalable: false,
}; };
const MyApp: AppType = ({ Component, pageProps }) => { export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode;
};
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout;
};
const MyApp: AppType = ({ Component, pageProps }: AppPropsWithLayout) => {
const posthogKey = env("NEXT_PUBLIC_POSTHOG_KEY"); const posthogKey = env("NEXT_PUBLIC_POSTHOG_KEY");
useEffect(() => { useEffect(() => {
@@ -50,6 +59,8 @@ const MyApp: AppType = ({ Component, pageProps }) => {
} }
}, [posthogKey]); }, [posthogKey]);
const getLayout = Component.getLayout ?? ((page) => page);
return ( return (
<> <>
<style jsx global>{` <style jsx global>{`
@@ -75,10 +86,10 @@ const MyApp: AppType = ({ Component, pageProps }) => {
<PopupProvider> <PopupProvider>
{posthogKey ? ( {posthogKey ? (
<PostHogProvider client={posthog}> <PostHogProvider client={posthog}>
<Component {...pageProps} /> {getLayout(<Component {...pageProps} />)}
</PostHogProvider> </PostHogProvider>
) : ( ) : (
<Component {...pageProps} /> getLayout(<Component {...pageProps} />)
)} )}
</PopupProvider> </PopupProvider>
</ModalProvider> </ModalProvider>

View File

@@ -1,15 +1,17 @@
import { WorkspaceProvider } from "~/providers/workspace"; import type { NextPageWithLayout } from "~/pages/_app";
import Dashboard from "~/components/Dashboard"; import { getDashboardLayout } from "~/components/Dashboard";
import Popup from "~/components/Popup"; import Popup from "~/components/Popup";
import BoardView from "~/views/board"; import BoardView from "~/views/board";
export default function BoardPage() { const BoardPage: NextPageWithLayout = () => {
return ( return (
<WorkspaceProvider> <>
<Dashboard> <BoardView />
<BoardView />
</Dashboard>
<Popup /> <Popup />
</WorkspaceProvider> </>
); );
} };
BoardPage.getLayout = (page) => getDashboardLayout(page);
export default BoardPage;

View File

@@ -1,15 +1,17 @@
import { WorkspaceProvider } from "~/providers/workspace"; import type { NextPageWithLayout } from "../_app";
import Dashboard from "~/components/Dashboard"; import { getDashboardLayout } from "~/components/Dashboard";
import Popup from "~/components/Popup"; import Popup from "~/components/Popup";
import BoardsView from "~/views/boards"; import BoardsView from "~/views/boards";
export default function BoardsPage() { const BoardsPage: NextPageWithLayout = () => {
return ( return (
<WorkspaceProvider> <>
<Dashboard> <BoardsView />
<BoardsView />
</Dashboard>
<Popup /> <Popup />
</WorkspaceProvider> </>
); );
} };
BoardsPage.getLayout = (page) => getDashboardLayout(page);
export default BoardsPage;

View File

@@ -1,15 +1,18 @@
import Dashboard from "~/components/Dashboard"; import type { NextPageWithLayout } from "~/pages/_app";
import { getDashboardLayout } from "~/components/Dashboard";
import Popup from "~/components/Popup"; import Popup from "~/components/Popup";
import { WorkspaceProvider } from "~/providers/workspace";
import CardView, { CardRightPanel } from "~/views/card"; import CardView, { CardRightPanel } from "~/views/card";
export default function CardPage() { const CardPage: NextPageWithLayout = () => {
return ( return (
<WorkspaceProvider> <>
<Dashboard hasRightPanel rightPanel={<CardRightPanel />}> <CardView />
<CardView />
</Dashboard>
<Popup /> <Popup />
</WorkspaceProvider> </>
); );
} };
CardPage.getLayout = (page) =>
getDashboardLayout(page, <CardRightPanel />, true);
export default CardPage;

View File

@@ -1,15 +1,17 @@
import { WorkspaceProvider } from "~/providers/workspace"; import type { NextPageWithLayout } from "~/pages/_app";
import Dashboard from "~/components/Dashboard"; import { getDashboardLayout } from "~/components/Dashboard";
import Popup from "~/components/Popup"; import Popup from "~/components/Popup";
import MembersView from "~/views/members"; import MembersView from "~/views/members";
export default function MembersPage() { const MembersPage: NextPageWithLayout = () => {
return ( return (
<WorkspaceProvider> <>
<Dashboard> <MembersView />
<MembersView />
</Dashboard>
<Popup /> <Popup />
</WorkspaceProvider> </>
); );
} };
MembersPage.getLayout = (page) => getDashboardLayout(page);
export default MembersPage;

View File

@@ -1,15 +1,17 @@
import { WorkspaceProvider } from "~/providers/workspace"; import type { NextPageWithLayout } from "~/pages/_app";
import Dashboard from "~/components/Dashboard"; import { getDashboardLayout } from "~/components/Dashboard";
import SettingsView from "~/views/settings";
import Popup from "~/components/Popup"; import Popup from "~/components/Popup";
import SettingsView from "~/views/settings";
export default function SettingsPage() { const SettingsPage: NextPageWithLayout = () => {
return ( return (
<WorkspaceProvider> <>
<Dashboard> <SettingsView />
<SettingsView />
</Dashboard>
<Popup /> <Popup />
</WorkspaceProvider> </>
); );
} };
SettingsPage.getLayout = (page) => getDashboardLayout(page);
export default SettingsPage;

View File

@@ -44,7 +44,7 @@ export function BoardsList() {
); );
return ( return (
<div className="grid h-fit w-full grid-cols-1 gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-7"> <div className="grid h-fit w-full grid-cols-1 gap-4 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 2xl:grid-cols-7">
{data?.map((board) => ( {data?.map((board) => (
<Link key={board.publicId} href={`boards/${board.publicId}`}> <Link key={board.publicId} href={`boards/${board.publicId}`}>
<div className="align-center relative mr-5 flex h-[150px] w-full items-center justify-center rounded-md border border-dashed border-light-400 bg-light-50 shadow-sm hover:bg-light-200 dark:border-dark-600 dark:bg-dark-50 dark:hover:bg-dark-100"> <div className="align-center relative mr-5 flex h-[150px] w-full items-center justify-center rounded-md border border-dashed border-light-400 bg-light-50 shadow-sm hover:bg-light-200 dark:border-dark-600 dark:bg-dark-50 dark:hover:bg-dark-100">

View File

@@ -21,8 +21,8 @@ export default function BoardsPage() {
return ( return (
<> <>
<PageHead title={t`Boards | ${workspace.name ?? "Workspace"}`} /> <PageHead title={t`Boards | ${workspace.name ?? "Workspace"}`} />
<div className="relative m-auto h-full max-w-[1600px] px-5 py-6 md:px-8 md:py-8"> <PatternedBackground />
<PatternedBackground /> <div className="m-auto h-full max-w-[1200px] p-6 px-5 md:px-28 md:py-12">
<div className="relative z-10 mb-8 flex w-full items-center justify-between"> <div className="relative z-10 mb-8 flex w-full items-center justify-between">
<h1 className="font-bold tracking-tight text-neutral-900 dark:text-dark-1000 sm:text-[1.2rem]"> <h1 className="font-bold tracking-tight text-neutral-900 dark:text-dark-1000 sm:text-[1.2rem]">
{t`Boards`} {t`Boards`}