@@ -25,6 +25,8 @@ import InternalLink from "../../components/Link";
2525import EmailIcon from "../../public/profile/email.svg" ;
2626import LinkedInIcon from "../../public/profile/linkedin.svg" ;
2727import NavbarBuilder from "../../components/NavBar" ;
28+ import { FieldOfStudy , Year } from "../../constants/profile" ;
29+ import MemberBadges from "../../components/MemberBadges" ;
2830
2931// Username included separately
3032export type Profile = {
@@ -101,9 +103,9 @@ const UserProfile: NextPage = () => {
101103 ) ;
102104
103105 useEffect ( ( ) => {
104- document . body . classList . add ( "bg-gray-800 " ) ;
106+ document . body . classList . add ( "bg-gray-50 " ) ;
105107 return ( ) => {
106- document . body . classList . remove ( "bg-gray-800 " ) ;
108+ document . body . classList . remove ( "bg-gray-50 " ) ;
107109 } ;
108110 } , [ ] ) ;
109111
@@ -233,7 +235,7 @@ const UserProfile: NextPage = () => {
233235 }
234236
235237 return (
236- < div className = "h-screen" >
238+ < div className = "min- h-screen bg-gray-50 " >
237239 < NavbarBuilder />
238240 < Formik
239241 enableReinitialize // to update when resetting initialProfile after submit
@@ -242,122 +244,259 @@ const UserProfile: NextPage = () => {
242244 onSubmit = { saveProfile }
243245 >
244246 { ( { values, isSubmitting } ) => (
245- < div className = "bg-gray-800 min-w-screen p-4 md:p-8 md:pl-16 flex items-center text-white " >
247+ < div className = "py-10 " >
246248 < Head title = { profileUsername } />
247249 < Form >
248- { values . avatar && (
249- < div className = "flex flex-col md:flex-row justify-around items-center" >
250- < div className = "m-4 mx-0 flex flex-row justify-between items-center" >
251- < ViewAvatar avatar = { values . avatar } />
252- < h2 className = "text-2xl ml-4" >
253- < b > { values . name } </ b > ({ profileUsername } )
254- < div className = "flex flex-row gap-x-5 p-0 py-2" >
255- < InternalLink
256- href = { `mailto:${ values . email } ` }
257- target = "_blank"
258- rel = "noopener noreferrer"
259- >
260- < EmailIcon className = "h-6 w-6 fill-white" />
261- </ InternalLink >
262- < InternalLink
263- href = { values . linkedin ?? "" }
264- target = "_blank"
265- >
250+ < div className = "max-w-4xl mx-auto px-4 sm:px-6 lg:px-8" >
251+ < div className = "bg-white shadow-sm rounded-lg overflow-hidden" >
252+ { /* Header Section with Avatar and Basic Info */ }
253+ < div className = "bg-gradient-to-r from-gray-100 to-gray-200 px-6 py-8" >
254+ < div className = "flex flex-col md:flex-row items-center" >
255+ < div className = "relative" >
256+ { values . avatar ? (
257+ < div className = "relative" >
258+ < ViewAvatar avatar = { values . avatar } />
259+ { editMode && (
260+ < div className = "absolute -bottom-2 -right-2" >
261+ < EditAvatar />
262+ </ div >
263+ ) }
264+ </ div >
265+ ) : (
266+ < div className = "h-32 w-32 rounded-full bg-gray-300 flex items-center justify-center text-gray-500" >
267+ { values . name ? values . name . charAt ( 0 ) . toUpperCase ( ) : "?" }
268+ </ div >
269+ ) }
270+ </ div >
271+
272+ < div className = "mt-6 md:mt-0 md:ml-8 text-center md:text-left" >
273+ < h1 className = "text-3xl font-bold text-gray-900" > { values . name } </ h1 >
274+ < p className = "text-gray-600" > @{ profileUsername } </ p >
275+
276+ { /* Social links */ }
277+ < div className = "flex items-center justify-center md:justify-start space-x-4 mt-3" >
278+ < a
279+ href = { `mailto:${ values . email } ` }
280+ target = "_blank"
281+ rel = "noopener noreferrer"
282+ className = "text-gray-600 hover:text-gray-900"
283+ >
284+ < EmailIcon className = "h-5 w-5 fill-current" />
285+ </ a >
266286 { values . linkedin && (
267- < LinkedInIcon className = "h-6 w-6 fill-white" />
287+ < a
288+ href = { values . linkedin }
289+ target = "_blank"
290+ rel = "noopener noreferrer"
291+ className = "text-gray-600 hover:text-gray-900"
292+ >
293+ < LinkedInIcon className = "h-5 w-5 fill-current" />
294+ </ a >
295+ ) }
296+ { values . website && / ^ h t t p s ? : \/ \/ / . test ( values . website ) && (
297+ < a
298+ href = { values . website }
299+ target = "_blank"
300+ rel = "noopener noreferrer"
301+ className = "text-gray-600 hover:text-gray-900"
302+ >
303+ < svg xmlns = "http://www.w3.org/2000/svg" className = "h-5 w-5" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" >
304+ < path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = { 2 } d = "M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9" />
305+ </ svg >
306+ </ a >
268307 ) }
269- </ InternalLink >
308+ </ div >
309+ </ div >
310+
311+ { /* Edit button for small screens */ }
312+ < div className = "mt-6 md:hidden w-full" >
313+ { ! editMode && isCurrentUser && (
314+ < button
315+ className = "w-full py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
316+ onClick = { ( ) => {
317+ ReactGA . event ( {
318+ category : "Profile" ,
319+ action : "Entered edit mode" ,
320+ } ) ;
321+ setEditMode ( true ) ;
322+ } }
323+ type = "button"
324+ >
325+ Edit Profile
326+ </ button >
327+ ) }
270328 </ div >
271- </ h2 >
329+
330+ { /* Edit button for larger screens */ }
331+ < div className = "hidden md:block md:ml-auto" >
332+ { ! editMode && isCurrentUser && (
333+ < button
334+ className = "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
335+ onClick = { ( ) => {
336+ ReactGA . event ( {
337+ category : "Profile" ,
338+ action : "Entered edit mode" ,
339+ } ) ;
340+ setEditMode ( true ) ;
341+ } }
342+ type = "button"
343+ >
344+ < svg className = "-ml-1 mr-2 h-4 w-4" xmlns = "http://www.w3.org/2000/svg" viewBox = "0 0 20 20" fill = "currentColor" >
345+ < path d = "M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
346+ </ svg >
347+ Edit Profile
348+ </ button >
349+ ) }
350+ </ div >
351+ </ div >
272352 </ div >
273- < div className = "flex-1" > { editMode && < EditAvatar /> } </ div >
274- </ div >
275- ) }
276353
277- { /* Not allowing name or username changes for now */ }
278- { /* <h2 className="text-2xl my-4">
279- <b>{values.name}</b> ({profileUsername})
280- </h2> */ }
281- { editMode ? (
282- < EditProfile profile = { values } />
283- ) : (
284- < ViewProfile profile = { values } />
285- ) }
354+ { /* Profile Content */ }
355+ < div className = "px-6 py-6" >
356+ { /* Main content area */ }
357+ < div className = "grid grid-cols-1 md:grid-cols-3 gap-6" >
358+ { /* Left column - Personal info */ }
359+ < div className = "md:col-span-2" >
360+ { /* Bio section */ }
361+ < div className = "mb-6" >
362+ < h2 className = "text-lg font-medium text-gray-900 mb-2" > About</ h2 >
363+ < div className = "prose max-w-none text-gray-700" >
364+ { editMode ? (
365+ < EditProfile profile = { values } />
366+ ) : (
367+ < >
368+ { values . bio ? (
369+ < p > { values . bio } </ p >
370+ ) : (
371+ < p className = "text-gray-400 italic" > No bio provided</ p >
372+ ) }
373+ </ >
374+ ) }
375+ </ div >
376+ </ div >
286377
287- { /* Don't want to show resume on public profile */ }
288- { editMode && (
289- < div className = "grid grid-cols-6 gap-6 pb-4 justify-center items-center pt-4" >
290- { values . resume && (
291- < div className = "col-span-6 sm:col-span-3" >
292- < ViewResume resume = { values . resume } />
378+ { /* Resume section - Only in edit mode */ }
379+ { editMode && (
380+ < div className = "mb-6 border-t border-gray-200 pt-6" >
381+ < h2 className = "text-lg font-medium text-gray-900 mb-2" > Resume</ h2 >
382+ < div className = "grid grid-cols-1 sm:grid-cols-2 gap-4 items-center" >
383+ { values . resume && (
384+ < div className = "col-span-1" >
385+ < ViewResume resume = { values . resume } />
386+ </ div >
387+ ) }
388+ < div className = "col-span-1" >
389+ < EditResume label = "Upload your resume" />
390+ </ div >
391+ </ div >
392+ </ div >
393+ ) }
394+
395+ { /* Partner sharing consent - Only in edit mode */ }
396+ { editMode && (
397+ < div className = "mb-6 border-t border-gray-200 pt-6" >
398+ < PartnerSharingConsentField />
399+ </ div >
400+ ) }
401+ </ div >
402+
403+ { /* Right column - Education & Skills */ }
404+ < div className = "md:col-span-1" >
405+ < div className = "bg-gray-50 rounded-lg p-4" >
406+ { /* Education info */ }
407+ < div className = "mb-4" >
408+ < h2 className = "text-lg font-medium text-gray-900 mb-2" > Education</ h2 >
409+ { values . year && (
410+ < div className = "mb-1 flex" >
411+ < span className = "text-gray-500 w-20" > Year:</ span >
412+ < span className = "text-gray-900" > { Year [ values . year ] } </ span >
413+ </ div >
414+ ) }
415+ { values . fields_of_study && values . fields_of_study . majors . length > 0 && (
416+ < div className = "mb-1 flex flex-wrap" >
417+ < span className = "text-gray-500 w-20" > Major{ values . fields_of_study . majors . length > 1 ? 's' : '' } :</ span >
418+ < span className = "text-gray-900" >
419+ { values . fields_of_study . majors
420+ . map ( ( majorKey ) => FieldOfStudy [ majorKey ] )
421+ . join ( ", " ) }
422+ </ span >
423+ </ div >
424+ ) }
425+ { values . fields_of_study && values . fields_of_study . minors && values . fields_of_study . minors . length > 0 && (
426+ < div className = "mb-1 flex flex-wrap" >
427+ < span className = "text-gray-500 w-20" > Minor{ values . fields_of_study . minors . length > 1 ? 's' : '' } :</ span >
428+ < span className = "text-gray-900" >
429+ { values . fields_of_study . minors
430+ . map ( ( minorKey ) => FieldOfStudy [ minorKey ] )
431+ . join ( ", " ) }
432+ </ span >
433+ </ div >
434+ ) }
435+ </ div >
436+
437+ { /* Interests & roles */ }
438+ { ( values . interests ?. length > 0 || values . roles ?. length > 0 ) && (
439+ < div >
440+ < h2 className = "text-lg font-medium text-gray-900 mb-2" > Interests & Skills </ h2 >
441+ < div className = "mt-2" >
442+ { values . interests && (
443+ < div className = "pt-1" >
444+ < MemberBadges roles = { values . roles } interests = { values . interests } />
445+ </ div >
446+ ) }
447+ </ div >
448+ </ div >
449+ ) }
450+ </ div >
451+ </ div >
293452 </ div >
294- ) }
295- < div className = "col-span-6 sm:col-span-3" >
296- < EditResume label = "Upload your resume" />
297453 </ div >
298- </ div >
299- ) }
300-
301- { editMode && < PartnerSharingConsentField /> }
302454
303- { dataFetchErrors . map ( ( error ) => (
304- < p key = { error } className = "text-red-500" >
305- { error }
306- </ p >
307- ) ) }
455+ { /* Error Messages */ }
456+ { ( dataFetchErrors . length > 0 || formSubmitErrors . length > 0 ) && (
457+ < div className = "bg-red-50 px-6 py-4 border-t border-red-200" >
458+ { dataFetchErrors . map ( ( error ) => (
459+ < p key = { error } className = "text-red-600 text-sm" >
460+ { error }
461+ </ p >
462+ ) ) }
463+ { formSubmitErrors . map ( ( error ) => (
464+ < p key = { error } className = "text-red-600 text-sm" >
465+ { error }
466+ </ p >
467+ ) ) }
468+ </ div >
469+ ) }
308470
309- < div className = "mt-8 flex" >
310- { editMode ? (
311- < >
312- < button
313- className = "btn-gold-gradient"
314- onClick = { ( ) => {
315- ReactGA . event ( {
316- category : "Profile" ,
317- action : "Exited edit mode" ,
318- } ) ;
319- setEditMode ( false ) ;
320- } }
321- disabled = { isSubmitting }
322- type = "button"
323- >
324- Cancel
325- </ button >
326-
327- < button
328- className = "btn-gold-gradient"
329- disabled = {
330- isSubmitting || isObjectEqual ( values , initialProfile )
331- }
332- type = "submit"
333- >
334- { isSubmitting ? "Saving..." : "Save Profile" }
335- </ button >
336- { formSubmitErrors . map ( ( error ) => (
337- < p key = { error } className = "text-red-500" >
338- { error }
339- </ p >
340- ) ) }
341- </ >
342- ) : (
343- < >
344- { isCurrentUser && (
471+ { /* Edit Mode Footer */ }
472+ { editMode && (
473+ < div className = "bg-gray-50 px-6 py-4 border-t border-gray-200 flex justify-end space-x-3" >
345474 < button
346- className = "btn-gold-gradient "
475+ className = "py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 "
347476 onClick = { ( ) => {
348477 ReactGA . event ( {
349478 category : "Profile" ,
350- action : "Entered edit mode" ,
479+ action : "Exited edit mode" ,
351480 } ) ;
352- setEditMode ( true ) ;
481+ setEditMode ( false ) ;
353482 } }
483+ disabled = { isSubmitting }
354484 type = "button"
355485 >
356- Edit Profile
486+ Cancel
357487 </ button >
358- ) }
359- </ >
360- ) }
488+ < button
489+ className = "py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
490+ disabled = {
491+ isSubmitting || isObjectEqual ( values , initialProfile )
492+ }
493+ type = "submit"
494+ >
495+ { isSubmitting ? "Saving..." : "Save Profile" }
496+ </ button >
497+ </ div >
498+ ) }
499+ </ div >
361500 </ div >
362501 </ Form >
363502 </ div >
0 commit comments