1
1
'use client' ;
2
2
3
+ import UserFields from '@/components/UserFields/UserFields' ;
4
+ import useUserFields from '@/components/UserFields/hooks/useUserFields' ;
3
5
import ButtonWithLoading from '@/components/common/ButtonWithLoading' ;
6
+ import { authClient } from '@/graphql/clients/authClient' ;
4
7
import {
5
8
EMPTY_CART_MUTATION ,
6
9
UPDATE_SHIPPING_METHOD ,
@@ -9,8 +12,11 @@ import {
9
12
CHECKOUT_MUTATION ,
10
13
GET_PAYMENT_GATEWAYS ,
11
14
} from '@/graphql/queries/checkout' ;
15
+ import { GET_CUSTOMER_BILLING } from '@/graphql/queries/customer' ;
12
16
import {
13
17
CheckoutMutation ,
18
+ GetCustomerBillingQuery ,
19
+ GetCustomerBillingQueryVariables ,
14
20
GetPaymentGatewaysQuery ,
15
21
RemoveItemsFromCartMutation ,
16
22
ShippingRate ,
@@ -20,6 +26,8 @@ import useCartQuery from '@/hooks/useCartQuery';
20
26
import { redirect } from '@/navigation' ;
21
27
import { cartAtom } from '@/store/atoms' ;
22
28
import { useMutation , useQuery } from '@apollo/client' ;
29
+ import { yupResolver } from '@hookform/resolvers/yup' ;
30
+ import { LocationOnOutlined } from '@mui/icons-material' ;
23
31
import {
24
32
Card ,
25
33
CardActions ,
@@ -31,31 +39,55 @@ import {
31
39
import { useAtomValue } from 'jotai' ;
32
40
import { useTranslations } from 'next-intl' ;
33
41
import { useEffect } from 'react' ;
34
- import { Controller , useForm } from 'react-hook-form' ;
42
+ import { Controller , FormProvider , useForm } from 'react-hook-form' ;
43
+ import * as yup from 'yup' ;
35
44
import CheckoutBox from '../cart/components/CheckoutBox' ;
36
45
import CheckoutBoxSkeleton from '../cart/components/CheckoutBoxSkeleton' ;
37
46
import DiscountCode from '../cart/components/DiscountCode' ;
38
47
import AvailablePaymentGateways from './components/AvailablePaymentGateways' ;
39
48
import AvailableShippingMethods from './components/AvailableShippingMethods' ;
40
- import Billing from './components/Billing' ;
41
- import { authClient } from '@/graphql/clients/authClient' ;
49
+ import CardHeader from './components/CardHeader' ;
42
50
import Loading from './loading' ;
43
51
44
52
const Page = ( ) => {
45
53
const t = useTranslations ( ) ;
46
- const form = useForm ( ) ;
54
+
55
+ const { schema } = useUserFields ( ) ;
56
+
57
+ const checkoutSchema = yup . object ( {
58
+ paymentMethod : yup . string ( ) . nullable ( ) . required ( ) ,
59
+ customerNote : yup . string ( ) . max ( 500 ) . nullable ( ) ,
60
+ } ) ;
61
+
62
+ const form = useForm ( {
63
+ resolver : yupResolver ( schema . concat ( checkoutSchema ) ) ,
64
+ } ) ;
65
+
47
66
const content = useAtomValue ( cartAtom ) ;
48
67
49
- const gatewaysQuery = useQuery < GetPaymentGatewaysQuery > (
50
- GET_PAYMENT_GATEWAYS ,
51
- {
52
- onCompleted : ( data ) => {
53
- form . reset ( {
54
- paymentMethod : data ?. paymentGateways ?. nodes ?. [ 0 ] . id ,
55
- } ) ;
56
- } ,
57
- } ,
58
- ) ;
68
+ const gatewaysQuery = useQuery < GetPaymentGatewaysQuery > ( GET_PAYMENT_GATEWAYS ) ;
69
+ const customer = useQuery <
70
+ GetCustomerBillingQuery ,
71
+ GetCustomerBillingQueryVariables
72
+ > ( GET_CUSTOMER_BILLING , {
73
+ client : authClient ,
74
+ } ) ;
75
+
76
+ useEffect ( ( ) => {
77
+ if (
78
+ ! customer . loading &&
79
+ ! ! customer . data &&
80
+ ! gatewaysQuery . loading &&
81
+ ! ! gatewaysQuery . data
82
+ ) {
83
+ const billing = customer . data . customer ?. billing ! ;
84
+ delete billing . __typename ;
85
+ form . reset ( {
86
+ ...billing ,
87
+ paymentMethod : gatewaysQuery . data ?. paymentGateways ?. nodes ?. [ 0 ] . id ! ,
88
+ } ) ;
89
+ }
90
+ } , [ gatewaysQuery . loading , customer . loading ] ) ;
59
91
60
92
const { loading, refetch } = useCartQuery ( ) ;
61
93
@@ -104,10 +136,13 @@ const Page = () => {
104
136
} ;
105
137
106
138
const onSubmit = async ( payload : any ) => {
139
+ const { customerNote, paymentMethod, ...billing } = payload ;
107
140
const { data } = await checkout ( {
108
141
variables : {
109
- customerNote : payload . customerNote ,
110
- paymentMethod : payload . paymentMethod ,
142
+ customerNote : customerNote ,
143
+ paymentMethod : paymentMethod ,
144
+ billing : billing ,
145
+ shipping : billing ,
111
146
} ,
112
147
} ) ;
113
148
@@ -120,101 +155,121 @@ const Page = () => {
120
155
} ;
121
156
122
157
const isCartLoading = loading || shippingMethodLoading ;
123
- const isButtonLoading = isCartLoading || checkoutLoading || emptyCartLoading ;
158
+ const isButtonLoading =
159
+ isCartLoading || customer . loading || checkoutLoading || emptyCartLoading ;
124
160
125
161
return (
126
- < Grid
127
- container
128
- spacing = { 2 }
129
- position = "relative"
130
- component = "form"
131
- onSubmit = { form . handleSubmit ( onSubmit ) }
132
- >
133
- < Grid item lg = { 9 } md = { 6 } xs = { 12 } >
134
- < Stack spacing = { 3 } >
135
- < AvailableShippingMethods
136
- isFree = { isFree }
137
- rates = { notFreeRates }
138
- defaultValue = { content . chosenShippingMethods ?. [ 0 ] }
139
- onChange = { onShippingMethodChange }
140
- />
141
-
142
- < Billing />
143
- < Controller
144
- control = { form . control }
145
- name = "customerNote"
146
- render = { ( {
147
- field : { name, value, onChange } ,
148
- fieldState : { error } ,
149
- } ) => {
150
- return (
151
- < TextField
152
- onChange = { onChange }
153
- name = { name }
154
- multiline
155
- rows = { 4 }
156
- value = { value }
157
- variant = "outlined"
158
- fullWidth
159
- placeholder = { t ( 'pages.checkout.fields.description' ) }
160
- error = { ! ! error ?. message }
161
- helperText = { error ?. message ?. toString ( ) }
162
- />
163
- ) ;
164
- } }
165
- />
166
-
167
- < Controller
168
- name = "paymentMethod"
169
- control = { form . control }
170
- render = { ( props ) => {
171
- const {
172
- field : { value, onChange } ,
173
- } = props ;
174
- return (
175
- < AvailablePaymentGateways
176
- value = { value }
177
- onChange = { onChange }
178
- items = { gatewaysQuery . data }
179
- />
180
- ) ;
181
- } }
182
- />
183
- </ Stack >
184
- </ Grid >
162
+ < FormProvider { ...form } >
163
+ < Grid
164
+ container
165
+ spacing = { 2 }
166
+ position = "relative"
167
+ component = "form"
168
+ onSubmit = { form . handleSubmit ( onSubmit ) }
169
+ >
170
+ < Grid item lg = { 9 } md = { 6 } xs = { 12 } >
171
+ < Stack spacing = { 3 } >
172
+ < AvailableShippingMethods
173
+ isFree = { isFree }
174
+ rates = { notFreeRates }
175
+ defaultValue = { content . chosenShippingMethods ?. [ 0 ] }
176
+ onChange = { onShippingMethodChange }
177
+ />
178
+
179
+ < Card variant = "outlined" >
180
+ < CardHeader
181
+ title = { t ( 'pages.checkout.shipmentTo' ) }
182
+ icon = { LocationOnOutlined }
183
+ />
184
+
185
+ < CardContent >
186
+ < Grid container spacing = { 2 } >
187
+ < UserFields
188
+ loading = { customer . loading }
189
+ disabled = {
190
+ checkoutLoading || customer . loading || emptyCartLoading
191
+ }
192
+ />
193
+ </ Grid >
194
+ </ CardContent >
195
+ </ Card >
185
196
186
- < Grid item lg = { 3 } md = { 6 } xs = { 12 } >
187
- < Stack spacing = { 3 } >
188
- < Card variant = "outlined" >
189
- < CardContent >
190
- < DiscountCode />
191
- </ CardContent >
192
- </ Card >
193
-
194
- < Card variant = "outlined" >
195
- < CardContent >
196
- { isCartLoading ? (
197
- < CheckoutBoxSkeleton />
198
- ) : (
199
- < CheckoutBox content = { content } />
200
- ) }
201
- </ CardContent >
202
- < CardActions >
203
- < ButtonWithLoading
204
- isLoading = { isButtonLoading }
205
- type = "submit"
206
- variant = "contained"
207
- color = "primary"
208
- fullWidth
209
- size = "large"
210
- >
211
- { t ( 'pages.checkout.buttons.placeOrder' ) }
212
- </ ButtonWithLoading >
213
- </ CardActions >
214
- </ Card >
215
- </ Stack >
197
+ < Controller
198
+ control = { form . control }
199
+ name = "customerNote"
200
+ render = { ( {
201
+ field : { name, value, onChange } ,
202
+ fieldState : { error } ,
203
+ } ) => {
204
+ return (
205
+ < TextField
206
+ onChange = { onChange }
207
+ name = { name }
208
+ multiline
209
+ rows = { 4 }
210
+ value = { value }
211
+ variant = "outlined"
212
+ fullWidth
213
+ placeholder = { t ( 'pages.checkout.fields.description' ) }
214
+ error = { ! ! error ?. message }
215
+ helperText = { error ?. message ?. toString ( ) }
216
+ />
217
+ ) ;
218
+ } }
219
+ />
220
+
221
+ < Controller
222
+ name = "paymentMethod"
223
+ control = { form . control }
224
+ render = { ( props ) => {
225
+ const {
226
+ field : { value, onChange } ,
227
+ } = props ;
228
+ return (
229
+ < AvailablePaymentGateways
230
+ value = { value }
231
+ onChange = { onChange }
232
+ items = { gatewaysQuery . data }
233
+ />
234
+ ) ;
235
+ } }
236
+ />
237
+ </ Stack >
238
+ </ Grid >
239
+
240
+ < Grid item lg = { 3 } md = { 6 } xs = { 12 } >
241
+ < Stack spacing = { 3 } >
242
+ < Card variant = "outlined" >
243
+ < CardContent >
244
+ < DiscountCode />
245
+ </ CardContent >
246
+ </ Card >
247
+
248
+ < Card variant = "outlined" >
249
+ < CardContent >
250
+ { isCartLoading ? (
251
+ < CheckoutBoxSkeleton />
252
+ ) : (
253
+ < CheckoutBox content = { content } />
254
+ ) }
255
+ </ CardContent >
256
+ < CardActions >
257
+ < ButtonWithLoading
258
+ isLoading = { isButtonLoading }
259
+ type = "submit"
260
+ variant = "contained"
261
+ color = "primary"
262
+ fullWidth
263
+ size = "large"
264
+ >
265
+ { t ( 'pages.checkout.buttons.placeOrder' ) }
266
+ </ ButtonWithLoading >
267
+ </ CardActions >
268
+ </ Card >
269
+ </ Stack >
270
+ </ Grid >
216
271
</ Grid >
217
- </ Grid >
272
+ </ FormProvider >
218
273
) ;
219
274
} ;
220
275
0 commit comments