@webbycrown/webbycommerce 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -3
- package/admin/app.js +3 -0
- package/admin/jsconfig.json +20 -0
- package/admin/src/components/ApiCollectionsContent.jsx +4626 -0
- package/admin/src/components/CompareContent.jsx +300 -0
- package/admin/src/components/ConfigureContent.jsx +407 -0
- package/admin/src/components/Initializer.jsx +64 -0
- package/admin/src/components/LoginRegisterContent.jsx +280 -0
- package/admin/src/components/PluginIcon.jsx +6 -0
- package/admin/src/components/ShippingTypeContent.jsx +230 -0
- package/admin/src/components/SmtpContent.jsx +316 -0
- package/admin/src/components/WishlistContent.jsx +273 -0
- package/admin/src/index.js +81 -0
- package/admin/src/pages/ApiCollections.jsx +169 -0
- package/admin/src/pages/Configure.jsx +55 -0
- package/admin/src/pages/Settings.jsx +93 -0
- package/admin/src/pluginId.js +4 -0
- package/{dist/_chunks/en-CiQ97iC8.js → admin/src/translations/en.json} +712 -574
- package/bin/setup.js +50 -3
- package/package.json +14 -13
- package/server/bootstrap.js +3 -0
- package/server/register.js +3 -0
- package/server/src/bootstrap.js +3826 -0
- package/server/src/components/content-block.json +37 -0
- package/server/src/components/shipping-zone-location.json +27 -0
- package/server/src/config/index.js +7 -0
- package/server/src/content-types/address/index.js +7 -0
- package/server/src/content-types/address/schema.json +74 -0
- package/server/src/content-types/cart/index.js +61 -0
- package/server/src/content-types/cart-item/index.js +79 -0
- package/server/src/content-types/compare.js +73 -0
- package/server/src/content-types/coupon/index.js +7 -0
- package/server/src/content-types/coupon/schema.json +67 -0
- package/server/src/content-types/index.js +42 -0
- package/server/src/content-types/order/index.js +7 -0
- package/server/src/content-types/order/schema.json +121 -0
- package/server/src/content-types/payment-transaction/index.js +7 -0
- package/server/src/content-types/payment-transaction/schema.json +73 -0
- package/server/src/content-types/product/index.js +7 -0
- package/server/src/content-types/product/schema.json +104 -0
- package/server/src/content-types/product-attribute/index.js +7 -0
- package/server/src/content-types/product-attribute/schema.json +80 -0
- package/server/src/content-types/product-attribute-value/index.js +7 -0
- package/server/src/content-types/product-attribute-value/schema.json +52 -0
- package/server/src/content-types/product-category/index.js +7 -0
- package/server/src/content-types/product-category/schema.json +54 -0
- package/server/src/content-types/product-tag/index.js +7 -0
- package/server/src/content-types/product-tag/schema.json +38 -0
- package/server/src/content-types/product-variation/index.js +7 -0
- package/server/src/content-types/product-variation/schema.json +74 -0
- package/server/src/content-types/shipping-method/index.js +7 -0
- package/server/src/content-types/shipping-method/schema.json +91 -0
- package/server/src/content-types/shipping-rate/index.js +7 -0
- package/server/src/content-types/shipping-rate/schema.json +73 -0
- package/server/src/content-types/shipping-rule/index.js +7 -0
- package/server/src/content-types/shipping-rule/schema.json +84 -0
- package/server/src/content-types/shipping-zone/index.js +7 -0
- package/server/src/content-types/shipping-zone/schema.json +57 -0
- package/server/src/content-types/wishlist.js +66 -0
- package/server/src/controllers/address.js +374 -0
- package/server/src/controllers/auth.js +1409 -0
- package/server/src/controllers/cart.js +337 -0
- package/server/src/controllers/category.js +388 -0
- package/server/src/controllers/compare.js +246 -0
- package/server/src/controllers/controller.js +168 -0
- package/server/src/controllers/ecommerce.js +20 -0
- package/server/src/controllers/index.js +34 -0
- package/server/src/controllers/order.js +1100 -0
- package/server/src/controllers/payment.js +243 -0
- package/server/src/controllers/product.js +1006 -0
- package/server/src/controllers/productTag.js +370 -0
- package/server/src/controllers/productVariation.js +181 -0
- package/server/src/controllers/shipping.js +1046 -0
- package/server/src/controllers/wishlist.js +332 -0
- package/server/src/destroy.js +6 -0
- package/server/src/index.js +26 -0
- package/server/src/middlewares/index.js +4 -0
- package/server/src/policies/index.js +4 -0
- package/server/src/register.js +67 -0
- package/server/src/routes/index.js +1130 -0
- package/server/src/services/cart.js +531 -0
- package/server/src/services/compare.js +300 -0
- package/server/src/services/index.js +16 -0
- package/server/src/services/service.js +19 -0
- package/server/src/services/shipping.js +513 -0
- package/server/src/services/wishlist.js +238 -0
- package/server/src/utils/check-ecommerce-permission.js +204 -0
- package/server/src/utils/extend-user-schema.js +161 -0
- package/server/src/utils/seed-data.js +639 -0
- package/server/src/utils/send-email.js +98 -0
- package/strapi-server.js +1 -6
- package/dist/_chunks/Settings-DZXAkI24.js +0 -31539
- package/dist/_chunks/Settings-yLx-YvVy.mjs +0 -31520
- package/dist/_chunks/en-DE15m4xZ.mjs +0 -574
- package/dist/_chunks/index-CXGrFKp6.mjs +0 -128
- package/dist/_chunks/index-DgocXUgC.js +0 -127
- package/dist/admin/index.js +0 -3
- package/dist/admin/index.mjs +0 -4
- package/dist/robots.txt +0 -3
- package/dist/server/index.js +0 -27078
- package/dist/uploads/.gitkeep +0 -0
- package/dist/uploads/accessories_category_2a5631094b.jpeg +0 -0
- package/dist/uploads/beauty_personal_care_category_57f8a8f1e3.jpeg +0 -0
- package/dist/uploads/books_category_a9a253eada.jpeg +0 -0
- package/dist/uploads/classic_cotton_tshirt_1_cd713425f6.png +0 -0
- package/dist/uploads/clothing_category_d5c60ef07b.jpeg +0 -0
- package/dist/uploads/daviddoe_strapi_adbcd41787.jpeg +0 -0
- package/dist/uploads/electronics_category_fc3e5ef571.jpeg +0 -0
- package/dist/uploads/ergonomic_office_chair_1_c751cffb07.png +0 -0
- package/dist/uploads/home_garden_category_4f6eb3f8d6.jpeg +0 -0
- package/dist/uploads/istockphoto_1188462138_612x612_11f295b9c0.jpg +0 -0
- package/dist/uploads/istockphoto_1188462138_612x612_396fb272fd.jpg +0 -0
- package/dist/uploads/large_daviddoe_strapi_adbcd41787.jpeg +0 -0
- package/dist/uploads/leather_travel_backpack_1_238bc1ae4d.png +0 -0
- package/dist/uploads/mechanical_keyboard_pro_1_0cd391a6ac.png +0 -0
- package/dist/uploads/medium_classic_cotton_tshirt_1_cd713425f6.png +0 -0
- package/dist/uploads/medium_daviddoe_strapi_adbcd41787.jpeg +0 -0
- package/dist/uploads/medium_ergonomic_office_chair_1_c751cffb07.png +0 -0
- package/dist/uploads/medium_leather_travel_backpack_1_238bc1ae4d.png +0 -0
- package/dist/uploads/medium_mechanical_keyboard_pro_1_0cd391a6ac.png +0 -0
- package/dist/uploads/medium_smart_watch_series_5_1_cdc2511fb7.png +0 -0
- package/dist/uploads/medium_smartphone_x_pro_1_c3f0cbd080.png +0 -0
- package/dist/uploads/medium_the_great_gatsby_special_1_2e7c76d997.png +0 -0
- package/dist/uploads/medium_wireless_headphones_1_fa75cd50c3.png +0 -0
- package/dist/uploads/medium_yoga_mat_premium_1_01f9a3b5fa.png +0 -0
- package/dist/uploads/predictive_maintenance_icons_industry_automation_600nw_2685943461_e18a8aa3b0.webp +0 -0
- package/dist/uploads/small_classic_cotton_tshirt_1_cd713425f6.png +0 -0
- package/dist/uploads/small_daviddoe_strapi_adbcd41787.jpeg +0 -0
- package/dist/uploads/small_ergonomic_office_chair_1_c751cffb07.png +0 -0
- package/dist/uploads/small_leather_travel_backpack_1_238bc1ae4d.png +0 -0
- package/dist/uploads/small_mechanical_keyboard_pro_1_0cd391a6ac.png +0 -0
- package/dist/uploads/small_smart_watch_series_5_1_cdc2511fb7.png +0 -0
- package/dist/uploads/small_smartphone_x_pro_1_c3f0cbd080.png +0 -0
- package/dist/uploads/small_the_great_gatsby_special_1_2e7c76d997.png +0 -0
- package/dist/uploads/small_wireless_headphones_1_fa75cd50c3.png +0 -0
- package/dist/uploads/small_yoga_mat_premium_1_01f9a3b5fa.png +0 -0
- package/dist/uploads/smart_watch_series_5_1_cdc2511fb7.png +0 -0
- package/dist/uploads/smartphone_x_pro_1_c3f0cbd080.png +0 -0
- package/dist/uploads/the_great_gatsby_special_1_2e7c76d997.png +0 -0
- package/dist/uploads/thumbnail_accessories_category_2a5631094b.jpeg +0 -0
- package/dist/uploads/thumbnail_beauty_personal_care_category_57f8a8f1e3.jpeg +0 -0
- package/dist/uploads/thumbnail_books_category_a9a253eada.jpeg +0 -0
- package/dist/uploads/thumbnail_classic_cotton_tshirt_1_cd713425f6.png +0 -0
- package/dist/uploads/thumbnail_clothing_category_d5c60ef07b.jpeg +0 -0
- package/dist/uploads/thumbnail_daviddoe_strapi_adbcd41787.jpeg +0 -0
- package/dist/uploads/thumbnail_electronics_category_fc3e5ef571.jpeg +0 -0
- package/dist/uploads/thumbnail_ergonomic_office_chair_1_c751cffb07.png +0 -0
- package/dist/uploads/thumbnail_home_garden_category_4f6eb3f8d6.jpeg +0 -0
- package/dist/uploads/thumbnail_istockphoto_1188462138_612x612_11f295b9c0.jpg +0 -0
- package/dist/uploads/thumbnail_istockphoto_1188462138_612x612_396fb272fd.jpg +0 -0
- package/dist/uploads/thumbnail_leather_travel_backpack_1_238bc1ae4d.png +0 -0
- package/dist/uploads/thumbnail_mechanical_keyboard_pro_1_0cd391a6ac.png +0 -0
- package/dist/uploads/thumbnail_predictive_maintenance_icons_industry_automation_600nw_2685943461_e18a8aa3b0.webp +0 -0
- package/dist/uploads/thumbnail_smart_watch_series_5_1_cdc2511fb7.png +0 -0
- package/dist/uploads/thumbnail_smartphone_x_pro_1_c3f0cbd080.png +0 -0
- package/dist/uploads/thumbnail_the_great_gatsby_special_1_2e7c76d997.png +0 -0
- package/dist/uploads/thumbnail_wireless_headphones_1_fa75cd50c3.png +0 -0
- package/dist/uploads/thumbnail_yoga_mat_premium_1_01f9a3b5fa.png +0 -0
- package/dist/uploads/webby-commerce.png +0 -0
- package/dist/uploads/wireless_headphones_1_fa75cd50c3.png +0 -0
- package/dist/uploads/yoga_mat_premium_1_01f9a3b5fa.png +0 -0
- /package/{dist → server/src}/data/demo-data.json +0 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* wishlist controller
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { createCoreController } = require('@strapi/strapi').factories;
|
|
8
|
+
|
|
9
|
+
module.exports = createCoreController('plugin::webbycommerce.wishlist', ({ strapi }) => ({
|
|
10
|
+
async getWishlist(ctx) {
|
|
11
|
+
try {
|
|
12
|
+
const user = ctx.state.user;
|
|
13
|
+
if (!user) {
|
|
14
|
+
return ctx.unauthorized('Authentication required');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const wishlist = await strapi
|
|
18
|
+
.plugin('webbycommerce')
|
|
19
|
+
.service('wishlist')
|
|
20
|
+
.findUserWishlist(user.id);
|
|
21
|
+
|
|
22
|
+
if (!wishlist) {
|
|
23
|
+
// Return empty wishlist structure
|
|
24
|
+
return ctx.send({
|
|
25
|
+
data: {
|
|
26
|
+
id: null,
|
|
27
|
+
userId: user.id,
|
|
28
|
+
userEmail: user.email,
|
|
29
|
+
products: [],
|
|
30
|
+
isPublic: false,
|
|
31
|
+
name: null,
|
|
32
|
+
description: null,
|
|
33
|
+
},
|
|
34
|
+
meta: {
|
|
35
|
+
totalProducts: 0,
|
|
36
|
+
totalValue: 0,
|
|
37
|
+
categories: [],
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const stats = await strapi
|
|
43
|
+
.plugin('webbycommerce')
|
|
44
|
+
.service('wishlist')
|
|
45
|
+
.getWishlistStats(user.id);
|
|
46
|
+
|
|
47
|
+
ctx.send({
|
|
48
|
+
data: wishlist,
|
|
49
|
+
meta: stats,
|
|
50
|
+
});
|
|
51
|
+
} catch (error) {
|
|
52
|
+
strapi.log.error('Error fetching wishlist:', error);
|
|
53
|
+
ctx.badRequest('Failed to fetch wishlist', { error: error.message });
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
async addToWishlist(ctx) {
|
|
58
|
+
try {
|
|
59
|
+
const user = ctx.state.user;
|
|
60
|
+
if (!user) {
|
|
61
|
+
return ctx.unauthorized('Authentication required');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const { productId } = ctx.request.body;
|
|
65
|
+
if (!productId) {
|
|
66
|
+
return ctx.badRequest('Product ID is required');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const wishlist = await strapi
|
|
70
|
+
.plugin('webbycommerce')
|
|
71
|
+
.service('wishlist')
|
|
72
|
+
.addProductToWishlist(user.id, user.email, productId);
|
|
73
|
+
|
|
74
|
+
const stats = await strapi
|
|
75
|
+
.plugin('webbycommerce')
|
|
76
|
+
.service('wishlist')
|
|
77
|
+
.getWishlistStats(user.id);
|
|
78
|
+
|
|
79
|
+
ctx.send({
|
|
80
|
+
data: wishlist,
|
|
81
|
+
meta: stats,
|
|
82
|
+
message: 'Product added to wishlist successfully',
|
|
83
|
+
});
|
|
84
|
+
} catch (error) {
|
|
85
|
+
strapi.log.error('Error adding to wishlist:', error);
|
|
86
|
+
ctx.badRequest('Failed to add product to wishlist', { error: error.message });
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
async removeFromWishlist(ctx) {
|
|
91
|
+
try {
|
|
92
|
+
const user = ctx.state.user;
|
|
93
|
+
if (!user) {
|
|
94
|
+
return ctx.unauthorized('Authentication required');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const { productId } = ctx.params;
|
|
98
|
+
if (!productId) {
|
|
99
|
+
return ctx.badRequest('Product ID is required');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const wishlist = await strapi
|
|
103
|
+
.plugin('webbycommerce')
|
|
104
|
+
.service('wishlist')
|
|
105
|
+
.removeProductFromWishlist(user.id, productId);
|
|
106
|
+
|
|
107
|
+
const stats = await strapi
|
|
108
|
+
.plugin('webbycommerce')
|
|
109
|
+
.service('wishlist')
|
|
110
|
+
.getWishlistStats(user.id);
|
|
111
|
+
|
|
112
|
+
ctx.send({
|
|
113
|
+
data: wishlist,
|
|
114
|
+
meta: stats,
|
|
115
|
+
message: 'Product removed from wishlist successfully',
|
|
116
|
+
});
|
|
117
|
+
} catch (error) {
|
|
118
|
+
strapi.log.error('Error removing from wishlist:', error);
|
|
119
|
+
ctx.badRequest('Failed to remove product from wishlist', { error: error.message });
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
async clearWishlist(ctx) {
|
|
124
|
+
try {
|
|
125
|
+
const user = ctx.state.user;
|
|
126
|
+
if (!user) {
|
|
127
|
+
return ctx.unauthorized('Authentication required');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const wishlist = await strapi
|
|
131
|
+
.plugin('webbycommerce')
|
|
132
|
+
.service('wishlist')
|
|
133
|
+
.clearWishlist(user.id);
|
|
134
|
+
|
|
135
|
+
ctx.send({
|
|
136
|
+
data: wishlist,
|
|
137
|
+
meta: {
|
|
138
|
+
totalProducts: 0,
|
|
139
|
+
totalValue: 0,
|
|
140
|
+
categories: [],
|
|
141
|
+
},
|
|
142
|
+
message: 'Wishlist cleared successfully',
|
|
143
|
+
});
|
|
144
|
+
} catch (error) {
|
|
145
|
+
strapi.log.error('Error clearing wishlist:', error);
|
|
146
|
+
ctx.badRequest('Failed to clear wishlist', { error: error.message });
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
async updateWishlist(ctx) {
|
|
151
|
+
try {
|
|
152
|
+
const user = ctx.state.user;
|
|
153
|
+
if (!user) {
|
|
154
|
+
return ctx.unauthorized('Authentication required');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const { name, description, isPublic } = ctx.request.body;
|
|
158
|
+
|
|
159
|
+
const wishlist = await strapi
|
|
160
|
+
.plugin('webbycommerce')
|
|
161
|
+
.service('wishlist')
|
|
162
|
+
.updateWishlist(user.id, { name, description, isPublic });
|
|
163
|
+
|
|
164
|
+
ctx.send({
|
|
165
|
+
data: wishlist,
|
|
166
|
+
message: 'Wishlist updated successfully',
|
|
167
|
+
});
|
|
168
|
+
} catch (error) {
|
|
169
|
+
strapi.log.error('Error updating wishlist:', error);
|
|
170
|
+
ctx.badRequest('Failed to update wishlist', { error: error.message });
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
async checkWishlistStatus(ctx) {
|
|
175
|
+
try {
|
|
176
|
+
const user = ctx.state.user;
|
|
177
|
+
if (!user) {
|
|
178
|
+
return ctx.unauthorized('Authentication required');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const { productIds } = ctx.query;
|
|
182
|
+
if (!productIds) {
|
|
183
|
+
return ctx.badRequest('Product IDs are required');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const wishlist = await strapi
|
|
187
|
+
.plugin('webbycommerce')
|
|
188
|
+
.service('wishlist')
|
|
189
|
+
.findUserWishlist(user.id);
|
|
190
|
+
|
|
191
|
+
const productIdArray = Array.isArray(productIds)
|
|
192
|
+
? productIds.map(id => parseInt(id))
|
|
193
|
+
: [parseInt(productIds)];
|
|
194
|
+
|
|
195
|
+
const inWishlist = {};
|
|
196
|
+
if (wishlist) {
|
|
197
|
+
productIdArray.forEach(productId => {
|
|
198
|
+
inWishlist[productId] = wishlist.products.some(product => product.id === productId);
|
|
199
|
+
});
|
|
200
|
+
} else {
|
|
201
|
+
productIdArray.forEach(productId => {
|
|
202
|
+
inWishlist[productId] = false;
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
ctx.send({
|
|
207
|
+
data: inWishlist,
|
|
208
|
+
});
|
|
209
|
+
} catch (error) {
|
|
210
|
+
strapi.log.error('Error checking wishlist status:', error);
|
|
211
|
+
ctx.badRequest('Failed to check wishlist status', { error: error.message });
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
async moveToCart(ctx) {
|
|
216
|
+
try {
|
|
217
|
+
const user = ctx.state.user;
|
|
218
|
+
if (!user) {
|
|
219
|
+
return ctx.unauthorized('Authentication required');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const { id } = ctx.params;
|
|
223
|
+
const { quantity = 1 } = ctx.request.body;
|
|
224
|
+
|
|
225
|
+
if (!id) {
|
|
226
|
+
return ctx.badRequest('Wishlist item ID is required');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Get wishlist service to find the product in wishlist
|
|
230
|
+
const wishlist = await strapi
|
|
231
|
+
.plugin('webbycommerce')
|
|
232
|
+
.service('wishlist')
|
|
233
|
+
.findUserWishlist(user.id);
|
|
234
|
+
|
|
235
|
+
if (!wishlist) {
|
|
236
|
+
return ctx.notFound('Wishlist not found');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Find the product in wishlist
|
|
240
|
+
const wishlistItem = wishlist.products.find(product => product.id === parseInt(id));
|
|
241
|
+
if (!wishlistItem) {
|
|
242
|
+
return ctx.notFound('Product not found in wishlist');
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Check if product exists and is available
|
|
246
|
+
const product = await strapi.db.query('plugin::webbycommerce.product').findOne({
|
|
247
|
+
where: { id: wishlistItem.id },
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
if (!product) {
|
|
251
|
+
return ctx.notFound('Product not found');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Log stock information for debugging
|
|
255
|
+
strapi.log.debug(`Product ${product.id} stock status: ${product.stock_status}, quantity: ${product.stock_quantity}, requested quantity: ${quantity}`);
|
|
256
|
+
|
|
257
|
+
// Check stock status and quantity
|
|
258
|
+
if (product.stock_status === 'out_of_stock') {
|
|
259
|
+
return ctx.badRequest('Product is currently out of stock');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (product.stock_status === 'on_backorder') {
|
|
263
|
+
// Allow backordered items but warn about delay
|
|
264
|
+
strapi.log.warn(`Moving backordered product ${product.id} (${product.name}) to cart for user ${user.id} - item is on backorder`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Check quantity only if stock_quantity is defined and we're not allowing unlimited stock
|
|
268
|
+
// If stock_quantity is null/undefined, assume unlimited stock (common for digital products)
|
|
269
|
+
if (product.stock_quantity !== null && product.stock_quantity !== undefined && product.stock_quantity < quantity) {
|
|
270
|
+
return ctx.badRequest(`Insufficient stock. Available: ${product.stock_quantity}, Requested: ${quantity}`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Check if item already exists in cart
|
|
274
|
+
const existingCartItem = await strapi.db.query('plugin::webbycommerce.cart-item').findOne({
|
|
275
|
+
where: {
|
|
276
|
+
user: user.id,
|
|
277
|
+
product: product.id,
|
|
278
|
+
},
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
let cartItem;
|
|
282
|
+
if (existingCartItem) {
|
|
283
|
+
// Update existing cart item
|
|
284
|
+
const newQuantity = existingCartItem.quantity + quantity;
|
|
285
|
+
|
|
286
|
+
// Check stock again for updated quantity (only if stock_quantity is defined)
|
|
287
|
+
if (product.stock_quantity !== null && product.stock_quantity !== undefined && product.stock_quantity < newQuantity) {
|
|
288
|
+
return ctx.badRequest(`Insufficient stock for updated quantity. Available: ${product.stock_quantity}, Total requested: ${newQuantity}`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
cartItem = await strapi.db.query('plugin::webbycommerce.cart-item').update({
|
|
292
|
+
where: { id: existingCartItem.id },
|
|
293
|
+
data: { quantity: newQuantity },
|
|
294
|
+
});
|
|
295
|
+
} else {
|
|
296
|
+
// Create new cart item
|
|
297
|
+
cartItem = await strapi.db.query('plugin::webbycommerce.cart-item').create({
|
|
298
|
+
data: {
|
|
299
|
+
user: user.id,
|
|
300
|
+
product: product.id,
|
|
301
|
+
quantity: quantity,
|
|
302
|
+
unit_price: product.price,
|
|
303
|
+
total_price: product.price * quantity,
|
|
304
|
+
},
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Remove from wishlist
|
|
309
|
+
await strapi
|
|
310
|
+
.plugin('webbycommerce')
|
|
311
|
+
.service('wishlist')
|
|
312
|
+
.removeProductFromWishlist(user.id, product.id);
|
|
313
|
+
|
|
314
|
+
ctx.send({
|
|
315
|
+
data: {
|
|
316
|
+
cart_item: {
|
|
317
|
+
id: cartItem.id,
|
|
318
|
+
product_id: product.id,
|
|
319
|
+
product_name: product.name,
|
|
320
|
+
quantity: cartItem.quantity,
|
|
321
|
+
unit_price: parseFloat(product.price),
|
|
322
|
+
total_price: parseFloat(product.price) * cartItem.quantity,
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
message: 'Product moved to cart successfully',
|
|
326
|
+
});
|
|
327
|
+
} catch (error) {
|
|
328
|
+
strapi.log.error('Error moving item to cart:', error);
|
|
329
|
+
ctx.badRequest('Failed to move item to cart', { error: error.message });
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
}));
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const register = require('./register');
|
|
4
|
+
const bootstrap = require('./bootstrap');
|
|
5
|
+
const destroy = require('./destroy');
|
|
6
|
+
const config = require('./config');
|
|
7
|
+
const controllers = require('./controllers');
|
|
8
|
+
const routes = require('./routes');
|
|
9
|
+
const services = require('./services');
|
|
10
|
+
const middlewares = require('./middlewares');
|
|
11
|
+
const policies = require('./policies');
|
|
12
|
+
const contentTypes = require('./content-types');
|
|
13
|
+
|
|
14
|
+
module.exports = {
|
|
15
|
+
register,
|
|
16
|
+
bootstrap,
|
|
17
|
+
destroy,
|
|
18
|
+
config,
|
|
19
|
+
controllers,
|
|
20
|
+
contentTypes,
|
|
21
|
+
policies,
|
|
22
|
+
middlewares,
|
|
23
|
+
routes,
|
|
24
|
+
services,
|
|
25
|
+
};
|
|
26
|
+
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = async ({ strapi }) => {
|
|
4
|
+
strapi.log.info('[webbycommerce] ========================================');
|
|
5
|
+
strapi.log.info('[webbycommerce] Registering plugin...');
|
|
6
|
+
|
|
7
|
+
// Register plugin components
|
|
8
|
+
try {
|
|
9
|
+
// Register shipping-zone-location component
|
|
10
|
+
const shippingZoneSchema = require('./components/shipping-zone-location.json');
|
|
11
|
+
const shippingZoneUid = 'plugin::webbycommerce.shipping-zone-location';
|
|
12
|
+
|
|
13
|
+
const existingShippingZone = strapi.get('components').get(shippingZoneUid);
|
|
14
|
+
if (!existingShippingZone) {
|
|
15
|
+
strapi.get('components').set(shippingZoneUid, {
|
|
16
|
+
...shippingZoneSchema,
|
|
17
|
+
uid: shippingZoneUid,
|
|
18
|
+
modelType: 'component',
|
|
19
|
+
modelName: 'shipping-zone-location',
|
|
20
|
+
globalId: 'ComponentPluginWebbycommerceShippingZoneLocation',
|
|
21
|
+
category: shippingZoneSchema.category || 'WebbyCommerce Shared',
|
|
22
|
+
});
|
|
23
|
+
strapi.log.info(`[webbycommerce] Component registered: ${shippingZoneUid}`);
|
|
24
|
+
} else {
|
|
25
|
+
strapi.log.info(`[webbycommerce] Component already exists: ${shippingZoneUid}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Register content-block component
|
|
29
|
+
const contentBlockSchema = require('./components/content-block.json');
|
|
30
|
+
const contentBlockUid = 'plugin::webbycommerce.content-block';
|
|
31
|
+
|
|
32
|
+
const existingContentBlock = strapi.get('components').get(contentBlockUid);
|
|
33
|
+
if (!existingContentBlock) {
|
|
34
|
+
strapi.get('components').set(contentBlockUid, {
|
|
35
|
+
...contentBlockSchema,
|
|
36
|
+
uid: contentBlockUid,
|
|
37
|
+
modelType: 'component',
|
|
38
|
+
modelName: 'content-block',
|
|
39
|
+
globalId: 'ComponentPluginWebbycommerceContentBlock',
|
|
40
|
+
category: contentBlockSchema.category || 'WebbyCommerce Shared',
|
|
41
|
+
});
|
|
42
|
+
strapi.log.info(`[webbycommerce] Component registered: ${contentBlockUid}`);
|
|
43
|
+
} else {
|
|
44
|
+
strapi.log.info(`[webbycommerce] Component already exists: ${contentBlockUid}`);
|
|
45
|
+
}
|
|
46
|
+
} catch (error) {
|
|
47
|
+
strapi.log.error('[webbycommerce] Failed to register component:', error.message);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Verify routes are loaded
|
|
51
|
+
try {
|
|
52
|
+
const routes = require('./routes');
|
|
53
|
+
strapi.log.info('[webbycommerce] Routes structure:', JSON.stringify({
|
|
54
|
+
hasAdmin: !!routes.admin,
|
|
55
|
+
hasContentApi: !!routes['content-api'],
|
|
56
|
+
adminRoutes: routes.admin?.routes?.length || 0,
|
|
57
|
+
contentApiRoutes: routes['content-api']?.routes?.length || 0,
|
|
58
|
+
}, null, 2));
|
|
59
|
+
} catch (error) {
|
|
60
|
+
strapi.log.error('[webbycommerce] Error loading routes:', error.message);
|
|
61
|
+
strapi.log.error('[webbycommerce] Error stack:', error.stack);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
strapi.log.info('[webbycommerce] Plugin registered successfully');
|
|
65
|
+
strapi.log.info('[webbycommerce] ========================================');
|
|
66
|
+
};
|
|
67
|
+
|