{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"cards-04","type":"registry:component","title":"Project Cards","description":"Project management cards with progress tracking, team members, status indicators, priority flags, and task counters. Features time tracking, due dates, attachments, comments, and dropdown actions. Perfect for project dashboards, team management, and workflow tracking.","dependencies":["lucide-react"],"registryDependencies":["card","button","badge","avatar","progress","separator","dropdown-menu"],"files":[{"path":"src/registry/blocks/application/data-display/cards/cards-04.tsx","content":"\"use client\";\n\nimport {\n  AlertCircle,\n  Calendar,\n  CheckCircle2,\n  Clock,\n  Flag,\n  MessageSquare,\n  MoreHorizontal,\n  Paperclip,\n  Play,\n  Users,\n} from \"lucide-react\";\n\nimport { cn } from \"@/registry/lib/utils\";\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/registry/ui/avatar\";\nimport { Badge } from \"@/registry/ui/badge\";\nimport { Button } from \"@/registry/ui/button\";\nimport {\n  Card,\n  CardContent,\n  CardDescription,\n  CardHeader,\n  CardTitle,\n} from \"@/registry/ui/card\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuSeparator,\n  DropdownMenuTrigger,\n} from \"@/registry/ui/dropdown-menu\";\nimport { Progress } from \"@/registry/ui/progress\";\nimport { Separator } from \"@/registry/ui/separator\";\n\nexport interface Cards04Props {\n  className?: string;\n}\n\nconst content = {\n  projects: [\n    {\n      id: 1,\n      title: \"E-commerce Platform Redesign\",\n      description:\n        \"Complete overhaul of the user interface and user experience for our main e-commerce platform.\",\n      status: \"in-progress\" as const,\n      priority: \"high\" as const,\n      progress: 68,\n      dueDate: \"Dec 15, 2024\",\n      timeTracked: \"24h 30m\",\n      team: [\n        {\n          name: \"Sarah J.\",\n          avatar: \"https://randomuser.me/api/portraits/women/2.jpg\",\n        },\n        {\n          name: \"Mike C.\",\n          avatar: \"https://randomuser.me/api/portraits/men/2.jpg\",\n        },\n        {\n          name: \"Lisa T.\",\n          avatar: \"https://randomuser.me/api/portraits/women/3.jpg\",\n        },\n      ],\n      tasks: { completed: 12, total: 18 },\n      comments: 8,\n      attachments: 5,\n      tags: [\"Design\", \"Frontend\", \"UX\"],\n    },\n    {\n      id: 2,\n      title: \"Mobile App Development\",\n      description:\n        \"Native iOS and Android app development for customer engagement and loyalty program.\",\n      status: \"planning\" as const,\n      priority: \"medium\" as const,\n      progress: 25,\n      dueDate: \"Jan 30, 2025\",\n      timeTracked: \"8h 15m\",\n      team: [\n        {\n          name: \"David P.\",\n          avatar: \"https://randomuser.me/api/portraits/men/3.jpg\",\n        },\n        {\n          name: \"Emily R.\",\n          avatar: \"https://randomuser.me/api/portraits/women/4.jpg\",\n        },\n      ],\n      tasks: { completed: 3, total: 12 },\n      comments: 4,\n      attachments: 2,\n      tags: [\"Mobile\", \"React Native\", \"API\"],\n    },\n    {\n      id: 3,\n      title: \"Data Analytics Dashboard\",\n      description:\n        \"Real-time analytics dashboard for business intelligence and reporting capabilities.\",\n      status: \"completed\" as const,\n      priority: \"low\" as const,\n      progress: 100,\n      dueDate: \"Nov 20, 2024\",\n      timeTracked: \"45h 20m\",\n      team: [\n        {\n          name: \"James W.\",\n          avatar: \"https://randomuser.me/api/portraits/men/4.jpg\",\n        },\n        {\n          name: \"Rachel G.\",\n          avatar: \"https://randomuser.me/api/portraits/women/5.jpg\",\n        },\n        {\n          name: \"Tom A.\",\n          avatar: \"https://randomuser.me/api/portraits/men/5.jpg\",\n        },\n        {\n          name: \"Amy S.\",\n          avatar: \"https://randomuser.me/api/portraits/women/6.jpg\",\n        },\n      ],\n      tasks: { completed: 15, total: 15 },\n      comments: 12,\n      attachments: 8,\n      tags: [\"Analytics\", \"Dashboard\", \"Charts\"],\n    },\n  ],\n};\n\nexport function Cards04({ className }: Cards04Props) {\n  const getStatusColor = (status: string) => {\n    switch (status) {\n      case \"completed\":\n        return \"bg-green-100 text-green-800 dark:bg-green-900/20 dark:text-green-400\";\n      case \"in-progress\":\n        return \"bg-blue-100 text-blue-800 dark:bg-blue-900/20 dark:text-blue-400\";\n      case \"planning\":\n        return \"bg-yellow-100 text-yellow-800 dark:bg-yellow-900/20 dark:text-yellow-400\";\n      case \"on-hold\":\n        return \"bg-gray-100 text-gray-800 dark:bg-gray-900/20 dark:text-gray-400\";\n      default:\n        return \"bg-gray-100 text-gray-800 dark:bg-gray-900/20 dark:text-gray-400\";\n    }\n  };\n\n  const getPriorityColor = (priority: string) => {\n    switch (priority) {\n      case \"high\":\n        return \"text-red-600\";\n      case \"medium\":\n        return \"text-yellow-600\";\n      case \"low\":\n        return \"text-green-600\";\n      default:\n        return \"text-gray-600\";\n    }\n  };\n\n  const getStatusIcon = (status: string) => {\n    switch (status) {\n      case \"completed\":\n        return CheckCircle2;\n      case \"in-progress\":\n        return Play;\n      case \"planning\":\n        return Clock;\n      default:\n        return AlertCircle;\n    }\n  };\n\n  const getInitials = (name: string) => {\n    return name\n      .split(\" \")\n      .map((n) => n[0])\n      .join(\"\")\n      .toUpperCase();\n  };\n\n  const handleDropdownAction = (\n    action: string,\n    project: (typeof content.projects)[0],\n  ) => {\n    console.log(`${action} action for project:`, project.title);\n  };\n\n  const handleViewProject = (project: (typeof content.projects)[0]) => {\n    console.log(\"View project:\", project.title);\n  };\n\n  return (\n    <div\n      className={cn(\n        \"grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-6\",\n        className,\n      )}\n      data-testid=\"project-cards-grid\"\n    >\n      {content.projects.map((project) => {\n        const StatusIcon = getStatusIcon(project.status);\n        const maxVisibleAvatars = 3;\n        const extraMembers = Math.max(\n          0,\n          project.team.length - maxVisibleAvatars,\n        );\n\n        return (\n          <Card\n            key={project.id}\n            className=\"hover:shadow-lg transition-shadow\"\n            data-testid={`project-card-${project.id}`}\n          >\n            <CardHeader className=\"pb-3\">\n              <div className=\"flex items-start justify-between space-x-2\">\n                <div className=\"flex-1 min-w-0\">\n                  <CardTitle className=\"text-base sm:text-lg leading-tight mb-1\">\n                    {project.title}\n                  </CardTitle>\n                  <CardDescription className=\"text-sm leading-relaxed line-clamp-2\">\n                    {project.description}\n                  </CardDescription>\n                </div>\n                <DropdownMenu>\n                  <DropdownMenuTrigger asChild>\n                    <Button\n                      variant=\"ghost\"\n                      size=\"icon\"\n                      className=\"h-8 w-8 flex-shrink-0\"\n                      data-testid={`project-menu-${project.id}`}\n                    >\n                      <MoreHorizontal className=\"h-4 w-4\" />\n                    </Button>\n                  </DropdownMenuTrigger>\n                  <DropdownMenuContent\n                    align=\"end\"\n                    data-testid={`project-menu-content-${project.id}`}\n                  >\n                    <DropdownMenuItem\n                      onClick={() => handleDropdownAction(\"edit\", project)}\n                      data-testid={`edit-project-${project.id}`}\n                    >\n                      Edit Project\n                    </DropdownMenuItem>\n                    <DropdownMenuItem\n                      onClick={() => handleDropdownAction(\"view\", project)}\n                      data-testid={`view-details-${project.id}`}\n                    >\n                      View Details\n                    </DropdownMenuItem>\n                    <DropdownMenuItem\n                      onClick={() => handleDropdownAction(\"share\", project)}\n                      data-testid={`share-project-${project.id}`}\n                    >\n                      Share\n                    </DropdownMenuItem>\n                    <DropdownMenuSeparator />\n                    <DropdownMenuItem\n                      className=\"text-destructive\"\n                      onClick={() => handleDropdownAction(\"archive\", project)}\n                      data-testid={`archive-project-${project.id}`}\n                    >\n                      Archive\n                    </DropdownMenuItem>\n                  </DropdownMenuContent>\n                </DropdownMenu>\n              </div>\n\n              <div className=\"flex items-center justify-between pt-2\">\n                <Badge\n                  className={getStatusColor(project.status)}\n                  variant=\"secondary\"\n                  data-testid={`status-badge-${project.id}`}\n                >\n                  <StatusIcon className=\"h-3 w-3 mr-1\" />\n                  {project.status.replace(\"-\", \" \")}\n                </Badge>\n                <div className=\"flex items-center space-x-1\">\n                  <Flag\n                    className={cn(\n                      \"h-3 w-3\",\n                      getPriorityColor(project.priority),\n                    )}\n                  />\n                  <span\n                    className={cn(\n                      \"text-xs font-medium\",\n                      getPriorityColor(project.priority),\n                    )}\n                    data-testid={`priority-${project.id}`}\n                  >\n                    {project.priority}\n                  </span>\n                </div>\n              </div>\n            </CardHeader>\n\n            <CardContent className=\"space-y-4\">\n              <div className=\"space-y-2\">\n                <div className=\"flex items-center justify-between text-sm\">\n                  <span className=\"text-muted-foreground\">Progress</span>\n                  <span\n                    className=\"font-medium\"\n                    data-testid={`progress-text-${project.id}`}\n                  >\n                    {project.progress}%\n                  </span>\n                </div>\n                <Progress\n                  value={project.progress}\n                  className=\"h-2\"\n                  data-testid={`progress-bar-${project.id}`}\n                />\n              </div>\n\n              <div className=\"flex items-center justify-between text-sm\">\n                <div className=\"flex items-center space-x-2\">\n                  <CheckCircle2 className=\"h-4 w-4 text-muted-foreground\" />\n                  <span className=\"text-muted-foreground\">Tasks</span>\n                </div>\n                <span\n                  className=\"font-medium\"\n                  data-testid={`tasks-count-${project.id}`}\n                >\n                  {project.tasks.completed}/{project.tasks.total}\n                </span>\n              </div>\n\n              <div className=\"grid grid-cols-2 gap-4 text-sm\">\n                <div className=\"flex items-center space-x-2\">\n                  <Clock className=\"h-4 w-4 text-muted-foreground\" />\n                  <span className=\"text-muted-foreground truncate\">\n                    {project.timeTracked}\n                  </span>\n                </div>\n                <div className=\"flex items-center space-x-2\">\n                  <Calendar className=\"h-4 w-4 text-muted-foreground\" />\n                  <span className=\"text-muted-foreground truncate\">\n                    {project.dueDate}\n                  </span>\n                </div>\n              </div>\n\n              <Separator />\n\n              <div className=\"space-y-2\">\n                <div className=\"flex items-center justify-between\">\n                  <div className=\"flex items-center space-x-2\">\n                    <Users className=\"h-4 w-4 text-muted-foreground\" />\n                    <span className=\"text-sm text-muted-foreground\">Team</span>\n                  </div>\n                  <div\n                    className=\"flex items-center -space-x-2\"\n                    data-testid={`team-avatars-${project.id}`}\n                  >\n                    {project.team\n                      .slice(0, maxVisibleAvatars)\n                      .map((member, index) => (\n                        <Avatar\n                          key={index}\n                          className=\"h-6 w-6 border-2 border-background\"\n                        >\n                          <AvatarImage src={member.avatar} alt={member.name} />\n                          <AvatarFallback className=\"text-xs\">\n                            {getInitials(member.name)}\n                          </AvatarFallback>\n                        </Avatar>\n                      ))}\n                    {extraMembers > 0 && (\n                      <div className=\"h-6 w-6 rounded-full bg-muted border-2 border-background flex items-center justify-center\">\n                        <span className=\"text-xs font-medium text-muted-foreground\">\n                          +{extraMembers}\n                        </span>\n                      </div>\n                    )}\n                  </div>\n                </div>\n              </div>\n\n              <div className=\"flex flex-wrap gap-1\">\n                {project.tags.map((tag) => (\n                  <Badge\n                    key={tag}\n                    variant=\"outline\"\n                    className=\"text-xs\"\n                    data-testid={`tag-${tag.toLowerCase()}`}\n                  >\n                    {tag}\n                  </Badge>\n                ))}\n              </div>\n\n              <div className=\"flex items-center justify-between pt-2\">\n                <div className=\"flex items-center space-x-4 text-sm text-muted-foreground\">\n                  <div className=\"flex items-center space-x-1\">\n                    <MessageSquare className=\"h-4 w-4\" />\n                    <span data-testid={`comments-count-${project.id}`}>\n                      {project.comments}\n                    </span>\n                  </div>\n                  <div className=\"flex items-center space-x-1\">\n                    <Paperclip className=\"h-4 w-4\" />\n                    <span data-testid={`attachments-count-${project.id}`}>\n                      {project.attachments}\n                    </span>\n                  </div>\n                </div>\n                <Button\n                  size=\"sm\"\n                  variant=\"outline\"\n                  onClick={() => handleViewProject(project)}\n                  data-testid={`view-project-button-${project.id}`}\n                >\n                  View Project\n                </Button>\n              </div>\n            </CardContent>\n          </Card>\n        );\n      })}\n    </div>\n  );\n}\n","type":"registry:component"},{"path":"src/registry/lib/utils.ts","content":"import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n","type":"registry:lib"}]}