1+ 'use client' ;
2+
3+ import { useEffect , useState } from 'react' ;
4+ import { usePatientProfile } from '../hooks/usePatientProfile' ;
5+ import { useUpdateProfile } from '../hooks/useUpdateProfile' ;
6+ import {
7+ User ,
8+ Activity ,
9+ MapPin ,
10+ PhoneCall ,
11+ Pencil ,
12+ Save ,
13+ X ,
14+ Droplets ,
15+ Ruler ,
16+ Weight ,
17+ Calendar ,
18+ ShieldAlert ,
19+ Mail ,
20+ Phone ,
21+ Fingerprint
22+ } from 'lucide-react' ;
23+
24+ export default function ProfileForm ( ) {
25+ const { data, isLoading } = usePatientProfile ( ) ;
26+ const updateProfile = useUpdateProfile ( ) ;
27+ const [ isEditing , setIsEditing ] = useState ( false ) ;
28+
29+ const [ form , setForm ] = useState ( {
30+ name : '' ,
31+ email : '' ,
32+ phone : '' ,
33+ dob : '' ,
34+ blood_group : '' ,
35+ height_cm : '' ,
36+ weight_kg : '' ,
37+ allergies : '' ,
38+ chronic_conditions : '' ,
39+ address_line1 : '' ,
40+ city : '' ,
41+ state : '' ,
42+ country : '' ,
43+ pincode : '' ,
44+ emergency_contact_name : '' ,
45+ emergency_contact_phone : '' ,
46+ emergency_contact_relation : ''
47+ } ) ;
48+
49+ useEffect ( ( ) => {
50+ if ( ! data ) return ;
51+ setForm ( {
52+ name : data . name || '' ,
53+ email : data . email || '' ,
54+ phone : data . phone || '' ,
55+ dob : data . dob || '' ,
56+ blood_group : data . blood_group || '' ,
57+ height_cm : data . height_cm || '' ,
58+ weight_kg : data . weight_kg || '' ,
59+ allergies : data . allergies || '' ,
60+ chronic_conditions : data . chronic_conditions || '' ,
61+ address_line1 : data . address_line1 || '' ,
62+ city : data . city || '' ,
63+ state : data . state || '' ,
64+ country : data . country || '' ,
65+ pincode : data . pincode || '' ,
66+ emergency_contact_name : data . emergency_contact_name || '' ,
67+ emergency_contact_phone : data . emergency_contact_phone || '' ,
68+ emergency_contact_relation : data . emergency_contact_relation || ''
69+ } ) ;
70+ } , [ data ] ) ;
71+
72+ if ( isLoading ) {
73+ return (
74+ < div className = "flex items-center justify-center min-h-100" >
75+ < div className = "animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-blue-600" > </ div >
76+ </ div >
77+ ) ;
78+ }
79+
80+ const handleChange = ( e : React . ChangeEvent < HTMLInputElement | HTMLTextAreaElement > ) => {
81+ setForm ( { ...form , [ e . target . name ] : e . target . value } ) ;
82+ } ;
83+
84+ const handleSubmit = ( e : React . FormEvent ) => {
85+ e . preventDefault ( ) ;
86+ updateProfile . mutate ( form , {
87+ onSuccess : ( ) => setIsEditing ( false )
88+ } ) ;
89+ } ;
90+
91+ const age = data ?. dob ? new Date ( ) . getFullYear ( ) - new Date ( data . dob ) . getFullYear ( ) : '—' ;
92+
93+ return (
94+ < div className = "max-w-5xl mx-auto space-y-6 pb-12 animate-in fade-in slide-in-from-bottom-4 duration-700" >
95+
96+ { /* HEADER SECTION */ }
97+ < div className = "relative overflow-hidden bg-white border border-slate-200 rounded-3xl p-8 shadow-sm" >
98+ < div className = "absolute top-0 right-0 w-48 h-48 bg-blue-50/50 rounded-full -mr-20 -mt-20 blur-3xl" />
99+
100+ < div className = "flex flex-col md:flex-row md:items-center justify-between gap-6 relative z-10" >
101+ < div className = "flex items-center gap-6" >
102+ < div className = "h-24 w-24 rounded-2xl bg-linear-to-br from-blue-600 to-indigo-700 flex items-center justify-center text-white text-3xl font-bold shadow-xl shadow-blue-100" >
103+ { form . name ?. charAt ( 0 ) }
104+ </ div >
105+ < div >
106+ < h1 className = "text-3xl font-extrabold text-slate-900 tracking-tight" > { form . name } </ h1 >
107+ < div className = "flex flex-wrap gap-3 mt-2" >
108+ < span className = "inline-flex items-center px-3 py-1 rounded-full text-[10px] font-bold bg-blue-50 text-blue-700 border border-blue-100 uppercase tracking-widest" >
109+ Patient ID: { data . id ?. slice ( - 6 ) . toUpperCase ( ) || 'H-OS-092' }
110+ </ span >
111+ < span className = "text-slate-400 text-sm font-medium flex items-center gap-1" >
112+ < Activity size = { 14 } className = "text-emerald-500" /> Account Active
113+ </ span >
114+ </ div >
115+ </ div >
116+ </ div >
117+
118+ < div className = "flex items-center gap-3" >
119+ { ! isEditing ? (
120+ < button
121+ onClick = { ( ) => setIsEditing ( true ) }
122+ className = "flex items-center gap-2 px-6 py-3 bg-slate-900 text-white rounded-2xl font-bold hover:bg-slate-800 transition-all active:scale-95 shadow-lg shadow-slate-200"
123+ >
124+ < Pencil size = { 18 } /> Edit Profile
125+ </ button >
126+ ) : (
127+ < button
128+ onClick = { ( ) => setIsEditing ( false ) }
129+ className = "flex items-center gap-2 px-6 py-3 bg-white border border-slate-200 text-slate-600 rounded-2xl font-bold hover:bg-slate-50 transition-all"
130+ >
131+ < X size = { 18 } /> Cancel
132+ </ button >
133+ ) }
134+ </ div >
135+ </ div >
136+
137+ < div className = "grid grid-cols-2 md:grid-cols-4 gap-4 mt-10 pt-8 border-t border-slate-100" >
138+ < StatBox label = "Blood Type" value = { form . blood_group || 'Unknown' } icon = { < Droplets className = "text-red-500" size = { 16 } /> } />
139+ < StatBox label = "Height" value = { form . height_cm ? `${ form . height_cm } cm` : '—' } icon = { < Ruler className = "text-blue-500" size = { 16 } /> } />
140+ < StatBox label = "Weight" value = { form . weight_kg ? `${ form . weight_kg } kg` : '—' } icon = { < Weight className = "text-emerald-500" size = { 16 } /> } />
141+ < StatBox label = "Age" value = { `${ age } Years` } icon = { < User className = "text-indigo-500" size = { 16 } /> } />
142+ </ div >
143+ </ div >
144+
145+ < form onSubmit = { handleSubmit } className = "grid grid-cols-1 lg:grid-cols-3 gap-6" >
146+
147+ { /* LEFT COLUMN */ }
148+ < div className = "lg:col-span-2 space-y-6" >
149+
150+ { /* PERSONAL IDENTITY SECTION */ }
151+ < section className = "bg-white border border-slate-200 rounded-3xl p-6 shadow-sm" >
152+ < header className = "flex items-center gap-3 mb-8" >
153+ < div className = "p-2 bg-blue-50 rounded-xl text-blue-600" > < Fingerprint size = { 20 } /> </ div >
154+ < h2 className = "text-xl font-bold text-slate-900 tracking-tight" > Personal Identity</ h2 >
155+ </ header >
156+
157+ < div className = "grid grid-cols-1 md:grid-cols-2 gap-6" >
158+ < div className = "md:col-span-2" >
159+ < FloatingInput label = "Full Name" name = "name" value = { form . name } onChange = { handleChange } disabled = { ! isEditing } icon = { < User size = { 16 } /> } />
160+ </ div >
161+ < FloatingInput label = "Email Address" name = "email" value = { form . email } onChange = { handleChange } disabled = { ! isEditing } icon = { < Mail size = { 16 } /> } />
162+ < FloatingInput label = "Phone Number" name = "phone" value = { form . phone } onChange = { handleChange } disabled = { ! isEditing } icon = { < Phone size = { 16 } /> } />
163+ < div className = "md:col-span-2" >
164+ < FloatingInput label = "Date of Birth" name = "dob" type = "date" value = { form . dob } onChange = { handleChange } disabled = { ! isEditing } icon = { < Calendar size = { 16 } /> } />
165+ </ div >
166+ </ div >
167+ </ section >
168+
169+ { /* CLINICAL BACKGROUND */ }
170+ < section className = "bg-white border border-slate-200 rounded-3xl p-6 shadow-sm" >
171+ < header className = "flex items-center gap-3 mb-8" >
172+ < div className = "p-2 bg-rose-50 rounded-xl text-rose-600" > < ShieldAlert size = { 20 } /> </ div >
173+ < h2 className = "text-xl font-bold text-slate-900 tracking-tight" > Clinical Background</ h2 >
174+ </ header >
175+
176+ < div className = "grid grid-cols-1 md:grid-cols-2 gap-6" >
177+ < div className = "space-y-4" >
178+ < label className = "text-xs font-bold text-slate-400 uppercase tracking-widest ml-1" > Physical Metrics</ label >
179+ < div className = "grid grid-cols-2 gap-4" >
180+ < FloatingInput label = "Blood Type" name = "blood_group" value = { form . blood_group } onChange = { handleChange } disabled = { ! isEditing } />
181+ < FloatingInput label = "Height (cm)" name = "height_cm" value = { form . height_cm } onChange = { handleChange } disabled = { ! isEditing } />
182+ < FloatingInput label = "Weight (kg)" name = "weight_kg" value = { form . weight_kg } onChange = { handleChange } disabled = { ! isEditing } />
183+ </ div >
184+ </ div >
185+
186+ < div className = "space-y-4" >
187+ < label className = "text-xs font-bold text-slate-400 uppercase tracking-widest ml-1" > Medical Notes</ label >
188+ < textarea
189+ name = "allergies"
190+ value = { form . allergies }
191+ onChange = { handleChange }
192+ disabled = { ! isEditing }
193+ placeholder = "Record allergies or sensitivities..."
194+ className = "w-full bg-slate-50 border-slate-200 rounded-2xl p-4 text-sm font-medium focus:ring-2 focus:ring-blue-500 transition-all min-h-27.5 disabled:bg-slate-50/50 resize-none"
195+ />
196+ </ div >
197+ </ div >
198+ </ section >
199+ </ div >
200+
201+ { /* RIGHT COLUMN */ }
202+ < div className = "space-y-6" >
203+
204+ { /* EMERGENCY SECTION */ }
205+ < section className = "bg-white border border-slate-200 rounded-3xl p-6 shadow-sm" >
206+ < header className = "flex items-center gap-3 mb-8" >
207+ < div className = "p-2 bg-amber-50 rounded-xl text-amber-600" > < PhoneCall size = { 20 } /> </ div >
208+ < h2 className = "text-xl font-bold text-slate-900 tracking-tight" > Emergency Contact</ h2 >
209+ </ header >
210+
211+ < div className = "space-y-5" >
212+ < FloatingInput label = "Contact Name" name = "emergency_contact_name" value = { form . emergency_contact_name } onChange = { handleChange } disabled = { ! isEditing } />
213+ < FloatingInput label = "Emergency Phone" name = "emergency_contact_phone" value = { form . emergency_contact_phone } onChange = { handleChange } disabled = { ! isEditing } />
214+ < FloatingInput label = "Relationship" name = "emergency_contact_relation" value = { form . emergency_contact_relation } onChange = { handleChange } disabled = { ! isEditing } />
215+ </ div >
216+ </ section >
217+
218+ { /* ADDRESS SECTION */ }
219+ < section className = "bg-white border border-slate-200 rounded-3xl p-6 shadow-sm" >
220+ < header className = "flex items-center gap-3 mb-8" >
221+ < div className = "p-2 bg-emerald-50 rounded-xl text-emerald-600" > < MapPin size = { 20 } /> </ div >
222+ < h2 className = "text-xl font-bold text-slate-900 tracking-tight" > Residency</ h2 >
223+ </ header >
224+
225+ < div className = "space-y-5" >
226+ < FloatingInput label = "Street Address" name = "address_line1" value = { form . address_line1 } onChange = { handleChange } disabled = { ! isEditing } />
227+ < div className = "grid grid-cols-2 gap-4" >
228+ < FloatingInput label = "City" name = "city" value = { form . city } onChange = { handleChange } disabled = { ! isEditing } />
229+ < FloatingInput label = "Pincode" name = "pincode" value = { form . pincode } onChange = { handleChange } disabled = { ! isEditing } />
230+ </ div >
231+ </ div >
232+ </ section >
233+
234+ { isEditing && (
235+ < div className = "sticky bottom-6 bg-blue-600 rounded-3xl p-6 text-white shadow-2xl shadow-blue-300 animate-in zoom-in-95 duration-300" >
236+ < h3 className = "font-bold flex items-center gap-2 mb-2" > < Activity size = { 18 } /> Review Updates</ h3 >
237+ < p className = "text-xs text-blue-100 mb-6 font-medium" > Please verify the contact and medical data accuracy before saving to the central database.</ p >
238+ < button
239+ type = "submit"
240+ className = "w-full flex items-center justify-center gap-2 bg-white text-blue-600 py-4 rounded-2xl font-black hover:shadow-lg transition-all active:scale-95"
241+ >
242+ < Save size = { 20 } /> SYNC PROFILE
243+ </ button >
244+ </ div >
245+ ) }
246+ </ div >
247+ </ form >
248+ </ div >
249+ ) ;
250+ }
251+
252+ /* HELPER COMPONENTS */
253+
254+ function StatBox ( { label, value, icon } : { label : string , value : string , icon : React . ReactNode } ) {
255+ return (
256+ < div className = "flex items-center gap-4 p-4 rounded-2xl bg-slate-50 border border-slate-100/50" >
257+ < div className = "p-2.5 bg-white rounded-xl shadow-sm border border-slate-100" > { icon } </ div >
258+ < div >
259+ < p className = "text-[10px] uppercase font-black text-slate-400 tracking-tighter leading-none mb-1" > { label } </ p >
260+ < p className = "text-sm font-bold text-slate-800" > { value } </ p >
261+ </ div >
262+ </ div >
263+ ) ;
264+ }
265+
266+ function FloatingInput ( { label, name, value, onChange, disabled, type = "text" , icon } : any ) {
267+ return (
268+ < div className = "group relative flex flex-col gap-1.5 w-full" >
269+ < div className = "flex items-center justify-between px-1" >
270+ < label className = "text-[10px] font-black text-slate-400 group-focus-within:text-blue-600 uppercase tracking-widest transition-colors" >
271+ { label }
272+ </ label >
273+ { icon && < span className = "text-slate-300 group-focus-within:text-blue-400 transition-colors" > { icon } </ span > }
274+ </ div >
275+ < input
276+ type = { type }
277+ name = { name }
278+ value = { value }
279+ onChange = { onChange }
280+ disabled = { disabled }
281+ className = { `
282+ w-full px-4 py-3.5 bg-slate-50 border border-slate-200 rounded-2xl text-sm font-bold tracking-tight transition-all
283+ focus:ring-4 focus:ring-blue-100 focus:border-blue-500 focus:bg-white outline-none
284+ disabled:opacity-50 disabled:cursor-not-allowed disabled:bg-slate-50/20 disabled:border-slate-100 disabled:font-medium
285+ ` }
286+ />
287+ </ div >
288+ ) ;
289+ }
0 commit comments