70 lines
2.4 KiB
TypeScript
70 lines
2.4 KiB
TypeScript
import { ArrowUpRight, CalendarDays } from "lucide-react";
|
|
import Link from "next/link";
|
|
|
|
import { MotionFade } from "@/components/motion-fade";
|
|
import { AppCard } from "@/components/ui/app-card";
|
|
import { CardContent } from "@/components/ui/card";
|
|
import { getLocalizedPath, type AppLocale } from "@/lib/locale";
|
|
import { getLocalizedValue, type PortfolioProjectView } from "@/lib/portfolio";
|
|
|
|
type PortfolioProjectGridProps = {
|
|
locale: AppLocale;
|
|
defaultLocale: AppLocale;
|
|
projects: PortfolioProjectView[];
|
|
emptyLabel: string;
|
|
openLabel: string;
|
|
};
|
|
|
|
export function PortfolioProjectGrid({
|
|
locale,
|
|
defaultLocale,
|
|
projects,
|
|
emptyLabel,
|
|
openLabel,
|
|
}: PortfolioProjectGridProps) {
|
|
return (
|
|
<section className="grid gap-4 md:grid-cols-2">
|
|
{projects.map((item, index) => (
|
|
<MotionFade key={item.slug} delay={index * 0.05}>
|
|
<AppCard interactive>
|
|
<CardContent className="p-5">
|
|
<Link
|
|
href={getLocalizedPath(locale, `/portfolio/${item.slug}`, defaultLocale)}
|
|
className="group block"
|
|
>
|
|
<div className="flex items-center justify-between gap-3">
|
|
<p className="text-sm text-muted-foreground/80">
|
|
{getLocalizedValue(item.category.name, locale)}
|
|
</p>
|
|
<p className="inline-flex items-center gap-1 text-xs text-muted-foreground/80">
|
|
<CalendarDays className="h-3.5 w-3.5" />
|
|
{item.projectYear}
|
|
</p>
|
|
</div>
|
|
<h2 className="mt-3 text-xl font-semibold text-foreground">
|
|
{getLocalizedValue(item.title, locale)}
|
|
</h2>
|
|
<p className="mt-2 text-sm text-muted-foreground">
|
|
{getLocalizedValue(item.summary, locale)}
|
|
</p>
|
|
<p className="mt-4 inline-flex items-center gap-2 text-sm font-medium text-foreground/80 group-hover:text-foreground">
|
|
{openLabel}
|
|
<ArrowUpRight className="h-4 w-4" />
|
|
</p>
|
|
</Link>
|
|
</CardContent>
|
|
</AppCard>
|
|
</MotionFade>
|
|
))}
|
|
|
|
{projects.length === 0 ? (
|
|
<AppCard className="md:col-span-2">
|
|
<CardContent className="p-6 text-sm text-muted-foreground">
|
|
{emptyLabel}
|
|
</CardContent>
|
|
</AppCard>
|
|
) : null}
|
|
</section>
|
|
);
|
|
}
|