cpns / apps /web /src /components /bank /QuestionCard.tsx
pramudya-yb
Rename project from Labas to Pram
2b62b0c
Raw
History Blame Contribute Delete
7.55 kB
import { Button } from "@pram/ui/components/button";
import { MaterialIcon } from "@/components/ui/MaterialIcon";
import { formatLabel } from "@/lib/format";
import type { Question } from "@/lib/types";
interface QuestionCardProps {
q: Question;
isFeatured?: boolean;
isInBundle: boolean;
disabled?: boolean;
selected?: boolean;
bulkSelect?: boolean;
onToggle: () => void;
onOpenDetail: () => void;
isOwner: boolean;
onTogglePublic: () => void;
onDelete: () => void;
}
export function QuestionCard({
q,
isFeatured = false,
isInBundle,
disabled = false,
selected = false,
bulkSelect = false,
onToggle,
onOpenDetail,
isOwner,
onTogglePublic,
onDelete,
}: QuestionCardProps) {
const isPrivate = isOwner && !q.isPublic;
return (
<div
onClick={() => { if (!disabled) onOpenDetail(); }}
className={`border-2 rounded-[var(--radius-xl)] h-full flex flex-col transition-all ${
disabled
? "bg-[var(--oat-light)] border-[var(--oat-border)] opacity-40 cursor-not-allowed"
: selected
? "bg-[var(--matcha-100)] border-[var(--matcha-600)] clay-hover cursor-pointer ring-2 ring-[var(--matcha-400)]"
: isInBundle
? "bg-[var(--matcha-100)] border-[var(--clay-black)] clay-shadow clay-hover cursor-pointer"
: isPrivate
? "bg-[var(--pure-white)] border-[var(--oat-border)] border-l-[var(--warm-charcoal)] border-l-4 clay-shadow clay-hover cursor-pointer"
: "bg-[var(--pure-white)] border-[var(--oat-border)] clay-shadow clay-hover cursor-pointer"
}`}
>
<div className="p-5 flex flex-col h-full">
<div className="flex items-start justify-between mb-3">
<div className="flex gap-2 flex-wrap">
{bulkSelect && (
<span className={`px-2 py-1 rounded-full text-[10px] font-semibold flex items-center gap-1 ${
selected
? "bg-[var(--matcha-600)] text-[var(--pure-white)]"
: "bg-[var(--oat-light)] text-[var(--warm-charcoal)]"
}`}>
<MaterialIcon name={selected ? "check_circle" : "radio_button_unchecked"} className="text-xs" />
{selected ? "Terpilih" : "Pilih"}
</span>
)}
<span className="inline-flex items-center px-2.5 py-1 rounded-full bg-[var(--matcha-300)] text-[var(--matcha-800)] text-xs font-semibold leading-none whitespace-nowrap">
{q.examTypeName}
</span>
<span className="inline-flex items-center px-2.5 py-1 rounded-full bg-[var(--slushie-500)]/20 text-[var(--slushie-800)] text-xs font-semibold leading-none whitespace-nowrap">
{q.sectionTypeName}
</span>
{disabled && (
<span className="px-2 py-1 rounded-full bg-[var(--warm-silver)]/30 text-[var(--warm-charcoal)] text-[10px] font-semibold flex items-center gap-1">
<MaterialIcon name="lock" className="text-[10px]" />
Terkunci
</span>
)}
</div>
{q.avgRating && (
<div className="flex items-center gap-1 text-[var(--lemon-700)]">
<MaterialIcon name="star" className="text-sm" />
<span className="text-xs font-bold">{q.avgRating}</span>
</div>
)}
</div>
<h3
className={`font-headline font-bold text-[var(--clay-black)] mb-2 ${
isFeatured ? "text-xl line-clamp-3" : "text-base line-clamp-2"
}`}
>
{q.questionText}
</h3>
<p
className={`text-sm text-[var(--warm-charcoal)] flex-1 ${
isFeatured ? "line-clamp-4 mb-4" : "line-clamp-2 mb-4"
}`}
>
{q.passageText}
</p>
<div className="flex items-center justify-between mt-auto pt-3 border-t border-[var(--oat-border)]">
<div className="flex gap-2">
<span className="px-2 py-0.5 rounded bg-[var(--oat-light)] text-[var(--warm-charcoal)] text-xs font-medium">
{formatLabel(q.format ?? "")}
</span>
<span className="px-2 py-0.5 rounded bg-[var(--oat-light)] text-[var(--warm-charcoal)] text-xs font-medium">
Lv.{q.difficulty}
</span>
<span className="px-2 py-0.5 rounded bg-[var(--oat-light)] text-[var(--warm-charcoal)] text-xs font-medium">
{q.usageCount}x digunakan
</span>
</div>
</div>
{/* Actions row */}
<div className="flex items-center justify-between mt-3 pt-3 border-t border-[var(--oat-border)]">
{isOwner && !bulkSelect && (
<div className="flex items-center gap-2" onClick={(e) => e.stopPropagation()}>
<button
onClick={onTogglePublic}
aria-label={q.isPublic ? "Jadikan privat" : "Jadikan publik"}
className={`group text-xs font-semibold px-3 py-1.5 rounded-full transition-all cursor-pointer flex items-center gap-1 ${
q.isPublic
? "bg-[var(--matcha-300)] text-[var(--matcha-800)] hover:bg-[var(--pomegranate-400)]/15 hover:text-[var(--pomegranate-600)]"
: "bg-[var(--slushie-500)]/15 text-[var(--slushie-800)] hover:bg-[var(--matcha-300)] hover:text-[var(--matcha-800)]"
}`}
>
{/* Default state icon */}
<MaterialIcon
name={q.isPublic ? "public" : "lock"}
className="text-xs group-hover:hidden"
/>
{/* Hover state icon (shows intent) */}
<MaterialIcon
name={q.isPublic ? "lock" : "public"}
className="text-xs hidden group-hover:inline"
/>
{/* Default label */}
<span className="group-hover:hidden">
{q.isPublic ? "Publik" : "Privat"}
</span>
{/* Hover label (shows what will happen) */}
<span className="hidden group-hover:inline">
{q.isPublic ? "Jadikan Privat" : "Jadikan Publik"}
</span>
</button>
<button
onClick={onDelete}
aria-label="Hapus soal"
className="text-[var(--pomegranate-400)] hover:bg-[var(--pomegranate-400)]/10 px-2 py-1 rounded-full transition-colors cursor-pointer"
>
<MaterialIcon name="delete" className="text-sm" />
</button>
</div>
)}
{!bulkSelect && (
<Button
size="sm"
disabled={disabled}
onClick={(e) => {
e.stopPropagation();
if (!disabled) onToggle();
}}
className={`ml-auto rounded-[var(--radius-lg)] text-xs ${
disabled
? "bg-[var(--oat-light)] text-[var(--warm-silver)] cursor-not-allowed"
: isInBundle
? "bg-[var(--clay-black)] text-[var(--pure-white)] hover:bg-[var(--warm-charcoal)] cursor-pointer"
: "bg-[var(--matcha-300)] text-[var(--matcha-800)] hover:bg-[var(--matcha-400)] cursor-pointer"
}`}
>
{disabled ? "Tidak Tersedia" : isInBundle ? "Hapus dari Paket" : "Tambah ke Paket"}
</Button>
)}
</div>
</div>
</div>
);
}