@@ -8,6 +8,84 @@ interface ProfileEditorProps {
88 onNext : ( ) => void ;
99}
1010
11+ type InputKind = 'text' | 'url' | 'email' | 'textarea' ;
12+
13+ interface InputFieldProps {
14+ label : string ;
15+ field : keyof NostrProfile ;
16+ type ?: InputKind ;
17+ placeholder : string ;
18+ validation ?: 'url' | 'email' ;
19+ maxLength ?: number ;
20+ localProfile : NostrProfile ;
21+ validationErrors : Record < string , string > ;
22+ handleChange : ( field : keyof NostrProfile , value : string ) => void ;
23+ handleUrlBlur : ( field : keyof NostrProfile , value : string ) => void ;
24+ handleEmailBlur : ( field : keyof NostrProfile , value : string ) => void ;
25+ }
26+
27+ const InputField = ( {
28+ label,
29+ field,
30+ type = 'text' ,
31+ placeholder,
32+ validation,
33+ maxLength,
34+ localProfile,
35+ validationErrors,
36+ handleChange,
37+ handleUrlBlur,
38+ handleEmailBlur
39+ } : InputFieldProps ) => (
40+ < div className = "space-y-2" >
41+ < label className = "block text-sm font-medium text-gray-800" >
42+ { label }
43+ { typeof maxLength === 'number' && (
44+ < span className = "text-xs text-gray-500 ml-2" >
45+ ({ ( localProfile [ field ] as string ) ?. length || 0 } /{ maxLength } )
46+ </ span >
47+ ) }
48+ </ label >
49+
50+ < div className = "relative" >
51+ { type === 'textarea' ? (
52+ < textarea
53+ value = { ( localProfile [ field ] as string ) || '' }
54+ onChange = { ( e ) => handleChange ( field , e . target . value ) }
55+ placeholder = { placeholder }
56+ maxLength = { maxLength }
57+ rows = { 4 }
58+ className = { `w-full p-3 rounded-xl border bg-white text-gray-900 placeholder-gray-400
59+ focus:outline-none focus:ring-2 focus:ring-black focus:border-black transition
60+ ${ validationErrors [ field ] ? 'border-red-300' : 'border-gray-300 hover:border-gray-400' } resize-none text-sm` }
61+ />
62+ ) : (
63+ < input
64+ type = { type }
65+ value = { ( localProfile [ field ] as string ) || '' }
66+ onChange = { ( e ) => handleChange ( field , e . target . value ) }
67+ onBlur = { ( e ) => {
68+ if ( validation === 'url' ) handleUrlBlur ( field , e . target . value ) ;
69+ if ( validation === 'email' ) handleEmailBlur ( field , e . target . value ) ;
70+ } }
71+ placeholder = { placeholder }
72+ maxLength = { maxLength }
73+ className = { `w-full p-3 rounded-xl border bg-white text-gray-900 placeholder-gray-400
74+ focus:outline-none focus:ring-2 focus:ring-black focus:border-black transition
75+ ${ validationErrors [ field ] ? 'border-red-300' : 'border-gray-300 hover:border-gray-400' } text-sm` }
76+ />
77+ ) }
78+
79+ { validationErrors [ field ] && (
80+ < div className = "absolute -bottom-5 left-0 flex items-center text-red-600 text-xs" >
81+ < AlertCircle className = "w-4 h-4 mr-1.5" />
82+ { validationErrors [ field ] }
83+ </ div >
84+ ) }
85+ </ div >
86+ </ div >
87+ ) ;
88+
1189export default function ProfileEditor ( { profile, onProfileUpdate, onNext } : ProfileEditorProps ) {
1290 const [ localProfile , setLocalProfile ] = useState < NostrProfile > ( profile ) ;
1391 const [ validationErrors , setValidationErrors ] = useState < Record < string , string > > ( { } ) ;
@@ -66,72 +144,6 @@ export default function ProfileEditor({ profile, onProfileUpdate, onNext }: Prof
66144 }
67145 } ;
68146
69- type InputKind = 'text' | 'url' | 'email' | 'textarea' ;
70-
71- const InputField = ( {
72- label,
73- field,
74- type = 'text' ,
75- placeholder,
76- validation,
77- maxLength
78- } : {
79- label : string ;
80- field : keyof NostrProfile ;
81- type ?: InputKind ;
82- placeholder : string ;
83- validation ?: 'url' | 'email' ;
84- maxLength ?: number ;
85- } ) => (
86- < div className = "space-y-2" >
87- < label className = "block text-sm font-medium text-gray-800" >
88- { label }
89- { typeof maxLength === 'number' && (
90- < span className = "text-xs text-gray-500 ml-2" >
91- ({ ( localProfile [ field ] as string ) ?. length || 0 } /{ maxLength } )
92- </ span >
93- ) }
94- </ label >
95-
96- < div className = "relative" >
97- { type === 'textarea' ? (
98- < textarea
99- value = { ( localProfile [ field ] as string ) || '' }
100- onChange = { ( e ) => handleChange ( field , e . target . value ) }
101- placeholder = { placeholder }
102- maxLength = { maxLength }
103- rows = { 4 }
104- className = { `w-full p-3 rounded-xl border bg-white text-gray-900 placeholder-gray-400
105- focus:outline-none focus:ring-2 focus:ring-black focus:border-black transition
106- ${ validationErrors [ field ] ? 'border-red-300' : 'border-gray-300 hover:border-gray-400' } resize-none text-sm` }
107- />
108- ) : (
109- < input
110- type = { type }
111- value = { ( localProfile [ field ] as string ) || '' }
112- onChange = { ( e ) => handleChange ( field , e . target . value ) }
113- onBlur = { ( e ) => {
114- if ( validation === 'url' ) handleUrlBlur ( field , e . target . value ) ;
115- if ( validation === 'email' ) handleEmailBlur ( field , e . target . value ) ;
116- } }
117- placeholder = { placeholder }
118- maxLength = { maxLength }
119- className = { `w-full p-3 rounded-xl border bg-white text-gray-900 placeholder-gray-400
120- focus:outline-none focus:ring-2 focus:ring-black focus:border-black transition
121- ${ validationErrors [ field ] ? 'border-red-300' : 'border-gray-300 hover:border-gray-400' } text-sm` }
122- />
123- ) }
124-
125- { validationErrors [ field ] && (
126- < div className = "absolute -bottom-5 left-0 flex items-center text-red-600 text-xs" >
127- < AlertCircle className = "w-4 h-4 mr-1.5" />
128- { validationErrors [ field ] }
129- </ div >
130- ) }
131- </ div >
132- </ div >
133- ) ;
134-
135147 return (
136148 < div className = "min-h-screen bg-white flex items-center justify-center p-6" >
137149 < div className = "max-w-2xl mx-auto w-full" >
@@ -154,13 +166,23 @@ export default function ProfileEditor({ profile, onProfileUpdate, onNext }: Prof
154166 field = "display_name"
155167 placeholder = "John Doe"
156168 maxLength = { 100 }
169+ localProfile = { localProfile }
170+ validationErrors = { validationErrors }
171+ handleChange = { handleChange }
172+ handleUrlBlur = { handleUrlBlur }
173+ handleEmailBlur = { handleEmailBlur }
157174 />
158175
159176 < InputField
160177 label = "Username"
161178 field = "name"
162179 placeholder = "johndoe"
163180 maxLength = { 50 }
181+ localProfile = { localProfile }
182+ validationErrors = { validationErrors }
183+ handleChange = { handleChange }
184+ handleUrlBlur = { handleUrlBlur }
185+ handleEmailBlur = { handleEmailBlur }
164186 />
165187
166188 < InputField
@@ -169,6 +191,11 @@ export default function ProfileEditor({ profile, onProfileUpdate, onNext }: Prof
169191 type = "textarea"
170192 placeholder = "Tell people about yourself..."
171193 maxLength = { 500 }
194+ localProfile = { localProfile }
195+ validationErrors = { validationErrors }
196+ handleChange = { handleChange }
197+ handleUrlBlur = { handleUrlBlur }
198+ handleEmailBlur = { handleEmailBlur }
172199 />
173200
174201 < InputField
@@ -177,6 +204,11 @@ export default function ProfileEditor({ profile, onProfileUpdate, onNext }: Prof
177204 type = "url"
178205 placeholder = "https://example.com/photo.jpg"
179206 validation = "url"
207+ localProfile = { localProfile }
208+ validationErrors = { validationErrors }
209+ handleChange = { handleChange }
210+ handleUrlBlur = { handleUrlBlur }
211+ handleEmailBlur = { handleEmailBlur }
180212 />
181213
182214 < InputField
@@ -185,6 +217,11 @@ export default function ProfileEditor({ profile, onProfileUpdate, onNext }: Prof
185217 type = "url"
186218 placeholder = "https://yoursite.com"
187219 validation = "url"
220+ localProfile = { localProfile }
221+ validationErrors = { validationErrors }
222+ handleChange = { handleChange }
223+ handleUrlBlur = { handleUrlBlur }
224+ handleEmailBlur = { handleEmailBlur }
188225 />
189226
190227 < InputField
@@ -193,6 +230,11 @@ export default function ProfileEditor({ profile, onProfileUpdate, onNext }: Prof
193230 type = "email"
194231 placeholder = "you@getalby.com"
195232 validation = "email"
233+ localProfile = { localProfile }
234+ validationErrors = { validationErrors }
235+ handleChange = { handleChange }
236+ handleUrlBlur = { handleUrlBlur }
237+ handleEmailBlur = { handleEmailBlur }
196238 />
197239 </ div >
198240 </ div >
0 commit comments