Files
website/components/project-card.tsx
Steffen Illium 0444067c2d
Some checks failed
Next.js App CI / docker (push) Failing after 3m19s
refined design
2025-09-14 22:49:23 +02:00

113 lines
3.1 KiB
TypeScript

"use client";
import { Badge } from "@/components/ui/badge";
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { cn } from "@/lib/utils";
import { ChevronRightIcon } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import Markdown from "react-markdown";
interface Props {
title: string;
href?: string;
description: string;
dates: string;
tags: readonly string[];
link?: string;
image?: string;
video?: string;
links?: readonly {
icon: React.ReactNode;
type: string;
href: string;
}[];
className?: string;
}
export function ProjectCard({
title,
href,
description,
dates,
link,
image,
video,
links,
className,
}: Props) {
return (
<Link
href={href || "#"}
className={cn("block cursor-pointer font-normal cards", className)}
>
<Card
className={
"group flex flex-col overflow-hidden h-75 gap-2 py-3 border"
}
>
{video && (
<video
src={video}
autoPlay
loop
muted
playsInline
className="pointer-events-none mx-auto max-h-40 h-full object-cover object-top" // needed because random black line at bottom of video
/>
)}
{image && (
<Image
src={image}
alt={title}
width={500}
height={300}
className="m-auto h-full max-h-40 min-h-40 max-w-60 overflow-hidden object-scale-down object-center place-content-center"
/>
)}
<CardHeader className="px-4">
<div className="space-y-1">
<CardTitle className="mt-1 text-base flex items-center">
<span className="truncate max-w-65">{title}</span>
<ChevronRightIcon
className="size-4 shrink-0 translate-x-0 transform opacity-0 transition-all duration-300 ease-out group-hover:translate-x-1 group-hover:opacity-100"
/>
</CardTitle>
<time className="font-sans text-xs">{dates}</time>
<div className="hidden font-sans text-xs underline print:visible">
{link?.replace("https://", "").replace("www.", "").replace("/", "")}
</div>
<div className="prose max-w-full text-pretty font-sans text-xs text-muted-foreground dark:prose-invert">
<Markdown>
{description}
</Markdown>
</div>
</div>
</CardHeader>
<CardContent className="mt-auto flex flex-col px-2" />
<CardFooter className="px-0 pb-2">
{links && links.length > 0 && (
<div className="flex flex-row flex-wrap items-start gap-1">
{links?.map((link, idx) => (
<Link href={link?.href} key={idx} target="_blank">
<Badge key={idx} className="flex gap-2 px-2 py-1 text-[10px]">
{link.icon}
{link.type}
</Badge>
</Link>
))}
</div>
)}
</CardFooter>
</Card>
</Link>
);
}