Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit10537dc

Browse files
committed
create dynamic profile page
1 parent0cd4dc4 commit10537dc

File tree

1 file changed

+183
-0
lines changed

1 file changed

+183
-0
lines changed

‎app/profile/[username]/page.tsx‎

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import{formatDistanceToNow}from'date-fns'
2+
importLinkfrom'next/link'
3+
import{notFound}from'next/navigation'
4+
5+
import{Avatar,AvatarFallback}from'@/components/ui/avatar'
6+
import{Card,CardContent,CardHeader,CardTitle}from'@/components/ui/card'
7+
import{createClient}from'@/lib/supabase/server'
8+
9+
exportdefaultasyncfunctionProfilePage({
10+
params,
11+
}:{
12+
params:{
13+
username:string
14+
}
15+
}){
16+
constsupabase=awaitcreateClient()
17+
const{username:profileUsername}=params
18+
19+
const{data:profile,error:profileError}=awaitsupabase
20+
.from('profiles')
21+
.select('id, username, updated_at')
22+
.eq('username',profileUsername)
23+
.single()
24+
25+
if(profileError||!profile){
26+
notFound()
27+
}
28+
29+
const{data:publicGraphs}=awaitsupabase
30+
.from('graphs')
31+
.select('*')
32+
.eq('user_id',profile.id)
33+
.eq('is_public',true)
34+
.order('created_at',{ascending:false})
35+
36+
const{
37+
data:{user:loggedInUser},
38+
}=awaitsupabase.auth.getUser()
39+
constisOwnProfile=loggedInUser?.id===profile.id
40+
41+
const{data:allUserGraphs}=awaitsupabase
42+
.from('graphs')
43+
.select('id')
44+
.eq('user_id',profile.id)
45+
46+
return(
47+
<divclassName="container mx-auto p-4 py-8 md:py-12">
48+
<divclassName="mb-10 flex flex-col items-center space-y-4 md:flex-row md:space-y-0 md:space-x-6">
49+
<AvatarclassName="border-primary h-24 w-24 border-2 md:h-32 md:w-32">
50+
<AvatarFallbackclassName="text-4xl md:text-5xl">
51+
{profile.username.charAt(0).toUpperCase()}
52+
</AvatarFallback>
53+
</Avatar>
54+
<divclassName="text-center md:text-left">
55+
<h1className="text-3xl font-bold tracking-tight md:text-4xl">
56+
{profile.username}
57+
</h1>
58+
<pclassName="text-muted-foreground text-lg">
59+
Public profile and graphs.
60+
</p>
61+
{isOwnProfile&&(
62+
<Link
63+
href="/dashboard"
64+
className="mt-1 inline-block text-sm text-blue-600 hover:underline"
65+
>
66+
Manage your graphs (Dashboard)
67+
</Link>
68+
)}
69+
</div>
70+
</div>
71+
72+
<divclassName="mb-12 grid gap-6 md:grid-cols-2">
73+
<CardclassName="shadow-sm">
74+
<CardHeader>
75+
<CardTitleclassName="text-xl">User Information</CardTitle>
76+
</CardHeader>
77+
<CardContentclassName="space-y-4">
78+
<div>
79+
<h3className="text-sm font-medium text-gray-500">Username</h3>
80+
<pclassName="mt-1 text-lg font-semibold text-gray-800">
81+
{profile.username}
82+
</p>
83+
</div>
84+
<div>
85+
<h3className="text-sm font-medium text-gray-500">
86+
Profile Updated
87+
</h3>
88+
<pclassName="mt-1 text-gray-700">
89+
{profile.updated_at
90+
?formatDistanceToNow(newDate(profile.updated_at),{
91+
addSuffix:true,
92+
})
93+
:'N/A'}
94+
</p>
95+
</div>
96+
</CardContent>
97+
</Card>
98+
99+
<CardclassName="shadow-sm">
100+
<CardHeader>
101+
<CardTitleclassName="text-xl">Graph Statistics</CardTitle>
102+
</CardHeader>
103+
<CardContent>
104+
<divclassName="grid grid-cols-2 gap-4 text-center">
105+
<divclassName="rounded-lg border bg-gray-50 p-4">
106+
<h3className="text-sm font-medium text-gray-500">
107+
Total Graphs
108+
</h3>
109+
<pclassName="mt-1 text-3xl font-bold text-gray-800">
110+
{allUserGraphs?.length||0}
111+
</p>
112+
</div>
113+
<divclassName="rounded-lg border bg-gray-50 p-4">
114+
<h3className="text-sm font-medium text-gray-500">
115+
Public Graphs
116+
</h3>
117+
<pclassName="mt-1 text-3xl font-bold text-gray-800">
118+
{publicGraphs?.length||0}
119+
</p>
120+
</div>
121+
</div>
122+
</CardContent>
123+
</Card>
124+
</div>
125+
126+
<div>
127+
<h2className="mb-6 text-2xl font-bold">
128+
Public Graphs by{profile.username}
129+
</h2>
130+
{publicGraphs&&publicGraphs.length>0 ?(
131+
<divclassName="grid gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
132+
{publicGraphs.map((graph)=>(
133+
<Linkkey={graph.id}href={`/share/${graph.id}`}>
134+
<CardclassName="group h-full overflow-hidden shadow-sm transition-all hover:shadow-lg">
135+
<CardContentclassName="p-0">
136+
<div
137+
className="bg-muted aspect-video w-full overflow-hidden transition-transform group-hover:scale-105"
138+
dangerouslySetInnerHTML={{__html:graph.svg_graph}}
139+
/>
140+
</CardContent>
141+
<CardHeaderclassName="pt-4">
142+
<CardTitleclassName="truncate text-lg group-hover:text-blue-600">
143+
{graph.book_name}
144+
</CardTitle>
145+
<pclassName="text-muted-foreground text-xs">
146+
Created{formatDistanceToNow(newDate(graph.created_at))}{' '}
147+
ago
148+
</p>
149+
</CardHeader>
150+
</Card>
151+
</Link>
152+
))}
153+
</div>
154+
) :(
155+
<divclassName="rounded-lg border-2 border-dashed border-gray-200 p-12 text-center">
156+
<svg
157+
className="mx-auto h-12 w-12 text-gray-400"
158+
fill="none"
159+
viewBox="0 0 24 24"
160+
stroke="currentColor"
161+
aria-hidden="true"
162+
>
163+
<path
164+
vectorEffect="non-scaling-stroke"
165+
strokeLinecap="round"
166+
strokeLinejoin="round"
167+
strokeWidth="2"
168+
d="M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z"
169+
/>
170+
</svg>
171+
<h3className="mt-2 text-sm font-medium text-gray-900">
172+
No public graphs
173+
</h3>
174+
<pclassName="text-muted-foreground mt-1 text-sm">
175+
{profile.username} hasn&apos;t made any of their graphs public
176+
yet.
177+
</p>
178+
</div>
179+
)}
180+
</div>
181+
</div>
182+
)
183+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp