11"use client" ;
22
33import { CodeInline } from "@/components/CodeInline" ;
4+ import { LoadingOverlay } from "@/components/LoadingOverlay" ;
5+ import { MessageDialog } from "@/components/MessageDialog" ;
46import { Box , Button , TextField , Typography } from "@mui/material" ;
57import { useState } from "react" ;
68
7- const AccountForm = ( { name, slug } : { name : string ; slug : string } ) => {
8- const [ formData , setFormData ] = useState ( { name, slug } ) ;
9- const [ errors , setErrors ] = useState < { name ?: string ; slug ?: string } > ( { } ) ;
9+ const AccountForm = ( {
10+ name,
11+ slug,
12+ displayEmail,
13+ title,
14+ location,
15+ siteTitle,
16+ siteDescription,
17+ } : {
18+ name : string ;
19+ slug : string ;
20+ displayEmail : string ;
21+ title : string ;
22+ location : string ;
23+ siteTitle : string ;
24+ siteDescription : string ;
25+ } ) => {
26+ const [ formData , setFormData ] = useState ( {
27+ name,
28+ slug,
29+ displayEmail,
30+ title,
31+ location,
32+ siteTitle,
33+ siteDescription,
34+ } ) ;
35+ const [ errors , setErrors ] = useState < { name ?: string ; slug ?: string ; displayEmail ?: string } > ( { } ) ;
36+ const [ loading , setLoading ] = useState ( false ) ;
37+ const [ message , setMessage ] = useState ( "" ) ;
1038
1139 const handleChange = ( e : React . ChangeEvent < HTMLInputElement > ) => {
1240 const { name, value } = e . target ;
@@ -34,7 +62,7 @@ const AccountForm = ({ name, slug }: { name: string; slug: string }) => {
3462
3563 const handleSubmit = ( e : React . FormEvent ) => {
3664 e . preventDefault ( ) ;
37- const { name, slug } = formData ;
65+ const { name, slug, displayEmail } = formData ;
3866
3967 if ( ! name . trim ( ) || ! slug . trim ( ) ) {
4068 setErrors ( {
@@ -44,6 +72,13 @@ const AccountForm = ({ name, slug }: { name: string; slug: string }) => {
4472 return ;
4573 }
4674
75+ if ( displayEmail . trim ( ) ?. length > 0 && ! / ^ [ ^ \s @ ] + @ [ ^ \s @ ] + \. [ ^ \s @ ] + $ / . test ( displayEmail ) ) {
76+ setErrors ( { displayEmail : "Invalid email address" } ) ;
77+ return ;
78+ }
79+
80+ setLoading ( true ) ;
81+
4782 // Send the form data to the next.js API route to
4883 // update the user's account information. Use POST method.
4984 fetch ( "/api/account" , {
@@ -53,73 +88,153 @@ const AccountForm = ({ name, slug }: { name: string; slug: string }) => {
5388 } ,
5489 body : JSON . stringify ( formData ) ,
5590 } )
56- . then ( ( res ) => {
91+ . then ( async ( res ) => {
5792 if ( ! res . ok ) {
58- alert ( "Failed to submit the form!" ) ;
93+ const { error } = await res . json ( ) ;
94+ setMessage ( error ) ;
5995 return ;
6096 }
6197
62- alert ( "Form submitted successfully !") ;
98+ setMessage ( "Saved !") ;
6399 } )
64100 . catch ( ( err ) => {
65- // eslint-disable-next-line no-console
66- console . error ( err ) ;
67- alert ( "Failed to submit the form!" ) ;
101+ setMessage ( err . message ) ;
102+ } )
103+ . finally ( ( ) => {
104+ setLoading ( false ) ;
68105 } ) ;
69106 } ;
70107
71108 return (
72- < Box
73- component = "form"
74- onSubmit = { handleSubmit }
75- sx = { {
76- display : "flex" ,
77- flexDirection : "column" ,
78- gap : 2 ,
79- mt : 4 ,
80- } }
81- >
82- < Typography variant = "body2" color = "textSecondary" >
83- Displayed on your public resume.
84- </ Typography >
85- < TextField
86- label = "Full Name"
87- name = "name"
88- value = { formData . name }
89- onChange = { handleChange }
90- error = { ! ! errors . name }
91- helperText = { errors . name ? errors . name : " " }
92- fullWidth
93- />
94- < Typography variant = "body2" color = "textSecondary" >
95- Used to generate your public resume URL, e.g.{ " " }
96- < CodeInline >
97- https://openresume.org/r/< strong > your-custom-slug</ strong > /
98- </ CodeInline >
99- . If you change it, an automatic redirect will < strong > not</ strong > be created, so please
100- update your shared links.
101- </ Typography >
102- < TextField
103- label = "Slug"
104- name = "slug"
105- value = { formData . slug }
106- onChange = { handleChange }
107- error = { ! ! errors . slug }
108- helperText = { errors . slug ? errors . slug : " " }
109- fullWidth
109+ < Box >
110+ < LoadingOverlay open = { loading } message = "Submitting form..." />
111+ < MessageDialog
112+ open = { message ?. length > 0 }
113+ message = { message }
114+ onClose = { ( ) => setMessage ( "" ) }
115+ onConfirm = { ( ) => setMessage ( "" ) }
110116 />
111- < Button
112- type = "submit"
113- variant = "contained"
114- color = "primary"
115- fullWidth
117+ < Box
118+ component = "form"
119+ onSubmit = { handleSubmit }
116120 sx = { {
117- mt : 2 ,
118- width : "200px" ,
121+ display : "flex" ,
122+ flexDirection : "column" ,
123+ gap : 2 ,
124+ mt : 4 ,
119125 } }
120126 >
121- Save
122- </ Button >
127+ < Typography variant = "body2" color = "textSecondary" >
128+ Displayed on your public resume.
129+ </ Typography >
130+ < TextField
131+ label = "Full Name"
132+ name = "name"
133+ value = { formData . name }
134+ onChange = { handleChange }
135+ error = { ! ! errors . name }
136+ helperText = { errors . name ? errors . name : " " }
137+ fullWidth
138+ />
139+ < Typography variant = "body2" color = "textSecondary" >
140+ Used to generate your public resume URL, e.g.{ " " }
141+ < CodeInline >
142+ https://openresume.org/r/
143+ < strong >
144+ { formData ?. slug ? formData . slug : "your-custom-slug" }
145+ </ strong > /
146+ </ CodeInline >
147+ . If you change it, an automatic redirect will < strong > not</ strong > be created, so please
148+ update your shared links.
149+ </ Typography >
150+ < TextField
151+ label = "Slug"
152+ name = "slug"
153+ value = { formData . slug }
154+ onChange = { handleChange }
155+ error = { ! ! errors . slug }
156+ helperText = { errors . slug ? errors . slug : " " }
157+ fullWidth
158+ />
159+ < Typography variant = "body2" color = "textSecondary" >
160+ Your display email is publicly visible on your resume!
161+ </ Typography >
162+ < TextField
163+ label = "Display Email"
164+ name = "displayEmail"
165+ value = { formData . displayEmail }
166+ onChange = { handleChange }
167+ error = { ! ! errors . displayEmail }
168+ helperText = { errors . displayEmail ? errors . displayEmail : " " }
169+ fullWidth
170+ />
171+ < Typography variant = "body2" color = "textSecondary" >
172+ Your Title is the job title you're looking for, or identify as. Example:{ " " }
173+ < strong > Software Engineer</ strong >
174+ </ Typography >
175+ < TextField
176+ label = "Title"
177+ name = "title"
178+ value = { formData . title }
179+ onChange = { handleChange }
180+ fullWidth
181+ />
182+ < Typography variant = "body2" color = "textSecondary" >
183+ Your Location is where you are currently located. General is advised. Example:{ " " }
184+ < strong > Los Angeles, CA</ strong >
185+ </ Typography >
186+ < TextField
187+ label = "Location"
188+ name = "location"
189+ value = { formData . location }
190+ onChange = { handleChange }
191+ fullWidth
192+ />
193+ < Typography variant = "body2" color = "textSecondary" >
194+ Your Site Title is the title of your personal website. Example:{ " " }
195+ < strong > { formData ?. name ? formData . name : "John Doe" } | OpenResume</ strong > { " " }
196+ < em >
197+ This is used in the title tag of your website as well as for social media sharing.
198+ </ em >
199+ </ Typography >
200+ < TextField
201+ label = "Site Title"
202+ name = "siteTitle"
203+ value = { formData . siteTitle }
204+ onChange = { handleChange }
205+ fullWidth
206+ />
207+ < Typography variant = "body2" color = "textSecondary" >
208+ Your Site Description is a short description of your personal website. Example:{ " " }
209+ < strong >
210+ { formData ?. name ? formData . name : "John Doe" } is a seasoned professional with 10 years
211+ experience.
212+ </ strong > { " " }
213+ < em >
214+ This is used in the meta description tag of your website as well as for social media
215+ sharing.
216+ </ em >
217+ </ Typography >
218+ < TextField
219+ label = "Site Description"
220+ name = "siteDescription"
221+ value = { formData . siteDescription }
222+ onChange = { handleChange }
223+ fullWidth
224+ />
225+ < Button
226+ type = "submit"
227+ variant = "contained"
228+ color = "primary"
229+ fullWidth
230+ sx = { {
231+ mt : 2 ,
232+ width : "200px" ,
233+ } }
234+ >
235+ Save
236+ </ Button >
237+ </ Box >
123238 </ Box >
124239 ) ;
125240} ;
0 commit comments