import type { CourseView } from '@/types/views'; import React from 'react'; import { ArrowRight, Clock, Sparkles, BookOpen, ShieldCheck, Flame, Wine, Users, CheckCircle2 } from 'lucide-react'; import { cn } from '@/lib/utils'; import { Badge } from '@/components/ds'; import { unlocksLabel } from '@/lib/skillGraph'; import { DIFF_TONE, owliverCriteria, statusOf, STATUS_TONE, TYPE_ICON, TYPE_LABEL, trainingType, typeOf, } from './challengeMeta'; // Category color and icon resolver function getCategoryMeta(category = '') { const cat = category.toLowerCase(); if (cat.includes('leadership') || cat.includes('manager')) { return { bg: 'from-blue-500/10 via-indigo-500/5 to-transparent', accent: 'text-blue-600 dark:text-blue-400', border: 'border-blue-500/20', icon: Users, }; } if (cat.includes('kitchen') || cat.includes('chef') || cat.includes('food')) { return { bg: 'from-amber-500/10 via-orange-500/5 to-transparent', accent: 'text-amber-600 dark:text-amber-400', border: 'border-amber-500/20', icon: Flame, }; } if (cat.includes('bar') || cat.includes('beverage') || cat.includes('wine')) { return { bg: 'from-purple-500/10 via-violet-500/5 to-transparent', accent: 'text-purple-600 dark:text-purple-400', border: 'border-purple-500/20', icon: Wine, }; } return { bg: 'from-emerald-500/10 via-teal-500/5 to-transparent', accent: 'text-emerald-600 dark:text-emerald-400', border: 'border-emerald-500/20', icon: ShieldCheck, }; } /** * The state of this module for the person being viewed — the four states a * training card can be in, named the way a learner would name them. */ const MODULE_STATE = { completed: { label: 'Completed', className: 'bg-success-muted text-success border-success/30' }, available: { label: 'Available', className: 'bg-krow-blue-tint text-krow-blue border-krow-blue/30' }, locked: { label: 'Locked', className: 'bg-surface-sunken text-ink-4 border-border' }, }; interface SkillCardProps { course: CourseView; onOpen?: (...args: any[]) => void; moduleState?: any; } export function SkillCard({ course, onOpen, moduleState }: SkillCardProps) { const status = statusOf(course); const type = typeOf(course); const ProofIcon = TYPE_ICON[type] || TYPE_ICON.roleplay; const criteria = owliverCriteria(course); const hasChallenge = Boolean(course.challenge); const catMeta = getCategoryMeta(course.category); const CatIcon = catMeta.icon; const unlocks = unlocksLabel(course); const isPublished = status === 'published'; return (
onOpen(course)} className="group relative flex h-full min-w-0 flex-col justify-between rounded-2xl border border-border/80 bg-surface p-4 sm:p-5 shadow-xs transition-all duration-300 hover:-translate-y-1 hover:border-blue-500/40 hover:shadow-xl cursor-pointer overflow-hidden" > {/* Top Background Gradient Highlight */}
{/* Main Body Area */}
{/* Top Header Row: Category Badge (Left) & Publication Status Badge (Right) */}
{course.category}
{/* What this module is *to the reader* leads: completed, available or locked. Publication state is an authoring fact and only shows when there is no learner state to report. */} {moduleState ? ( {(MODULE_STATE[moduleState] || MODULE_STATE.available).label} ) : ( {status} )}
{/* Title & Difficulty Tag */}

{course.title}

{course.difficulty && (
)}
{/* Description */}

{course.description}

{/* Course Stats Pills */}
    {course.estimated_minutes > 0 && (
  • )}
  • {hasChallenge && (
  • )}
{/* Owliver Criteria AI Badge */} {criteria.length > 0 && (
)}
{/* Footer Divider & Action Button */}
{/* What completing this does to your record — the reason to take it. "Verifies " is the fallback for library material that no level depends on, so those cards still say what they prove. */} {unlocks ? ( Unlocks {unlocks} ) : course.proof_skill ? ( Verifies {course.proof_skill} ) : }
); }