This commit is contained in:
freedakgmail
2025-11-23 00:49:16 +08:00
parent c033e46782
commit 626d7dddde
184 changed files with 321926 additions and 2720 deletions
+111 -66
View File
@@ -4,11 +4,14 @@ import { SiteHeader } from "@/components/site-header"
import { useFamily } from "@/context/family-context"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Download, Upload, AlertTriangle, RefreshCw, FileText, Bell } from "lucide-react"
import { Download, Upload, AlertTriangle, RefreshCw, FileText, Bell, History, Database } from "lucide-react"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { useRef, useState } from "react"
import { exportToGedcom, importFromGedcom } from "@/lib/gedcom"
import { NotificationSettings } from "@/components/settings/notification-settings"
import { ActivityLogViewer } from "@/components/settings/activity-log-viewer"
import { upgradeDatabase } from "@/lib/db-upgrade"
import { ErrorBoundary } from "@/components/error-boundary"
export default function SettingsPage() {
const { treeData, loadData, resetData } = useFamily()
@@ -20,14 +23,15 @@ export default function SettingsPage() {
return (
<div className="min-h-screen bg-background flex flex-col font-sans">
<SiteHeader />
<main className="container mx-auto py-8 px-4 md:px-6 flex-1 max-w-3xl">
<main className="container mx-auto py-8 px-4 md:px-6 flex-1 max-w-6xl">
<h1 className="text-3xl font-serif font-bold mb-8"> (Settings)</h1>
<div className="space-y-6">
{/* 通知设置 */}
<NotificationSettings />
<Card>
{/* 通知设置和数据备份 - 两列布局 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<NotificationSettings />
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Download className="h-5 w-5" /> (Backup)
@@ -52,66 +56,65 @@ export default function SettingsPage() {
</Button>
</CardContent>
</Card>
</div>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Upload className="h-5 w-5" /> (Restore)
</CardTitle>
<CardDescription> JSON </CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Alert variant="destructive">
<AlertTriangle className="h-4 w-4" />
<AlertTitle></AlertTitle>
<AlertDescription></AlertDescription>
</Alert>
{/* 数据恢复和 GEDCOM - 两列布局 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Upload className="h-5 w-5" /> (Restore)
</CardTitle>
<CardDescription> JSON </CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Alert variant="destructive">
<AlertTriangle className="h-4 w-4" />
<AlertTitle></AlertTitle>
<AlertDescription></AlertDescription>
</Alert>
<div className="flex items-center gap-4">
<input
type="file"
ref={fileInputRef}
className="hidden"
accept=".json"
onChange={(e) => {
const file = e.target.files?.[0]
if (!file) return
<div className="flex items-center gap-4">
<input
type="file"
ref={fileInputRef}
className="hidden"
accept=".json"
onChange={(e) => {
const file = e.target.files?.[0]
if (!file) return
const reader = new FileReader()
reader.onload = async (event) => {
try {
const json = JSON.parse(event.target?.result as string)
if (json.members && json.rootId) {
await loadData(json)
const reader = new FileReader()
reader.onload = async (event) => {
try {
const jsonStr = event.target?.result as string
const data = JSON.parse(jsonStr)
await loadData(data)
setImportStatus("导入成功!")
// Clear input so same file can be selected again if needed
if (fileInputRef.current) fileInputRef.current.value = ""
} else {
setImportStatus("错误:无效的数据格式")
} catch (err) {
console.error(err)
setImportStatus("错误:无法解析 JSON 文件")
}
} catch (err) {
console.error(err)
setImportStatus("错误:无法解析文件")
}
}
reader.readAsText(file)
}}
/>
<Button variant="outline" onClick={() => fileInputRef.current?.click()}>
...
</Button>
<span className="text-sm text-muted-foreground">{importStatus}</span>
</div>
</CardContent>
</Card>
reader.readAsText(file)
}}
/>
<Button variant="outline" onClick={() => fileInputRef.current?.click()}>
...
</Button>
<span className="text-sm text-muted-foreground">{importStatus}</span>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<FileText className="h-5 w-5" /> GEDCOM
</CardTitle>
<CardDescription>/ GEDCOM AncestryMyHeritage</CardDescription>
</CardHeader>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<FileText className="h-5 w-5" /> GEDCOM
</CardTitle>
<CardDescription>/ GEDCOM AncestryMyHeritage</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center gap-4">
<Button
@@ -164,14 +167,50 @@ export default function SettingsPage() {
{gedcomStatus && <span className="text-sm text-muted-foreground">{gedcomStatus}</span>}
</div>
</CardContent>
</Card>
</Card>
</div>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<RefreshCw className="h-5 w-5" />
</CardTitle>
<CardDescription></CardDescription>
{/* 数据库和系统管理 - 两列布局 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Database className="h-5 w-5" />
</CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<Alert>
<AlertDescription>
</AlertDescription>
</Alert>
<Button
variant="outline"
onClick={async () => {
if (confirm("确定要升级数据库吗?这会保留所有现有数据。")) {
try {
await upgradeDatabase()
alert("数据库升级成功!请刷新页面。")
window.location.reload()
} catch (error) {
alert("数据库升级失败:" + (error as Error).message)
}
}
}}
>
<Database className="mr-2 h-4 w-4" />
</Button>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<AlertTriangle className="h-5 w-5 text-destructive" /> (Danger Zone)
</CardTitle>
<CardDescription>使</CardDescription>
</CardHeader>
<CardContent>
<Button
@@ -186,7 +225,13 @@ export default function SettingsPage() {
</Button>
</CardContent>
</Card>
</Card>
</div>
{/* 操作日志 - 全宽 */}
<ErrorBoundary>
<ActivityLogViewer />
</ErrorBoundary>
</div>
</main>
</div>