@@ -3,33 +3,85 @@ import {
3
3
doc ,
4
4
getDoc ,
5
5
setDoc ,
6
- serverTimestamp
6
+ serverTimestamp ,
7
+ runTransaction ,
7
8
} from 'firebase/firestore' ;
8
9
import type { Product } from '@/src/types/product' ;
9
10
11
+ // ✅ Updated: use Firestore transaction to safely update stock
12
+ async function updateProductStock ( productId : string , quantityDelta : number ) {
13
+ const productRef = doc ( db , 'products' , productId ) ;
14
+
15
+ try {
16
+ await runTransaction ( db , async ( transaction ) => {
17
+ const productDoc = await transaction . get ( productRef ) ;
18
+
19
+ if ( ! productDoc . exists ( ) ) {
20
+ throw new Error ( 'Product does not exist!' ) ;
21
+ }
22
+
23
+ const currentStock = productDoc . data ( ) . quantity || 0 ;
24
+ const newStock = currentStock + quantityDelta ;
25
+
26
+ if ( newStock < 0 ) {
27
+ throw new Error ( 'Not enough stock available' ) ;
28
+ }
29
+
30
+ transaction . update ( productRef , { quantity : newStock } ) ;
31
+ } ) ;
32
+ } catch ( error ) {
33
+ console . error ( 'Failed to update stock:' , error ) ;
34
+ throw error ;
35
+ }
36
+ }
10
37
11
38
export async function addProductToCart (
12
39
userId : string ,
13
40
product : Product ,
14
41
quantity : number
15
42
) {
16
43
const cartRef = doc ( db , 'carts' , userId ) ;
17
- const snapshot = await getDoc ( cartRef ) ;
44
+ const productRef = doc ( db , 'products' , product . id ) ;
45
+
46
+ const [ cartSnap , productSnap ] = await Promise . all ( [
47
+ getDoc ( cartRef ) ,
48
+ getDoc ( productRef ) ,
49
+ ] ) ;
50
+
51
+ if ( ! productSnap . exists ( ) ) {
52
+ throw new Error ( 'Product does not exist!' ) ;
53
+ }
54
+
55
+ const availableStock = productSnap . data ( ) . quantity || 0 ;
56
+
57
+ if ( quantity > availableStock ) {
58
+ throw new Error ( `Only ${ availableStock } of ${ product . name } left in stock` ) ;
59
+ }
60
+
18
61
let existingProducts : ( Product & { quantity : number } ) [ ] = [ ] ;
19
62
20
- if ( snapshot . exists ( ) ) {
21
- existingProducts = snapshot . data ( ) . products || [ ] ;
63
+ if ( cartSnap . exists ( ) ) {
64
+ existingProducts = cartSnap . data ( ) . products || [ ] ;
22
65
}
23
66
24
67
const updatedProducts = [ ...existingProducts ] ;
25
68
const index = updatedProducts . findIndex ( ( p ) => p . id === product . id ) ;
26
69
27
70
if ( index !== - 1 ) {
71
+ // ✅ We no longer need to pre-check cart + quantity vs stock here
28
72
updatedProducts [ index ] . quantity += quantity ;
29
73
} else {
30
74
updatedProducts . push ( { ...product , quantity } ) ;
31
75
}
32
76
77
+ // ✅ Still use the transaction to actually update stock safely
78
+ try {
79
+ await updateProductStock ( product . id , - quantity ) ;
80
+ } catch ( error ) {
81
+ console . error ( 'Error updating stock:' , error ) ;
82
+ throw new Error ( 'Failed to update stock' ) ;
83
+ }
84
+
33
85
await setDoc (
34
86
cartRef ,
35
87
{
@@ -42,6 +94,42 @@ export async function addProductToCart(
42
94
}
43
95
44
96
97
+ export async function removeProductFromCart (
98
+ userId : string ,
99
+ productId : string
100
+ ) {
101
+ const cartRef = doc ( db , 'carts' , userId ) ;
102
+ const snapshot = await getDoc ( cartRef ) ;
103
+
104
+ if ( ! snapshot . exists ( ) ) return ;
105
+
106
+ const existingProducts : ( Product & { quantity : number } ) [ ] =
107
+ snapshot . data ( ) . products || [ ] ;
108
+
109
+ const removedProduct = existingProducts . find ( ( p ) => p . id === productId ) ;
110
+
111
+ if ( ! removedProduct ) return ;
112
+
113
+ const updatedProducts = existingProducts . filter ( ( p ) => p . id !== productId ) ;
114
+
115
+ // ✅ Add product quantity back to stock
116
+ try {
117
+ await updateProductStock ( productId , removedProduct . quantity ) ;
118
+ } catch ( error ) {
119
+ console . error ( 'Error restoring stock on removal:' , error ) ;
120
+ }
121
+
122
+ await setDoc (
123
+ cartRef ,
124
+ {
125
+ customerId : userId ,
126
+ products : updatedProducts ,
127
+ updatedAt : serverTimestamp ( ) ,
128
+ } ,
129
+ { merge : true }
130
+ ) ;
131
+ }
132
+
45
133
export async function saveCartToFirestore (
46
134
userId : string ,
47
135
products : ( Product & { quantity : number } ) [ ]
@@ -70,4 +158,4 @@ export async function getCartFromFirestore(
70
158
}
71
159
72
160
return [ ] ;
73
- }
161
+ }
0 commit comments