Files
WooNooW/admin-spa/src/components/LoadingState.tsx
dwindown 232059e928 feat: Complete Dashboard API Integration with Analytics Controller
 Features:
- Implemented API integration for all 7 dashboard pages
- Added Analytics REST API controller with 7 endpoints
- Full loading and error states with retry functionality
- Seamless dummy data toggle for development

📊 Dashboard Pages:
- Customers Analytics (complete)
- Revenue Analytics (complete)
- Orders Analytics (complete)
- Products Analytics (complete)
- Coupons Analytics (complete)
- Taxes Analytics (complete)
- Dashboard Overview (complete)

🔌 Backend:
- Created AnalyticsController.php with REST endpoints
- All endpoints return 501 (Not Implemented) for now
- Ready for HPOS-based implementation
- Proper permission checks

🎨 Frontend:
- useAnalytics hook for data fetching
- React Query caching
- ErrorCard with retry functionality
- TypeScript type safety
- Zero build errors

📝 Documentation:
- DASHBOARD_API_IMPLEMENTATION.md guide
- Backend implementation roadmap
- Testing strategy

🔧 Build:
- All pages compile successfully
- Production-ready with dummy data fallback
- Zero TypeScript errors
2025-11-04 11:19:00 +07:00

118 lines
2.8 KiB
TypeScript

import React from 'react';
import { Loader2 } from 'lucide-react';
import { __ } from '@/lib/i18n';
interface LoadingStateProps {
message?: string;
size?: 'sm' | 'md' | 'lg';
fullScreen?: boolean;
className?: string;
}
/**
* Global Loading State Component
*
* Consistent loading UI across the application
* - i18n support
* - Responsive sizing
* - Full-screen or inline mode
* - Customizable message
*
* @example
* // Default loading
* <LoadingState />
*
* // Custom message
* <LoadingState message="Loading order..." />
*
* // Full screen
* <LoadingState fullScreen />
*
* // Small inline
* <LoadingState size="sm" message="Saving..." />
*/
export function LoadingState({
message,
size = 'md',
fullScreen = false,
className = ''
}: LoadingStateProps) {
const sizeClasses = {
sm: 'w-4 h-4',
md: 'w-8 h-8',
lg: 'w-12 h-12'
};
const textSizeClasses = {
sm: 'text-xs',
md: 'text-sm',
lg: 'text-base'
};
const containerClasses = fullScreen
? 'fixed inset-0 flex items-center justify-center bg-background/80 backdrop-blur-sm z-50'
: 'flex items-center justify-center p-8';
return (
<div className={`${containerClasses} ${className}`}>
<div className="text-center space-y-3">
<Loader2
className={`${sizeClasses[size]} animate-spin mx-auto text-primary`}
/>
<p className={`${textSizeClasses[size]} text-muted-foreground`}>
{message || __('Loading...')}
</p>
</div>
</div>
);
}
/**
* Page Loading State
* Optimized for full page loads
*/
export function PageLoadingState({ message }: { message?: string }) {
return <LoadingState size="lg" fullScreen message={message} />;
}
/**
* Inline Loading State
* For loading within components
*/
export function InlineLoadingState({ message }: { message?: string }) {
return <LoadingState size="sm" message={message} />;
}
/**
* Card Loading Skeleton
* For loading card content
*/
export function CardLoadingSkeleton() {
return (
<div className="space-y-3 p-6 animate-pulse">
<div className="h-4 bg-muted rounded w-3/4"></div>
<div className="h-4 bg-muted rounded w-1/2"></div>
<div className="h-4 bg-muted rounded w-5/6"></div>
</div>
);
}
/**
* Table Loading Skeleton
* For loading table rows
*/
export function TableLoadingSkeleton({ rows = 5 }: { rows?: number }) {
return (
<div className="space-y-2">
{Array.from({ length: rows }).map((_, i) => (
<div key={i} className="flex gap-4 p-4 animate-pulse">
<div className="h-4 bg-muted rounded w-1/6"></div>
<div className="h-4 bg-muted rounded w-1/4"></div>
<div className="h-4 bg-muted rounded w-1/3"></div>
<div className="h-4 bg-muted rounded w-1/6"></div>
</div>
))}
</div>
);
}