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 */}
)}
>
)
}
/**
* 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 (
)
}
/**
* 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) => (
))}
)
}