diff --git a/app/[locale]/about/page.tsx b/app/[locale]/about/page.tsx index 4668238..516c43c 100644 --- a/app/[locale]/about/page.tsx +++ b/app/[locale]/about/page.tsx @@ -1,5 +1,5 @@ import type { Metadata } from "next"; -import { getDictionary, isLocale, type Locale } from "@/lib/i18n"; +import { getActiveLocale, getDictionary, isLocale, type Locale } from "@/lib/i18n"; import { buildPageMetadata } from "@/lib/metadata"; import { isComingSoonMode } from "@/lib/site"; import { notFound, redirect } from "next/navigation"; @@ -9,7 +9,7 @@ export function generateMetadata({ params }: { params: { locale: string } }): Me return {}; } - return buildPageMetadata(params.locale, "about"); + return buildPageMetadata(getActiveLocale(params.locale), "about"); } export default function AboutPage({ params }: { params: { locale: string } }) { @@ -18,10 +18,10 @@ export default function AboutPage({ params }: { params: { locale: string } }) { } if (isComingSoonMode()) { - redirect(`/${params.locale}`); + redirect("/"); } - const locale = params.locale as Locale; + const locale = getActiveLocale(params.locale as Locale); const dictionary = getDictionary(locale); return ( diff --git a/app/[locale]/contact/page.tsx b/app/[locale]/contact/page.tsx index 5a6a5dd..3306f36 100644 --- a/app/[locale]/contact/page.tsx +++ b/app/[locale]/contact/page.tsx @@ -1,5 +1,5 @@ import type { Metadata } from "next"; -import { getDictionary, isLocale, type Locale } from "@/lib/i18n"; +import { getActiveLocale, getDictionary, isLocale, type Locale } from "@/lib/i18n"; import { buildPageMetadata } from "@/lib/metadata"; import { getContactChannels, isComingSoonMode } from "@/lib/site"; import { notFound, redirect } from "next/navigation"; @@ -9,7 +9,7 @@ export function generateMetadata({ params }: { params: { locale: string } }): Me return {}; } - return buildPageMetadata(params.locale, "contact"); + return buildPageMetadata(getActiveLocale(params.locale), "contact"); } export default function ContactPage({ params }: { params: { locale: string } }) { @@ -18,10 +18,10 @@ export default function ContactPage({ params }: { params: { locale: string } }) } if (isComingSoonMode()) { - redirect(`/${params.locale}`); + redirect("/"); } - const locale = params.locale as Locale; + const locale = getActiveLocale(params.locale as Locale); const dictionary = getDictionary(locale); const channels = getContactChannels(dictionary.contact); diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx index 8898c0f..434850f 100644 --- a/app/[locale]/layout.tsx +++ b/app/[locale]/layout.tsx @@ -1,14 +1,15 @@ import type { ReactNode } from "react"; -import { notFound } from "next/navigation"; +import { notFound, redirect } from "next/navigation"; import BottomNav from "@/components/BottomNav"; import SiteFooter from "@/components/SiteFooter"; import SiteHeader from "@/components/SiteHeader"; -import { getDictionary, getDirection, isLocale, locales, type Locale } from "@/lib/i18n"; +import { getActiveLocale, getDictionary, getDirection, getEnabledLocales, isLocale, type Locale } from "@/lib/i18n"; +import { isComingSoonMode } from "@/lib/site"; export const dynamicParams = false; export function generateStaticParams() { - return locales.map((locale) => ({ locale })); + return getEnabledLocales().map((locale) => ({ locale })); } export default function LocaleLayout({ @@ -22,7 +23,11 @@ export default function LocaleLayout({ notFound(); } - const locale = params.locale as Locale; + if (isComingSoonMode()) { + redirect("/"); + } + + const locale = getActiveLocale(params.locale as Locale); const dictionary = getDictionary(locale); return ( diff --git a/app/[locale]/page.tsx b/app/[locale]/page.tsx index d6d75e7..3af8851 100644 --- a/app/[locale]/page.tsx +++ b/app/[locale]/page.tsx @@ -1,6 +1,6 @@ import type { Metadata } from "next"; import HeroSection from "@/components/HeroSection"; -import { getDictionary, isLocale, type Locale } from "@/lib/i18n"; +import { getActiveLocale, getDictionary, isLocale, type Locale } from "@/lib/i18n"; import { buildPageMetadata } from "@/lib/metadata"; import { notFound } from "next/navigation"; @@ -9,7 +9,7 @@ export function generateMetadata({ params }: { params: { locale: string } }): Me return {}; } - return buildPageMetadata(params.locale, "home"); + return buildPageMetadata(getActiveLocale(params.locale), "home"); } export default function HomePage({ params }: { params: { locale: string } }) { @@ -17,7 +17,7 @@ export default function HomePage({ params }: { params: { locale: string } }) { notFound(); } - const locale = params.locale as Locale; + const locale = getActiveLocale(params.locale as Locale); const dictionary = getDictionary(locale); return ; diff --git a/app/globals.css b/app/globals.css index a47222c..95f82a1 100644 --- a/app/globals.css +++ b/app/globals.css @@ -200,6 +200,22 @@ a { animation: fade-up 460ms ease both; } +.coming-soon-page { + min-height: 100vh; + display: grid; + place-items: center; + padding: 2rem; +} + +.coming-soon-title { + margin: 0; + font-size: clamp(2.5rem, 10vw, 5.5rem); + font-weight: 800; + line-height: 1; + letter-spacing: -0.05em; + text-align: center; +} + .panel { background: var(--surface); border: 1px solid var(--line-strong); @@ -305,6 +321,72 @@ a { grid-template-columns: repeat(3, minmax(0, 1fr)); } +.coming-soon { + position: relative; + overflow: hidden; + display: grid; + grid-template-columns: minmax(0, 1.4fr) minmax(280px, 0.8fr); + gap: 1.4rem; + align-items: stretch; + min-height: min(72vh, 760px); + padding: clamp(1.5rem, 3vw, 2.4rem); +} + +.coming-soon::before { + content: ""; + position: absolute; + inset: 0; + background: + radial-gradient(circle at 18% 20%, color-mix(in srgb, var(--brand) 18%, transparent), transparent 28%), + radial-gradient(circle at 86% 18%, color-mix(in srgb, var(--brand-2) 18%, transparent), transparent 24%), + linear-gradient(135deg, color-mix(in srgb, var(--surface-2) 42%, transparent), transparent 66%); + pointer-events: none; +} + +.coming-soon-copy, +.coming-soon-meta { + position: relative; + z-index: 1; +} + +.coming-soon-copy { + display: grid; + align-content: center; + gap: 0.25rem; +} + +.coming-soon-copy .cta-row { + margin-top: 1.65rem; +} + +.coming-soon-meta { + display: grid; + gap: 0.9rem; + align-content: end; +} + +.coming-soon-card { + border-radius: 20px; + border: 1px solid color-mix(in srgb, var(--line-strong) 92%, transparent); + background: color-mix(in srgb, var(--card-bg) 88%, transparent); + backdrop-filter: blur(16px); + padding: 1.1rem 1rem; +} + +.coming-soon-value { + display: block; + font-size: 1rem; + font-weight: 800; + color: var(--text); +} + +.coming-soon-label { + display: block; + margin-top: 0.28rem; + color: var(--muted); + font-size: 0.9rem; +} + .split-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } @@ -531,6 +613,24 @@ a { } .split-grid, + .stat-grid, + .coming-soon { + grid-template-columns: 1fr; + } + + .coming-soon { + min-height: auto; + } + + .coming-soon-meta, + .coming-soon-copy { + align-content: start; + } + + .coming-soon-meta { + gap: 0.8rem; + } + .stat-grid { grid-template-columns: 1fr; } diff --git a/app/layout.tsx b/app/layout.tsx index 04c5442..ff911e7 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,31 +1,46 @@ import type { Metadata } from "next"; import type { ReactNode } from "react"; import { siteConfig } from "@/lib/site"; +import { isComingSoonMode } from "@/lib/site"; import "./globals.css"; -export const metadata: Metadata = { - metadataBase: new URL(siteConfig.siteUrl), - title: { - default: "Diyaa", - template: "%s | Diyaa", - }, - description: "Bilingual professional website built for private-server deployment.", - applicationName: "Diyaa", - authors: [{ name: "Diyaa" }], - creator: "Diyaa", - publisher: "Diyaa", - alternates: { - languages: { - ar: "/ar", - en: "/en", - "x-default": "/ar", - }, - }, -}; +export function generateMetadata(): Metadata { + const comingSoon = isComingSoonMode(); -const themeScript = ` + return { + metadataBase: new URL(siteConfig.siteUrl), + title: { + default: "Diyaa", + template: "%s | Diyaa", + }, + description: comingSoon + ? "Minimal coming soon page for the upcoming launch." + : "Bilingual professional website built for private-server deployment.", + applicationName: "Diyaa", + authors: [{ name: "Diyaa" }], + creator: "Diyaa", + publisher: "Diyaa", + alternates: { + languages: comingSoon + ? { + en: "/", + "x-default": "/", + } + : { + ar: "/ar", + en: "/en", + "x-default": "/ar", + }, + }, + }; +} + +function getThemeScript() { + const comingSoon = isComingSoonMode(); + + return ` (() => { - const locale = window.location.pathname.split("/").filter(Boolean)[0] === "en" ? "en" : "ar"; + const locale = ${comingSoon ? '"en"' : 'window.location.pathname.split("/").filter(Boolean)[0] === "en" ? "en" : "ar"'}; const direction = locale === "ar" ? "rtl" : "ltr"; document.documentElement.lang = locale; document.documentElement.dir = direction; @@ -38,12 +53,15 @@ const themeScript = ` } })(); `; +} export default function RootLayout({ children }: { children: ReactNode }) { + const comingSoon = isComingSoonMode(); + return ( - + -