@@ -8,6 +8,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
8
8
import { zodResolver } from '@hookform/resolvers/zod' ;
9
9
import { deepFreeze } from '@shared/validation/common/deepFreeze' ;
10
10
import type { UserProfileViewDto } from '@shared/validation/user/dto/UserProfileView.dto' ;
11
+ import Image from 'next/image' ;
11
12
import Link from 'next/link' ;
12
13
import { useEffect } from 'react' ;
13
14
import { useForm } from 'react-hook-form' ;
@@ -18,14 +19,7 @@ import {
18
19
Input ,
19
20
TextArea ,
20
21
} from '@web/src/modules/shared/components/client/FormElements' ;
21
-
22
- type UserProfileEditStore = {
23
- isLoading : boolean ;
24
- isLocked : boolean ;
25
- userData : UserProfileViewDto | null ;
26
- setUserData : ( data : UserProfileViewDto ) => void ;
27
- updateUserData : ( data : Partial < UserProfileViewDto > ) => void ;
28
- } ;
22
+ import { EarlySupporterBadge } from '@web/src/modules/user/components/UserBadges' ;
29
23
30
24
export const LinkRegexes = deepFreeze ( {
31
25
bandcamp : / h t t p s ? : \/ \/ [ a - z A - Z 0 - 9 _ - ] + \. b a n d c a m p \. c o m \/ ? / ,
@@ -66,12 +60,21 @@ const socialLinksSchema = zod.object({
66
60
} ) ;
67
61
68
62
const userProfileEditFormSchema = zod . object ( {
63
+ username : zod . string ( ) . min ( 3 ) . max ( 20 ) ,
69
64
description : zod . string ( ) . optional ( ) ,
70
65
socialLinks : socialLinksSchema ,
71
66
} ) ;
72
67
73
68
type UserProfileEditFormSchema = zod . infer < typeof userProfileEditFormSchema > ;
74
69
70
+ type UserProfileEditStore = {
71
+ isLoading : boolean ;
72
+ isLocked : boolean ;
73
+ userData : UserProfileViewDto | null ;
74
+ setUserData : ( data : UserProfileViewDto ) => void ;
75
+ updateUserData : ( data : Partial < UserProfileViewDto > ) => void ;
76
+ } ;
77
+
75
78
const useUserProfileEdit = create < UserProfileEditStore > ( ( set , get ) => {
76
79
return {
77
80
isLoading : true ,
@@ -109,7 +112,7 @@ type UserEditProfileProps = {
109
112
export const UserEditProfile : React . FC < UserEditProfileProps > = ( {
110
113
initialUserData,
111
114
} ) => {
112
- const { setUserData, isLoading, isLocked } = useUserProfileEdit ( ) ;
115
+ const { setUserData, isLoading, isLocked, userData } = useUserProfileEdit ( ) ;
113
116
114
117
useEffect ( ( ) => {
115
118
setUserData ( initialUserData ) ;
@@ -205,38 +208,74 @@ export const UserEditProfile: React.FC<UserEditProfileProps> = ({
205
208
} ,
206
209
] ;
207
210
211
+ const { username, profileImage } = initialUserData ;
212
+
208
213
return (
209
214
< div className = 'max-w-screen-lg mx-auto' >
210
215
< section >
211
- < h1 > Edit Profile</ h1 >
212
- { /* Add your edit profile form here */ }
213
-
216
+ < div className = 'flex items-center justify-center gap-2 my-3 bg-cyan-800 border-cyan-400 text-cyan-300 border-2 rounded-lg px-3 py-2 text-sm' >
217
+ < FontAwesomeIcon icon = { faExclamationCircle } className = 'h-5' />
218
+ < p >
219
+ Please make sure to carefully review our{ ' ' }
220
+ < Link
221
+ href = '/guidelines'
222
+ target = '_blank'
223
+ className = 'text-blue-400 hover:text-blue-300 hover:underline'
224
+ >
225
+ Community Guidelines
226
+ </ Link >
227
+ < FontAwesomeIcon
228
+ className = 'text-blue-400 ml-1 mr-1'
229
+ size = 'xs'
230
+ icon = { faExternalLink }
231
+ /> { ' ' }
232
+ before sharing your profile. We want to ensure a safe and positive
233
+ environment for all users.
234
+ </ p >
235
+ </ div >
236
+ < div className = 'flex items-center gap-8' >
237
+ < Image
238
+ src = { profileImage }
239
+ alt = { `Profile picture of ${ username } ` }
240
+ className = 'w-32 h-32 rounded-full'
241
+ width = { 128 }
242
+ height = { 128 }
243
+ />
244
+ < div >
245
+ { /* Display name */ }
246
+ < div className = 'flex items-center gap-8' >
247
+ < h1 className = 'text-3xl font-bold mb-1 relative' >
248
+ Editing { username } 's profile
249
+ </ h1 >
250
+ < EarlySupporterBadge />
251
+ </ div >
252
+ </ div >
253
+ </ div >
214
254
< form
215
255
className = { `flex flex-col gap-6` }
216
256
onSubmit = { formMethods . handleSubmit ( ( ) => {
217
257
// Handle form submission
218
258
console . log ( 'Form submitted' ) ;
219
259
} ) }
220
260
>
221
- < div className = 'flex items-center justify-center gap-2 my-3 bg-cyan-800 border-cyan-400 text-cyan-300 border-2 rounded-lg px-3 py-2 text-sm' >
222
- < FontAwesomeIcon icon = { faExclamationCircle } className = 'h-5' />
223
- < p >
224
- Please make sure to carefully review our{ ' ' }
225
- < Link
226
- href = '/guidelines'
227
- target = '_blank'
228
- className = 'text-blue-400 hover:text-blue-300 hover:underline'
229
- >
230
- Community Guidelines
231
- </ Link >
232
- < FontAwesomeIcon
233
- className = 'text-blue-400 ml-1 mr-1'
234
- size = 'xs'
235
- icon = { faExternalLink }
236
- /> { ' ' }
237
- before sharing your profile. We want to ensure a safe and positive
238
- environment for all users.
239
- </ p >
261
+ { /* Username */ }
262
+ < div >
263
+ < Input
264
+ id = 'username'
265
+ label = 'Username'
266
+ tooltip = {
267
+ < >
268
+ < p >
269
+ This is your username. It will be shown on your profile
270
+ page, as well in all your songs and comments.
271
+ </ p >
272
+ </ >
273
+ }
274
+ isLoading = { isLoading }
275
+ disabled = { isLocked }
276
+ errorMessage = { errors . username ?. message }
277
+ { ...register ( 'username' ) }
278
+ />
240
279
</ div >
241
280
{ /* Description */ }
242
281
< div >
@@ -264,9 +303,9 @@ export const UserEditProfile: React.FC<UserEditProfileProps> = ({
264
303
Add links to your social media profiles. These links will be shown
265
304
on your profile page.
266
305
</ p >
267
- < div className = 'flex-row gap-4 mt-2' >
306
+ < div className = 'grid grid-cols-1 sm:grid-cols-2 gap-4 mt-2' >
268
307
{ LinkFields . map ( ( link ) => (
269
- < div key = { link . key } className = 'flex-1 min-w-[200px]' >
308
+ < div key = { link . key } className = 'min-w-[200px]' >
270
309
< Input
271
310
id = { link . key }
272
311
label = { link . label }
0 commit comments