162 lines
6.1 KiB
JavaScript
162 lines
6.1 KiB
JavaScript
// Reusable skeleton components for loading states
|
|
|
|
export function SkeletonCard() {
|
|
return (
|
|
<div className="bg-white rounded-xl border border-border p-5 animate-pulse">
|
|
<div className="h-4 bg-surface-tertiary rounded w-3/4 mb-3"></div>
|
|
<div className="h-3 bg-surface-tertiary rounded w-1/2 mb-2"></div>
|
|
<div className="h-3 bg-surface-tertiary rounded w-2/3"></div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function SkeletonStatCard() {
|
|
return (
|
|
<div className="bg-white rounded-xl border border-border p-5 animate-pulse">
|
|
<div className="flex items-start justify-between mb-4">
|
|
<div className="w-10 h-10 bg-surface-tertiary rounded-lg"></div>
|
|
<div className="h-3 bg-surface-tertiary rounded w-16"></div>
|
|
</div>
|
|
<div className="h-8 bg-surface-tertiary rounded w-20 mb-2"></div>
|
|
<div className="h-3 bg-surface-tertiary rounded w-24"></div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function SkeletonTable({ rows = 5, cols = 6 }) {
|
|
return (
|
|
<div className="bg-white rounded-xl border border-border overflow-hidden animate-pulse">
|
|
<div className="border-b border-border bg-surface-secondary p-4">
|
|
<div className="flex gap-4">
|
|
{[...Array(cols)].map((_, i) => (
|
|
<div key={i} className="h-3 bg-surface-tertiary rounded w-20"></div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<div className="divide-y divide-border-light">
|
|
{[...Array(rows)].map((_, i) => (
|
|
<div key={i} className="p-4">
|
|
<div className="flex gap-4">
|
|
{[...Array(cols)].map((_, j) => (
|
|
<div key={j} className="h-4 bg-surface-tertiary rounded flex-1"></div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function SkeletonKanbanBoard() {
|
|
return (
|
|
<div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-4">
|
|
{[...Array(5)].map((_, colIdx) => (
|
|
<div key={colIdx} className="animate-pulse">
|
|
<div className="flex items-center gap-2 mb-3">
|
|
<div className="w-2.5 h-2.5 bg-surface-tertiary rounded-full"></div>
|
|
<div className="h-4 bg-surface-tertiary rounded w-24"></div>
|
|
<div className="h-5 bg-surface-tertiary rounded-full w-8"></div>
|
|
</div>
|
|
<div className="bg-surface-secondary rounded-xl p-2 space-y-2 min-h-[400px]">
|
|
{[...Array(3)].map((_, cardIdx) => (
|
|
<div key={cardIdx} className="bg-white rounded-lg border border-border p-3">
|
|
<div className="h-4 bg-surface-tertiary rounded w-full mb-2"></div>
|
|
<div className="h-3 bg-surface-tertiary rounded w-3/4 mb-3"></div>
|
|
<div className="flex gap-2">
|
|
<div className="h-5 bg-surface-tertiary rounded w-16"></div>
|
|
<div className="h-5 bg-surface-tertiary rounded w-20"></div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function SkeletonCalendar() {
|
|
return (
|
|
<div className="bg-white rounded-xl border border-border overflow-hidden animate-pulse">
|
|
<div className="flex items-center justify-between px-6 py-4 border-b border-border">
|
|
<div className="h-6 bg-surface-tertiary rounded w-40"></div>
|
|
<div className="h-8 bg-surface-tertiary rounded w-20"></div>
|
|
</div>
|
|
<div className="grid grid-cols-7 border-b border-border bg-surface-secondary">
|
|
{[...Array(7)].map((_, i) => (
|
|
<div key={i} className="text-center py-3">
|
|
<div className="h-3 bg-surface-tertiary rounded w-8 mx-auto"></div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="grid grid-cols-7">
|
|
{[...Array(35)].map((_, i) => (
|
|
<div key={i} className="border-r border-b border-border min-h-[100px] p-2">
|
|
<div className="h-5 w-5 bg-surface-tertiary rounded-full mb-2"></div>
|
|
<div className="space-y-1">
|
|
<div className="h-3 bg-surface-tertiary rounded w-full"></div>
|
|
{i % 3 === 0 && <div className="h-3 bg-surface-tertiary rounded w-3/4"></div>}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function SkeletonAssetGrid({ count = 10 }) {
|
|
return (
|
|
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-5 gap-4">
|
|
{[...Array(count)].map((_, i) => (
|
|
<div key={i} className="animate-pulse">
|
|
<div className="aspect-square bg-surface-tertiary rounded-xl"></div>
|
|
<div className="mt-2 h-3 bg-surface-tertiary rounded w-3/4"></div>
|
|
<div className="mt-1 h-3 bg-surface-tertiary rounded w-1/2"></div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export function SkeletonDashboard() {
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Header */}
|
|
<div className="animate-pulse">
|
|
<div className="h-8 w-64 bg-surface-tertiary rounded-lg mb-2"></div>
|
|
<div className="h-4 w-48 bg-surface-tertiary rounded"></div>
|
|
</div>
|
|
|
|
{/* Stat cards */}
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
|
{[...Array(4)].map((_, i) => (
|
|
<SkeletonStatCard key={i} />
|
|
))}
|
|
</div>
|
|
|
|
{/* Content cards */}
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
{[...Array(2)].map((_, i) => (
|
|
<div key={i} className="bg-white rounded-xl border border-border animate-pulse">
|
|
<div className="px-5 py-4 border-b border-border">
|
|
<div className="h-5 bg-surface-tertiary rounded w-32"></div>
|
|
</div>
|
|
<div className="divide-y divide-border-light">
|
|
{[...Array(5)].map((_, j) => (
|
|
<div key={j} className="px-5 py-3 flex gap-3">
|
|
<div className="flex-1 space-y-2">
|
|
<div className="h-4 bg-surface-tertiary rounded w-2/3"></div>
|
|
<div className="h-3 bg-surface-tertiary rounded w-1/2"></div>
|
|
</div>
|
|
<div className="h-6 bg-surface-tertiary rounded w-16"></div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|