'use client'; import React from 'react'; import { StateContainerProps, StateContainerConfig } from '@/ui/state-types'; import { LoadingWrapper } from '@/ui/LoadingWrapper'; import { ErrorDisplay } from '@/ui/ErrorDisplay'; import { EmptyState } from '@/ui/EmptyState'; import { Box } from '@/ui/Box'; import { Heading } from '@/ui/Heading'; import { Text } from '@/ui/Text'; import { Inbox, AlertCircle, Grid, List, LucideIcon } from 'lucide-react'; /** * StateContainer Component * * Combined wrapper that automatically handles all states (loading, error, empty, success) * based on the provided data and state values. * * Features: * - Automatic state detection and rendering * - Customizable configuration for each state * - Custom render functions for advanced use cases * - Consistent behavior across all pages * * Usage Example: * ```typescript * * {(content) => } * * ``` */ export function StateContainer({ data, isLoading, error, retry, children, config, showEmpty = true, isEmpty, }: StateContainerProps) { // Determine if data is empty const isDataEmpty = (data: T | null | undefined): boolean => { if (data === null || data === undefined) return true; if (isEmpty) return isEmpty(data); // Default empty checks if (Array.isArray(data)) return data.length === 0; if (typeof data === 'object' && data !== null) { return Object.keys(data).length === 0; } return false; }; // Priority order: Loading > Error > Empty > Success if (isLoading) { const loadingConfig = config?.loading || {}; // Custom render if (config?.customRender?.loading) { return <>{config.customRender.loading()}; } return ( ); } if (error) { const errorConfig = config?.error || {}; // Custom render if (config?.customRender?.error) { return <>{config.customRender.error(error)}; } return ( ); } if (showEmpty && isDataEmpty(data)) { const emptyConfig = config?.empty; // Custom render if (config?.customRender?.empty) { return <>{config.customRender.empty()}; } // If no empty config provided, show nothing (or could show default empty state) if (!emptyConfig) { return ( ); } return ( ); } // Success state - render children with data if (data === null || data === undefined) { // This shouldn't happen if we've handled all cases above, but as a fallback return ( ); } // At this point, data is guaranteed to be non-null and non-undefined return <>{children(data as T)}; } /** * ListStateContainer - Specialized for list data * Automatically handles empty arrays with appropriate messaging */ export function ListStateContainer({ data, isLoading, error, retry, children, config, emptyConfig, }: StateContainerProps & { emptyConfig?: { icon: LucideIcon; title: string; description?: string; action?: { label: string; onClick: () => void; }; }; }) { const listConfig: StateContainerConfig = { ...config, empty: emptyConfig || { icon: List, title: 'No items found', description: 'This list is currently empty', }, }; return ( !arr || arr.length === 0} > {children} ); } /** * DetailStateContainer - Specialized for detail pages * Includes back/refresh functionality */ export function DetailStateContainer({ data, isLoading, error, retry, children, config, onBack, onRefresh, }: StateContainerProps & { onBack?: () => void; onRefresh?: () => void; }) { const detailConfig: StateContainerConfig = { ...config, error: { ...config?.error, actions: [ ...(config?.error?.actions || []), ...(onBack ? [{ label: 'Go Back', onClick: onBack, variant: 'secondary' as const }] : []), ...(onRefresh ? [{ label: 'Refresh', onClick: onRefresh, variant: 'primary' as const }] : []), ], showNavigation: config?.error?.showNavigation ?? true, }, }; return ( {children} ); } /** * PageStateContainer - Full page state management * Wraps content in proper page structure */ export function PageStateContainer({ data, isLoading, error, retry, children, config, title, description, }: StateContainerProps & { title?: string; description?: string; }) { const pageConfig: StateContainerConfig = { loading: { variant: 'full-screen', message: title ? `Loading ${title}...` : 'Loading...', ...config?.loading, }, error: { variant: 'full-screen', ...config?.error, }, empty: config?.empty, }; if (isLoading) { return {children} ; } if (error) { return {children} ; } if (!data || (Array.isArray(data) && data.length === 0)) { if (config?.empty) { return ( {title && ( {title} {description && ( {description} )} )} {children} ); } } return ( {title && ( {title} {description && ( {description} )} )} {children} ); } /** * GridStateContainer - Specialized for grid layouts * Handles card-based empty states */ export function GridStateContainer({ data, isLoading, error, retry, children, config, emptyConfig, }: StateContainerProps & { emptyConfig?: { icon: LucideIcon; title: string; description?: string; action?: { label: string; onClick: () => void; }; }; }) { const gridConfig: StateContainerConfig = { loading: { variant: 'card', ...config?.loading, }, ...config, empty: emptyConfig || { icon: Grid, title: 'No items to display', description: 'Try adjusting your filters or search', }, }; return ( !arr || arr.length === 0} > {children} ); }