Spaces:
Sleeping
Sleeping
| /** | |
| * PageSkeleton β pixel-perfect loading skeletons that EXACTLY match each page's | |
| * actual DOM structure. | |
| * | |
| * KEY PRINCIPLE: When the real content loads and replaces the skeleton, there | |
| * must be ZERO layout shift. Every skeleton block has the same width, height, | |
| * padding, margin, border-radius, and position as its real counterpart. | |
| * | |
| * The shimmer class is defined in globals.css. | |
| */ | |
| type Variant = | |
| | 'home' | 'categories' | 'product' | 'cart' | 'checkout' | 'orders' | |
| | 'search' | 'profile' | 'login' | 'ai-chat' | 'videos' | 'live' | |
| | 'group-buy' | 'wishlist' | 'link-account' | 'telegram' | 'payment' | |
| | 'seller-dashboard' | 'seller-products' | 'seller-orders' | 'seller-profile' | |
| | 'seller-go-live' | 'seller-videos' | 'seller-stories' | 'seller-settings' | |
| | 'seller-academy' | 'become-seller' | 'settings' | 'messenger' | |
| | 'notifications' | 'create' | 'minimal'; | |
| export function PageSkeleton({ variant = 'minimal' }: { variant?: Variant }) { | |
| switch (variant) { | |
| case 'home': return <HomeSkeleton />; | |
| case 'categories': return <CategoriesSkeleton />; | |
| case 'product': return <ProductSkeleton />; | |
| case 'cart': return <CartSkeleton />; | |
| case 'checkout': return <CheckoutSkeleton />; | |
| case 'orders': return <OrdersSkeleton />; | |
| case 'search': return <SearchSkeleton />; | |
| case 'profile': return <ProfileSkeleton />; | |
| case 'login': return <LoginSkeleton />; | |
| case 'ai-chat': return <AIChatSkeleton />; | |
| case 'videos': return <VideosSkeleton />; | |
| case 'live': return <LiveSkeleton />; | |
| case 'group-buy': return <GroupBuySkeleton />; | |
| case 'wishlist': return <WishlistSkeleton />; | |
| case 'link-account': return <LinkAccountSkeleton />; | |
| case 'telegram': return <TelegramSkeleton />; | |
| case 'payment': return <PaymentSkeleton />; | |
| case 'seller-dashboard': return <SellerDashboardSkeleton />; | |
| case 'seller-products': return <SellerProductsSkeleton />; | |
| case 'seller-orders': return <SellerOrdersSkeleton />; | |
| case 'seller-profile': return <SellerProfileSkeleton />; | |
| case 'seller-go-live': return <SellerGoLiveSkeleton />; | |
| case 'seller-videos': return <SellerVideosSkeleton />; | |
| case 'seller-stories': return <SellerStoriesSkeleton />; | |
| case 'seller-settings': return <SellerSettingsSkeleton />; | |
| case 'seller-academy': return <SellerAcademySkeleton />; | |
| case 'become-seller': return <BecomeSellerSkeleton />; | |
| case 'settings': return <SettingsSkeleton />; | |
| case 'messenger': return <MessengerSkeleton />; | |
| case 'notifications': return <NotificationsSkeleton />; | |
| case 'create': return <CreateSkeleton />; | |
| default: return <MinimalSkeleton />; | |
| } | |
| } | |
| // ---- Reusable shimmer block ---- | |
| function S({ className = '' }: { className?: string }) { | |
| return <div className={`shimmer ${className}`} />; | |
| } | |
| // ============================================================ | |
| // HOME (feed) β matches page.tsx exactly: | |
| // top bar (logo+search+icons) β stories bar β live banner β feed posts | |
| // ============================================================ | |
| function HomeSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen max-w-xl mx-auto"> | |
| {/* Top bar: logo + search + icons */} | |
| <div className="px-3 pt-3 pb-2 bg-white sticky top-0 z-30 flex items-center gap-2"> | |
| <S className="w-8 h-8 rounded-lg shrink-0" /> | |
| <S className="flex-1 h-10 rounded-full" /> | |
| <S className="w-10 h-10 rounded-full shrink-0" /> | |
| </div> | |
| {/* Stories bar */} | |
| <div className="flex gap-3 px-3 py-2 border-b border-white/5"> | |
| {Array.from({ length: 6 }).map((_, i) => ( | |
| <div key={i} className="shrink-0 flex flex-col items-center gap-1"> | |
| <S className="w-14 h-14 rounded-full" /> | |
| <S className="w-10 h-2" /> | |
| </div> | |
| ))} | |
| </div> | |
| {/* Live banner */} | |
| <div className="px-3 mt-2"> | |
| <S className="w-full h-12 rounded-xl" /> | |
| </div> | |
| {/* Feed posts β match FeedPostCard structure */} | |
| {Array.from({ length: 2 }).map((_, i) => ( | |
| <div key={i} className="border-b border-white/5"> | |
| {/* Seller header */} | |
| <div className="flex items-center gap-2.5 px-3 py-2.5"> | |
| <S className="w-9 h-9 rounded-full shrink-0" /> | |
| <div className="flex-1"> | |
| <S className="w-24 h-3 mb-1" /> | |
| <S className="w-12 h-2" /> | |
| </div> | |
| <S className="w-16 h-8 rounded-full" /> | |
| </div> | |
| {/* Media β aspect-square */} | |
| <S className="w-full aspect-square rounded-none" /> | |
| {/* Action bar */} | |
| <div className="flex items-center gap-4 px-3 py-2"> | |
| <S className="w-6 h-6" /> | |
| <S className="w-6 h-6" /> | |
| <S className="w-6 h-6" /> | |
| <S className="w-6 h-6 ml-auto" /> | |
| </div> | |
| {/* Likes + caption */} | |
| <div className="px-3 pb-3 space-y-1.5"> | |
| <S className="w-20 h-3" /> | |
| <S className="w-3/4 h-3" /> | |
| <S className="w-1/2 h-3" /> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // CATEGORIES β matches categories/page.tsx: | |
| // search bar (sticky) β category tabs β subcategory grid β sort bar β 2-col grid | |
| // ============================================================ | |
| function CategoriesSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen"> | |
| {/* Search bar */} | |
| <div className="sticky top-0 z-30 bg-white px-3 pt-3 pb-2 border-b border-white/5 flex items-center gap-2"> | |
| <S className="w-5 h-5 shrink-0" /> | |
| <S className="flex-1 h-9 rounded-full" /> | |
| <S className="w-5 h-5 shrink-0" /> | |
| </div> | |
| {/* Category tabs */} | |
| <div className="flex items-center gap-5 px-3 py-2 border-b border-white/5"> | |
| {Array.from({ length: 7 }).map((_, i) => ( | |
| <S key={i} className="w-16 h-4 shrink-0" /> | |
| ))} | |
| </div> | |
| {/* Subcategory grid (5-col) */} | |
| <div className="grid grid-cols-5 gap-2 px-3 py-3 border-b border-white/5"> | |
| {Array.from({ length: 10 }).map((_, i) => ( | |
| <div key={i} className="flex flex-col items-center gap-1"> | |
| <S className="w-11 h-11 rounded-full" /> | |
| <S className="w-10 h-2" /> | |
| </div> | |
| ))} | |
| </div> | |
| {/* Sort bar */} | |
| <div className="flex items-center justify-between px-3 py-2 border-b border-white/5"> | |
| <S className="w-20 h-3" /> | |
| <S className="w-20 h-7 rounded-lg" /> | |
| </div> | |
| {/* Product grid 2-col β matches MobileCategoryProductCard */} | |
| <div className="grid grid-cols-2 gap-2 px-3 py-3"> | |
| {Array.from({ length: 8 }).map((_, i) => ( | |
| <div key={i} className="bg-white rounded-lg overflow-hidden border border-white/5"> | |
| <S className="w-full aspect-square rounded-none" /> | |
| <div className="p-2 space-y-1.5"> | |
| <S className="w-full h-3" /> | |
| <S className="w-2/3 h-3" /> | |
| <S className="w-16 h-3 rounded-full" /> | |
| <S className="w-14 h-4" /> | |
| <S className="w-10 h-2" /> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // PRODUCT DETAIL β matches product/page.tsx: | |
| // back btn β image gallery β price β title β trust badges β seller card β | |
| // group buy banner β reviews β details β bottom action bar | |
| // ============================================================ | |
| function ProductSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen pb-20"> | |
| {/* Top bar with back button */} | |
| <div className="flex items-center px-3 py-3"> | |
| <S className="w-9 h-9 rounded-full" /> | |
| </div> | |
| {/* Image gallery β aspect-square */} | |
| <S className="w-full aspect-square rounded-none" /> | |
| {/* Image dots */} | |
| <div className="flex justify-center gap-1 py-2"> | |
| {Array.from({ length: 4 }).map((_, i) => ( | |
| <S key={i} className="w-1.5 h-1.5 rounded-full" /> | |
| ))} | |
| </div> | |
| {/* Price + title */} | |
| <div className="px-4 py-3 space-y-2"> | |
| <S className="w-24 h-7" /> | |
| <S className="w-3/4 h-5" /> | |
| <S className="w-20 h-3" /> | |
| </div> | |
| {/* Trust badges */} | |
| <div className="px-4 py-2 flex gap-2"> | |
| <S className="w-28 h-5 rounded-full" /> | |
| <S className="w-24 h-5 rounded-full" /> | |
| </div> | |
| {/* Group buy banner */} | |
| <div className="mx-4 my-3"> | |
| <S className="w-full h-16 rounded-xl" /> | |
| </div> | |
| {/* Seller card */} | |
| <div className="px-4 py-3 flex items-center gap-3 border-t border-white/5"> | |
| <S className="w-10 h-10 rounded-full" /> | |
| <div className="flex-1 space-y-1.5"> | |
| <S className="w-20 h-3" /> | |
| <S className="w-16 h-2" /> | |
| </div> | |
| <S className="w-14 h-7 rounded-full" /> | |
| </div> | |
| {/* Reviews section */} | |
| <div className="px-4 py-3 border-t border-white/5 space-y-2"> | |
| <S className="w-28 h-4" /> | |
| <S className="w-full h-16 rounded-lg" /> | |
| </div> | |
| {/* Bottom action bar β fixed */} | |
| <div className="fixed bottom-0 left-0 right-0 bg-white border-t border-white/5 px-3 py-2 flex gap-2 items-center"> | |
| <S className="w-10 h-10 rounded-lg" /> | |
| <S className="w-10 h-10 rounded-lg" /> | |
| <S className="flex-1 h-10 rounded-full" /> | |
| <S className="flex-1 h-10 rounded-full" /> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // CART β matches cart/page.tsx: | |
| // header β cart items (image + name + qty controls) β summary | |
| // ============================================================ | |
| function CartSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen max-w-3xl mx-auto px-4 py-4"> | |
| <S className="w-20 h-7 mb-4" /> | |
| {Array.from({ length: 3 }).map((_, i) => ( | |
| <div key={i} className="flex gap-3 p-3 border border-white/5 rounded-xl mb-3"> | |
| <S className="w-20 h-20 rounded-xl shrink-0" /> | |
| <div className="flex-1 space-y-2"> | |
| <S className="w-3/4 h-4" /> | |
| <S className="w-16 h-5" /> | |
| <div className="flex items-center gap-2"> | |
| <S className="w-8 h-8 rounded-lg" /> | |
| <S className="w-6 h-4" /> | |
| <S className="w-8 h-8 rounded-lg" /> | |
| </div> | |
| </div> | |
| <S className="w-16 h-4" /> | |
| </div> | |
| ))} | |
| {/* Summary */} | |
| <div className="border-t border-white/5 pt-4 mt-4 space-y-2"> | |
| <div className="flex justify-between"><S className="w-16 h-4" /><S className="w-20 h-4" /></div> | |
| <div className="flex justify-between"><S className="w-20 h-4" /><S className="w-16 h-4" /></div> | |
| <div className="flex justify-between"><S className="w-12 h-5" /><S className="w-24 h-5" /></div> | |
| <S className="w-full h-11 rounded-full mt-3" /> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // CHECKOUT β matches checkout/page.tsx: | |
| // header β shipping form fields β payment method card β place order button | |
| // ============================================================ | |
| function CheckoutSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen max-w-2xl mx-auto px-4 py-4 space-y-4"> | |
| <S className="w-28 h-7" /> | |
| {/* Shipping form */} | |
| <div className="space-y-3"> | |
| <S className="w-full h-11 rounded-lg" /> | |
| <S className="w-full h-11 rounded-lg" /> | |
| <div className="grid grid-cols-2 gap-3"> | |
| <S className="w-full h-11 rounded-lg" /> | |
| <S className="w-full h-11 rounded-lg" /> | |
| </div> | |
| <S className="w-full h-20 rounded-lg" /> | |
| </div> | |
| {/* Payment method */} | |
| <S className="w-full h-16 rounded-xl" /> | |
| {/* Place order button */} | |
| <S className="w-full h-12 rounded-full" /> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // ORDERS β matches orders/page.tsx: | |
| // header β order cards (header + item thumbnails + total) | |
| // ============================================================ | |
| function OrdersSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen max-w-3xl mx-auto px-4 py-4"> | |
| <S className="w-24 h-7 mb-4" /> | |
| <div className="space-y-3"> | |
| {Array.from({ length: 4 }).map((_, i) => ( | |
| <div key={i} className="border border-white/5 rounded-xl overflow-hidden"> | |
| {/* Order header */} | |
| <div className="p-4 flex items-center gap-3"> | |
| <S className="w-10 h-10 rounded-xl shrink-0" /> | |
| <div className="flex-1 space-y-1.5"> | |
| <S className="w-20 h-4" /> | |
| <S className="w-16 h-2" /> | |
| </div> | |
| <S className="w-16 h-5 rounded-full" /> | |
| </div> | |
| {/* Item thumbnails */} | |
| <div className="px-4 pb-3 flex gap-2"> | |
| <S className="w-12 h-12 rounded-lg" /> | |
| <S className="w-12 h-12 rounded-lg" /> | |
| <S className="w-12 h-12 rounded-lg" /> | |
| </div> | |
| {/* Total */} | |
| <div className="px-4 py-3 border-t border-white/5 flex justify-between"> | |
| <S className="w-16 h-4" /> | |
| <S className="w-20 h-4" /> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // SEARCH β matches search/page.tsx: | |
| // header (search bar + tabs) β AI answer bubble β product grid β follow-up input | |
| // ============================================================ | |
| function SearchSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen"> | |
| {/* Header with search + tabs */} | |
| <div className="sticky top-0 z-40 bg-white/95 backdrop-blur-xl border-b border-white/5"> | |
| <div className="max-w-3xl mx-auto px-4 py-3 flex items-center gap-3"> | |
| <S className="w-9 h-9 rounded-full shrink-0" /> | |
| <S className="flex-1 h-10 rounded-full" /> | |
| </div> | |
| <div className="max-w-3xl mx-auto px-4 flex gap-4"> | |
| <S className="w-10 h-6" /> | |
| <S className="w-24 h-6" /> | |
| <S className="w-16 h-6" /> | |
| </div> | |
| </div> | |
| <div className="max-w-3xl mx-auto px-4 py-4 space-y-4"> | |
| {/* User query bubble */} | |
| <div className="flex justify-end"> | |
| <S className="w-40 h-9 rounded-2xl" /> | |
| </div> | |
| {/* AI answer */} | |
| <div className="flex items-start gap-3"> | |
| <S className="w-8 h-8 rounded-full shrink-0" /> | |
| <div className="flex-1 space-y-2"> | |
| <S className="w-full h-3" /> | |
| <S className="w-5/6 h-3" /> | |
| <S className="w-4/6 h-3" /> | |
| </div> | |
| </div> | |
| {/* Product recommendations */} | |
| <div className="grid grid-cols-2 sm:grid-cols-4 gap-2"> | |
| {Array.from({ length: 4 }).map((_, i) => ( | |
| <div key={i} className="space-y-1.5"> | |
| <S className="w-full aspect-square rounded-lg" /> | |
| <S className="w-3/4 h-3" /> | |
| <S className="w-12 h-4" /> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| {/* Follow-up input bar */} | |
| <div className="fixed bottom-0 left-0 right-0 bg-white border-t border-white/5 px-4 py-3"> | |
| <div className="max-w-3xl mx-auto flex items-center gap-2"> | |
| <S className="w-10 h-10 rounded-full" /> | |
| <S className="flex-1 h-10 rounded-full" /> | |
| <S className="w-10 h-10 rounded-full" /> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // PROFILE β matches profile/page.tsx: | |
| // header β user card β stats grid β menu items β logout | |
| // ============================================================ | |
| function ProfileSkeleton() { | |
| return ( | |
| <div className="max-w-3xl mx-auto px-4 py-4 space-y-4"> | |
| <div className="flex items-center justify-between"> | |
| <S className="w-24 h-7" /> | |
| <S className="w-9 h-9 rounded-full" /> | |
| </div> | |
| {/* User card */} | |
| <div className="bg-white p-4 rounded-xl border border-white/5"> | |
| <div className="flex items-center gap-3 mb-4"> | |
| <S className="w-16 h-16 rounded-full" /> | |
| <div className="flex-1 space-y-2"> | |
| <S className="w-24 h-4" /> | |
| <S className="w-32 h-3" /> | |
| </div> | |
| <S className="w-14 h-8 rounded-lg" /> | |
| </div> | |
| <div className="space-y-2"> | |
| <S className="w-40 h-3" /> | |
| <S className="w-32 h-3" /> | |
| </div> | |
| </div> | |
| {/* Stats grid */} | |
| <div className="grid grid-cols-3 gap-2"> | |
| {Array.from({ length: 3 }).map((_, i) => ( | |
| <div key={i} className="bg-white p-3 rounded-xl border border-white/5 text-center space-y-1"> | |
| <S className="w-6 h-5 mx-auto" /> | |
| <S className="w-12 h-3 mx-auto" /> | |
| </div> | |
| ))} | |
| </div> | |
| {/* Menu items */} | |
| {Array.from({ length: 5 }).map((_, i) => ( | |
| <div key={i} className="bg-white p-3 rounded-xl border border-white/5 flex items-center gap-3"> | |
| <S className="w-10 h-10 rounded-xl" /> | |
| <div className="flex-1 space-y-1"> | |
| <S className="w-24 h-4" /> | |
| <S className="w-16 h-3" /> | |
| </div> | |
| <S className="w-4 h-4" /> | |
| </div> | |
| ))} | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // LOGIN β matches login/page.tsx: | |
| // centered logo β title β tab switcher β email/password form β submit button | |
| // ============================================================ | |
| function LoginSkeleton() { | |
| return ( | |
| <div className="min-h-screen flex items-center justify-center p-4 bg-gradient-to-b from-cyan-50 via-white to-white"> | |
| <div className="w-full max-w-md space-y-6"> | |
| <div className="text-center space-y-2"> | |
| <S className="w-14 h-14 rounded-2xl mx-auto" /> | |
| <S className="w-20 h-7 mx-auto" /> | |
| <S className="w-32 h-3 mx-auto" /> | |
| </div> | |
| {/* Tab switcher */} | |
| <S className="w-full h-10 rounded-xl" /> | |
| {/* Form fields */} | |
| <div className="space-y-4"> | |
| <div className="space-y-1.5"> | |
| <S className="w-12 h-3" /> | |
| <S className="w-full h-11 rounded-xl" /> | |
| </div> | |
| <div className="space-y-1.5"> | |
| <S className="w-16 h-3" /> | |
| <S className="w-full h-11 rounded-xl" /> | |
| </div> | |
| <S className="w-full h-11 rounded-xl" /> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // AI CHAT β matches ai-chat/page.tsx: | |
| // header β messages (user bubble + AI bubble) β suggestion chips β input bar | |
| // ============================================================ | |
| function AIChatSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen flex flex-col max-w-3xl mx-auto"> | |
| {/* Header */} | |
| <div className="px-4 py-3 border-b border-white/5 flex items-center gap-3"> | |
| <S className="w-9 h-9 rounded-xl shrink-0" /> | |
| <div className="flex-1"> | |
| <S className="w-32 h-4 mb-1" /> | |
| <S className="w-20 h-2" /> | |
| </div> | |
| <S className="w-8 h-8 rounded-lg" /> | |
| </div> | |
| {/* Messages */} | |
| <div className="flex-1 p-4 space-y-4"> | |
| <div className="flex justify-end"> | |
| <S className="w-40 h-9 rounded-2xl" /> | |
| </div> | |
| <div className="flex items-start gap-2"> | |
| <S className="w-8 h-8 rounded-full shrink-0" /> | |
| <div className="flex-1 space-y-2"> | |
| <S className="w-full h-3" /> | |
| <S className="w-5/6 h-3" /> | |
| <S className="w-4/6 h-3" /> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Suggestion chips */} | |
| <div className="px-4 py-2 flex gap-2 flex-wrap"> | |
| {Array.from({ length: 3 }).map((_, i) => ( | |
| <S key={i} className="w-32 h-8 rounded-full" /> | |
| ))} | |
| </div> | |
| {/* Input bar */} | |
| <div className="px-4 py-3 border-t border-white/5 flex items-center gap-2"> | |
| <S className="w-10 h-10 rounded-full" /> | |
| <S className="flex-1 h-10 rounded-full" /> | |
| <S className="w-10 h-10 rounded-full" /> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // VIDEOS β TikTok-style vertical feed: | |
| // full-screen video cards with right action rail + bottom caption | |
| // ============================================================ | |
| function VideosSkeleton() { | |
| return ( | |
| <div className="bg-black min-h-screen"> | |
| {Array.from({ length: 2 }).map((_, i) => ( | |
| <div key={i} className="relative w-full aspect-[9/16] bg-slate-900"> | |
| <S className="w-full h-full rounded-none" /> | |
| {/* Right action rail */} | |
| <div className="absolute right-2 bottom-20 flex flex-col gap-4"> | |
| <S className="w-10 h-10 rounded-full" /> | |
| <S className="w-10 h-10 rounded-full" /> | |
| <S className="w-10 h-10 rounded-full" /> | |
| <S className="w-10 h-10 rounded-full" /> | |
| </div> | |
| {/* Bottom caption */} | |
| <div className="absolute bottom-4 left-4 right-16 space-y-2"> | |
| <S className="w-24 h-4" /> | |
| <S className="w-full h-3" /> | |
| <S className="w-3/4 h-3" /> | |
| <S className="w-32 h-8 rounded-lg" /> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // LIVE β matches live/page.tsx: | |
| // header β "LIVE NOW" section β grid of live session cards | |
| // ============================================================ | |
| function LiveSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen max-w-5xl mx-auto px-4 py-4 space-y-6"> | |
| <div className="flex items-center gap-2"> | |
| <S className="w-6 h-6" /> | |
| <S className="w-32 h-7" /> | |
| </div> | |
| <div> | |
| <S className="w-24 h-5 mb-3" /> | |
| <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3"> | |
| {Array.from({ length: 3 }).map((_, i) => ( | |
| <div key={i} className="rounded-xl overflow-hidden border border-white/5"> | |
| <S className="w-full aspect-video" /> | |
| <div className="p-3 space-y-2"> | |
| <S className="w-3/4 h-4" /> | |
| <S className="w-16 h-3" /> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // GROUP BUY β matches group-buy/page.tsx: | |
| // back link β group buy banner β product card β join section | |
| // ============================================================ | |
| function GroupBuySkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen max-w-2xl mx-auto px-4 py-4"> | |
| <S className="w-32 h-4 mb-3" /> | |
| {/* Group buy banner */} | |
| <S className="w-full h-24 rounded-2xl mb-4" /> | |
| {/* Product card */} | |
| <div className="flex gap-3 p-3 border border-white/5 rounded-xl mb-4"> | |
| <S className="w-20 h-20 rounded-xl shrink-0" /> | |
| <div className="flex-1 space-y-2"> | |
| <S className="w-3/4 h-4" /> | |
| <S className="w-16 h-5" /> | |
| <S className="w-20 h-3" /> | |
| </div> | |
| </div> | |
| {/* Join section */} | |
| <S className="w-full h-16 rounded-xl" /> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // WISHLIST β matches wishlist/page.tsx: | |
| // header β list of product cards (image + name + price + remove) | |
| // ============================================================ | |
| function WishlistSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen max-w-4xl mx-auto px-4 py-4"> | |
| <S className="w-28 h-7 mb-4" /> | |
| <div className="grid grid-cols-1 sm:grid-cols-2 gap-3"> | |
| {Array.from({ length: 4 }).map((_, i) => ( | |
| <div key={i} className="p-3 border border-white/5 rounded-xl flex gap-3"> | |
| <S className="w-20 h-20 rounded-xl shrink-0" /> | |
| <div className="flex-1 space-y-2"> | |
| <S className="w-3/4 h-4" /> | |
| <S className="w-16 h-5" /> | |
| <S className="w-20 h-3" /> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // LINK ACCOUNT β matches link-account/page.tsx: | |
| // back link β header card β info card β steps | |
| // ============================================================ | |
| function LinkAccountSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen max-w-2xl mx-auto px-4 py-4"> | |
| <S className="w-32 h-4 mb-3" /> | |
| <div className="flex items-center gap-3 mb-4"> | |
| <S className="w-12 h-12 rounded-2xl" /> | |
| <div> | |
| <S className="w-32 h-6 mb-1" /> | |
| <S className="w-48 h-3" /> | |
| </div> | |
| </div> | |
| <S className="w-full h-24 rounded-xl mb-4" /> | |
| <S className="w-full h-12 rounded-xl" /> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // TELEGRAM β matches telegram/page.tsx: | |
| // back link β header β channel info card β features list | |
| // ============================================================ | |
| function TelegramSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen max-w-2xl mx-auto px-4 py-4"> | |
| <S className="w-32 h-4 mb-3" /> | |
| <div className="flex items-center gap-3 mb-4"> | |
| <S className="w-12 h-12 rounded-2xl" /> | |
| <div> | |
| <S className="w-36 h-6 mb-1" /> | |
| <S className="w-48 h-3" /> | |
| </div> | |
| </div> | |
| <S className="w-full h-20 rounded-xl mb-4" /> | |
| <div className="space-y-3"> | |
| {Array.from({ length: 3 }).map((_, i) => ( | |
| <S key={i} className="w-full h-12 rounded-xl" /> | |
| ))} | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // PAYMENT β matches payment/page.tsx: | |
| // steps indicator β payment details card β status card | |
| // ============================================================ | |
| function PaymentSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen max-w-2xl mx-auto px-4 py-4 space-y-4"> | |
| <S className="w-24 h-7" /> | |
| {/* Steps indicator */} | |
| <div className="flex items-center justify-center gap-2"> | |
| {Array.from({ length: 3 }).map((_, i) => ( | |
| <S key={i} className="w-8 h-8 rounded-full" /> | |
| ))} | |
| </div> | |
| {/* Payment details */} | |
| <S className="w-full h-40 rounded-xl" /> | |
| {/* Status */} | |
| <S className="w-full h-20 rounded-xl" /> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // SELLER DASHBOARD β matches seller-dashboard/page.tsx: | |
| // header β store profile card β stats grid β menu sections | |
| // ============================================================ | |
| function SellerDashboardSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen max-w-2xl mx-auto pb-24"> | |
| <div className="px-4 py-3 border-b border-white/5 flex items-center gap-3"> | |
| <S className="w-9 h-9 rounded-full" /> | |
| <S className="w-20 h-5" /> | |
| </div> | |
| {/* Store header */} | |
| <div className="px-4 py-6 text-center border-b border-white/5"> | |
| <S className="w-20 h-20 rounded-full mx-auto mb-3" /> | |
| <S className="w-32 h-6 mx-auto mb-2" /> | |
| <S className="w-24 h-3 mx-auto" /> | |
| </div> | |
| {/* Stats grid */} | |
| <div className="grid grid-cols-4 gap-2 px-4 py-4 border-b border-white/5"> | |
| {Array.from({ length: 4 }).map((_, i) => ( | |
| <div key={i} className="text-center space-y-1"> | |
| <S className="w-4 h-4 mx-auto" /> | |
| <S className="w-8 h-4 mx-auto" /> | |
| <S className="w-10 h-2 mx-auto" /> | |
| </div> | |
| ))} | |
| </div> | |
| {/* Menu sections */} | |
| {Array.from({ length: 3 }).map((_, section) => ( | |
| <div key={section} className="px-4 py-3"> | |
| <S className="w-16 h-3 mb-2" /> | |
| <div className="border border-white/5 rounded-xl divide-y divide-white/5"> | |
| {Array.from({ length: 3 }).map((_, i) => ( | |
| <div key={i} className="flex items-center gap-3 p-3"> | |
| <S className="w-9 h-9 rounded-lg" /> | |
| <div className="flex-1 space-y-1"> | |
| <S className="w-24 h-4" /> | |
| <S className="w-16 h-3" /> | |
| </div> | |
| <S className="w-4 h-4" /> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // SELLER PRODUCTS β matches seller/products/page.tsx: | |
| // header β search bar β product grid with edit/delete overlays | |
| // ============================================================ | |
| function SellerProductsSkeleton() { | |
| return ( | |
| <div className="space-y-4 max-w-4xl mx-auto p-4"> | |
| <S className="w-32 h-7" /> | |
| <S className="w-full h-11 rounded-full" /> | |
| <div className="grid grid-cols-2 sm:grid-cols-3 gap-3"> | |
| {Array.from({ length: 6 }).map((_, i) => ( | |
| <div key={i} className="rounded-xl overflow-hidden border border-white/5"> | |
| <S className="w-full aspect-square" /> | |
| <div className="p-3 space-y-2"> | |
| <S className="w-3/4 h-3" /> | |
| <S className="w-16 h-4" /> | |
| <div className="flex gap-2"> | |
| <S className="flex-1 h-8 rounded-lg" /> | |
| <S className="flex-1 h-8 rounded-lg" /> | |
| </div> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // SELLER ORDERS β matches seller/orders/page.tsx: | |
| // header β order list with item breakdown | |
| // ============================================================ | |
| function SellerOrdersSkeleton() { | |
| return ( | |
| <div className="space-y-3 max-w-4xl mx-auto p-4"> | |
| <S className="w-24 h-7 mb-4" /> | |
| {Array.from({ length: 4 }).map((_, i) => ( | |
| <div key={i} className="border border-white/5 rounded-xl p-4 space-y-2"> | |
| <div className="flex justify-between"> | |
| <S className="w-20 h-4" /> | |
| <S className="w-16 h-5 rounded-full" /> | |
| </div> | |
| <div className="flex gap-2"> | |
| <S className="w-12 h-12 rounded-lg" /> | |
| <S className="w-12 h-12 rounded-lg" /> | |
| </div> | |
| <S className="w-16 h-4" /> | |
| </div> | |
| ))} | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // SELLER PROFILE (edit) β matches seller/profile/page.tsx: | |
| // header β logo upload β form fields β save button | |
| // ============================================================ | |
| function SellerProfileSkeleton() { | |
| return ( | |
| <div className="space-y-4 max-w-2xl mx-auto p-4"> | |
| <S className="w-32 h-7" /> | |
| <div className="border border-white/5 rounded-xl p-4 space-y-3"> | |
| <S className="w-16 h-16 rounded-2xl mx-auto" /> | |
| <div className="space-y-2"> | |
| <S className="w-20 h-3" /> | |
| <S className="w-full h-11 rounded-lg" /> | |
| </div> | |
| <div className="space-y-2"> | |
| <S className="w-16 h-3" /> | |
| <S className="w-full h-11 rounded-lg" /> | |
| </div> | |
| <div className="space-y-2"> | |
| <S className="w-24 h-3" /> | |
| <S className="w-full h-20 rounded-lg" /> | |
| </div> | |
| </div> | |
| <S className="w-full h-11 rounded-full" /> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // SELLER GO LIVE β matches seller/go-live/page.tsx: | |
| // header β form β active session card | |
| // ============================================================ | |
| function SellerGoLiveSkeleton() { | |
| return ( | |
| <div className="space-y-4 max-w-2xl mx-auto p-4"> | |
| <S className="w-24 h-7" /> | |
| <div className="border border-white/5 rounded-xl p-4 space-y-3"> | |
| <div className="space-y-2"> | |
| <S className="w-20 h-3" /> | |
| <S className="w-full h-11 rounded-lg" /> | |
| </div> | |
| <div className="space-y-2"> | |
| <S className="w-16 h-3" /> | |
| <S className="w-full h-11 rounded-lg" /> | |
| </div> | |
| </div> | |
| <S className="w-full h-11 rounded-full" /> | |
| <S className="w-full h-24 rounded-xl" /> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // SELLER VIDEOS β matches seller/videos/page.tsx: | |
| // header β post form β video list | |
| // ============================================================ | |
| function SellerVideosSkeleton() { | |
| return ( | |
| <div className="space-y-4 max-w-2xl mx-auto p-4"> | |
| <S className="w-24 h-7" /> | |
| <div className="border border-white/5 rounded-xl p-4 space-y-3"> | |
| <div className="space-y-2"> | |
| <S className="w-20 h-3" /> | |
| <S className="w-full h-11 rounded-lg" /> | |
| </div> | |
| <div className="space-y-2"> | |
| <S className="w-16 h-3" /> | |
| <S className="w-full h-11 rounded-lg" /> | |
| </div> | |
| <S className="w-full h-11 rounded-full" /> | |
| </div> | |
| {Array.from({ length: 3 }).map((_, i) => ( | |
| <div key={i} className="flex gap-3 border border-white/5 rounded-xl p-3"> | |
| <S className="w-20 h-20 rounded-lg shrink-0" /> | |
| <div className="flex-1 space-y-2"> | |
| <S className="w-3/4 h-4" /> | |
| <S className="w-16 h-3" /> | |
| <div className="flex gap-2"> | |
| <S className="w-12 h-3" /> | |
| <S className="w-12 h-3" /> | |
| </div> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // SELLER STORIES β matches seller/stories/page.tsx: | |
| // header β story type selector β form | |
| // ============================================================ | |
| function SellerStoriesSkeleton() { | |
| return ( | |
| <div className="space-y-4 max-w-2xl mx-auto p-4"> | |
| <S className="w-24 h-7" /> | |
| <div className="grid grid-cols-2 gap-2"> | |
| {Array.from({ length: 4 }).map((_, i) => ( | |
| <S key={i} className="w-full h-16 rounded-xl" /> | |
| ))} | |
| </div> | |
| <div className="border border-white/5 rounded-xl p-4 space-y-3"> | |
| <div className="space-y-2"> | |
| <S className="w-20 h-3" /> | |
| <S className="w-full h-11 rounded-lg" /> | |
| </div> | |
| <div className="space-y-2"> | |
| <S className="w-16 h-3" /> | |
| <S className="w-full h-20 rounded-lg" /> | |
| </div> | |
| <S className="w-full h-11 rounded-full" /> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // SELLER SETTINGS β matches seller/settings/page.tsx: | |
| // header β account β notifications β region β security β danger zone | |
| // ============================================================ | |
| function SellerSettingsSkeleton() { | |
| return ( | |
| <div className="max-w-2xl mx-auto px-4 py-4 space-y-4"> | |
| <div className="flex items-center gap-3"> | |
| <S className="w-9 h-9 rounded-full" /> | |
| <S className="w-20 h-7" /> | |
| </div> | |
| {Array.from({ length: 4 }).map((_, section) => ( | |
| <div key={section}> | |
| <S className="w-20 h-3 mb-2" /> | |
| <div className="border border-white/5 rounded-xl divide-y divide-white/5"> | |
| {Array.from({ length: 3 }).map((_, i) => ( | |
| <div key={i} className="flex items-center gap-3 p-4"> | |
| <S className="w-9 h-9 rounded-lg" /> | |
| <div className="flex-1 space-y-1"> | |
| <S className="w-24 h-4" /> | |
| <S className="w-16 h-3" /> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // SELLER ACADEMY β matches seller/academy/page.tsx: | |
| // header β hero card β course cards grid | |
| // ============================================================ | |
| function SellerAcademySkeleton() { | |
| return ( | |
| <div className="space-y-6 max-w-4xl mx-auto p-4"> | |
| <S className="w-24 h-7" /> | |
| <S className="w-full h-32 rounded-xl" /> | |
| <S className="w-32 h-5" /> | |
| <div className="grid gap-3 sm:grid-cols-2"> | |
| {Array.from({ length: 4 }).map((_, i) => ( | |
| <div key={i} className="border border-white/5 rounded-xl p-4"> | |
| <div className="flex items-start gap-3"> | |
| <S className="w-10 h-10 rounded-lg shrink-0" /> | |
| <div className="flex-1 space-y-2"> | |
| <S className="w-3/4 h-4" /> | |
| <S className="w-full h-3" /> | |
| <div className="flex gap-2"> | |
| <S className="w-12 h-4 rounded-full" /> | |
| <S className="w-16 h-4 rounded-full" /> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // BECOME SELLER β matches become-seller/page.tsx: | |
| // header + progress bar β form fields | |
| // ============================================================ | |
| function BecomeSellerSkeleton() { | |
| return ( | |
| <div className="max-w-md mx-auto px-4 py-4"> | |
| <div className="flex items-center gap-3 mb-6"> | |
| <S className="w-9 h-9 rounded-full" /> | |
| <div className="flex-1"> | |
| <S className="w-28 h-5 mb-1" /> | |
| <S className="w-16 h-3" /> | |
| </div> | |
| </div> | |
| <div className="flex gap-1.5 mb-6"> | |
| {Array.from({ length: 3 }).map((_, i) => ( | |
| <S key={i} className="h-1.5 flex-1 rounded-full" /> | |
| ))} | |
| </div> | |
| <div className="space-y-5"> | |
| <div className="flex flex-col items-center gap-2"> | |
| <S className="w-24 h-24 rounded-full" /> | |
| </div> | |
| <div className="space-y-1.5"> | |
| <S className="w-20 h-3" /> | |
| <S className="w-full h-11 rounded-lg" /> | |
| </div> | |
| <div className="space-y-2"> | |
| <S className="w-16 h-3" /> | |
| <div className="grid grid-cols-3 gap-2"> | |
| {Array.from({ length: 3 }).map((_, i) => ( | |
| <S key={i} className="w-full h-14 rounded-xl" /> | |
| ))} | |
| </div> | |
| </div> | |
| <S className="w-full h-11 rounded-full" /> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // SETTINGS β matches settings/page.tsx: | |
| // header β account β preferences β selling β support β security | |
| // ============================================================ | |
| function SettingsSkeleton() { | |
| return ( | |
| <div className="max-w-2xl mx-auto px-4 py-4 space-y-4"> | |
| <div className="flex items-center gap-3"> | |
| <S className="w-9 h-9 rounded-full" /> | |
| <S className="w-20 h-7" /> | |
| </div> | |
| {Array.from({ length: 5 }).map((_, section) => ( | |
| <div key={section}> | |
| <S className="w-20 h-3 mb-2" /> | |
| <div className="border border-white/5 rounded-xl divide-y divide-white/5"> | |
| {Array.from({ length: 3 }).map((_, i) => ( | |
| <div key={i} className="flex items-center gap-3 p-4"> | |
| <S className="w-9 h-9 rounded-lg" /> | |
| <div className="flex-1 space-y-1"> | |
| <S className="w-24 h-4" /> | |
| <S className="w-16 h-3" /> | |
| </div> | |
| <S className="w-11 h-6 rounded-full" /> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // MESSENGER β matches messenger/page.tsx: | |
| // header β tabs β conversation list | |
| // ============================================================ | |
| function MessengerSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen max-w-2xl mx-auto"> | |
| <div className="sticky top-0 z-30 bg-white border-b border-white/5 px-4 py-3 flex items-center gap-3"> | |
| <S className="w-9 h-9 rounded-full" /> | |
| <S className="w-24 h-6 flex-1" /> | |
| <S className="w-9 h-9 rounded-full" /> | |
| </div> | |
| <div className="border-b border-white/5 px-2 flex gap-1"> | |
| {Array.from({ length: 4 }).map((_, i) => ( | |
| <S key={i} className="w-16 h-6 my-3" /> | |
| ))} | |
| </div> | |
| {Array.from({ length: 5 }).map((_, i) => ( | |
| <div key={i} className="flex items-center gap-3 px-4 py-3 border-b border-slate-50"> | |
| <S className="w-12 h-12 rounded-full shrink-0" /> | |
| <div className="flex-1 space-y-1.5"> | |
| <S className="w-24 h-4" /> | |
| <S className="w-40 h-3" /> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // NOTIFICATIONS β matches notifications/page.tsx: | |
| // header β notification list with typed icons | |
| // ============================================================ | |
| function NotificationsSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen max-w-2xl mx-auto"> | |
| <div className="sticky top-0 z-30 bg-white border-b border-white/5 px-4 py-3 flex items-center gap-3"> | |
| <S className="w-9 h-9 rounded-full" /> | |
| <S className="w-28 h-6 flex-1" /> | |
| <S className="w-20 h-3" /> | |
| </div> | |
| {Array.from({ length: 6 }).map((_, i) => ( | |
| <div key={i} className="flex items-start gap-3 px-4 py-3 border-b border-slate-50"> | |
| <S className="w-10 h-10 rounded-full shrink-0" /> | |
| <div className="flex-1 space-y-1.5"> | |
| <S className="w-32 h-4" /> | |
| <S className="w-full h-3" /> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // CREATE β matches create/page.tsx: | |
| // header β option cards | |
| // ============================================================ | |
| function CreateSkeleton() { | |
| return ( | |
| <div className="bg-white min-h-screen max-w-md mx-auto"> | |
| <div className="sticky top-0 z-30 bg-white border-b border-white/5 px-4 py-3 flex items-center justify-between"> | |
| <S className="w-16 h-6" /> | |
| <S className="w-9 h-9 rounded-full" /> | |
| </div> | |
| <div className="p-4 space-y-2"> | |
| {Array.from({ length: 4 }).map((_, i) => ( | |
| <div key={i} className="flex items-center gap-3 p-3 rounded-xl border border-white/5"> | |
| <S className="w-12 h-12 rounded-xl shrink-0" /> | |
| <div className="flex-1 space-y-1.5"> | |
| <S className="w-24 h-4" /> | |
| <S className="w-32 h-3" /> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| ); | |
| } | |
| // ============================================================ | |
| // MINIMAL (fallback) | |
| // ============================================================ | |
| function MinimalSkeleton() { | |
| return ( | |
| <div className="min-h-screen bg-white flex items-center justify-center p-4"> | |
| <div className="w-full max-w-md space-y-4"> | |
| <S className="w-24 h-8 mx-auto" /> | |
| <S className="w-full h-12" /> | |
| <S className="w-full h-12" /> | |
| <S className="w-full h-12" /> | |
| </div> | |
| </div> | |
| ); | |
| } | |