AdminPageFrame.tsx 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. import type { ReactNode, JSX } from 'react';
  2. import React from 'react';
  3. import dynamic from 'next/dynamic';
  4. import Head from 'next/head';
  5. import type { Container } from 'unstated';
  6. import { Provider } from 'unstated';
  7. // Dynamic imports to avoid SSR issues with admin-only components
  8. const AdminLayout = dynamic(() => import('~/components/Layout/AdminLayout'), { ssr: false });
  9. const ForbiddenPage = dynamic(() => import('~/client/components/Admin/ForbiddenPage').then(mod => mod.ForbiddenPage), { ssr: false });
  10. export type AnyContainer = Container<Record<string, unknown>>;
  11. export interface AdminPageFrameProps {
  12. /** Page <title> value (after generateCustomTitle) */
  13. title: string;
  14. /** Visible heading shown in AdminLayout header */
  15. componentTitle?: string;
  16. /** Access control flag */
  17. isAccessDeniedForNonAdminUser: boolean;
  18. /** Optional injected unstated containers */
  19. containers?: AnyContainer[];
  20. children?: ReactNode;
  21. }
  22. /**
  23. * Admin page frame that centralizes:
  24. * - Forbidden guard
  25. * - AdminLayout wrapping
  26. * - <Head><title /></Head>
  27. * - Unstated Provider injection
  28. */
  29. export const AdminPageFrame = ({
  30. title,
  31. componentTitle,
  32. isAccessDeniedForNonAdminUser,
  33. containers,
  34. children,
  35. }: AdminPageFrameProps): JSX.Element => {
  36. if (isAccessDeniedForNonAdminUser) {
  37. return <ForbiddenPage />;
  38. }
  39. return (
  40. <Provider inject={containers ?? []}>
  41. <AdminLayout componentTitle={componentTitle ?? title}>
  42. <Head>
  43. <title>{title}</title>
  44. </Head>
  45. {children}
  46. </AdminLayout>
  47. </Provider>
  48. );
  49. };
  50. export default AdminPageFrame;