@@ -72,8 +72,7 @@ const Checkout = () => {
7272 const e = { } ;
7373 if ( ! form . firstName . trim ( ) ) e . firstName = "Required" ;
7474 if ( ! form . lastName . trim ( ) ) e . lastName = "Required" ;
75- if ( ! form . email . match ( / ^ [ ^ \s @ ] + @ [ ^ \s @ ] + \. [ ^ \s @ ] + $ / ) )
76- e . email = "Enter a valid email" ;
75+ if ( ! / ^ [ ^ \s @ ] + @ [ ^ \s @ ] + \. [ ^ \s @ ] + $ / . test ( form . email ) ) e . email = "Enter a valid email" ;
7776 if ( ! form . address1 . trim ( ) ) e . address1 = "Required" ;
7877 if ( ! form . city . trim ( ) ) e . city = "Required" ;
7978 if ( ! form . zip . trim ( ) ) e . zip = "Required" ;
@@ -90,7 +89,8 @@ const Checkout = () => {
9089
9190 try {
9291 setSubmitting ( true ) ;
93- await new Promise ( ( r ) => setTimeout ( r , 600 ) ) ; // demo delay
92+ // Demo delay / here you'd call your API or payment provider
93+ await new Promise ( ( r ) => setTimeout ( r , 600 ) ) ;
9494 emptyCart ( ) ;
9595 navigate ( "/exit" ) ;
9696 } finally {
@@ -99,7 +99,7 @@ const Checkout = () => {
9999 }
100100
101101 return (
102- < div className = "mx-auto max-w-7xl" >
102+ < div className = "mx-auto max-w-7xl p-4 " >
103103 < h1 className = "mb-6 font-display text-3xl font-semibold text-ink" >
104104 Checkout
105105 </ h1 >
@@ -128,17 +128,12 @@ const Checkout = () => {
128128 value = { form . firstName }
129129 onChange = { handleChange }
130130 autoComplete = "given-name"
131- aria-describedby = {
132- errors . firstName ? "firstName-error" : undefined
133- }
131+ aria-describedby = { errors . firstName ? "firstName-error" : undefined }
134132 className = "mt-1 block w-full rounded-xl border-neutral-300 focus:border-primary focus:ring-primary"
135133 required
136134 />
137135 { errors . firstName && (
138- < p
139- id = "firstName-error"
140- className = "mt-1 text-xs text-danger"
141- >
136+ < p id = "firstName-error" className = "mt-1 text-xs text-danger" >
142137 { errors . firstName }
143138 </ p >
144139 ) }
@@ -152,9 +147,7 @@ const Checkout = () => {
152147 value = { form . lastName }
153148 onChange = { handleChange }
154149 autoComplete = "family-name"
155- aria-describedby = {
156- errors . lastName ? "lastName-error" : undefined
157- }
150+ aria-describedby = { errors . lastName ? "lastName-error" : undefined }
158151 className = "mt-1 block w-full rounded-xl border-neutral-300 focus:border-primary focus:ring-primary"
159152 required
160153 />
@@ -201,12 +194,171 @@ const Checkout = () => {
201194 </ section >
202195
203196 { /* Shipping address */ }
204- { /* (kept as-is from your version — unchanged) */ }
205- { /* ... */ }
197+ < section className = "rounded-2xl border border-neutral-200 bg-white p-6 shadow-card" >
198+ < h2 className = "font-display text-lg font-semibold text-ink" >
199+ Shipping address
200+ </ h2 >
201+ < div className = "mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2" >
202+ { /* Address 1 */ }
203+ < label className = "block sm:col-span-2" >
204+ < span className = "text-sm font-medium" > Address line 1</ span >
205+ < input
206+ name = "address1"
207+ value = { form . address1 }
208+ onChange = { handleChange }
209+ autoComplete = "address-line1"
210+ aria-describedby = { errors . address1 ? "address1-error" : undefined }
211+ className = "mt-1 block w-full rounded-xl border-neutral-300 focus:border-primary focus:ring-primary"
212+ required
213+ />
214+ { errors . address1 && (
215+ < p id = "address1-error" className = "mt-1 text-xs text-danger" >
216+ { errors . address1 }
217+ </ p >
218+ ) }
219+ </ label >
220+
221+ { /* Address 2 */ }
222+ < label className = "block sm:col-span-2" >
223+ < span className = "text-sm font-medium" > Address line 2 (optional)</ span >
224+ < input
225+ name = "address2"
226+ value = { form . address2 }
227+ onChange = { handleChange }
228+ autoComplete = "address-line2"
229+ className = "mt-1 block w-full rounded-xl border-neutral-300 focus:border-primary focus:ring-primary"
230+ />
231+ </ label >
232+
233+ { /* City */ }
234+ < label className = "block" >
235+ < span className = "text-sm font-medium" > City</ span >
236+ < input
237+ name = "city"
238+ value = { form . city }
239+ onChange = { handleChange }
240+ autoComplete = "address-level2"
241+ aria-describedby = { errors . city ? "city-error" : undefined }
242+ className = "mt-1 block w-full rounded-xl border-neutral-300 focus:border-primary focus:ring-primary"
243+ required
244+ />
245+ { errors . city && (
246+ < p id = "city-error" className = "mt-1 text-xs text-danger" >
247+ { errors . city }
248+ </ p >
249+ ) }
250+ </ label >
251+
252+ { /* State/Region */ }
253+ < label className = "block" >
254+ < span className = "text-sm font-medium" > State/Region</ span >
255+ < input
256+ name = "state"
257+ value = { form . state }
258+ onChange = { handleChange }
259+ autoComplete = "address-level1"
260+ className = "mt-1 block w-full rounded-xl border-neutral-300 focus:border-primary focus:ring-primary"
261+ />
262+ </ label >
263+
264+ { /* ZIP */ }
265+ < label className = "block" >
266+ < span className = "text-sm font-medium" > ZIP / Postal code</ span >
267+ < input
268+ name = "zip"
269+ value = { form . zip }
270+ onChange = { handleChange }
271+ autoComplete = "postal-code"
272+ aria-describedby = { errors . zip ? "zip-error" : undefined }
273+ className = "mt-1 block w-full rounded-xl border-neutral-300 focus:border-primary focus:ring-primary"
274+ required
275+ />
276+ { errors . zip && (
277+ < p id = "zip-error" className = "mt-1 text-xs text-danger" >
278+ { errors . zip }
279+ </ p >
280+ ) }
281+ </ label >
282+
283+ { /* Country */ }
284+ < label className = "block" >
285+ < span className = "text-sm font-medium" > Country</ span >
286+ < select
287+ name = "country"
288+ value = { form . country }
289+ onChange = { handleChange }
290+ aria-describedby = { errors . country ? "country-error" : undefined }
291+ className = "mt-1 block w-full rounded-xl border-neutral-300 focus:border-primary focus:ring-primary"
292+ required
293+ >
294+ < option value = "" > Select…</ option >
295+ < option value = "GR" > Greece</ option >
296+ < option value = "US" > United States</ option >
297+ < option value = "GB" > United Kingdom</ option >
298+ < option value = "DE" > Germany</ option >
299+ < option value = "FR" > France</ option >
300+ < option value = "ES" > Spain</ option >
301+ < option value = "IT" > Italy</ option >
302+ { /* add more as needed */ }
303+ </ select >
304+ { errors . country && (
305+ < p id = "country-error" className = "mt-1 text-xs text-danger" >
306+ { errors . country }
307+ </ p >
308+ ) }
309+ </ label >
310+
311+ { /* Terms */ }
312+ < label className = "mt-2 flex items-center gap-2 sm:col-span-2" >
313+ < input
314+ type = "checkbox"
315+ name = "agree"
316+ checked = { form . agree }
317+ onChange = { handleChange }
318+ className = "h-4 w-4 rounded border-neutral-300 text-primary focus:ring-primary"
319+ />
320+ < span className = "text-sm text-neutral-700" >
321+ I agree to the terms and conditions
322+ </ span >
323+ </ label >
324+ { errors . agree && (
325+ < p className = "text-xs text-danger sm:col-span-2" > { errors . agree } </ p >
326+ ) }
327+ </ div >
328+ </ section >
329+
330+ < button
331+ type = "submit"
332+ disabled = { submitting }
333+ className = "mt-2 inline-flex items-center justify-center rounded-xl bg-ink px-5 py-3 text-white shadow-card disabled:opacity-60 hover:bg-neutral-900"
334+ >
335+ { submitting ? "Processing…" : "Place order" }
336+ </ button >
206337 </ form >
207338
208339 { /* Order summary */ }
209- { /* (also unchanged — just as in your version) */ }
340+ < aside className = "h-fit rounded-2xl border border-neutral-200 bg-white p-6 shadow-card" >
341+ < h2 className = "mb-3 font-display text-lg font-semibold text-ink" >
342+ Order summary
343+ </ h2 >
344+ < ul className = "divide-y" >
345+ { items . map ( ( it ) => (
346+ < li key = { it . id } className = "py-3 flex items-start justify-between" >
347+ < div className = "mr-4" >
348+ < div className = "font-medium" > { it . name } </ div >
349+ < div className = "text-sm text-neutral-600" > Qty: { it . quantity } </ div >
350+ </ div >
351+ < div className = "text-right font-medium" >
352+ { formatCurrency ( ( it . price || 0 ) * ( it . quantity || 0 ) ) }
353+ </ div >
354+ </ li >
355+ ) ) }
356+ </ ul >
357+ < div className = "mt-4 flex items-center justify-between text-base font-semibold" >
358+ < span > Total</ span >
359+ < span > { formatCurrency ( cartTotal || 0 ) } </ span >
360+ </ div >
361+ </ aside >
210362 </ div >
211363 </ div >
212364 ) ;
0 commit comments