SPB Git forge

spb/spb-cloud

Public
10commits 1branches 0releases
3.2 MBsize
maindefault branch
2 h agolast push
JavaScript 56.7% TypeScript 42.6%
5.6 KB · 155 lines tsx
Raw Blame History
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