import { Award, Media } from "@/payload-types"; import { Item, ItemContent, ItemDescription, ItemHeader, ItemTitle } from "@/components/ui/item"; import { convertLexicalToPlaintext } from "@payloadcms/richtext-lexical/plaintext"; import { RibbonViewer } from "@/components/frontend/account/RibbonViewer"; import { RibbonDesign } from "@/components/admin/awards/RibbonDesigner"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; import { AwardIcon, LockIcon } from "lucide-react"; /** * Card shown on the awards listing page. Unlike the profile's AwardCard * (which wraps an individual's grant), this reviews a single definable award * type — ribbon or medal — so users can inspect the award art and its * full requirements. Commendations are intentionally out of scope. * * Ribbon art is rendered at a fixed, centered width so every ribbon (and its * container) is an identical, justified size across the grid. Medal art is * currently hidden; only the ribbon art is shown. */ interface AwardListingCardProps { award: Award; awardImage?: Media | null; backgroundImage?: Media | null; showGrantButton?: boolean; onGrantClick?: () => void; canGrant?: boolean | null; remainingGrants?: number | null; } export function AwardListingCard({ award, awardImage, backgroundImage, showGrantButton = false, onGrantClick, canGrant, remainingGrants, }: AwardListingCardProps) { const type = award.type ?? "commendation"; const isMedal = type === "medal"; const hasRibbonArt = type === "ribbon" && award.ribbonDesign && typeof award.ribbonDesign === "object" && Array.isArray((award.ribbonDesign as RibbonDesign).stripes); const hasArt = hasRibbonArt || Boolean(awardImage?.url); const bgUrl = backgroundImage?.url; return ( {bgUrl && ( <>
)} {hasArt && (
{hasRibbonArt ? (
) : awardImage?.url ? ( {award.name} ) : null}
)} {award.name} {type}
{award.description ? convertLexicalToPlaintext({ data: award.description }) : "No description available."} {award.experiencePoints != null && ( +{award.experiencePoints} XP )} {showGrantButton && (
{canGrant === false ? ( ) : onGrantClick ? ( ) : null}
)} ); }