Files
NextEdu/src/modules/parent/components/parent-children-data-page.tsx
SpecialX 1abf58c0b6 feat(parent): add attention banner, export button, and grade detail
- Add parent-attention-banner for highlighting items needing attention

- Add parent-export-button for data export capability

- Add child-grade-detail component for detailed grade viewing

- Update existing child-card, child-detail-header, child-grade-summary, child-schedule-card

- Update parent-children-data-page and data-access
2026-06-23 17:37:49 +08:00

92 lines
2.2 KiB
TypeScript

import type { LucideIcon } from "lucide-react"
import { EmptyState } from "@/shared/components/ui/empty-state"
/**
* 家长视角下"多子女数据聚合页"的共享布局。
* 用于 attendance / grades 等需要遍历所有子女展示同类数据的页面,
* 消除重复的 dataScope 空状态处理与页面骨架代码。
*/
export function ParentChildrenDataPage<T>({
title,
description,
icon,
noRecordsTitle,
noRecordsDescription,
items,
renderItem,
headerExtra,
}: {
title: string
description: string
icon: LucideIcon
noRecordsTitle: string
noRecordsDescription: string
items: T[]
renderItem: (item: T) => React.ReactNode
/** 渲染在标题与列表之间的额外内容(如预警横幅)。 */
headerExtra?: React.ReactNode
}) {
const hasItems = items.length > 0
return (
<div className="h-full flex-1 flex-col space-y-8 p-6 md:p-8 md:flex">
<div>
<h2 className="text-2xl font-bold tracking-tight">{title}</h2>
<p className="text-muted-foreground">{description}</p>
</div>
{headerExtra}
{!hasItems ? (
<EmptyState
title={noRecordsTitle}
description={noRecordsDescription}
icon={icon}
className="border-none shadow-none"
/>
) : (
<div className="space-y-8">
{items.map((item, idx) => (
<div key={idx} className="space-y-4">
{renderItem(item)}
</div>
))}
</div>
)}
</div>
)
}
/**
* dataScope 为空(家长未关联子女)时的统一空状态页面。
*/
export function ParentNoChildrenPage({
title,
description,
icon,
emptyTitle,
emptyDescription,
}: {
title: string
description: string
icon: LucideIcon
emptyTitle: string
emptyDescription: string
}) {
return (
<div className="h-full flex-1 flex-col space-y-8 p-6 md:p-8 md:flex">
<div>
<h2 className="text-2xl font-bold tracking-tight">{title}</h2>
<p className="text-muted-foreground">{description}</p>
</div>
<EmptyState
title={emptyTitle}
description={emptyDescription}
icon={icon}
className="border-none shadow-none"
/>
</div>
)
}