feat(parent,auth,onboarding,files,notifications,adaptive-practice,ai): add module updates

parent:

- Add parent-student-attendance-detail component

auth:

- Add actions, data-access, schema, services, types

onboarding:

- Add parent-children-form and hooks directory

files:

- Add actions, schema, hooks directory

notifications:

- Add schema and schema test

adaptive-practice:

- Add answer-input, answer-result, practice-result-view, practice-starter-with-nav

- Add question-card, question-content, lib and services directories

ai:

- Add context/create-ai-client-service, hooks/use-drag-position, hooks/use-position-persistence
This commit is contained in:
SpecialX
2026-07-03 10:26:12 +08:00
parent f3c223d914
commit e9a5264fe7
84 changed files with 6060 additions and 2530 deletions

View File

@@ -1,11 +1,14 @@
"use client"
import { Component, type ReactNode } from "react"
import { AlertCircle, RefreshCw } from "lucide-react"
import { useTranslations } from "next-intl"
/**
* AI 专用 Error Boundary
*
* 薄包装:委托给共享 SectionErrorBoundary使用 ai 命名空间。
* 保留同名导出以兼容现有 import。
*/
import { Button } from "@/shared/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
import type { ReactNode } from "react"
import { SectionErrorBoundary } from "@/shared/components/section-error-boundary"
type AiErrorBoundaryProps = {
children: ReactNode
@@ -15,74 +18,14 @@ type AiErrorBoundaryProps = {
onError?: (error: Error, info: unknown) => void
}
type AiErrorBoundaryState = {
error: Error | null
}
/**
* AI 专用 Error Boundary
*
* 包裹所有 AI 数据区块,防止单个 AI 调用失败导致整页崩溃。
* 提供重试按钮与友好的错误提示。
*/
export class AiErrorBoundary extends Component<
AiErrorBoundaryProps,
AiErrorBoundaryState
> {
constructor(props: AiErrorBoundaryProps) {
super(props)
this.state = { error: null }
}
static getDerivedStateFromError(error: Error): AiErrorBoundaryState {
return { error }
}
componentDidCatch(error: Error, info: unknown): void {
if (this.props.onError) {
this.props.onError(error, info)
}
}
private handleReset = (): void => {
this.setState({ error: null })
}
render(): ReactNode {
if (this.state.error) {
if (this.props.fallback) {
return this.props.fallback(this.state.error, this.handleReset)
}
return <DefaultAiErrorFallback error={this.state.error} onReset={this.handleReset} />
}
return this.props.children
}
}
function DefaultAiErrorFallback({
error,
onReset,
}: {
error: Error
onReset: () => void
}): ReactNode {
const t = useTranslations("ai")
export function AiErrorBoundary({
children,
fallback,
onError,
}: AiErrorBoundaryProps): ReactNode {
return (
<Card className="border-destructive/30">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-destructive">
<AlertCircle className="h-4 w-4" />
{t("error.boundaryTitle")}
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
<p className="text-sm text-muted-foreground">{t("error.boundaryDescription")}</p>
<p className="text-xs text-muted-foreground/70 font-mono">{error.message}</p>
<Button type="button" variant="outline" size="sm" onClick={onReset}>
<RefreshCw className="mr-1 h-3.5 w-3.5" />
{t("error.retry")}
</Button>
</CardContent>
</Card>
<SectionErrorBoundary namespace="ai" fallback={fallback} onError={onError}>
{children}
</SectionErrorBoundary>
)
}