import type React from "react" import { cn } from "../../utils/cn" import { UnifiedSkeleton, TextSkeleton, MediaSkeleton, InteractiveSkeleton } from "./unified-skeleton" interface CardSkeletonProps { className?: string /** * Card layout variant */ variant?: 'vendor' | 'blog' | 'category' | 'alternative' /** * Show action buttons area */ showActions?: boolean /** * Show metadata footer */ showMetadata?: boolean /** Optional tailwind classes to override the card container background & border */ containerClassName?: string } /** * Unified card skeleton component for consistent card loading states * * Supports different card types used across the application: * - vendor: Vendor cards with logo, title, description, and actions * - blog: Blog post cards with image, title, summary, and metadata * - category: Category cards with icon, title, and description * - alternative: Alternative vendor cards in comparison lists */ export function CardSkeleton({ className, containerClassName, variant = 'vendor', showActions = true, showMetadata = true, ...props }: CardSkeletonProps) { const cardContent = { vendor: , blog: , category: , alternative: } return (
{cardContent[variant]}
) } /** * Vendor card skeleton content - matches exact VendorCard structure */ function VendorCardContent({ showActions, showMetadata }: { showActions: boolean; showMetadata: boolean }) { return (
{/* Header Section - Row layout matching actual VendorCard */}
{/* Logo Frame - 60px width fixed, matching actual structure */}
{/* Text Container - Column layout, matching actual structure */}
{/* Title - Single line with proper width */} {/* Category - Single line, shorter */}
{/* Description Section - Fixed 48px height matching actual VendorCard */}
{/* Footer Section - Responsive layout matching actual structure */}
{/* Stats Container - Flexible width, no overflow */}
{/* OpenMSP Score skeleton */}
{/* GitHub Stats skeleton */}
{/* Tag Section - Contained within card boundaries */}
) } /** * Blog card skeleton content - matches fixed height structure */ function BlogCardContent({ showActions, showMetadata }: { showActions: boolean; showMetadata: boolean }) { return ( <> {/* Image Section — OG 1200×630 aspect (matches blog-card.tsx loaded state) */}
{/* Content - Fixed height structure to match BlogCard */}
{/* Title Section - Fixed 2 lines with vertical centering */}
{/* Chips Section - Fixed single line height */}
{/* Description Section - Fixed 2 lines with vertical centering */}
{/* Actions - only if requested */} {showActions && (
)} {/* Metadata footer - Matches BlogMeta horizontal layout */} {showMetadata && (
{/* Author section - matches AuthorMeta */}
{/* Date and reading time section - matches BlogMeta right side */}
{/* Separator dot */}
)}
) } /** * Category card skeleton content */ function CategoryCardContent() { return (
{/* Icon grid */}
{Array.from({ length: 6 }).map((_, index) => ( ))}
{/* Content */}
) } /** * Alternative card skeleton content (for vendor alternatives/comparisons) */ function AlternativeCardContent({ showActions }: { showActions: boolean }) { return (
{showActions && (
)}
) } /** * Grid of card skeletons for loading lists */ export function CardSkeletonGrid({ count = 6, variant = 'vendor', className, containerClassName, ...props }: { count?: number variant?: CardSkeletonProps['variant'] className?: string containerClassName?: string } & Omit) { return (
{Array.from({ length: count }, (_, index) => ( ))}
) }