udpate for coming soon

This commit is contained in:
diyaa
2026-03-13 03:45:13 +01:00
parent 243a182d33
commit cdf83cd466
32 changed files with 907 additions and 143 deletions
+21 -39
View File
@@ -1,9 +1,9 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import type { CommonContent } from "@/content/types";
import { locales, type Locale } from "@/lib/i18n";
import { type Locale } from "@/lib/i18n";
import { isComingSoonMode } from "@/lib/site";
type BottomNavProps = {
locale: Locale;
@@ -11,67 +11,49 @@ type BottomNavProps = {
};
export default function BottomNav({ locale, common }: BottomNavProps) {
const pathname = usePathname();
const segments = pathname.split("/").filter(Boolean);
if (isComingSoonMode()) {
return (
<nav aria-label={common.navLabel} className="bottom-nav">
<div className="container bottom-nav-inner">
<div className="bottom-nav-links">
<Link href={`/${locale}`} className="active" aria-current="page">
<span aria-hidden className="nav-icon">
</span>
{common.nav.home}
</Link>
</div>
</div>
</nav>
);
}
const normalizedPath = pathname !== "/" && pathname.endsWith("/") ? pathname.slice(0, -1) : pathname;
const homePath = `/${locale}`;
const aboutPath = `/${locale}/about`;
const contactPath = `/${locale}/contact`;
const isHome = normalizedPath === homePath;
const isAbout = normalizedPath === aboutPath || normalizedPath.startsWith(`${aboutPath}/`);
const isContact = normalizedPath === contactPath || normalizedPath.startsWith(`${contactPath}/`);
const targetLocale: Locale = locale === "ar" ? "en" : "ar";
const switchPath = () => {
const nextSegments = [...segments];
if (nextSegments.length === 0) {
return `/${targetLocale}`;
}
if (locales.includes(nextSegments[0] as Locale)) {
nextSegments[0] = targetLocale;
} else {
nextSegments.unshift(targetLocale);
}
return `/${nextSegments.join("/")}`;
};
return (
<nav aria-label={common.navLabel} className="bottom-nav">
<div className="container bottom-nav-inner">
<div className="bottom-nav-links">
<Link href={homePath} className={isHome ? "active" : undefined} aria-current={isHome ? "page" : undefined}>
<Link href={homePath} className="active" aria-current="page">
<span aria-hidden className="nav-icon">
</span>
{common.nav.home}
</Link>
<Link href={aboutPath} className={isAbout ? "active" : undefined} aria-current={isAbout ? "page" : undefined}>
<Link href={aboutPath}>
<span aria-hidden className="nav-icon">
</span>
{common.nav.about}
</Link>
<Link
href={contactPath}
className={isContact ? "active" : undefined}
aria-current={isContact ? "page" : undefined}
>
<Link href={contactPath}>
<span aria-hidden className="nav-icon">
</span>
{common.nav.contact}
</Link>
<Link href={switchPath()} className="lang-toggle" aria-label={common.languageSwitcherLabel}>
<span aria-hidden className="nav-icon">
</span>
{targetLocale.toUpperCase()}
</Link>
</div>
</div>
</nav>
+21 -10
View File
@@ -1,6 +1,7 @@
import Link from "next/link";
import type { CommonContent, HomeContent } from "@/content/types";
import type { CommonContent, HomeContent, HomeVariantContent } from "@/content/types";
import type { Locale } from "@/lib/i18n";
import { getEmailHref, isComingSoonMode } from "@/lib/site";
type HeroSectionProps = {
locale: Locale;
@@ -9,23 +10,33 @@ type HeroSectionProps = {
};
export default function HeroSection({ locale, home, common }: HeroSectionProps) {
const activeHome: HomeVariantContent = isComingSoonMode() ? home.comingSoon : home.full;
const emailHref = getEmailHref();
return (
<section className="hero panel">
<p className="eyebrow">{home.kicker}</p>
<h1>{home.title}</h1>
<p className="lead">{home.description}</p>
<p className="availability-badge">{activeHome.badge}</p>
<p className="eyebrow">{activeHome.kicker}</p>
<h1>{activeHome.title}</h1>
<p className="lead">{activeHome.description}</p>
<div className="cta-row">
<Link href={`/${locale}/contact`} className="cta-btn">
{home.primaryCta}
</Link>
<Link href={`/${locale}/about`} className="ghost-btn">
{common.nav.about}
{emailHref ? (
<a href={emailHref} className="cta-btn">
{activeHome.primaryCta}
</a>
) : (
<span className="cta-btn is-disabled" aria-disabled="true">
{activeHome.primaryCta}
</span>
)}
<Link href={isComingSoonMode() ? `/${locale}` : `/${locale}/about`} className="ghost-btn">
{activeHome.secondaryCta}
</Link>
</div>
<div className="stat-grid">
{home.highlights.map((item) => (
{activeHome.highlights.map((item) => (
<article key={item.label} className="stat-card">
<span className="stat-value">{item.value}</span>
<span className="stat-label">{item.label}</span>
+1 -1
View File
@@ -30,7 +30,7 @@ export default function LanguageSwitcher({ locale, label }: LanguageSwitcherProp
};
return (
<div className="language-switcher" aria-label={label}>
<div className="language-switcher" role="group" aria-label={label}>
{locales.map((item) => {
const active = item === locale;
+4 -1
View File
@@ -12,7 +12,10 @@ export default function SiteFooter({ locale, common }: SiteFooterProps) {
return (
<footer className="site-footer">
<div className="container footer-content">
<p>{common.footerRights.replace("{year}", String(year))}</p>
<div className="footer-copy">
<p>{common.footerRights.replace("{year}", String(year))}</p>
<p>{common.footerBuiltWith}</p>
</div>
<p className="locale-badge">{locale.toUpperCase()}</p>
</div>
</footer>
+27 -4
View File
@@ -1,7 +1,9 @@
import Link from "next/link";
import LanguageSwitcher from "@/components/LanguageSwitcher";
import ThemeToggle from "@/components/ThemeToggle";
import type { Locale } from "@/lib/i18n";
import type { CommonContent } from "@/content/types";
import { isComingSoonMode } from "@/lib/site";
type SiteHeaderProps = {
locale: Locale;
@@ -9,13 +11,34 @@ type SiteHeaderProps = {
};
export default function SiteHeader({ locale, common }: SiteHeaderProps) {
const isComingSoon = isComingSoonMode();
return (
<header className="site-header">
<div className="container bar">
<Link href={`/${locale}`} className="brand">
{common.siteTitle}
</Link>
<ThemeToggle />
<div className="brand-block">
<Link href={`/${locale}`} className="brand">
{common.siteTitle}
</Link>
<span className="brand-tagline">{common.siteTagline}</span>
</div>
{!isComingSoon ? (
<nav className="header-nav" aria-label={common.navLabel}>
<Link href={`/${locale}`}>{common.nav.home}</Link>
<Link href={`/${locale}/about`}>{common.nav.about}</Link>
<Link href={`/${locale}/contact`}>{common.nav.contact}</Link>
</nav>
) : null}
<div className="header-actions">
<LanguageSwitcher locale={locale} label={common.languageSwitcherLabel} />
<ThemeToggle
label={common.themeToggleLabel}
lightLabel={common.themeLight}
darkLabel={common.themeDark}
/>
</div>
</div>
</header>
);
+9 -3
View File
@@ -6,11 +6,17 @@ type Theme = "dark" | "light";
const STORAGE_KEY = "theme";
type ThemeToggleProps = {
label: string;
lightLabel: string;
darkLabel: string;
};
function isTheme(value: string | null): value is Theme {
return value === "dark" || value === "light";
}
export default function ThemeToggle() {
export default function ThemeToggle({ label, lightLabel, darkLabel }: ThemeToggleProps) {
const [theme, setTheme] = useState<Theme>("dark");
useEffect(() => {
@@ -34,9 +40,9 @@ export default function ThemeToggle() {
type="button"
onClick={handleToggle}
className="theme-toggle"
aria-label={`Switch to ${theme === "dark" ? "light" : "dark"} mode`}
aria-label={`${label}: ${theme === "dark" ? lightLabel : darkLabel}`}
>
<span className="theme-toggle-label">{theme === "dark" ? "Light" : "Dark"}</span>
<span className="theme-toggle-label">{theme === "dark" ? lightLabel : darkLabel}</span>
</button>
);
}