This commit is contained in:
freedakgmail
2025-11-23 19:38:51 +08:00
parent e44dd0bd95
commit 3b0d8255f6
649 changed files with 656698 additions and 4133 deletions
+1 -7
View File
@@ -86,12 +86,6 @@ export default function RegisterPage() {
</Alert>
)}
<Alert className="bg-blue-50 border-blue-200 dark:bg-blue-950 dark:border-blue-800">
<AlertDescription className="text-sm text-blue-800 dark:text-blue-200">
💡 <Link href="/auth/signin" className="underline font-semibold"></Link>使
</AlertDescription>
</Alert>
<div className="space-y-2">
<Label htmlFor="name"></Label>
<Input
@@ -144,7 +138,7 @@ export default function RegisterPage() {
</div>
</CardContent>
<CardFooter className="flex flex-col space-y-4">
<CardFooter className="flex flex-col space-y-4 pt-6">
<Button
type="submit"
className="w-full"
+3 -43
View File
@@ -13,8 +13,8 @@ import { BookOpen } from "lucide-react"
export default function SignInPage() {
const router = useRouter()
const [email, setEmail] = useState("wang@example.com")
const [password, setPassword] = useState("123456")
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const [error, setError] = useState("")
const [loading, setLoading] = useState(false)
@@ -76,46 +76,6 @@ export default function SignInPage() {
</Alert>
)}
<Alert className="bg-blue-50 border-blue-200 dark:bg-blue-950 dark:border-blue-800">
<AlertDescription className="text-sm text-blue-800 dark:text-blue-200">
<div className="space-y-2">
<div className="font-semibold">💡 </div>
<div className="space-y-1 text-xs">
<div className="flex items-center justify-between">
<span>王先生: wang@example.com / 123456</span>
<Button
type="button"
variant="ghost"
size="sm"
className="h-6 px-2 text-xs"
onClick={() => {
setEmail("wang@example.com")
setPassword("123456")
}}
>
使
</Button>
</div>
<div className="flex items-center justify-between">
<span>虞女士: yu@example.com / 123456</span>
<Button
type="button"
variant="ghost"
size="sm"
className="h-6 px-2 text-xs"
onClick={() => {
setEmail("yu@example.com")
setPassword("123456")
}}
>
使
</Button>
</div>
</div>
</div>
</AlertDescription>
</Alert>
<div className="space-y-2">
<Label htmlFor="email"></Label>
<Input
@@ -143,7 +103,7 @@ export default function SignInPage() {
</div>
</CardContent>
<CardFooter className="flex flex-col space-y-4">
<CardFooter className="flex flex-col space-y-4 pt-6">
<Button
type="submit"
className="w-full"
+11
View File
@@ -156,6 +156,17 @@
height: 0;
}
}
@keyframes scroll {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-50%);
}
}
--animate-scroll: scroll 20s linear infinite;
}
@layer base {
+296
View File
@@ -0,0 +1,296 @@
"use client"
import { SiteHeader } from "@/components/site-header"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { BookOpen, Users, Network, Settings, Shield, FileText } from "lucide-react"
export default function HelpPage() {
return (
<div className="min-h-screen flex flex-col bg-background">
<SiteHeader />
<main className="flex-1 container mx-auto px-4 py-8">
<div className="max-w-5xl mx-auto space-y-8">
{/* 标题 */}
<div>
<h1 className="text-3xl font-serif font-bold mb-2">使</h1>
<p className="text-muted-foreground">
使
</p>
</div>
<Tabs defaultValue="guide" className="space-y-6">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="guide"></TabsTrigger>
<TabsTrigger value="roles"></TabsTrigger>
<TabsTrigger value="features"></TabsTrigger>
</TabsList>
{/* 快速入门 */}
<TabsContent value="guide" className="space-y-6">
<Card className="border-none shadow-sm bg-card/50 backdrop-blur">
<CardHeader>
<div className="flex items-center gap-3">
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-primary/10 text-primary">
<BookOpen className="h-6 w-6" />
</div>
<div>
<CardTitle className="text-xl font-serif font-light">使</CardTitle>
<CardDescription className="font-light"></CardDescription>
</div>
</div>
</CardHeader>
<CardContent className="space-y-6">
<div className="space-y-4">
<div className="flex gap-4">
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-primary text-primary-foreground flex items-center justify-center font-bold">
1
</div>
<div className="flex-1">
<h3 className="font-medium mb-1"></h3>
<p className="text-sm text-muted-foreground font-light">
使
</p>
</div>
</div>
<div className="flex gap-4">
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-primary text-primary-foreground flex items-center justify-center font-bold">
2
</div>
<div className="flex-1">
<h3 className="font-medium mb-1"></h3>
<p className="text-sm text-muted-foreground font-light">
"开始记录"
</p>
</div>
</div>
<div className="flex gap-4">
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-primary text-primary-foreground flex items-center justify-center font-bold">
3
</div>
<div className="flex-1">
<h3 className="font-medium mb-1"></h3>
<p className="text-sm text-muted-foreground font-light">
</p>
</div>
</div>
<div className="flex gap-4">
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-primary text-primary-foreground flex items-center justify-center font-bold">
4
</div>
<div className="flex-1">
<h3 className="font-medium mb-1"></h3>
<p className="text-sm text-muted-foreground font-light">
</p>
</div>
</div>
<div className="flex gap-4">
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-primary text-primary-foreground flex items-center justify-center font-bold">
5
</div>
<div className="flex-1">
<h3 className="font-medium mb-1"></h3>
<p className="text-sm text-muted-foreground font-light">
</p>
</div>
</div>
<div className="flex gap-4">
<div className="flex-shrink-0 w-8 h-8 rounded-full bg-primary text-primary-foreground flex items-center justify-center font-bold">
6
</div>
<div className="flex-1">
<h3 className="font-medium mb-1"></h3>
<p className="text-sm text-muted-foreground font-light">
</p>
</div>
</div>
</div>
</CardContent>
</Card>
<Card className="border-none shadow-sm bg-card/50 backdrop-blur">
<CardHeader>
<CardTitle className="text-xl font-serif font-light"></CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-sm text-muted-foreground font-light">
<li> </li>
<li> 便</li>
<li> 使</li>
<li> 使</li>
<li> "编辑者"</li>
</ul>
</CardContent>
</Card>
</TabsContent>
{/* 角色权限 */}
<TabsContent value="roles" className="space-y-6">
<Card className="border-none shadow-sm bg-card/50 backdrop-blur">
<CardHeader>
<div className="flex items-center gap-3">
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-primary/10 text-primary">
<Shield className="h-6 w-6" />
</div>
<div>
<CardTitle className="text-xl font-serif font-light"></CardTitle>
<CardDescription className="font-light"></CardDescription>
</div>
</div>
</CardHeader>
<CardContent className="space-y-6">
<div className="space-y-6">
<div className="p-4 bg-primary/5 rounded-lg">
<div className="flex items-center gap-2 mb-2">
<div className="w-2 h-2 rounded-full bg-primary"></div>
<h3 className="font-medium"> (Owner)</h3>
</div>
<p className="text-sm text-muted-foreground font-light mb-3">
</p>
<div className="space-y-1 text-sm">
<p className="text-muted-foreground font-light"> </p>
<p className="text-muted-foreground font-light"> </p>
<p className="text-muted-foreground font-light"> </p>
<p className="text-muted-foreground font-light"> </p>
<p className="text-muted-foreground font-light"> </p>
</div>
</div>
<div className="p-4 bg-muted/30 rounded-lg">
<div className="flex items-center gap-2 mb-2">
<div className="w-2 h-2 rounded-full bg-blue-500"></div>
<h3 className="font-medium"> (Editor)</h3>
</div>
<p className="text-sm text-muted-foreground font-light mb-3">
</p>
<div className="space-y-1 text-sm">
<p className="text-muted-foreground font-light"> </p>
<p className="text-muted-foreground font-light"> </p>
<p className="text-muted-foreground font-light"> </p>
<p className="text-muted-foreground font-light"> </p>
<p className="text-muted-foreground font-light"> </p>
</div>
</div>
<div className="p-4 bg-muted/30 rounded-lg">
<div className="flex items-center gap-2 mb-2">
<div className="w-2 h-2 rounded-full bg-green-500"></div>
<h3 className="font-medium"> (Viewer)</h3>
</div>
<p className="text-sm text-muted-foreground font-light mb-3">
</p>
<div className="space-y-1 text-sm">
<p className="text-muted-foreground font-light"> </p>
<p className="text-muted-foreground font-light"> </p>
<p className="text-muted-foreground font-light"> </p>
<p className="text-muted-foreground font-light"> </p>
<p className="text-muted-foreground font-light"> </p>
</div>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
{/* 功能说明 */}
<TabsContent value="features" className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Card className="border-none shadow-sm bg-card/50 backdrop-blur">
<CardHeader>
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10 text-primary">
<Users className="h-5 w-5" />
</div>
<CardTitle className="text-lg font-serif font-light"></CardTitle>
</div>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-sm text-muted-foreground font-light">
<li> </li>
<li> </li>
<li> </li>
<li> </li>
<li> </li>
</ul>
</CardContent>
</Card>
<Card className="border-none shadow-sm bg-card/50 backdrop-blur">
<CardHeader>
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10 text-primary">
<Network className="h-5 w-5" />
</div>
<CardTitle className="text-lg font-serif font-light"></CardTitle>
</div>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-sm text-muted-foreground font-light">
<li> </li>
<li> </li>
<li> </li>
<li> </li>
<li> </li>
</ul>
</CardContent>
</Card>
<Card className="border-none shadow-sm bg-card/50 backdrop-blur">
<CardHeader>
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10 text-primary">
<FileText className="h-5 w-5" />
</div>
<CardTitle className="text-lg font-serif font-light"></CardTitle>
</div>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-sm text-muted-foreground font-light">
<li> </li>
<li> </li>
<li> </li>
<li> </li>
</ul>
</CardContent>
</Card>
<Card className="border-none shadow-sm bg-card/50 backdrop-blur">
<CardHeader>
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10 text-primary">
<Settings className="h-5 w-5" />
</div>
<CardTitle className="text-lg font-serif font-light"></CardTitle>
</div>
</CardHeader>
<CardContent>
<ul className="space-y-2 text-sm text-muted-foreground font-light">
<li> </li>
<li> </li>
<li> </li>
<li> </li>
<li> </li>
</ul>
</CardContent>
</Card>
</div>
</TabsContent>
</Tabs>
</div>
</main>
</div>
)
}
+2 -2
View File
@@ -1,7 +1,7 @@
import type React from "react"
import type { Metadata } from "next"
import { Geist, Geist_Mono } from "next/font/google"
import { Analytics } from "@vercel/analytics/next"
// import { Analytics } from "@vercel/analytics/next"
import "./globals.css"
import { FamilyProvider } from "@/context/family-context"
import { PWAProvider } from "@/components/pwa/pwa-provider"
@@ -57,7 +57,7 @@ export default function RootLayout({
</FamilyProvider>
</DialogProvider>
</SessionProvider>
<Analytics />
{/* <Analytics /> */}
</body>
</html>
)
+1 -1
View File
@@ -247,7 +247,7 @@ export default function MemberProfilePage() {
{member.bio ? (
<p className="whitespace-pre-line leading-relaxed">{member.bio}</p>
) : (
<p className="text-muted-foreground italic">...</p>
<p className="text-sm text-foreground font-light tracking-wide text-center py-8"></p>
)}
</div>
</section>
+6 -3
View File
@@ -90,11 +90,14 @@ export default function NewMemberPage() {
<div className="min-h-screen flex flex-col bg-background">
<SiteHeader />
<div className="container mx-auto py-8">
<div className="mb-6 flex items-center gap-2">
<Button variant="ghost" onClick={() => router.back()}>
<div className="mb-6">
<Button variant="ghost" onClick={() => router.back()} className="mb-4">
<ArrowLeft className="mr-2 h-4 w-4" />
</Button>
<h1 className="text-2xl font-bold font-serif"></h1>
<h1 className="text-3xl font-serif font-bold mb-2"></h1>
<p className="text-muted-foreground">
</p>
</div>
<MemberForm initialData={initialData} existingMembers={existingMembers} onSubmit={handleSave} onCancel={() => router.back()} />
</div>
+8 -3
View File
@@ -361,7 +361,7 @@ export default function MembersPage() {
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">
<main className="container mx-auto py-8 px-4 md:px-6 flex-1 flex flex-col">
<div className="flex flex-col gap-6 mb-8">
<div className="flex flex-col md:flex-row justify-between items-center gap-4">
<h1 className="text-3xl font-serif font-bold"></h1>
@@ -479,8 +479,13 @@ export default function MembersPage() {
</div>
{filteredMembers.length === 0 ? (
<div className="text-center py-12 text-muted-foreground">
<div className="flex-1 flex items-center justify-center">
<div className="text-center space-y-4">
<div className="inline-flex items-center justify-center w-16 h-16 text-primary/80">
<Search className="h-16 w-16" strokeWidth={1} />
</div>
<p className="text-base text-foreground font-light tracking-wide"></p>
</div>
</div>
) : (
<div className="space-y-8">
+150 -48
View File
@@ -2,6 +2,7 @@
import type React from "react"
import Link from "next/link"
import Image from "next/image"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { SiteHeader } from "@/components/site-header"
@@ -159,6 +160,122 @@ export default function DashboardPage() {
// 按日期排序
return anniversaries.sort((a, b) => a.day - b.day).slice(0, 5)
}, [treeData])
// 计算家族迁徙记录
const locationGroups = useMemo(() => {
const members = Object.values(treeData.members)
const groups: Record<string, any[]> = {}
members.forEach(member => {
if (member.ancestralHome) {
if (!groups[member.ancestralHome]) {
groups[member.ancestralHome] = []
}
groups[member.ancestralHome].push(member)
}
})
return Object.entries(groups).sort((a, b) => b[1].length - a[1].length)
}, [treeData])
// 如果用户没有家族树,显示欢迎页面
if (!isLoading && !currentTree) {
return (
<div className="flex min-h-screen flex-col bg-background font-sans">
<SiteHeader />
<main className="flex-1 flex items-center justify-center px-4 md:px-6 relative">
{/* 背景装饰图片 */}
<div className="absolute inset-0 pointer-events-none overflow-hidden">
{/* 建筑图片 - 左下 */}
<div className="absolute left-24 top-[60%] -translate-y-1/2 w-[30rem] h-[30rem] opacity-30 rotate-12">
<Image
src="/building.png"
alt=""
fill
className="object-contain"
priority
/>
</div>
{/* 树图片 - 右上 */}
<div className="absolute right-24 top-[40%] -translate-y-1/2 w-[30rem] h-[30rem] opacity-30 -rotate-12">
<Image
src="/tree.png"
alt=""
fill
className="object-contain"
priority
/>
</div>
</div>
{/* 内容区域 */}
<div className="max-w-3xl mx-auto text-center space-y-12">
{/* 图标 */}
<div className="inline-flex items-center justify-center w-16 h-16 text-primary/20">
<BookOpen className="h-16 w-16" strokeWidth={1} />
</div>
{/* 标题 */}
<div className="space-y-6">
<h1 className="text-4xl md:text-5xl font-serif font-light text-foreground tracking-wide">
</h1>
{/* 哲理文字 - 滚动显示 */}
<div className="h-48 overflow-hidden relative">
<div className="space-y-8 text-muted-foreground antialiased" style={{ animation: 'scroll 30s linear infinite' }}>
<p className="text-lg md:text-xl font-normal leading-relaxed">
</p>
<p className="text-base md:text-lg font-normal leading-relaxed opacity-85">
</p>
<p className="text-base md:text-lg font-normal leading-relaxed opacity-70">
</p>
<p className="text-base md:text-lg font-normal leading-relaxed opacity-60">
</p>
<p className="text-base md:text-lg font-normal leading-relaxed opacity-50">
</p>
<p className="text-base md:text-lg font-normal leading-relaxed opacity-40">
</p>
{/* 重复内容实现无缝滚动 */}
<p className="text-lg md:text-xl font-normal leading-relaxed">
</p>
<p className="text-base md:text-lg font-normal leading-relaxed opacity-85">
</p>
<p className="text-base md:text-lg font-normal leading-relaxed opacity-70">
</p>
</div>
</div>
</div>
{/* 创建按钮 */}
<div className="pt-8">
<Link href="/trees/new">
<Button
size="lg"
className="gap-2 bg-primary text-primary-foreground hover:bg-primary/90 transition-all duration-300"
>
<ArrowRight className="h-4 w-4" />
</Button>
</Link>
</div>
</div>
</main>
</div>
)
}
return (
<div className="flex min-h-screen flex-col bg-background font-sans">
<SiteHeader />
@@ -244,7 +361,9 @@ export default function DashboardPage() {
</div>
<div className="flex flex-col space-y-1">
<div className="flex items-center justify-center gap-3">
<span className="text-3xl font-serif font-bold text-primary tracking-tight">1</span>
<span className="text-3xl font-serif font-bold text-primary tracking-tight">
{stats.maleCount > 0 ? '1' : '0'}
</span>
<span className="text-2xl font-bold text-muted-foreground">:</span>
<span className="text-3xl font-serif font-bold text-primary tracking-tight">
{stats.maleCount > 0 ? (stats.femaleCount / stats.maleCount).toFixed(2) : '0'}
@@ -323,8 +442,8 @@ export default function DashboardPage() {
</div>
))
) : (
<div className="text-center text-muted-foreground py-8">
<div className="text-center py-12">
<p className="text-sm text-foreground font-light tracking-wide"></p>
</div>
)}
</div>
@@ -367,8 +486,8 @@ export default function DashboardPage() {
)
})
) : (
<div className="text-center text-muted-foreground py-8 text-sm">
<div className="text-center py-12">
<p className="text-sm text-foreground font-light tracking-wide"></p>
</div>
)}
</CardContent>
@@ -457,8 +576,8 @@ export default function DashboardPage() {
)
})
) : (
<div className="col-span-full text-center py-12 text-muted-foreground">
<div className="col-span-full text-center py-16">
<p className="text-sm text-foreground font-light tracking-wide"></p>
</div>
)
})()}
@@ -481,49 +600,32 @@ export default function DashboardPage() {
</CardHeader>
<CardContent>
<div className="space-y-6">
{useMemo(() => {
const members = Object.values(treeData.members)
const locationGroups: Record<string, any[]> = {}
members.forEach(member => {
if (member.ancestralHome) {
if (!locationGroups[member.ancestralHome]) {
locationGroups[member.ancestralHome] = []
}
locationGroups[member.ancestralHome].push(member)
}
})
const sortedLocations = Object.entries(locationGroups)
.sort((a, b) => b[1].length - a[1].length)
return sortedLocations.length > 0 ? (
sortedLocations.map(([location, locationMembers]) => (
<div key={location} className="border-l-4 border-primary pl-4">
<div className="flex items-center justify-between mb-3">
<h3 className="text-lg font-serif font-bold">{location}</h3>
<span className="text-sm text-muted-foreground">
{locationMembers.length}
</span>
</div>
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2">
{locationMembers.map((member: any) => (
<Link href={`/members/${member.id}`} key={member.id}>
<div className="px-3 py-2 bg-muted rounded hover:bg-muted/80 transition-colors">
<p className="text-sm font-medium truncate">{member.fullName}</p>
<p className="text-xs text-muted-foreground"> {member.generation} </p>
</div>
</Link>
))}
</div>
{locationGroups.length > 0 ? (
locationGroups.map(([location, locationMembers]) => (
<div key={location} className="border-l-4 border-primary pl-4">
<div className="flex items-center justify-between mb-3">
<h3 className="text-lg font-serif font-bold">{location}</h3>
<span className="text-sm text-muted-foreground">
{locationMembers.length}
</span>
</div>
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2">
{locationMembers.map((member: any) => (
<Link href={`/members/${member.id}`} key={member.id}>
<div className="px-3 py-2 bg-muted rounded hover:bg-muted/80 transition-colors">
<p className="text-sm font-medium truncate">{member.fullName}</p>
<p className="text-xs text-muted-foreground"> {member.generation} </p>
</div>
</Link>
))}
</div>
))
) : (
<div className="text-center py-12 text-muted-foreground">
</div>
)
}, [treeData])}
))
) : (
<div className="text-center py-16">
<p className="text-sm text-foreground font-light tracking-wide"></p>
</div>
)}
</div>
</CardContent>
</Card>
+26 -26
View File
@@ -42,24 +42,24 @@ export default function RelationshipTestPage() {
<div className="min-h-screen flex flex-col bg-background">
<SiteHeader />
<main className="flex-1 container mx-auto px-4 py-8">
<div className="max-w-4xl mx-auto space-y-6">
<main className="flex-1 container mx-auto px-4 py-12">
<div className="max-w-4xl mx-auto space-y-8">
{/* 标题 */}
<div>
<h1 className="text-3xl font-bold font-serif mb-2"></h1>
<h1 className="text-3xl font-serif font-bold mb-2"></h1>
<p className="text-muted-foreground">
</p>
</div>
{/* 选择成员 */}
<Card>
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
<Card className="border-none shadow-sm bg-card/50 backdrop-blur">
<CardHeader className="text-center">
<CardTitle className="text-2xl font-serif font-light"></CardTitle>
<CardDescription className="font-light"></CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<CardContent className="space-y-8">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<label className="text-sm font-medium">A</label>
<Popover open={openFromCombobox} onOpenChange={setOpenFromCombobox}>
@@ -158,7 +158,7 @@ export default function RelationshipTestPage() {
<Button
onClick={handleCalculate}
disabled={!fromId || !toId}
className="w-full"
className="w-full h-12 bg-primary text-primary-foreground hover:bg-primary/90"
>
</Button>
@@ -167,23 +167,23 @@ export default function RelationshipTestPage() {
{/* 计算结果 */}
{result && (
<Card>
<CardHeader>
<CardTitle></CardTitle>
<Card className="border-none shadow-sm bg-card/50 backdrop-blur">
<CardHeader className="text-center">
<CardTitle className="text-2xl font-serif font-light"></CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-center gap-4 p-6 bg-muted rounded-lg">
<div className="flex items-center justify-center gap-6 p-8 bg-muted/30 rounded-lg">
<div className="text-center">
<div className="text-2xl font-bold font-serif mb-1">
<div className="text-2xl font-serif font-light mb-1">
{result.from.fullName}
</div>
<div className="text-sm text-muted-foreground">
<div className="text-sm text-muted-foreground font-light">
{result.from.generation}
</div>
</div>
<div className="flex flex-col items-center gap-2">
<Badge variant="default" className="text-lg px-4 py-2">
<Badge className="text-lg px-6 py-2 bg-primary text-primary-foreground">
{result.relationship.term}
</Badge>
{result.relationship.path && (
@@ -194,19 +194,19 @@ export default function RelationshipTestPage() {
</div>
<div className="text-center">
<div className="text-2xl font-bold font-serif mb-1">
<div className="text-2xl font-serif font-light mb-1">
{result.to.fullName}
</div>
<div className="text-sm text-muted-foreground">
<div className="text-sm text-muted-foreground font-light">
{result.to.generation}
</div>
</div>
</div>
{result.relationship.description && (
<div className="p-4 bg-blue-50 dark:bg-blue-950 rounded-lg">
<div className="text-sm font-medium mb-1"></div>
<div className="text-muted-foreground">
<div className="p-6 bg-primary/5 rounded-lg">
<div className="text-sm font-light mb-2 text-center"></div>
<div className="text-muted-foreground font-light text-center">
<strong>{result.from.fullName}</strong> <strong>{result.to.fullName}</strong> <strong className="text-primary">{result.relationship.term}</strong>
</div>
</div>
@@ -227,9 +227,9 @@ export default function RelationshipTestPage() {
{(fromId || toId) && (
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{fromId && (
<Card>
<Card className="border-none shadow-sm bg-card/50 backdrop-blur">
<CardHeader>
<CardTitle>
<CardTitle className="text-xl font-serif font-light">
{treeData.members[fromId]?.fullName}
</CardTitle>
</CardHeader>
@@ -311,9 +311,9 @@ export default function RelationshipTestPage() {
)}
{toId && (
<Card>
<Card className="border-none shadow-sm bg-card/50 backdrop-blur">
<CardHeader>
<CardTitle>
<CardTitle className="text-xl font-serif font-light">
{treeData.members[toId]?.fullName}
</CardTitle>
</CardHeader>
+20 -14
View File
@@ -4,7 +4,7 @@ import { useFamily } from "@/context/family-context"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { ScrollArea } from "@/components/ui/scroll-area"
import { Clock, Map as MapIcon, ArrowRight } from "lucide-react"
import { Clock, Map as MapIcon, ArrowRight, Calendar } from "lucide-react"
import dynamic from 'next/dynamic'
import { useMemo, useEffect, useState } from 'react'
import * as echarts from 'echarts'
@@ -488,8 +488,13 @@ export default function TimelinePage() {
return (
<div className="min-h-screen bg-background flex flex-col">
<SiteHeader />
<div className="flex-1 flex items-center justify-center text-muted-foreground">
<div className="flex-1 flex items-center justify-center">
<div className="text-center space-y-4">
<div className="inline-flex items-center justify-center w-16 h-16 text-primary/80">
<Calendar className="h-16 w-16" strokeWidth={1} />
</div>
<p className="text-base text-foreground font-light tracking-wide"></p>
</div>
</div>
</div>
)
@@ -600,7 +605,9 @@ export default function TimelinePage() {
))}
{events.length === 0 && (
<div className="pl-8 text-muted-foreground italic"></div>
<div className="pl-8 py-8">
<p className="text-sm text-foreground font-light tracking-wide"></p>
</div>
)}
</div>
</CardContent>
@@ -640,11 +647,9 @@ export default function TimelinePage() {
</div>
))}
{migrations.length === 0 && (
<p className="text-center text-muted-foreground py-10">
<br />
/
</p>
<div className="text-center py-16">
<p className="text-sm text-foreground font-light tracking-wide"></p>
</div>
)}
</div>
</ScrollArea>
@@ -665,11 +670,12 @@ export default function TimelinePage() {
</div>
</div>
) : migrations.length === 0 ? (
<div className="flex items-center justify-center h-full text-muted-foreground">
<div className="text-center">
<MapIcon className="h-16 w-16 mx-auto mb-4 opacity-20" />
<p></p>
<p className="text-sm mt-2"></p>
<div className="flex items-center justify-center h-full">
<div className="text-center space-y-4">
<div className="inline-flex items-center justify-center w-16 h-16 text-primary/80">
<MapIcon className="h-16 w-16" strokeWidth={1} />
</div>
<p className="text-sm text-foreground font-light tracking-wide"></p>
</div>
</div>
) : (
+9 -2
View File
@@ -16,7 +16,7 @@ import {
} from "@/components/ui/dropdown-menu"
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"
import { Badge } from "@/components/ui/badge"
import { ZoomIn, ZoomOut, Move, Download, LayoutGrid, ChevronDown, Users } from "lucide-react"
import { ZoomIn, ZoomOut, Move, Download, LayoutGrid, ChevronDown, Users, Network } from "lucide-react"
import { useState, useRef, useEffect, useCallback } from "react"
import { useRouter, useSearchParams } from "next/navigation"
@@ -345,7 +345,14 @@ export default function TreePage() {
return (
<div className="min-h-screen bg-background flex flex-col">
<SiteHeader />
<div className="flex-1 flex items-center justify-center text-muted-foreground"></div>
<div className="flex-1 flex items-center justify-center">
<div className="text-center space-y-4">
<div className="inline-flex items-center justify-center w-16 h-16 text-primary/80">
<Network className="h-16 w-16" strokeWidth={1} />
</div>
<p className="text-base text-foreground font-light tracking-wide"></p>
</div>
</div>
</div>
)
}
+30 -46
View File
@@ -83,51 +83,36 @@ export default function NewTreePage() {
return (
<div className="min-h-screen bg-background flex flex-col">
<SiteHeader />
<main className="container mx-auto py-8 px-4 md:px-6 flex-1">
<main className="container mx-auto py-12 px-4 md:px-6 flex-1">
<div className="max-w-2xl mx-auto">
<div className="mb-6">
<Button
variant="ghost"
size="sm"
asChild
className="mb-4"
>
<Link href="/">
<ArrowLeft className="h-4 w-4 mr-2" />
</Link>
</Button>
<h1 className="text-3xl font-serif font-bold mb-2"></h1>
<p className="text-muted-foreground">
<div className="mb-8 text-center">
<h1 className="text-4xl font-serif font-light mb-3 tracking-wide"></h1>
<p className="text-muted-foreground font-light">
</p>
</div>
<Card>
<Card className="border-none shadow-sm bg-card/50 backdrop-blur">
<form onSubmit={handleSubmit}>
<CardHeader>
<div className="flex items-center gap-3">
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary text-primary-foreground">
<TreePine className="h-6 w-6" />
</div>
<div>
<CardTitle></CardTitle>
<CardDescription>
</CardDescription>
</div>
<CardHeader className="text-center pb-8">
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-primary/10 text-primary mb-4 mx-auto">
<TreePine className="h-8 w-8" strokeWidth={1.5} />
</div>
<CardTitle className="text-2xl font-serif font-light"></CardTitle>
<CardDescription className="font-light">
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<CardContent className="space-y-8 px-8">
{error && (
<Alert variant="destructive">
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
<div className="space-y-2">
<Label htmlFor="name">
<div className="space-y-3">
<Label htmlFor="name" className="text-base font-normal">
<span className="text-destructive">*</span>
</Label>
<Input
@@ -137,14 +122,15 @@ export default function NewTreePage() {
onChange={(e) => setName(e.target.value)}
maxLength={50}
required
className="h-12"
/>
<p className="text-xs text-muted-foreground">
<p className="text-xs text-muted-foreground font-light">
使+/
</p>
</div>
<div className="space-y-2">
<Label htmlFor="description"></Label>
<div className="space-y-3">
<Label htmlFor="description" className="text-base font-normal"></Label>
<Textarea
id="description"
placeholder="简要描述您的家族历史、祖籍地等信息"
@@ -152,36 +138,34 @@ export default function NewTreePage() {
onChange={(e) => setDescription(e.target.value)}
rows={4}
maxLength={500}
className="resize-none"
/>
<p className="text-xs text-muted-foreground">
<p className="text-xs text-muted-foreground font-light">
{description.length}/500
</p>
</div>
<div className="rounded-lg border bg-muted/50 p-4">
<h3 className="font-medium mb-2">💡 </h3>
<ul className="text-sm text-muted-foreground space-y-1">
<li> </li>
<li> </li>
<li> </li>
</ul>
<div className="rounded-lg bg-muted/30 p-6 space-y-2">
<p className="text-sm text-muted-foreground font-light leading-relaxed">
</p>
</div>
</CardContent>
<CardFooter className="flex gap-3">
<CardFooter className="flex gap-4 px-8 pt-8 pb-8">
<Button
type="button"
variant="outline"
onClick={() => router.back()}
disabled={loading}
className="flex-1"
className="flex-1 h-12"
>
</Button>
<Button
type="submit"
disabled={loading || !name.trim()}
className="flex-1"
className="flex-1 h-12 bg-primary text-primary-foreground hover:bg-primary/90"
>
{loading ? "创建中..." : "创建家族树"}
</Button>