Initial deploy
هذا الالتزام موجود في:
412
components/home-page.tsx
Normal file
412
components/home-page.tsx
Normal file
@@ -0,0 +1,412 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { ProjectCard } from "@/components/project-card";
|
||||
import { SectionHeading } from "@/components/section-heading";
|
||||
import { SiteShell } from "@/components/site-shell";
|
||||
import {
|
||||
getBasePath,
|
||||
getDirection,
|
||||
portfolioContent,
|
||||
resumeFile,
|
||||
sharedProfile,
|
||||
type Language,
|
||||
} from "@/data/portfolio";
|
||||
|
||||
export function HomePage({ language }: { language: Language }) {
|
||||
const dir = getDirection(language);
|
||||
const t = portfolioContent[language];
|
||||
const founderName = language === "ar" ? sharedProfile.founderNameAr : sharedProfile.founderNameEn;
|
||||
const address = language === "ar" ? sharedProfile.addressAr : sharedProfile.address;
|
||||
const heroImageAlt =
|
||||
language === "ar"
|
||||
? "لوحة بورتفوليو لمجمع إعلامي ومبنى التلفزيون"
|
||||
: "Portfolio board for Media Complex and TV Building";
|
||||
const basePath = getBasePath(language);
|
||||
const hasResume = resumeFile.available;
|
||||
const contactCards = [
|
||||
{
|
||||
kind: "phone",
|
||||
label: t.ui.phone,
|
||||
value: sharedProfile.phone,
|
||||
href: `tel:${sharedProfile.phone.replace(/\s+/g, "")}`,
|
||||
},
|
||||
{
|
||||
kind: "whatsapp",
|
||||
label: t.ui.whatsapp,
|
||||
value: sharedProfile.phone,
|
||||
href: sharedProfile.whatsappHref,
|
||||
},
|
||||
{
|
||||
kind: "email",
|
||||
label: t.ui.email,
|
||||
value: sharedProfile.email,
|
||||
href: `mailto:${sharedProfile.email}`,
|
||||
wide: true,
|
||||
},
|
||||
{ kind: "location", label: t.ui.basedIn, value: address, href: sharedProfile.locationHref },
|
||||
{ kind: "facebook", label: t.ui.facebook, value: sharedProfile.facebook, href: sharedProfile.facebookHref },
|
||||
];
|
||||
const hasWideContactCard = contactCards.some((item) => item.wide);
|
||||
|
||||
return (
|
||||
<SiteShell language={language}>
|
||||
<div className="top-wash pointer-events-none absolute inset-x-0 top-0 h-[720px]" />
|
||||
|
||||
<main className="site-container flex flex-col gap-12 py-6 md:gap-14 md:py-8 lg:gap-16 lg:py-10">
|
||||
<section className="grid gap-5">
|
||||
<div className="grid gap-8 lg:grid-cols-[0.92fr_1.08fr] lg:items-stretch">
|
||||
<div className="section-shell hero-glow line-frame h-full">
|
||||
<div className="section-padding editorial-grid flex h-full flex-col justify-center">
|
||||
<p className="section-kicker">{t.ui.editorialPortfolio}</p>
|
||||
<h1
|
||||
className={`type-hero-title mt-6 max-w-4xl text-[var(--color-ink)] ${
|
||||
language === "en" ? "type-hero-title-latin" : ""
|
||||
}`}
|
||||
>
|
||||
{founderName}
|
||||
</h1>
|
||||
|
||||
<div className="mt-8 max-w-2xl space-y-4">
|
||||
<p className="type-body-responsive uppercase tracking-[0.18em] text-[var(--color-muted)]">
|
||||
{t.ui.architectureEngineer}
|
||||
</p>
|
||||
<p className="type-body-responsive text-[var(--color-muted)]">
|
||||
{t.hero.subtitle} . {t.hero.supportingLine}
|
||||
</p>
|
||||
<p className="type-body-responsive max-w-xl text-[var(--color-muted)]">
|
||||
{t.hero.intro}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={`hero-action-group mt-10 flex flex-wrap gap-4 ${dir === "rtl" ? "justify-start" : ""}`}>
|
||||
<Link href={`${basePath}#projects`} className="button-primary">
|
||||
{t.ui.viewProjects}
|
||||
</Link>
|
||||
{hasResume ? (
|
||||
<Link href={resumeFile.href} className="button-secondary" download>
|
||||
{t.ui.downloadCv}
|
||||
</Link>
|
||||
) : (
|
||||
<Link href={`${basePath}/resume`} className="button-secondary">
|
||||
{t.ui.onlineResume}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="image-frame h-full">
|
||||
<div className="relative aspect-[5/6] overflow-hidden rounded-[28px]">
|
||||
<Image
|
||||
src={sharedProfile.heroImage}
|
||||
alt={heroImageAlt}
|
||||
fill
|
||||
priority
|
||||
sizes="(max-width: 1024px) 100vw, 52vw"
|
||||
className="object-cover saturate-[1.05] brightness-[0.96]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-5 md:grid-cols-2">
|
||||
<div className="soft-card">
|
||||
<p className="eyebrow-note">{t.ui.profile}</p>
|
||||
<p className="type-card-title-small display-face mt-4 text-[var(--color-ink)]">
|
||||
{t.hero.profileBlurb}
|
||||
</p>
|
||||
</div>
|
||||
<div className="gradient-card">
|
||||
<p className="eyebrow-note">{t.hero.featuredProjectTitle}</p>
|
||||
<p className="type-card-title display-face mt-4 text-[var(--color-ink)]">
|
||||
{t.featured.projects[0]?.location ?? address}
|
||||
</p>
|
||||
<p className="type-body mt-3 text-[var(--color-muted)]">{t.hero.featuredProjectText}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="about" className="grid gap-8 lg:grid-cols-[1.05fr_0.95fr]">
|
||||
<div className="section-shell">
|
||||
<div className="section-padding">
|
||||
<SectionHeading
|
||||
eyebrow={t.ui.navAbout}
|
||||
title={t.about.title}
|
||||
description={t.about.description}
|
||||
/>
|
||||
|
||||
<div className="type-body-editorial mt-8 max-w-2xl space-y-5 text-[var(--color-muted)]">
|
||||
{t.about.paragraphs.map((paragraph) => (
|
||||
<p key={paragraph}>{paragraph}</p>
|
||||
))}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="philosophy" className="grid gap-5 scroll-mt-28">
|
||||
<div className="gradient-card">
|
||||
<p className="eyebrow-note">{t.ui.designPhilosophy}</p>
|
||||
<p className="type-card-title display-face mt-4 text-[var(--color-ink)]">
|
||||
"{t.philosophy.quote}"
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="soft-card">
|
||||
<p className="eyebrow-note">{t.ui.materialsMood}</p>
|
||||
<p className="type-body-editorial mt-4 text-[var(--color-muted)]">{t.philosophy.body}</p>
|
||||
|
||||
<div className="mt-6 flex flex-wrap gap-3">
|
||||
{t.philosophy.tags.map((item) => (
|
||||
<span
|
||||
key={item}
|
||||
className="type-chip surface-chip px-4 py-2 font-medium text-[var(--color-ink)]"
|
||||
>
|
||||
{item}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="grid gap-8 lg:grid-cols-[0.88fr_1.12fr]">
|
||||
<div className="section-shell">
|
||||
<div className="section-padding">
|
||||
<SectionHeading
|
||||
eyebrow={t.ui.sectors}
|
||||
title={t.sectors.title}
|
||||
description={t.sectors.description}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-5 md:grid-cols-2 xl:grid-cols-3">
|
||||
{t.sectors.categories.map((category, index) => (
|
||||
<article key={category} className="soft-card min-h-[190px]">
|
||||
<p className="eyebrow-note">{String(index + 1).padStart(2, "0")}</p>
|
||||
<h3 className="type-card-title mt-8 text-[var(--color-ink)]">{category}</h3>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="projects" className="space-y-8 scroll-mt-28 md:space-y-10">
|
||||
<SectionHeading
|
||||
eyebrow={t.ui.featuredProjects}
|
||||
title={t.featured.title}
|
||||
description={t.featured.description}
|
||||
/>
|
||||
|
||||
<div className="space-y-10">
|
||||
{t.featured.projects.map((project, index) => (
|
||||
<ProjectCard
|
||||
key={project.id}
|
||||
project={project}
|
||||
reverse={index % 2 === 1}
|
||||
priority={index < 2}
|
||||
labels={{ year: t.ui.year, area: t.ui.area, role: t.ui.role, fullSheet: t.ui.viewFullSheet }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="awards" className="section-shell scroll-mt-28">
|
||||
<div className="section-padding">
|
||||
<div className="grid gap-10 lg:grid-cols-[0.82fr_1.18fr]">
|
||||
<SectionHeading
|
||||
eyebrow={t.ui.awards}
|
||||
title={t.awards.title}
|
||||
description={t.awards.description}
|
||||
/>
|
||||
|
||||
<div className="grid gap-4">
|
||||
{t.awards.items.map((award, index) => (
|
||||
<article
|
||||
key={`${award.year}-${award.title}`}
|
||||
className="surface-card rounded-[24px] p-5"
|
||||
>
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||
<p className="eyebrow-note">{String(index + 1).padStart(2, "0")}</p>
|
||||
<p className="type-label font-semibold text-[var(--color-blue-400)]">{award.year}</p>
|
||||
</div>
|
||||
<h3 className="type-card-title-small mt-4 text-[var(--color-ink)]">{award.title}</h3>
|
||||
<p className="type-body mt-3 text-[var(--color-muted)]">{award.description}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="grid gap-8 lg:grid-cols-[1.02fr_0.98fr]">
|
||||
<div className="section-shell">
|
||||
<div className="section-padding">
|
||||
<SectionHeading
|
||||
eyebrow={t.ui.craft}
|
||||
title={t.craft.title}
|
||||
description={t.craft.description}
|
||||
/>
|
||||
|
||||
<div className="mt-10 grid gap-6 md:grid-cols-2">
|
||||
<div>
|
||||
<p className="eyebrow-note">{t.ui.coreSkills}</p>
|
||||
<div className="mt-4 flex flex-wrap gap-3">
|
||||
{t.craft.skills.map((skill) => (
|
||||
<span
|
||||
key={skill}
|
||||
className="type-chip surface-chip px-4 py-2 font-medium text-[var(--color-ink)]"
|
||||
>
|
||||
{skill}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="eyebrow-note">{t.ui.software}</p>
|
||||
<div className="mt-4 flex flex-wrap gap-3">
|
||||
{t.craft.software.map((tool) => (
|
||||
<span
|
||||
key={tool}
|
||||
className="type-chip surface-chip-alt px-4 py-2 font-medium text-[var(--color-ink)]"
|
||||
>
|
||||
{tool}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 editorial-rule" />
|
||||
|
||||
<div className="mt-10 grid gap-6 md:grid-cols-2">
|
||||
<div className="space-y-4">
|
||||
<p className="eyebrow-note">{t.ui.documentation}</p>
|
||||
<p className="type-body text-[var(--color-muted)]">{t.craft.documentationText}</p>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<p className="eyebrow-note">{t.ui.visualization}</p>
|
||||
<p className="type-body text-[var(--color-muted)]">{t.craft.visualizationText}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-5">
|
||||
{t.craft.additionalProjects.map((project) => (
|
||||
<article key={project.title} className="gradient-card">
|
||||
<p className="eyebrow-note">{project.year}</p>
|
||||
<h3 className="type-card-title mt-4 text-[var(--color-ink)]">{project.title}</h3>
|
||||
<p className="type-body mt-3 text-[var(--color-muted)]">{project.type}</p>
|
||||
<p className="type-label-wide mt-8 font-semibold text-[var(--color-ink)]">
|
||||
{project.location}
|
||||
</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="section-shell">
|
||||
<div className="section-padding grid gap-8 lg:grid-cols-[0.95fr_1.05fr] lg:items-center">
|
||||
<div>
|
||||
<SectionHeading
|
||||
eyebrow={t.ui.onlineResume}
|
||||
title={t.resume.title}
|
||||
description={t.resume.description}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-5 sm:grid-cols-2">
|
||||
<Link href={`${basePath}/resume`} className="soft-card block">
|
||||
<p className="eyebrow-note">{t.ui.onlineResume}</p>
|
||||
<p className="type-card-title display-face mt-4 text-[var(--color-ink)]">
|
||||
{t.ui.viewFullResume}
|
||||
</p>
|
||||
</Link>
|
||||
{hasResume ? (
|
||||
<Link href={resumeFile.href} className="gradient-card block" download>
|
||||
<p className="eyebrow-note">{t.ui.pdfDownload}</p>
|
||||
<p className="type-card-title display-face mt-4 text-[var(--color-ink)]">
|
||||
{t.ui.downloadCv}
|
||||
</p>
|
||||
</Link>
|
||||
) : (
|
||||
<div className="gradient-card">
|
||||
<p className="eyebrow-note">{t.ui.pdfDownload}</p>
|
||||
<p className="type-card-title display-face mt-4 text-[var(--color-ink)]">
|
||||
{t.ui.downloadCv}
|
||||
</p>
|
||||
<p className="type-body mt-3 text-[var(--color-muted)]">{t.ui.resumeUnavailable}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="grid gap-5 md:grid-cols-2 xl:grid-cols-4">
|
||||
{t.metrics.map((metric) => (
|
||||
<article key={metric.label} className="soft-card text-center">
|
||||
<p className="type-metric display-face text-[var(--color-ink)]">{metric.value}</p>
|
||||
<p className="type-label-wide mt-4 text-[var(--color-muted)]">
|
||||
{metric.label}
|
||||
</p>
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
|
||||
<section id="contact" className="section-shell hero-glow scroll-mt-28">
|
||||
<div className="section-padding grid gap-10 lg:grid-cols-[minmax(0,1fr)_minmax(460px,0.78fr)] lg:items-start">
|
||||
<div className="contact-copy">
|
||||
<p className="section-kicker">{t.ui.contact}</p>
|
||||
<h2 className="contact-title">
|
||||
{t.ui.letsCreate}
|
||||
</h2>
|
||||
<p className="contact-description">
|
||||
{t.ui.availableFor}
|
||||
</p>
|
||||
<div className="mt-8 max-w-xl">
|
||||
<div className="editorial-rule" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid min-w-full gap-4 md:grid-cols-2 lg:min-w-[460px]">
|
||||
{contactCards.map((item, index) => {
|
||||
const shouldSpan =
|
||||
item.wide || (!hasWideContactCard && contactCards.length % 2 === 1 && index === contactCards.length - 1);
|
||||
const isEmail = item.kind === "email";
|
||||
const isPhoneValue = item.kind === "phone" || item.kind === "whatsapp";
|
||||
const isLtrValue = isEmail || isPhoneValue;
|
||||
const content = (
|
||||
<>
|
||||
<p className="contact-label text-[var(--color-muted)]">{item.label}</p>
|
||||
<p
|
||||
dir={isLtrValue ? "ltr" : undefined}
|
||||
className={`contact-value mt-3 break-words font-semibold text-[var(--color-ink)] ${
|
||||
isEmail ? "contact-value-email" : ""
|
||||
} ${isPhoneValue ? "contact-value-ltr" : ""}`}
|
||||
>
|
||||
{item.value}
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
const cardClass =
|
||||
`contact-card ${shouldSpan ? "md:col-span-2" : ""}`;
|
||||
|
||||
return item.href ? (
|
||||
<Link key={item.label} href={item.href} className={cardClass}>
|
||||
{content}
|
||||
</Link>
|
||||
) : (
|
||||
<div key={item.label} className={cardClass}>
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</SiteShell>
|
||||
);
|
||||
}
|
||||
المرجع في مشكلة جديدة
حظر مستخدم