137 lines
6.5 KiB
TypeScript
137 lines
6.5 KiB
TypeScript
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import type { ProjectType } from "@prisma/client";
|
|
import type { Locale } from "@/lib/i18n";
|
|
import { getProjectDirectoryCopy, getProjectPath } from "@/lib/project-content";
|
|
import AnalyticsTracker from "@/components/public/analytics-tracker";
|
|
import TrackedLink from "@/components/public/tracked-link";
|
|
|
|
type ProjectDetailProps = {
|
|
locale: Locale;
|
|
type: ProjectType;
|
|
project: {
|
|
id: string;
|
|
slug: string;
|
|
titleAr: string;
|
|
titleEn: string;
|
|
descAr: string | null;
|
|
descEn: string | null;
|
|
summaryAr: string | null;
|
|
summaryEn: string | null;
|
|
coverImage: string | null;
|
|
images: string[];
|
|
technologies: string[];
|
|
externalUrl: string | null;
|
|
repoUrl: string | null;
|
|
platform: string | null;
|
|
appStoreUrl: string | null;
|
|
testflightUrl: string | null;
|
|
appVersion: string | null;
|
|
supportUrl: string | null;
|
|
appPrivacyUrl: string | null;
|
|
category: { nameAr: string; nameEn: string } | null;
|
|
};
|
|
};
|
|
|
|
export default function ProjectDetail({ locale, type, project }: ProjectDetailProps) {
|
|
const title = locale === "ar" ? project.titleAr : project.titleEn;
|
|
const summary = locale === "ar" ? project.summaryAr : project.summaryEn;
|
|
const description = locale === "ar" ? project.descAr : project.descEn;
|
|
const category = project.category ? (locale === "ar" ? project.category.nameAr : project.category.nameEn) : null;
|
|
const copy = getProjectDirectoryCopy(locale, type);
|
|
const backLabel = locale === "ar" ? `العودة إلى ${copy.eyebrow}` : `Back to ${copy.eyebrow}`;
|
|
const isApp = type === "APP";
|
|
|
|
return (
|
|
<article className="mx-auto w-full max-w-6xl space-y-10 px-4 py-10 sm:px-6 lg:px-8">
|
|
<AnalyticsTracker event="PROJECT_OPEN" entityId={project.id} />
|
|
<Link href={getProjectPath(locale, type)} className="text-sm font-medium text-brand-2 hover:underline">
|
|
← {backLabel}
|
|
</Link>
|
|
|
|
<header className="max-w-3xl space-y-4">
|
|
{category ? <p className="text-sm font-semibold uppercase tracking-[0.18em] text-brand-2">{category}</p> : null}
|
|
<h1 className="text-4xl font-semibold tracking-tight sm:text-6xl">{title}</h1>
|
|
{summary ? <p className="text-xl leading-8 text-muted-foreground">{summary}</p> : null}
|
|
</header>
|
|
|
|
{project.coverImage ? (
|
|
<div className="relative aspect-[16/8] overflow-hidden rounded-2xl border border-border bg-muted">
|
|
<Image src={project.coverImage} alt={title} fill unoptimized sizes="100vw" className="object-cover" priority />
|
|
</div>
|
|
) : null}
|
|
|
|
<div className="grid gap-10 lg:grid-cols-[minmax(0,1fr)_18rem]">
|
|
<div className="space-y-8">
|
|
{description ? <p className="whitespace-pre-line text-lg leading-8 text-foreground">{description}</p> : null}
|
|
{project.images.length > 0 ? (
|
|
<div className="grid gap-4 sm:grid-cols-2">
|
|
{project.images.map((image, index) => (
|
|
<div key={image} className="relative aspect-[4/3] overflow-hidden rounded-xl border border-border bg-muted">
|
|
<Image src={image} alt={`${title} ${index + 1}`} fill unoptimized sizes="(max-width: 640px) 100vw, 50vw" className="object-cover" />
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
|
|
<aside className="space-y-6 rounded-xl border border-border bg-card p-5">
|
|
{project.technologies.length > 0 ? (
|
|
<div>
|
|
<h2 className="text-sm font-semibold">{locale === "ar" ? "التقنيات" : "Technologies"}</h2>
|
|
<div className="mt-3 flex flex-wrap gap-2">
|
|
{project.technologies.map((technology) => (
|
|
<span key={technology} className="rounded-full border border-border px-3 py-1 text-xs text-muted-foreground">
|
|
{technology}
|
|
</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
|
|
{isApp ? (
|
|
<div className="space-y-3 border-t border-border pt-5 text-sm">
|
|
<h2 className="font-semibold">{locale === "ar" ? "تفاصيل التطبيق" : "App details"}</h2>
|
|
{project.platform ? <p className="text-muted-foreground">{project.platform}</p> : null}
|
|
{project.appVersion ? <p className="text-muted-foreground">{locale === "ar" ? "الإصدار" : "Version"}: {project.appVersion}</p> : null}
|
|
</div>
|
|
) : null}
|
|
|
|
<div className="flex flex-col gap-3 text-sm">
|
|
{project.externalUrl ? (
|
|
<TrackedLink href={project.externalUrl} target="_blank" rel="noreferrer" className="font-medium text-brand-2 hover:underline" entityId={project.id}>
|
|
{locale === "ar" ? "فتح الرابط" : "Open live link"} ↗
|
|
</TrackedLink>
|
|
) : null}
|
|
{project.repoUrl ? (
|
|
<TrackedLink href={project.repoUrl} target="_blank" rel="noreferrer" className="font-medium text-brand-2 hover:underline" entityId={project.id}>
|
|
{locale === "ar" ? "مستودع GitHub" : "Open repository"} ↗
|
|
</TrackedLink>
|
|
) : null}
|
|
{project.appStoreUrl ? (
|
|
<TrackedLink href={project.appStoreUrl} target="_blank" rel="noreferrer" className="font-medium text-brand-2 hover:underline" entityId={project.id}>
|
|
{locale === "ar" ? "فتح App Store" : "Open App Store"} ↗
|
|
</TrackedLink>
|
|
) : null}
|
|
{project.testflightUrl ? (
|
|
<TrackedLink href={project.testflightUrl} target="_blank" rel="noreferrer" className="font-medium text-brand-2 hover:underline" entityId={project.id}>
|
|
{locale === "ar" ? "فتح TestFlight" : "Open TestFlight"} ↗
|
|
</TrackedLink>
|
|
) : null}
|
|
{project.supportUrl ? (
|
|
<TrackedLink href={project.supportUrl} target="_blank" rel="noreferrer" className="font-medium text-brand-2 hover:underline" entityId={project.id}>
|
|
{locale === "ar" ? "رابط الدعم" : "Support link"} ↗
|
|
</TrackedLink>
|
|
) : null}
|
|
{project.appPrivacyUrl ? (
|
|
<TrackedLink href={project.appPrivacyUrl} target="_blank" rel="noreferrer" className="font-medium text-brand-2 hover:underline" entityId={project.id}>
|
|
{locale === "ar" ? "سياسة الخصوصية" : "Privacy policy"} ↗
|
|
</TrackedLink>
|
|
) : null}
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
</article>
|
|
);
|
|
}
|