admin/app/layout.tsx
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

81 lines
2.5 KiB
TypeScript

import '@/styles/globals.css'
import '@/styles/main.scss'
import type { Metadata, Viewport } from 'next'
import { headers } from 'next/headers'
import clsx from 'clsx'
import React from 'react'
import { Providers } from '@/app/providers'
import { pinarFont } from '@/config/fonts'
import { texts } from '@/texts'
import { SITE_URL } from '@/lib/seo/metadata'
import { OfflineBanner } from '@/components/feedback/OfflineBanner'
import { AccountSuspensionBanner } from '@/components/feedback/AccountSuspensionBanner'
import { withBasePath } from '@/constants/images'
import { AppToastProvider } from '@/components/feedback/AppToastProvider'
export const metadata: Metadata = {
metadataBase: new URL(SITE_URL),
title: {
default: `${texts.common.brandName} — Backoffice`,
template: `%s | ${texts.common.brandName} Backoffice`,
},
description: 'پنل مدیریت قبیله',
applicationName: `${texts.common.brandName} Backoffice`,
robots: { index: false, follow: false, nocache: true },
icons: {
icon: [
{ url: withBasePath('/logo.svg'), type: 'image/svg+xml' },
{ url: withBasePath('/icons/icon-192.png'), sizes: '192x192', type: 'image/png' },
{ url: withBasePath('/icons/icon-512.png'), sizes: '512x512', type: 'image/png' },
],
apple: [{ url: withBasePath('/icons/apple-touch-icon.png'), sizes: '180x180' }],
},
formatDetection: {
telephone: false,
},
}
export const viewport: Viewport = {
themeColor: '#6d28d9',
width: 'device-width',
initialScale: 1,
viewportFit: 'cover',
}
export default async function RootLayout({ children }: { children: React.ReactNode }) {
// Reading the nonce-bearing request headers opts the route into dynamic
// rendering so Next can apply the per-request CSP nonce to its scripts.
const nonce = (await headers()).get('x-nonce') ?? undefined
return (
<html
suppressHydrationWarning
className={clsx('!overflow-auto', pinarFont.variable)}
dir="rtl"
lang="fa"
>
<body
suppressHydrationWarning
className="min-h-[100dvh] bg-background font-sans antialiased overflow-hidden"
>
<Providers
themeProps={{
attribute: 'class',
defaultTheme: 'light',
forcedTheme: 'light',
nonce,
}}
>
<AccountSuspensionBanner />
<OfflineBanner />
<div className="min-h-[100dvh]">{children}</div>
<AppToastProvider />
</Providers>
</body>
</html>
)
}