0.0.1.0
This commit is contained in:
+124
-2
@@ -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 />
|
||||
|
||||
Reference in New Issue
Block a user