Files
sass-mohfarawati/app/[locale]/(site)/about/page.tsx
T
2026-03-10 06:09:03 +01:00

345 lines
13 KiB
TypeScript

import type { Metadata } from "next";
import {
ArrowRight,
Blocks,
BriefcaseBusiness,
Compass,
Layers3,
MessageSquareMore,
Sparkles,
Workflow,
} from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { getTranslations } from "next-intl/server";
import { Container } from "@/components/layout/container";
import { PageHero } from "@/components/layout/page-hero";
import { SiteLogo } from "@/components/layout/site-logo";
import { MotionFade } from "@/components/motion-fade";
import { AppCard } from "@/components/ui/app-card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { CardContent } from "@/components/ui/card";
import { getSiteSettings, getSiteSettingsMediaBindings } from "@/lib/app-config";
import { getLocalizedPath, resolveLocale } from "@/lib/locale";
import { buildLocalizedMetadata } from "@/lib/metadata";
type AboutPageProps = {
params: {
locale: string;
};
};
export const dynamic = "force-dynamic";
export async function generateMetadata({
params: { locale },
}: AboutPageProps): Promise<Metadata> {
const localeKey = resolveLocale(locale);
const t = await getTranslations({ locale: localeKey, namespace: "aboutPage" });
return await buildLocalizedMetadata({
locale: localeKey,
pathname: "/about",
title: t("title"),
description: t("intro"),
});
}
export default async function AboutPage({ params: { locale } }: AboutPageProps) {
const localeKey = resolveLocale(locale);
const [t, siteSettings, mediaBindings] = await Promise.all([
getTranslations({ locale: localeKey, namespace: "aboutPage" }),
getSiteSettings(),
getSiteSettingsMediaBindings(),
]);
const siteName = siteSettings.locales[localeKey].siteName;
const valueItems = [
{
icon: Compass,
title: t("valueA"),
text: t("valueAText"),
},
{
icon: Layers3,
title: t("valueB"),
text: t("valueBText"),
},
{
icon: MessageSquareMore,
title: t("valueC"),
text: t("valueCText"),
},
];
const processItems = [
{
icon: Sparkles,
title: t("processOne"),
text: t("processOneText"),
},
{
icon: Blocks,
title: t("processTwo"),
text: t("processTwoText"),
},
{
icon: Workflow,
title: t("processThree"),
text: t("processThreeText"),
},
];
const statItems = [
{
value: t("statOneValue"),
label: t("statOneLabel"),
text: t("statOneText"),
},
{
value: t("statTwoValue"),
label: t("statTwoLabel"),
text: t("statTwoText"),
},
{
value: t("statThreeValue"),
label: t("statThreeLabel"),
text: t("statThreeText"),
},
];
const highlightItems = [t("highlightOne"), t("highlightTwo"), t("highlightThree")];
return (
<>
<PageHero
locale={localeKey}
badge={t("heroBadge")}
title={t("heroTitle")}
description={t("intro")}
/>
<Container className="relative z-10 flex flex-col gap-6 pb-12 lg:pb-16">
<MotionFade delay={0.05}>
<section className="grid gap-6 lg:grid-cols-12">
<AppCard className="overflow-hidden lg:col-span-7">
<CardContent className="p-6 lg:p-8">
<Badge
variant="outline"
className="border-border/70 bg-background/80 px-3 py-1.5 text-[0.72rem] font-semibold tracking-[0.18em] text-foreground/60"
>
{t("storyEyebrow")}
</Badge>
<h2 className="mt-5 max-w-[11ch] text-balance text-4xl font-semibold leading-[0.92] tracking-[-0.06em] text-foreground sm:text-5xl lg:text-6xl">
{t("storyTitle")}
</h2>
<div className="mt-6 grid gap-5 lg:grid-cols-[minmax(0,1fr)_minmax(12rem,15rem)] lg:items-start">
<div className="space-y-4">
<p className="text-sm leading-7 text-muted-foreground sm:text-base">
{t("storyTextOne")}
</p>
<p className="text-sm leading-7 text-muted-foreground sm:text-base">
{t("storyTextTwo")}
</p>
</div>
<div className="grid gap-3">
{highlightItems.map((item) => (
<AppCard key={item} padding="sm" className="text-sm font-medium leading-6 text-foreground/84">
{item}
</AppCard>
))}
</div>
</div>
<div className="mt-8 flex flex-wrap gap-3">
<Button asChild size="lg" className="min-w-[11rem]">
<Link href={getLocalizedPath(localeKey, "/contact")}>
{t("primaryCta")}
<ArrowRight className="h-4 w-4" />
</Link>
</Button>
<Button
asChild
size="lg"
variant="outline"
className="min-w-[11rem] border-border/80 bg-background/70 hover:bg-background"
>
<Link href={getLocalizedPath(localeKey, "/portfolio")}>
{t("secondaryCta")}
</Link>
</Button>
</div>
</CardContent>
</AppCard>
<AppCard className="overflow-hidden lg:col-span-5">
<CardContent className="p-5">
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
<AppCard className="sm:col-span-2 lg:col-span-4">
<CardContent className="p-4">
<div className="flex items-center justify-between gap-4">
<div>
<p className="text-xs font-medium uppercase tracking-[0.18em] text-foreground/48">
{t("logoEyebrow")}
</p>
<p className="mt-2 max-w-[16rem] text-sm leading-6 text-muted-foreground">
{t("logoText")}
</p>
</div>
<SiteLogo
alt={siteName}
priority
lightLogoUrl={mediaBindings.siteLogoLight?.url}
darkLogoUrl={mediaBindings.siteLogoDark?.url}
className="h-10 sm:h-11"
/>
</div>
</CardContent>
</AppCard>
<AppCard padding="none" className="overflow-hidden sm:col-span-1 lg:col-span-2">
<div className="relative min-h-[9rem] overflow-hidden rounded-nested">
<Image
src="/uploads/portfolio/demo-cover.svg"
alt={t("galleryAltOne")}
fill
sizes="(min-width: 1024px) 16rem, (min-width: 640px) 50vw, 100vw"
className="object-cover"
/>
</div>
</AppCard>
<AppCard className="min-h-[9rem] sm:col-span-1 lg:col-span-2">
<CardContent className="flex h-full flex-col justify-between p-4">
<div className="flex items-center gap-3 text-foreground/86">
<BriefcaseBusiness className="h-5 w-5 text-brand-primary" />
<span className="text-sm font-medium">{t("availabilityLabel")}</span>
</div>
<p className="text-sm leading-6 text-muted-foreground">
{t("availabilityText")}
</p>
</CardContent>
</AppCard>
<AppCard padding="none" className="overflow-hidden sm:col-span-2 lg:col-span-4">
<div className="relative min-h-[13rem] overflow-hidden rounded-nested">
<Image
src="/uploads/portfolio/demo-cover.svg"
alt={t("galleryAltTwo")}
fill
sizes="(min-width: 1024px) 28rem, (min-width: 640px) 100vw, 100vw"
className="object-cover"
/>
</div>
</AppCard>
</div>
</CardContent>
</AppCard>
</section>
</MotionFade>
<MotionFade delay={0.1}>
<section className="grid gap-6 lg:grid-cols-12">
<AppCard className="lg:col-span-3">
<CardContent className="flex h-full flex-col p-6">
<Badge
variant="outline"
className="w-fit border-border/70 bg-background/80 px-3 py-1.5 text-[0.72rem] font-semibold tracking-[0.18em] text-foreground/60"
>
{t("valuesEyebrow")}
</Badge>
<h2 className="mt-4 text-3xl font-semibold tracking-[-0.04em] text-foreground sm:text-4xl">
{t("valuesTitle")}
</h2>
<p className="mt-4 max-w-[28rem] text-sm leading-7 text-muted-foreground">
{t("valuesIntro")}
</p>
</CardContent>
</AppCard>
{valueItems.map((item) => {
const Icon = item.icon;
return (
<AppCard key={item.title} className="lg:col-span-3">
<CardContent className="p-5">
<div className="flex h-11 w-11 items-center justify-center rounded-full bg-[linear-gradient(135deg,hsl(var(--hero-left)/0.42),hsl(var(--hero-right)/0.26))]">
<Icon className="h-5 w-5 text-foreground/80" />
</div>
<h3 className="mt-4 text-lg font-semibold text-foreground">{item.title}</h3>
<p className="mt-3 text-sm leading-6 text-muted-foreground">{item.text}</p>
</CardContent>
</AppCard>
);
})}
</section>
</MotionFade>
<MotionFade delay={0.15}>
<section className="grid gap-6 lg:grid-cols-12">
<AppCard className="lg:col-span-4">
<CardContent className="flex h-full flex-col p-6 lg:p-7">
<Badge
variant="outline"
className="w-fit border-border/70 bg-background/80 px-3 py-1.5 text-[0.72rem] font-semibold tracking-[0.18em] text-foreground/60"
>
{t("processEyebrow")}
</Badge>
<h2 className="mt-4 text-3xl font-semibold tracking-[-0.04em] text-foreground sm:text-4xl">
{t("processTitle")}
</h2>
<p className="mt-4 max-w-[28rem] text-sm leading-7 text-muted-foreground">
{t("processIntro")}
</p>
<div className="mt-6 grid gap-3 sm:grid-cols-3 lg:grid-cols-1">
{statItems.map((item) => (
<AppCard key={item.label} layer="single" padding="sm" className="shadow-none">
<p className="text-3xl font-semibold tracking-[-0.05em] text-foreground">
{item.value}
</p>
<h3 className="mt-2 text-sm font-semibold uppercase tracking-[0.14em] text-foreground/58">
{item.label}
</h3>
<p className="mt-2 text-sm leading-6 text-muted-foreground">{item.text}</p>
</AppCard>
))}
</div>
</CardContent>
</AppCard>
<div className="grid gap-6 lg:col-span-8 lg:grid-cols-2">
{processItems.map((item, index) => {
const Icon = item.icon;
const isWide = index === 0;
return (
<AppCard key={item.title} className={isWide ? "lg:col-span-2" : ""}>
<CardContent className="p-5 lg:p-6">
<div className="flex items-center justify-between gap-4">
<span className="inline-flex h-8 min-w-[2.5rem] items-center justify-center rounded-pill border border-border/70 px-3 text-xs font-semibold tracking-[0.18em] text-foreground/60">
0{index + 1}
</span>
<Icon className="h-5 w-5 text-brand-primary" />
</div>
<h3 className="mt-5 text-xl font-semibold text-foreground">{item.title}</h3>
<p className="mt-3 max-w-[34rem] text-sm leading-6 text-muted-foreground">
{item.text}
</p>
</CardContent>
</AppCard>
);
})}
</div>
</section>
</MotionFade>
</Container>
</>
);
}