3
3
import { revalidateTag } from 'next/cache' ;
4
4
import { cookies } from 'next/headers' ;
5
5
import { getFormatter , getTranslations } from 'next-intl/server' ;
6
+ import { z } from 'zod' ;
6
7
7
8
import { addCartLineItem } from '~/client/mutations/add-cart-line-item' ;
8
- import { createCartWithGiftCertificate } from '../_mutations/create-cart-with-gift-certificate' ;
9
9
import { getCart } from '~/client/queries/get-cart' ;
10
10
import { TAGS } from '~/client/tags' ;
11
11
12
- const GIFT_CERTIFICATE_THEMES = [ 'GENERAL' , 'BIRTHDAY' , 'BOY' , 'CELEBRATION' , 'CHRISTMAS' , 'GIRL' , 'NONE' ] ;
13
- type giftCertificateTheme = "GENERAL" | "BIRTHDAY" | "BOY" | "CELEBRATION" | "CHRISTMAS" | "GIRL" | "NONE" ;
12
+ import { createCartWithGiftCertificate } from '../_mutations/create-cart-with-gift-certificate' ;
14
13
15
- export const addGiftCertificateToCart = async ( data : FormData ) => {
14
+ const giftCertificateThemes = [
15
+ 'GENERAL' ,
16
+ 'BIRTHDAY' ,
17
+ 'BOY' ,
18
+ 'CELEBRATION' ,
19
+ 'CHRISTMAS' ,
20
+ 'GIRL' ,
21
+ 'NONE' ,
22
+ ] as const ;
23
+
24
+ const GiftCertificateThemeSchema = z . enum ( giftCertificateThemes ) ;
25
+
26
+ const ValidatedFormDataSchema = z . object ( {
27
+ theme : GiftCertificateThemeSchema ,
28
+ amount : z . number ( ) . positive ( ) ,
29
+ senderEmail : z . string ( ) . email ( ) ,
30
+ senderName : z . string ( ) . min ( 1 ) ,
31
+ recipientEmail : z . string ( ) . email ( ) ,
32
+ recipientName : z . string ( ) . min ( 1 ) ,
33
+ message : z . string ( ) . nullable ( ) ,
34
+ } ) ;
35
+
36
+ type ValidatedFormData = z . infer < typeof ValidatedFormDataSchema > ;
37
+
38
+ const CartResponseSchema = z . object ( {
39
+ status : z . enum ( [ 'success' , 'error' ] ) ,
40
+ data : z . unknown ( ) . optional ( ) ,
41
+ error : z . string ( ) . optional ( ) ,
42
+ } ) ;
43
+
44
+ type CartResponse = z . infer < typeof CartResponseSchema > ;
45
+
46
+ function parseFormData ( data : FormData ) : ValidatedFormData {
47
+ const theme = data . get ( 'theme' ) ;
48
+ const amount = data . get ( 'amount' ) ;
49
+ const senderEmail = data . get ( 'senderEmail' ) ;
50
+ const senderName = data . get ( 'senderName' ) ;
51
+ const recipientEmail = data . get ( 'recipientEmail' ) ;
52
+ const recipientName = data . get ( 'recipientName' ) ;
53
+ const message = data . get ( 'message' ) ;
54
+
55
+ // Parse and validate the form data
56
+ const validatedData = ValidatedFormDataSchema . parse ( {
57
+ theme,
58
+ amount : amount ? Number ( amount ) : undefined ,
59
+ senderEmail,
60
+ senderName,
61
+ recipientEmail,
62
+ recipientName,
63
+ message : message ? String ( message ) : null ,
64
+ } ) ;
65
+
66
+ return validatedData ;
67
+ }
68
+
69
+ export async function addGiftCertificateToCart ( data : FormData ) : Promise < CartResponse > {
16
70
const format = await getFormatter ( ) ;
17
71
const t = await getTranslations ( 'GiftCertificate.Actions.AddToCart' ) ;
18
72
19
- let theme = String ( data . get ( 'theme' ) ) as giftCertificateTheme ;
20
- const amount = Number ( data . get ( 'amount' ) ) ;
21
- const senderEmail = String ( data . get ( 'senderEmail' ) ) ;
22
- const senderName = String ( data . get ( 'senderName' ) ) ;
23
- const recipientEmail = String ( data . get ( 'recipientEmail' ) ) ;
24
- const recipientName = String ( data . get ( 'recipientName' ) ) ;
25
- const message = data . get ( 'message' ) ? String ( data . get ( 'message' ) ) : null ;
26
-
27
- if ( ! GIFT_CERTIFICATE_THEMES . includes ( theme ) ) {
28
- theme = 'GENERAL'
29
- }
30
-
31
- const giftCertificate = {
32
- name : t ( 'certificateName' , {
33
- amount : format . number ( amount , {
34
- style : 'currency' ,
35
- currency : 'USD' , // TODO: Determine this from the selected currency
36
- } )
37
- } ) ,
38
- theme,
39
- amount,
40
- "quantity" : 1 ,
41
- "sender" : {
42
- "email" : senderEmail ,
43
- "name" : senderName ,
44
- } ,
45
- "recipient" : {
46
- "email" : recipientEmail ,
47
- "name" : recipientName ,
48
- } ,
49
- message,
50
- }
51
-
52
- const cartId = cookies ( ) . get ( 'cartId' ) ?. value ;
53
- let cart ;
54
-
55
73
try {
56
- cart = await getCart ( cartId ) ;
74
+ const validatedData = parseFormData ( data ) ;
75
+
76
+ const giftCertificate = {
77
+ name : t ( 'certificateName' , {
78
+ amount : format . number ( validatedData . amount , {
79
+ style : 'currency' ,
80
+ currency : 'USD' ,
81
+ } ) ,
82
+ } ) ,
83
+ theme : validatedData . theme ,
84
+ amount : validatedData . amount ,
85
+ quantity : 1 ,
86
+ sender : {
87
+ email : validatedData . senderEmail ,
88
+ name : validatedData . senderName ,
89
+ } ,
90
+ recipient : {
91
+ email : validatedData . recipientEmail ,
92
+ name : validatedData . recipientName ,
93
+ } ,
94
+ message : validatedData . message ,
95
+ } ;
96
+
97
+ const cartId = cookies ( ) . get ( 'cartId' ) ?. value ;
98
+ let cart ;
99
+
100
+ if ( cartId ) {
101
+ cart = await getCart ( cartId ) ;
102
+ }
57
103
58
104
if ( cart ) {
59
105
cart = await addCartLineItem ( cart . entityId , {
60
- giftCertificates : [
61
- giftCertificate
62
- ] ,
106
+ giftCertificates : [ giftCertificate ] ,
63
107
} ) ;
64
108
65
109
if ( ! cart ?. entityId ) {
66
- return { status : 'error' , error : t ( 'error' ) } ;
110
+ return CartResponseSchema . parse ( {
111
+ status : 'error' ,
112
+ error : t ( 'error' ) ,
113
+ } ) ;
67
114
}
68
115
69
116
revalidateTag ( TAGS . cart ) ;
70
117
71
- return { status : 'success' , data : cart } ;
118
+ return CartResponseSchema . parse ( {
119
+ status : 'success' ,
120
+ data : cart ,
121
+ } ) ;
72
122
}
73
123
74
124
cart = await createCartWithGiftCertificate ( [ giftCertificate ] ) ;
75
125
76
126
if ( ! cart ?. entityId ) {
77
- return { status : 'error' , error : t ( 'error' ) } ;
127
+ return CartResponseSchema . parse ( {
128
+ status : 'error' ,
129
+ error : t ( 'error' ) ,
130
+ } ) ;
78
131
}
79
132
80
133
cookies ( ) . set ( {
@@ -88,12 +141,33 @@ export const addGiftCertificateToCart = async (data: FormData) => {
88
141
89
142
revalidateTag ( TAGS . cart ) ;
90
143
91
- return { status : 'success' , data : cart } ;
144
+ return CartResponseSchema . parse ( {
145
+ status : 'success' ,
146
+ data : cart ,
147
+ } ) ;
92
148
} catch ( error : unknown ) {
149
+ if ( error instanceof z . ZodError ) {
150
+ // Handle validation errors
151
+ const errorMessage = error . errors
152
+ . map ( ( err ) => `${ err . path . join ( '.' ) } : ${ err . message } ` )
153
+ . join ( ', ' ) ;
154
+
155
+ return CartResponseSchema . parse ( {
156
+ status : 'error' ,
157
+ error : errorMessage ,
158
+ } ) ;
159
+ }
160
+
93
161
if ( error instanceof Error ) {
94
- return { status : 'error' , error : error . message } ;
162
+ return CartResponseSchema . parse ( {
163
+ status : 'error' ,
164
+ error : error . message ,
165
+ } ) ;
95
166
}
96
167
97
- return { status : 'error' , error : t ( 'error' ) } ;
168
+ return CartResponseSchema . parse ( {
169
+ status : 'error' ,
170
+ error : t ( 'error' ) ,
171
+ } ) ;
98
172
}
99
- } ;
173
+ }
0 commit comments