1
- import { useContext , useEffect } from 'react' ;
1
+ import { ChangeEvent } from 'react' ;
2
2
import { useMutation , useQuery } from '@apollo/client' ;
3
3
import Link from 'next/link' ;
4
4
import Image from 'next/image' ;
5
5
import { useRouter } from 'next/router' ;
6
6
import { v4 as uuidv4 } from 'uuid' ;
7
7
8
- import { CartContext } from '@/stores/CartProvider ' ;
8
+ import useCartStore , { RootObject , Product } from '@/stores/cart ' ;
9
9
import Button from '@/components/UI/Button.component' ;
10
- import LoadingSpinner from '../LoadingSpinner/LoadingSpinner.component' ;
11
10
12
11
import {
13
12
getFormattedCart ,
14
- getUpdatedItems ,
15
13
handleQuantityChange ,
16
- IProductRootObject ,
17
14
} from '@/utils/functions/functions' ;
18
15
19
16
import { GET_CART } from '@/utils/gql/GQL_QUERIES' ;
20
17
import { UPDATE_CART } from '@/utils/gql/GQL_MUTATIONS' ;
21
18
22
19
const CartContents = ( ) => {
23
20
const router = useRouter ( ) ;
24
- const { setCart } = useContext ( CartContext ) ;
21
+ const { cart , setCart } = useCartStore ( ) ;
25
22
const isCheckoutPage = router . pathname === '/kasse' ;
26
23
27
- const { data , refetch } = useQuery ( GET_CART , {
24
+ useQuery ( GET_CART , {
28
25
notifyOnNetworkStatusChange : true ,
29
- onCompleted : ( ) => {
30
- const updatedCart = getFormattedCart ( data ) ;
31
- if ( ! updatedCart && ! data . cart . contents . nodes . length ) {
32
- localStorage . removeItem ( 'woocommerce-cart' ) ;
33
- setCart ( null ) ;
34
- return ;
26
+ onCompleted : ( data ) => {
27
+ // Only update if there's a significant difference to avoid unnecessary re-renders
28
+ const updatedCart = getFormattedCart ( data ) as RootObject | undefined ;
29
+ if ( ! cart || cart . totalProductsCount !== updatedCart ?. totalProductsCount ) {
30
+ setCart ( updatedCart || null ) ;
35
31
}
36
- localStorage . setItem ( 'woocommerce-cart' , JSON . stringify ( updatedCart ) ) ;
37
- setCart ( updatedCart ) ;
38
32
} ,
39
33
} ) ;
40
34
41
- const [ updateCart , { loading : updateCartProcessing } ] = useMutation (
42
- UPDATE_CART ,
43
- {
44
- onCompleted : ( ) => {
45
- refetch ( ) ;
46
- setTimeout ( ( ) => {
47
- refetch ( ) ;
48
- } , 3000 ) ;
49
- } ,
50
- } ,
51
- ) ;
52
-
53
- const handleRemoveProductClick = (
54
- cartKey : string ,
55
- products : IProductRootObject [ ] ,
56
- ) => {
57
- if ( products ?. length ) {
58
- const updatedItems = getUpdatedItems ( products , 0 , cartKey ) ;
59
- updateCart ( {
60
- variables : {
61
- input : {
62
- clientMutationId : uuidv4 ( ) ,
63
- items : updatedItems ,
64
- } ,
65
- } ,
66
- } ) ;
67
- }
68
- refetch ( ) ;
69
- setTimeout ( ( ) => {
70
- refetch ( ) ;
71
- } , 3000 ) ;
72
- } ;
73
-
74
- useEffect ( ( ) => {
75
- refetch ( ) ;
76
- } , [ refetch ] ) ;
35
+ const [ updateCart ] = useMutation ( UPDATE_CART ) ;
77
36
78
- const cartTotal = data ?. cart ?. total || '0' ;
37
+ const handleRemoveProductClick = ( cartKey : string ) => {
38
+ // Update local state
39
+ useCartStore . getState ( ) . removeProduct ( cartKey ) ;
79
40
80
- const getUnitPrice = ( subtotal : string , quantity : number ) => {
81
- const numericSubtotal = parseFloat ( subtotal . replace ( / [ ^ 0 - 9 . - ] + / g, '' ) ) ;
82
- return isNaN ( numericSubtotal )
83
- ? 'N/A'
84
- : ( numericSubtotal / quantity ) . toFixed ( 2 ) ;
41
+ // Update remote state in background
42
+ updateCart ( {
43
+ variables : {
44
+ input : {
45
+ clientMutationId : uuidv4 ( ) ,
46
+ items : [ {
47
+ key : cartKey ,
48
+ quantity : 0
49
+ } ] ,
50
+ } ,
51
+ } ,
52
+ } ) ;
85
53
} ;
86
54
87
55
return (
88
56
< div className = "container mx-auto px-4 py-8" >
89
- { data ?. cart ?. contents ?. nodes ?. length ? (
57
+ { cart ?. products ?. length ? (
90
58
< >
91
59
< div className = "bg-white rounded-lg p-6 mb-8 md:w-full" >
92
- { data . cart . contents . nodes . map ( ( item : IProductRootObject ) => (
60
+ { cart . products . map ( ( item : Product ) => (
93
61
< div
94
- key = { item . key }
62
+ key = { item . cartKey }
95
63
className = "flex items-center border-b border-gray-200 py-4"
96
64
>
97
65
< div className = "flex-shrink-0 w-24 h-24 relative hidden md:block" >
98
66
< Image
99
- src = {
100
- item . product . node . image ?. sourceUrl || '/placeholder.png'
101
- }
102
- alt = { item . product . node . name }
67
+ src = { item . image ?. sourceUrl || '/placeholder.png' }
68
+ alt = { item . name }
103
69
layout = "fill"
104
70
objectFit = "cover"
105
71
className = "rounded"
106
72
/>
107
73
</ div >
108
74
< div className = "flex-grow ml-4" >
109
75
< h2 className = "text-lg font-semibold" >
110
- { item . product . node . name }
76
+ { item . name }
111
77
</ h2 >
112
78
< p className = "text-gray-600" >
113
- kr { getUnitPrice ( item . subtotal , item . quantity ) }
79
+ kr { item . price }
114
80
</ p >
115
81
</ div >
116
82
< div className = "flex items-center" >
117
83
< input
118
84
type = "number"
119
85
min = "1"
120
- value = { item . quantity }
121
- onChange = { ( event ) => {
86
+ value = { item . qty }
87
+ onChange = { ( event : ChangeEvent < HTMLInputElement > ) => {
88
+ const newQty = parseInt ( event . target . value , 10 ) ;
89
+ if ( isNaN ( newQty ) || newQty < 1 ) return ;
90
+
91
+ // Update local state
92
+ useCartStore . getState ( ) . updateProductQuantity ( item . cartKey , newQty ) ;
93
+
94
+ // Update remote state in background
122
95
handleQuantityChange (
123
96
event ,
124
- item . key ,
125
- data . cart . contents . nodes ,
126
- updateCart ,
127
- updateCartProcessing ,
97
+ item . cartKey ,
98
+ newQty ,
99
+ updateCart
128
100
) ;
129
101
} }
130
102
className = "w-16 px-2 py-1 text-center border border-gray-300 rounded mr-2"
131
103
/>
132
104
< Button
133
- handleButtonClick = { ( ) =>
134
- handleRemoveProductClick (
135
- item . key ,
136
- data . cart . contents . nodes ,
137
- )
138
- }
105
+ handleButtonClick = { ( ) => handleRemoveProductClick ( item . cartKey ) }
139
106
variant = "secondary"
140
- buttonDisabled = { updateCartProcessing }
141
107
>
142
108
Fjern
143
109
</ Button >
144
110
</ div >
145
111
< div className = "ml-4" >
146
- < p className = "text-lg font-semibold" > { item . subtotal } </ p >
112
+ < p className = "text-lg font-semibold" > { item . totalPrice } </ p >
147
113
</ div >
148
114
</ div >
149
115
) ) }
150
116
</ div >
151
117
< div className = "bg-white rounded-lg p-6 md:w-full" >
152
118
< div className = "flex justify-end mb-4" >
153
119
< span className = "font-semibold pr-2" > Subtotal:</ span >
154
- < span > { cartTotal } </ span >
120
+ < span > { cart . totalProductsPrice } </ span >
155
121
</ div >
156
122
{ ! isCheckoutPage && (
157
123
< div className = "flex justify-center mb-4" >
@@ -172,14 +138,6 @@ const CartContents = () => {
172
138
</ Link >
173
139
</ div >
174
140
) }
175
- { updateCartProcessing && (
176
- < div className = "fixed inset-0 flex items-center justify-center bg-black bg-opacity-50" >
177
- < div className = "bg-white p-4 rounded-lg" >
178
- < p className = "text-lg mb-2" > Oppdaterer handlekurv...</ p >
179
- < LoadingSpinner />
180
- </ div >
181
- </ div >
182
- ) }
183
141
</ div >
184
142
) ;
185
143
} ;
0 commit comments