This commit is contained in:
freedakgmail
2025-11-22 20:08:07 +08:00
parent 43c8389705
commit 33cf0c50d1
142 changed files with 44912 additions and 213 deletions
+22 -5
View File
@@ -8,17 +8,34 @@ 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 } from "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">
@@ -47,7 +64,7 @@ export default function MemberProfilePage() {
return (
<div className="min-h-screen flex flex-col bg-background">
<SiteHeader />
<div className="container py-8">
<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" />
@@ -63,14 +80,14 @@ export default function MemberProfilePage() {
return (
<div className="min-h-screen flex flex-col bg-background">
<SiteHeader />
<main className="container py-8 px-4 md:px-6">
<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">
{member.avatarUrl ? (
{avatarBlobUrl || member.avatarUrl ? (
<img
src={member.avatarUrl || "/placeholder.svg"}
src={avatarBlobUrl || member.avatarUrl || "/placeholder.svg"}
alt={member.fullName}
className="h-full w-full object-cover"
/>
+1 -1
View File
@@ -26,7 +26,7 @@ export default function NewMemberPage() {
return (
<div className="min-h-screen flex flex-col bg-background">
<SiteHeader />
<div className="container py-8">
<div className="container mx-auto py-8">
<div className="mb-6 flex items-center gap-2">
<Button variant="ghost" onClick={() => router.back()}>
<ArrowLeft className="mr-2 h-4 w-4" />
+11 -8
View File
@@ -9,6 +9,7 @@ import { Card, CardContent } from "@/components/ui/card"
import { Search, Plus, Filter } from "lucide-react"
import Link from "next/link"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { AvatarDisplay } from "@/components/ui/avatar-display"
export default function MembersPage() {
const { treeData, searchResults, searchMembers } = useFamily()
@@ -17,13 +18,13 @@ export default function MembersPage() {
const displayMembers = query
? searchResults
: Object.values(treeData.members).sort(
(a, b) => a.generation - b.generation || (a.birthDate || "").localeCompare(b.birthDate || ""),
)
(a, b) => a.generation - b.generation || (a.birthDate || "").localeCompare(b.birthDate || ""),
)
return (
<div className="min-h-screen bg-background flex flex-col font-sans">
<SiteHeader />
<main className="container py-8 px-4 md:px-6 flex-1">
<main className="container mx-auto py-8 px-4 md:px-6 flex-1">
<div className="flex flex-col md:flex-row justify-between items-center mb-8 gap-4">
<h1 className="text-3xl font-serif font-bold"></h1>
<div className="flex items-center gap-2 w-full md:w-auto">
@@ -57,10 +58,12 @@ export default function MembersPage() {
<Link href={`/members/${member.id}`} key={member.id}>
<Card className="hover:border-primary transition-colors cursor-pointer h-full">
<CardContent className="p-6 flex items-start gap-4">
<Avatar className="h-16 w-16 border-2 border-border">
<AvatarImage src={member.avatarUrl || "/placeholder.svg"} />
<AvatarFallback className="text-lg font-serif bg-muted">{member.surname}</AvatarFallback>
</Avatar>
<AvatarDisplay
imageId={member.avatarImageId}
fallbackUrl={member.avatarUrl}
fallbackText={member.surname}
className="h-16 w-16 border-2 border-border"
/>
<div className="flex-1 min-w-0">
<div className="flex items-baseline justify-between">
<h3 className="text-xl font-bold font-serif truncate">
@@ -80,7 +83,7 @@ export default function MembersPage() {
</p>
<p className="text-sm text-muted-foreground truncate">{member.ancestralHome || "籍贯未知"}</p>
<div className="mt-3 flex flex-wrap gap-1">
{member.lifeStatus === "deceased" && (
{member.deathDate && (
<span className="text-[10px] bg-neutral-100 text-neutral-600 px-1.5 py-0.5 rounded border border-neutral-200">
</span>