JavaScript 56.7%
TypeScript 42.6%
1"use client";23interface SkeletonProps {4 className?: string;5 variant?: "text" | "circular" | "rectangular";6 style?: React.CSSProperties;7}89export default function Skeleton({ className = "", variant = "rectangular", style }: SkeletonProps) {10 const baseClasses = "relative overflow-hidden bg-gray-200/80 dark:bg-white/[0.07]";11 const variantClasses = {12 text: "rounded-md h-4 w-full",13 circular: "rounded-full",14 rectangular: "rounded-xl",15 };16 return (17 <div className={`${baseClasses} ${variantClasses[variant]} ${className}`} style={style} aria-hidden>18 <div className="absolute inset-0 animate-shimmer" />19 </div>20 );21}2223/* Preset skeleton layouts */2425export function SkeletonCard() {26 return (27 <div className="card p-3 sm:p-4">28 <div className="flex items-center gap-3">29 <Skeleton variant="circular" className="w-10 h-10 flex-shrink-0" />30 <div className="flex-1 space-y-2">31 <Skeleton variant="text" className="h-4 w-3/4" />32 <Skeleton variant="text" className="h-3 w-1/2" />33 </div>34 </div>35 </div>36 );37}3839export function SkeletonStat() {40 return (41 <div className="card p-3.5 sm:p-4 flex items-center gap-3">42 <Skeleton className="w-10 h-10 flex-shrink-0 rounded-xl" />43 <div className="flex-1 space-y-2">44 <Skeleton variant="text" className="h-5 w-14" />45 <Skeleton variant="text" className="h-3 w-20" />46 </div>47 </div>48 );49}5051export function SkeletonFileCard() {52 return (53 <div className="card shrink-0 w-[150px] sm:w-[176px] p-2">54 <Skeleton className="w-full aspect-[4/3] rounded-xl mb-2" />55 <Skeleton variant="text" className="h-3 w-4/5 mb-1.5 mx-1" />56 <Skeleton variant="text" className="h-2.5 w-1/3 mx-1" />57 </div>58 );59}6061export function SkeletonActivity() {62 return (63 <div className="flex items-start gap-3 px-1 py-2.5">64 <Skeleton className="w-9 h-9 flex-shrink-0 rounded-xl" />65 <div className="flex-1 space-y-1.5 pt-1">66 <Skeleton variant="text" className="h-3 w-3/4" />67 <Skeleton variant="text" className="h-2.5 w-1/3" />68 </div>69 <Skeleton variant="text" className="h-3 w-12 flex-shrink-0 mt-1" />70 </div>71 );72}7374export function SkeletonRow() {75 return (76 <div className="flex items-center gap-3 px-2 h-11">77 <Skeleton className="w-8 h-8 rounded-lg flex-shrink-0" />78 <Skeleton variant="text" className="h-3 flex-1" />79 <Skeleton variant="text" className="h-3 w-12 flex-shrink-0" />80 </div>81 );82}8384export function DashboardSkeleton() {85 return (86 <div className="p-3 sm:p-5 lg:p-6 space-y-4 sm:space-y-5 max-w-[1400px] mx-auto" aria-busy="true" aria-label="Chargement du tableau de bord">87 {/* Bandeau */}88 <div className="card p-5 sm:p-7 flex flex-col lg:flex-row lg:items-end gap-5 lg:gap-8 rounded-3xl">89 <div className="flex-1 space-y-3">90 <Skeleton variant="text" className="h-3 w-40" />91 <Skeleton variant="text" className="h-8 w-72" />92 <Skeleton variant="text" className="h-4 w-56" />93 </div>94 <div className="grid grid-cols-2 sm:grid-cols-4 gap-2 lg:w-[560px] shrink-0">95 {[...Array(4)].map((_, i) => <Skeleton key={i} className="h-16 rounded-2xl" />)}96 </div>97 </div>9899 {/* KPI */}100 <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-2 sm:gap-3">101 {[...Array(5)].map((_, i) => <SkeletonStat key={i} />)}102 </div>103104 {/* Stockage + activité */}105 <div className="grid grid-cols-1 lg:grid-cols-12 gap-4 sm:gap-5">106 <div className="card p-4 sm:p-5 lg:col-span-5">107 <Skeleton variant="text" className="h-3 w-24 mb-4" />108 <div className="flex flex-col sm:flex-row sm:items-center gap-5">109 <Skeleton variant="circular" className="w-[168px] h-[168px] mx-auto sm:mx-0 shrink-0" />110 <div className="flex-1 space-y-2.5">111 {[...Array(5)].map((_, i) => <Skeleton key={i} variant="text" className="h-3.5" />)}112 </div>113 </div>114 </div>115 <div className="card p-4 sm:p-5 lg:col-span-7">116 <Skeleton variant="text" className="h-3 w-24 mb-4" />117 <div className="flex items-baseline gap-4 mb-3">118 <Skeleton variant="text" className="h-7 w-16" />119 <Skeleton variant="text" className="h-7 w-16" />120 </div>121 <div className="flex items-end gap-1.5 h-[150px] ml-8">122 {[...Array(14)].map((_, i) => <Skeleton key={i} className="flex-1 rounded-t-[4px]" style={{ height: `${20 + ((i * 37) % 70)}%` }} />)}123 </div>124 </div>125 </div>126127 {/* Reprendre */}128 <div>129 <Skeleton variant="text" className="h-3 w-28 mb-3" />130 <div className="flex gap-2.5 sm:gap-3 overflow-hidden">131 {[...Array(6)].map((_, i) => <SkeletonFileCard key={i} />)}132 </div>133 </div>134135 {/* Timeline + latéral */}136 <div className="grid grid-cols-1 lg:grid-cols-12 gap-4 sm:gap-5">137 <div className="card p-4 sm:p-5 lg:col-span-7">138 <Skeleton variant="text" className="h-3 w-32 mb-3" />139 {[...Array(6)].map((_, i) => <SkeletonActivity key={i} />)}140 </div>141 <div className="lg:col-span-5 space-y-4 sm:space-y-5">142 <div className="card p-4 sm:p-5">143 <Skeleton variant="text" className="h-3 w-20 mb-3" />144 {[...Array(4)].map((_, i) => <SkeletonRow key={i} />)}145 </div>146 <div className="card p-4 sm:p-5">147 <Skeleton variant="text" className="h-3 w-36 mb-3" />148 {[...Array(4)].map((_, i) => <SkeletonRow key={i} />)}149 </div>150 </div>151 </div>152 </div>153 );154}155