'use client';
import { EmptyState } from '@/ui/EmptyState';
import { ErrorDisplay } from '@/components/shared/ErrorDisplay';
import { LoadingWrapper } from '@/ui/LoadingWrapper';
import { Box } from '@/ui/Box';
import { Heading } from '@/ui/Heading';
import { StateContainerConfig, StateContainerProps } from '@/ui/state-types';
import { Text } from '@/ui/Text';
import { AlertCircle, Grid, Inbox, 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 (
);
}
// Custom success render
if (config?.customRender?.success) {
return <>{config.customRender.success(data as T)}>;
}
// 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}
);
}