103 lines
3.2 KiB
TypeScript
103 lines
3.2 KiB
TypeScript
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import { vibeMeta } from "@/lib/vibe";
|
|
import type { Vibe } from "@/app/generated/prisma/enums";
|
|
|
|
interface UserCardProps {
|
|
id: string;
|
|
name: string;
|
|
image: string | null;
|
|
isVerifiedOrganizer: boolean;
|
|
profile: {
|
|
bio: string | null;
|
|
city: string | null;
|
|
interests: string[];
|
|
vibe: Vibe | null;
|
|
} | null;
|
|
}
|
|
|
|
export function UserCard({
|
|
id,
|
|
name,
|
|
image,
|
|
isVerifiedOrganizer,
|
|
profile,
|
|
}: UserCardProps) {
|
|
const interests = profile?.interests ?? [];
|
|
return (
|
|
<Link
|
|
href={`/u/${id}`}
|
|
className="group flex h-full flex-col rounded-2xl border border-neutral-200 bg-white p-4 shadow-sm transition-all hover:-translate-y-0.5 hover:border-primary-300 hover:shadow-md"
|
|
>
|
|
<div className="flex items-start gap-3">
|
|
{image ? (
|
|
<Image
|
|
src={image}
|
|
alt=""
|
|
width={56}
|
|
height={56}
|
|
className="h-14 w-14 shrink-0 rounded-full object-cover"
|
|
/>
|
|
) : (
|
|
<div className="flex h-14 w-14 shrink-0 items-center justify-center rounded-full bg-primary-600 text-lg font-bold text-white">
|
|
{name.charAt(0).toUpperCase()}
|
|
</div>
|
|
)}
|
|
<div className="min-w-0 flex-1">
|
|
<p className="truncate text-sm font-bold text-neutral-800 group-hover:text-primary-700">
|
|
{name}
|
|
</p>
|
|
{profile?.city && (
|
|
<p className="truncate text-[11px] text-neutral-500 sm:text-xs">
|
|
📍 {profile.city}
|
|
</p>
|
|
)}
|
|
<div className="mt-1 flex flex-wrap gap-1">
|
|
{isVerifiedOrganizer && (
|
|
<span
|
|
className="inline-flex items-center gap-0.5 rounded-full bg-primary-100 px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wide text-primary-800"
|
|
title="Organizer terverifikasi"
|
|
>
|
|
✅ Organizer
|
|
</span>
|
|
)}
|
|
{profile?.vibe && (
|
|
<span
|
|
className="inline-flex items-center gap-0.5 rounded-full bg-secondary-100 px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wide text-secondary-800"
|
|
title={vibeMeta(profile.vibe).description}
|
|
>
|
|
<span aria-hidden>{vibeMeta(profile.vibe).icon}</span>
|
|
<span>{vibeMeta(profile.vibe).label}</span>
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{profile?.bio && (
|
|
<p className="mt-3 line-clamp-2 text-xs leading-relaxed text-neutral-600">
|
|
{profile.bio}
|
|
</p>
|
|
)}
|
|
|
|
{interests.length > 0 && (
|
|
<div className="mt-3 flex flex-wrap gap-1">
|
|
{interests.slice(0, 5).map((tag) => (
|
|
<span
|
|
key={tag}
|
|
className="rounded-full bg-secondary-50 px-2 py-0.5 text-[10px] font-medium text-secondary-700"
|
|
>
|
|
#{tag}
|
|
</span>
|
|
))}
|
|
{interests.length > 5 && (
|
|
<span className="text-[10px] text-neutral-400">
|
|
+{interests.length - 5}
|
|
</span>
|
|
)}
|
|
</div>
|
|
)}
|
|
</Link>
|
|
);
|
|
}
|