"use client"
import { Component, type ReactNode, Suspense } from "react"
import { AlertCircle } from "lucide-react"
import { EmptyState } from "@/shared/components/ui/empty-state"
import { Card, CardContent, CardHeader } from "@/shared/components/ui/card"
import { Skeleton } from "@/shared/components/ui/skeleton"
import { useTranslations } from "next-intl"
/**
* 仪表盘分区 Error Boundary
*
* 包裹每个独立数据区块,避免单个区块崩溃导致整页不可用。
* 与路由级 error.tsx 不同,此组件仅替换出错区块,其余区块继续渲染。
*/
export class DashboardSectionErrorBoundary extends Component<
{ children: ReactNode },
{ hasError: boolean }
> {
state: { hasError: boolean } = { hasError: false }
static getDerivedStateFromError(): { hasError: boolean } {
return { hasError: true }
}
handleRetry = (): void => {
this.setState({ hasError: false })
}
render(): ReactNode {
if (this.state.hasError) {
return
}
return this.props.children
}
}
function DashboardSectionErrorFallback({
onRetry,
}: {
onRetry: () => void
}): ReactNode {
const t = useTranslations("dashboard.error")
return (
)
}
/**
* 分区骨架屏变体
*
* 不同数据区块使用不同骨架布局,提供更贴近真实内容的加载占位。
*/
type SkeletonVariant = "stats" | "card" | "chart" | "table" | "list"
export function DashboardSectionSkeleton({
variant = "card",
}: {
variant?: SkeletonVariant
}): ReactNode {
switch (variant) {
case "stats":
return (
{Array.from({ length: 4 }).map((_, i) => (
))}
)
case "chart":
return (
)
case "table":
return (
{Array.from({ length: 5 }).map((_, i) => (
))}
)
case "list":
return (
{Array.from({ length: 4 }).map((_, i) => (
))}
)
case "card":
default:
return (
)
}
}
/**
* 仪表盘分区包装器
*
* 组合 Error Boundary + Suspense + 骨架屏,包裹每个独立数据区块。
* 单个区块出错或加载中时,仅影响该区块,不波及整页。
*
* @example
*
*
*
*/
export function DashboardSection({
children,
variant = "card",
}: {
children: ReactNode
variant?: SkeletonVariant
}): ReactNode {
return (
}>
{children}
)
}