This commit is contained in:
freedakgmail
2025-11-23 03:07:39 +08:00
parent 626d7dddde
commit e92884ae53
679 changed files with 208175 additions and 1183474 deletions
+124 -2
View File
@@ -4,21 +4,82 @@ 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, History, Database } from "lucide-react"
import { Download, Upload, AlertTriangle, RefreshCw, FileText, Bell, History, Database, User as UserIcon } from "lucide-react"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import { useRef, useState } from "react"
import { useRef, useState, useEffect } 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"
import { useSession } from "next-auth/react"
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import { ChangePassword } from "@/components/settings/change-password"
interface UserProfile {
id: string
email: string
name: string | null
createdAt: string
updatedAt: string
ownedTreesCount: number
collaborations: Array<{
treeId: string
treeName: string
role: string
}>
}
export default function SettingsPage() {
const { treeData, loadData, resetData } = useFamily()
const { data: session } = useSession()
const [importStatus, setImportStatus] = useState<string>("")
const [gedcomStatus, setGedcomStatus] = useState<string>("")
const [userProfile, setUserProfile] = useState<UserProfile | null>(null)
const fileInputRef = useRef<HTMLInputElement>(null)
const gedcomInputRef = useRef<HTMLInputElement>(null)
const getUserInitial = () => {
if (session?.user?.name) {
return session.user.name.charAt(0).toUpperCase()
}
if (session?.user?.email) {
return session.user.email.charAt(0).toUpperCase()
}
return "U"
}
// 获取用户资料
useEffect(() => {
if (session?.user?.id) {
fetch('/api/user/profile')
.then(res => res.json())
.then(data => {
if (data.user) {
setUserProfile(data.user)
}
})
.catch(err => console.error('获取用户资料失败:', err))
}
}, [session?.user?.id])
const getRoleText = (role: string) => {
const roleMap: Record<string, string> = {
'OWNER': '所有者',
'EDITOR': '编辑者',
'VIEWER': '查看者'
}
return roleMap[role] || role
}
const getRoleBadgeColor = (role: string) => {
const colorMap: Record<string, string> = {
'OWNER': 'bg-purple-100 text-purple-700 border-purple-200',
'EDITOR': 'bg-blue-100 text-blue-700 border-blue-200',
'VIEWER': 'bg-gray-100 text-gray-700 border-gray-200'
}
return colorMap[role] || 'bg-gray-100 text-gray-700'
}
return (
<div className="min-h-screen bg-background flex flex-col font-sans">
@@ -27,6 +88,67 @@ export default function SettingsPage() {
<h1 className="text-3xl font-serif font-bold mb-8"> (Settings)</h1>
<div className="space-y-6">
{/* 用户信息和密码修改 - 两列布局 */}
{session && (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<UserIcon className="h-5 w-5" />
</CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center gap-4">
<Avatar className="h-16 w-16 border-2 border-border">
<AvatarFallback className="text-2xl">{getUserInitial()}</AvatarFallback>
</Avatar>
<div className="flex-1">
<h3 className="text-lg font-semibold">{session.user?.name || "用户"}</h3>
<p className="text-sm text-muted-foreground">{session.user?.email}</p>
<p className="text-xs text-muted-foreground mt-1">
ID: {session.user?.id}
</p>
</div>
</div>
{userProfile && (userProfile.ownedTreesCount > 0 || userProfile.collaborations.length > 0) && (
<div className="space-y-3 pt-3 border-t">
<p className="text-sm font-medium"></p>
<div className="space-y-2">
{/* 显示拥有的家族树(所有者) */}
{userProfile.ownedTreesCount > 0 && (
<div className="text-sm text-muted-foreground">
<span className={`inline-flex items-center px-2 py-1 rounded-full border text-xs ${getRoleBadgeColor('OWNER')}`}>
{getRoleText('OWNER')}
</span>
<span className="ml-2">{userProfile.ownedTreesCount} </span>
</div>
)}
{/* 显示协作的家族树 */}
{userProfile.collaborations.length > 0 && (
<div className="space-y-2">
{userProfile.collaborations.map((collab) => (
<div key={collab.treeId} className="flex items-center justify-between text-sm p-2 rounded-md bg-muted/50">
<span className="text-muted-foreground truncate flex-1">{collab.treeName}</span>
<span className={`text-xs px-2 py-1 rounded-full border ${getRoleBadgeColor(collab.role)}`}>
{getRoleText(collab.role)}
</span>
</div>
))}
</div>
)}
</div>
</div>
)}
</CardContent>
</Card>
<ChangePassword />
</div>
)}
{/* 通知设置和数据备份 - 两列布局 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<NotificationSettings />