{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tutorial-card",
  "type": "registry:component",
  "title": "Tutorial Card",
  "description": "Card for displaying tutorial previews with metadata.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/tutorial-card/tutorial-card.tsx",
      "content": "\"use client\";\n\nimport { memo, useEffect, useState } from \"react\";\n\nimport type { ReactNode } from \"react\";\n\nimport { useMounted } from \"@vllnt/ui\";\nimport { Badge } from \"@vllnt/ui\";\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"@vllnt/ui\";\n\nexport type TutorialCardProgress = {\n  completedCount: number;\n  totalSections: number;\n};\n\nexport type TutorialCardMeta = {\n  description: string;\n  difficulty: \"advanced\" | \"beginner\" | \"intermediate\";\n  estimatedTime: string;\n  id: string;\n  sectionCount: number;\n  tags: string[];\n  title: string;\n};\n\nexport type TutorialCardLabels = {\n  completed: string;\n  difficulty: Record<string, string>;\n  sectionsCount: string;\n};\n\nexport type TutorialCardProps = {\n  /** Function to get progress (for localStorage etc) */\n  getProgress?: (id: string) => null | TutorialCardProgress;\n  href: string;\n  labels: TutorialCardLabels;\n  /** Link component (e.g., Next.js Link) */\n  linkComponent?: React.ComponentType<{\n    children: ReactNode;\n    className?: string;\n    href: string;\n  }>;\n  tutorial: TutorialCardMeta;\n};\n\nconst DIFFICULTY_VARIANTS = {\n  advanced: \"destructive\",\n  beginner: \"secondary\",\n  intermediate: \"default\",\n} satisfies Record<\n  TutorialCardMeta[\"difficulty\"],\n  \"default\" | \"destructive\" | \"secondary\"\n>;\n\nfunction DefaultLink({\n  children,\n  className,\n  href,\n}: {\n  children: ReactNode;\n  className?: string;\n  href: string;\n}): React.ReactNode {\n  return (\n    <a className={className} href={href}>\n      {children}\n    </a>\n  );\n}\n\nfunction TutorialCardImpl({\n  getProgress,\n  href,\n  labels,\n  linkComponent: LinkComponent = DefaultLink,\n  tutorial,\n}: TutorialCardProps): React.ReactNode {\n  const [progress, setProgress] = useState<null | TutorialCardProgress>(null);\n  const isHydrated = useMounted();\n\n  useEffect(() => {\n    if (getProgress) {\n      const result = getProgress(tutorial.id);\n      requestAnimationFrame(() => {\n        setProgress(result);\n      });\n    }\n  }, [getProgress, tutorial.id]);\n\n  const difficultyVariant = DIFFICULTY_VARIANTS[tutorial.difficulty];\n  // Cap completedCount at sectionCount to handle stale localStorage data\n  const safeCompletedCount = progress\n    ? Math.min(progress.completedCount, tutorial.sectionCount)\n    : 0;\n  const showProgress = isHydrated && safeCompletedCount > 0;\n\n  return (\n    <LinkComponent className=\"block h-full\" href={href}>\n      <Card className=\"h-full flex flex-col hover:shadow-lg transition-shadow cursor-pointer\">\n        <CardHeader>\n          <div className=\"flex items-center gap-2 mb-2\">\n            <Badge className=\"text-xs capitalize\" variant={difficultyVariant}>\n              {labels.difficulty[tutorial.difficulty] || tutorial.difficulty}\n            </Badge>\n            {showProgress ? (\n              <span className=\"text-xs text-muted-foreground\">\n                {safeCompletedCount}/{tutorial.sectionCount} {labels.completed}\n              </span>\n            ) : null}\n          </div>\n\n          <CardTitle className=\"line-clamp-2 text-lg\">\n            {tutorial.title}\n          </CardTitle>\n          <CardDescription className=\"line-clamp-3\">\n            {tutorial.description}\n          </CardDescription>\n        </CardHeader>\n\n        <CardContent className=\"mt-auto space-y-2\">\n          <div className=\"flex flex-wrap gap-2 text-xs text-muted-foreground\">\n            <span>{tutorial.estimatedTime}</span>\n            <span>•</span>\n            <span>\n              {tutorial.sectionCount} {labels.sectionsCount}\n            </span>\n          </div>\n\n          {tutorial.tags.length > 0 ? (\n            <div className=\"flex flex-wrap gap-1\">\n              {tutorial.tags.map((tag) => (\n                <Badge className=\"text-xs\" key={tag} variant=\"outline\">\n                  {tag}\n                </Badge>\n              ))}\n            </div>\n          ) : null}\n        </CardContent>\n      </Card>\n    </LinkComponent>\n  );\n}\n\nexport const TutorialCard = memo(TutorialCardImpl);\nTutorialCard.displayName = \"TutorialCard\";\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
