Files
chinese-family-tree-2/app/members/[id]/page.tsx
T
freedakgmail 33cf0c50d1 0.0.0.2
2025-11-22 20:08:07 +08:00

233 lines
9.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client"
import Link from "next/link"
import { useParams, useRouter } from "next/navigation"
import { useFamily } from "@/context/family-context"
import { SiteHeader } from "@/components/site-header"
import { MemberForm } from "@/components/members/member-form"
import { Button } from "@/components/ui/button"
import { ArrowLeft, Edit, Trash, Share2, Printer } from "lucide-react"
import { useState, useEffect } from "react"
import type { FamilyMember } from "@/types/family"
import { db } from "@/lib/db"
export default function MemberProfilePage() {
const { id } = useParams()
const { getMember, updateMember, deleteMember } = useFamily()
const router = useRouter()
const [isEditing, setIsEditing] = useState(false)
const [avatarBlobUrl, setAvatarBlobUrl] = useState<string | undefined>(undefined)
const member = getMember(id as string)
// Load avatar blob if exists
useEffect(() => {
if (member?.avatarImageId) {
db.images.get(member.avatarImageId).then((image) => {
if (image) {
const url = URL.createObjectURL(image.blob)
setAvatarBlobUrl(url)
return () => URL.revokeObjectURL(url)
}
})
} else {
setAvatarBlobUrl(undefined)
}
}, [member?.avatarImageId])
if (!member) {
return (
<div className="min-h-screen flex flex-col bg-background">
<SiteHeader />
<div className="flex-1 flex flex-col items-center justify-center gap-4">
<p className="text-muted-foreground">Member not found</p>
<Button onClick={() => router.push("/members")}>Back to List</Button>
</div>
</div>
)
}
const handleSave = (updatedMember: FamilyMember) => {
updateMember(updatedMember.id, updatedMember)
setIsEditing(false)
}
const handleDelete = () => {
if (confirm("确定要删除这位成员吗?这将同时删除其关联关系。")) {
deleteMember(member.id)
router.push("/members")
}
}
if (isEditing) {
return (
<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={() => setIsEditing(false)}>
<ArrowLeft className="mr-2 h-4 w-4" />
</Button>
<h1 className="text-2xl font-bold font-serif">: {member.fullName}</h1>
</div>
<MemberForm initialData={member} onSubmit={handleSave} onCancel={() => setIsEditing(false)} />
</div>
</div>
)
}
return (
<div className="min-h-screen flex flex-col bg-background">
<SiteHeader />
<main className="container mx-auto py-8 px-4 md:px-6">
{/* Header Section */}
<div className="flex flex-col md:flex-row justify-between items-start gap-6 mb-8 border-b border-border pb-8">
<div className="flex items-start gap-6">
<div className="h-32 w-32 rounded-lg bg-muted flex items-center justify-center border-2 border-primary/20 overflow-hidden">
{avatarBlobUrl || member.avatarUrl ? (
<img
src={avatarBlobUrl || member.avatarUrl || "/placeholder.svg"}
alt={member.fullName}
className="h-full w-full object-cover"
/>
) : (
<span className="text-4xl font-serif font-bold text-muted-foreground/50">{member.surname}</span>
)}
</div>
<div>
<div className="flex items-center gap-3 mb-2">
<h1 className="text-4xl font-bold font-serif text-foreground">{member.fullName}</h1>
<span className="px-2 py-1 bg-primary/10 text-primary text-sm font-medium rounded-full border border-primary/20">
{member.generation}
</span>
</div>
<div className="space-y-1 text-muted-foreground">
{member.courtesyName && (
<p>
<span className="text-foreground">{member.courtesyName}</span>
</p>
)}
{member.artName && (
<p>
<span className="text-foreground">{member.artName}</span>
</p>
)}
{member.ancestralHome && <p>{member.ancestralHome}</p>}
</div>
</div>
</div>
<div className="flex gap-2">
<Button variant="outline" size="icon" onClick={() => window.print()}>
<Printer className="h-4 w-4" />
</Button>
<Button variant="outline" size="icon">
<Share2 className="h-4 w-4" />
</Button>
<Button variant="outline" onClick={() => setIsEditing(true)} className="gap-2">
<Edit className="h-4 w-4" />
</Button>
<Button variant="destructive" size="icon" onClick={handleDelete}>
<Trash className="h-4 w-4" />
</Button>
</div>
</div>
{/* Content Grid */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Main Biography */}
<div className="lg:col-span-2 space-y-8">
<section>
<h2 className="text-2xl font-serif font-bold mb-4 flex items-center gap-2">
<span className="w-1 h-6 bg-primary rounded-full"></span>
</h2>
<div className="prose prose-stone dark:prose-invert max-w-none bg-card p-6 rounded-lg border border-border shadow-sm">
{member.bio ? (
<p className="whitespace-pre-line leading-relaxed">{member.bio}</p>
) : (
<p className="text-muted-foreground italic">...</p>
)}
</div>
</section>
<section>
<h2 className="text-2xl font-serif font-bold mb-4 flex items-center gap-2">
<span className="w-1 h-6 bg-secondary rounded-full"></span>
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="bg-card p-4 rounded-lg border border-border">
<h3 className="font-medium text-muted-foreground mb-2 text-sm uppercase tracking-wider">
(Father)
</h3>
{member.fatherId ? (
<Link
href={`/members/${member.fatherId}`}
className="text-lg font-serif font-bold hover:underline text-primary"
>
</Link>
) : (
<span className="text-muted-foreground">-</span>
)}
</div>
<div className="bg-card p-4 rounded-lg border border-border">
<h3 className="font-medium text-muted-foreground mb-2 text-sm uppercase tracking-wider">
(Children)
</h3>
<div className="flex flex-wrap gap-2">
{member.childrenIds?.length > 0 ? (
member.childrenIds.map((childId) => (
<Link
href={`/members/${childId}`}
key={childId}
className="px-3 py-1 bg-muted rounded hover:bg-muted/80 text-sm font-medium"
>
{childId} {/* In real app, fetch name */}
</Link>
))
) : (
<span className="text-muted-foreground"></span>
)}
</div>
</div>
</div>
</section>
</div>
{/* Sidebar Info */}
<div className="space-y-6">
<div className="bg-card rounded-lg border border-border p-6 shadow-sm">
<h3 className="font-serif font-bold text-lg mb-4 border-b border-border pb-2"></h3>
<dl className="space-y-4 text-sm">
<div>
<dt className="text-muted-foreground"></dt>
<dd className="font-medium">{member.birthDate || "-"}</dd>
</div>
<div>
<dt className="text-muted-foreground"></dt>
<dd className="font-medium">{member.deathDate || "-"}</dd>
</div>
<div>
<dt className="text-muted-foreground"></dt>
<dd className="font-medium">{member.generationName || "-"}</dd>
</div>
<div>
<dt className="text-muted-foreground"></dt>
<dd className="font-medium">{member.posthumousName || "-"}</dd>
</div>
<div>
<dt className="text-muted-foreground"></dt>
<dd className="font-medium">{member.burialPlace || "-"}</dd>
</div>
</dl>
</div>
</div>
</div>
</main>
</div>
)
}