94 lines
3.3 KiB
TypeScript
94 lines
3.3 KiB
TypeScript
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 { cn } from "@/lib/utils";
|
|
|
|
const getExperiencePointsClass = (experiencePoints: number) => {
|
|
if (experiencePoints >= 5000) return "award-xp-high";
|
|
if (experiencePoints >= 1000) return "text-emerald-400";
|
|
if (experiencePoints >= 501) return "text-lime-300";
|
|
if (experiencePoints >= 200) return "text-sky-300";
|
|
return "text-muted-foreground";
|
|
};
|
|
|
|
export default function AwardCard(props: { awardImage: Media | null; award: Award }) {
|
|
const isCommendation = props.award.type === "commendation" || !props.award.type;
|
|
const commendationColor =
|
|
isCommendation &&
|
|
typeof props.award.commendationColor === "string" &&
|
|
/^#[0-9a-f]{6}$/i.test(props.award.commendationColor)
|
|
? props.award.commendationColor
|
|
: null;
|
|
|
|
return (
|
|
<Item
|
|
variant="outline"
|
|
size={isCommendation ? "sm" : "default"}
|
|
className={cn("text-center", {
|
|
"medal-card": props.award.type === "medal",
|
|
"commendation-card": isCommendation,
|
|
})}
|
|
style={
|
|
commendationColor
|
|
? {
|
|
borderColor: commendationColor,
|
|
backgroundColor: `${commendationColor}33`,
|
|
}
|
|
: undefined
|
|
}
|
|
>
|
|
<ItemHeader className="flex flex-col items-center">
|
|
{props.award.ribbonDesign && (
|
|
<RibbonViewer
|
|
ribbonDesign={props.award.ribbonDesign as RibbonDesign}
|
|
showRibbonOnly={true}
|
|
/>
|
|
)}
|
|
<div className="flex items-center gap-2.5">
|
|
{props.awardImage?.url && (
|
|
<img
|
|
src={props.awardImage.url}
|
|
alt={props.award.name}
|
|
className={cn("object-contain", isCommendation ? "size-6" : "size-8")}
|
|
/>
|
|
)}
|
|
<div className="flex flex-col">
|
|
<ItemTitle
|
|
className={cn({
|
|
"text-xl text-shadow-lg text-shadow-white/15": props.award.type === "medal",
|
|
"text-xs": isCommendation,
|
|
})}
|
|
>
|
|
{props.award.name}
|
|
</ItemTitle>
|
|
<span className="text-[9px] uppercase tracking-wider text-muted-foreground font-mono">
|
|
{props.award.type ?? "commendation"}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</ItemHeader>
|
|
<ItemContent>
|
|
<ItemDescription
|
|
className={cn("text-center", isCommendation ? "line-clamp-none text-xs" : "line-clamp-3")}
|
|
>
|
|
{props.award.description
|
|
? convertLexicalToPlaintext({ data: props.award.description })
|
|
: "No description."}
|
|
</ItemDescription>
|
|
{props.award.experiencePoints != null && (
|
|
<span
|
|
className={cn(
|
|
"mt-1 font-mono",
|
|
getExperiencePointsClass(props.award.experiencePoints),
|
|
isCommendation ? "text-[10px]" : "text-xs",
|
|
)}
|
|
>
|
|
+{props.award.experiencePoints} XP
|
|
</span>
|
|
)}
|
|
</ItemContent>
|
|
</Item>
|
|
);
|
|
}
|