@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.
Files changed (162) hide show
  1. package/README.md +26 -3
  2. package/admin/app.js +3 -0
  3. package/admin/jsconfig.json +20 -0
  4. package/admin/src/components/ApiCollectionsContent.jsx +4626 -0
  5. package/admin/src/components/CompareContent.jsx +300 -0
  6. package/admin/src/components/ConfigureContent.jsx +407 -0
  7. package/admin/src/components/Initializer.jsx +64 -0
  8. package/admin/src/components/LoginRegisterContent.jsx +280 -0
  9. package/admin/src/components/PluginIcon.jsx +6 -0
  10. package/admin/src/components/ShippingTypeContent.jsx +230 -0
  11. package/admin/src/components/SmtpContent.jsx +316 -0
  12. package/admin/src/components/WishlistContent.jsx +273 -0
  13. package/admin/src/index.js +81 -0
  14. package/admin/src/pages/ApiCollections.jsx +169 -0
  15. package/admin/src/pages/Configure.jsx +55 -0
  16. package/admin/src/pages/Settings.jsx +93 -0
  17. package/admin/src/pluginId.js +4 -0
  18. package/{dist/_chunks/en-CiQ97iC8.js → admin/src/translations/en.json} +712 -574
  19. package/bin/setup.js +50 -3
  20. package/package.json +14 -13
  21. package/server/bootstrap.js +3 -0
  22. package/server/register.js +3 -0
  23. package/server/src/bootstrap.js +3826 -0
  24. package/server/src/components/content-block.json +37 -0
  25. package/server/src/components/shipping-zone-location.json +27 -0
  26. package/server/src/config/index.js +7 -0
  27. package/server/src/content-types/address/index.js +7 -0
  28. package/server/src/content-types/address/schema.json +74 -0
  29. package/server/src/content-types/cart/index.js +61 -0
  30. package/server/src/content-types/cart-item/index.js +79 -0
  31. package/server/src/content-types/compare.js +73 -0
  32. package/server/src/content-types/coupon/index.js +7 -0
  33. package/server/src/content-types/coupon/schema.json +67 -0
  34. package/server/src/content-types/index.js +42 -0
  35. package/server/src/content-types/order/index.js +7 -0
  36. package/server/src/content-types/order/schema.json +121 -0
  37. package/server/src/content-types/payment-transaction/index.js +7 -0
  38. package/server/src/content-types/payment-transaction/schema.json +73 -0
  39. package/server/src/content-types/product/index.js +7 -0
  40. package/server/src/content-types/product/schema.json +104 -0
  41. package/server/src/content-types/product-attribute/index.js +7 -0
  42. package/server/src/content-types/product-attribute/schema.json +80 -0
  43. package/server/src/content-types/product-attribute-value/index.js +7 -0
  44. package/server/src/content-types/product-attribute-value/schema.json +52 -0
  45. package/server/src/content-types/product-category/index.js +7 -0
  46. package/server/src/content-types/product-category/schema.json +54 -0
  47. package/server/src/content-types/product-tag/index.js +7 -0
  48. package/server/src/content-types/product-tag/schema.json +38 -0
  49. package/server/src/content-types/product-variation/index.js +7 -0
  50. package/server/src/content-types/product-variation/schema.json +74 -0
  51. package/server/src/content-types/shipping-method/index.js +7 -0
  52. package/server/src/content-types/shipping-method/schema.json +91 -0
  53. package/server/src/content-types/shipping-rate/index.js +7 -0
  54. package/server/src/content-types/shipping-rate/schema.json +73 -0
  55. package/server/src/content-types/shipping-rule/index.js +7 -0
  56. package/server/src/content-types/shipping-rule/schema.json +84 -0
  57. package/server/src/content-types/shipping-zone/index.js +7 -0
  58. package/server/src/content-types/shipping-zone/schema.json +57 -0
  59. package/server/src/content-types/wishlist.js +66 -0
  60. package/server/src/controllers/address.js +374 -0
  61. package/server/src/controllers/auth.js +1409 -0
  62. package/server/src/controllers/cart.js +337 -0
  63. package/server/src/controllers/category.js +388 -0
  64. package/server/src/controllers/compare.js +246 -0
  65. package/server/src/controllers/controller.js +168 -0
  66. package/server/src/controllers/ecommerce.js +20 -0
  67. package/server/src/controllers/index.js +34 -0
  68. package/server/src/controllers/order.js +1100 -0
  69. package/server/src/controllers/payment.js +243 -0
  70. package/server/src/controllers/product.js +1006 -0
  71. package/server/src/controllers/productTag.js +370 -0
  72. package/server/src/controllers/productVariation.js +181 -0
  73. package/server/src/controllers/shipping.js +1046 -0
  74. package/server/src/controllers/wishlist.js +332 -0
  75. package/server/src/destroy.js +6 -0
  76. package/server/src/index.js +26 -0
  77. package/server/src/middlewares/index.js +4 -0
  78. package/server/src/policies/index.js +4 -0
  79. package/server/src/register.js +67 -0
  80. package/server/src/routes/index.js +1130 -0
  81. package/server/src/services/cart.js +531 -0
  82. package/server/src/services/compare.js +300 -0
  83. package/server/src/services/index.js +16 -0
  84. package/server/src/services/service.js +19 -0
  85. package/server/src/services/shipping.js +513 -0
  86. package/server/src/services/wishlist.js +238 -0
  87. package/server/src/utils/check-ecommerce-permission.js +204 -0
  88. package/server/src/utils/extend-user-schema.js +161 -0
  89. package/server/src/utils/seed-data.js +639 -0
  90. package/server/src/utils/send-email.js +98 -0
  91. package/strapi-server.js +1 -6
  92. package/dist/_chunks/Settings-DZXAkI24.js +0 -31539
  93. package/dist/_chunks/Settings-yLx-YvVy.mjs +0 -31520
  94. package/dist/_chunks/en-DE15m4xZ.mjs +0 -574
  95. package/dist/_chunks/index-CXGrFKp6.mjs +0 -128
  96. package/dist/_chunks/index-DgocXUgC.js +0 -127
  97. package/dist/admin/index.js +0 -3
  98. package/dist/admin/index.mjs +0 -4
  99. package/dist/robots.txt +0 -3
  100. package/dist/server/index.js +0 -27078
  101. package/dist/uploads/.gitkeep +0 -0
  102. package/dist/uploads/accessories_category_2a5631094b.jpeg +0 -0
  103. package/dist/uploads/beauty_personal_care_category_57f8a8f1e3.jpeg +0 -0
  104. package/dist/uploads/books_category_a9a253eada.jpeg +0 -0
  105. package/dist/uploads/classic_cotton_tshirt_1_cd713425f6.png +0 -0
  106. package/dist/uploads/clothing_category_d5c60ef07b.jpeg +0 -0
  107. package/dist/uploads/daviddoe_strapi_adbcd41787.jpeg +0 -0
  108. package/dist/uploads/electronics_category_fc3e5ef571.jpeg +0 -0
  109. package/dist/uploads/ergonomic_office_chair_1_c751cffb07.png +0 -0
  110. package/dist/uploads/home_garden_category_4f6eb3f8d6.jpeg +0 -0
  111. package/dist/uploads/istockphoto_1188462138_612x612_11f295b9c0.jpg +0 -0
  112. package/dist/uploads/istockphoto_1188462138_612x612_396fb272fd.jpg +0 -0
  113. package/dist/uploads/large_daviddoe_strapi_adbcd41787.jpeg +0 -0
  114. package/dist/uploads/leather_travel_backpack_1_238bc1ae4d.png +0 -0
  115. package/dist/uploads/mechanical_keyboard_pro_1_0cd391a6ac.png +0 -0
  116. package/dist/uploads/medium_classic_cotton_tshirt_1_cd713425f6.png +0 -0
  117. package/dist/uploads/medium_daviddoe_strapi_adbcd41787.jpeg +0 -0
  118. package/dist/uploads/medium_ergonomic_office_chair_1_c751cffb07.png +0 -0
  119. package/dist/uploads/medium_leather_travel_backpack_1_238bc1ae4d.png +0 -0
  120. package/dist/uploads/medium_mechanical_keyboard_pro_1_0cd391a6ac.png +0 -0
  121. package/dist/uploads/medium_smart_watch_series_5_1_cdc2511fb7.png +0 -0
  122. package/dist/uploads/medium_smartphone_x_pro_1_c3f0cbd080.png +0 -0
  123. package/dist/uploads/medium_the_great_gatsby_special_1_2e7c76d997.png +0 -0
  124. package/dist/uploads/medium_wireless_headphones_1_fa75cd50c3.png +0 -0
  125. package/dist/uploads/medium_yoga_mat_premium_1_01f9a3b5fa.png +0 -0
  126. package/dist/uploads/predictive_maintenance_icons_industry_automation_600nw_2685943461_e18a8aa3b0.webp +0 -0
  127. package/dist/uploads/small_classic_cotton_tshirt_1_cd713425f6.png +0 -0
  128. package/dist/uploads/small_daviddoe_strapi_adbcd41787.jpeg +0 -0
  129. package/dist/uploads/small_ergonomic_office_chair_1_c751cffb07.png +0 -0
  130. package/dist/uploads/small_leather_travel_backpack_1_238bc1ae4d.png +0 -0
  131. package/dist/uploads/small_mechanical_keyboard_pro_1_0cd391a6ac.png +0 -0
  132. package/dist/uploads/small_smart_watch_series_5_1_cdc2511fb7.png +0 -0
  133. package/dist/uploads/small_smartphone_x_pro_1_c3f0cbd080.png +0 -0
  134. package/dist/uploads/small_the_great_gatsby_special_1_2e7c76d997.png +0 -0
  135. package/dist/uploads/small_wireless_headphones_1_fa75cd50c3.png +0 -0
  136. package/dist/uploads/small_yoga_mat_premium_1_01f9a3b5fa.png +0 -0
  137. package/dist/uploads/smart_watch_series_5_1_cdc2511fb7.png +0 -0
  138. package/dist/uploads/smartphone_x_pro_1_c3f0cbd080.png +0 -0
  139. package/dist/uploads/the_great_gatsby_special_1_2e7c76d997.png +0 -0
  140. package/dist/uploads/thumbnail_accessories_category_2a5631094b.jpeg +0 -0
  141. package/dist/uploads/thumbnail_beauty_personal_care_category_57f8a8f1e3.jpeg +0 -0
  142. package/dist/uploads/thumbnail_books_category_a9a253eada.jpeg +0 -0
  143. package/dist/uploads/thumbnail_classic_cotton_tshirt_1_cd713425f6.png +0 -0
  144. package/dist/uploads/thumbnail_clothing_category_d5c60ef07b.jpeg +0 -0
  145. package/dist/uploads/thumbnail_daviddoe_strapi_adbcd41787.jpeg +0 -0
  146. package/dist/uploads/thumbnail_electronics_category_fc3e5ef571.jpeg +0 -0
  147. package/dist/uploads/thumbnail_ergonomic_office_chair_1_c751cffb07.png +0 -0
  148. package/dist/uploads/thumbnail_home_garden_category_4f6eb3f8d6.jpeg +0 -0
  149. package/dist/uploads/thumbnail_istockphoto_1188462138_612x612_11f295b9c0.jpg +0 -0
  150. package/dist/uploads/thumbnail_istockphoto_1188462138_612x612_396fb272fd.jpg +0 -0
  151. package/dist/uploads/thumbnail_leather_travel_backpack_1_238bc1ae4d.png +0 -0
  152. package/dist/uploads/thumbnail_mechanical_keyboard_pro_1_0cd391a6ac.png +0 -0
  153. package/dist/uploads/thumbnail_predictive_maintenance_icons_industry_automation_600nw_2685943461_e18a8aa3b0.webp +0 -0
  154. package/dist/uploads/thumbnail_smart_watch_series_5_1_cdc2511fb7.png +0 -0
  155. package/dist/uploads/thumbnail_smartphone_x_pro_1_c3f0cbd080.png +0 -0
  156. package/dist/uploads/thumbnail_the_great_gatsby_special_1_2e7c76d997.png +0 -0
  157. package/dist/uploads/thumbnail_wireless_headphones_1_fa75cd50c3.png +0 -0
  158. package/dist/uploads/thumbnail_yoga_mat_premium_1_01f9a3b5fa.png +0 -0
  159. package/dist/uploads/webby-commerce.png +0 -0
  160. package/dist/uploads/wireless_headphones_1_fa75cd50c3.png +0 -0
  161. package/dist/uploads/yoga_mat_premium_1_01f9a3b5fa.png +0 -0
  162. /package/{dist → server/src}/data/demo-data.json +0 -0
@@ -0,0 +1,64 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import { PLUGIN_ID } from '../pluginId';
3
+
4
+ // Fix for Strapi 5.x: Provide checkUserHasPermissions in multiple scopes
5
+ // This is a workaround for a known Strapi 5.x RBAC compatibility issue
6
+ const provideCheckUserHasPermissions = () => {
7
+ const defaultImpl = async () => true;
8
+
9
+ // Provide in multiple scopes to ensure it's accessible
10
+ if (typeof window !== 'undefined') {
11
+ if (!window.checkUserHasPermissions) {
12
+ window.checkUserHasPermissions = defaultImpl;
13
+ }
14
+ }
15
+
16
+ if (typeof globalThis !== 'undefined') {
17
+ if (!globalThis.checkUserHasPermissions) {
18
+ globalThis.checkUserHasPermissions = defaultImpl;
19
+ }
20
+ }
21
+
22
+ if (typeof global !== 'undefined') {
23
+ if (!global.checkUserHasPermissions) {
24
+ global.checkUserHasPermissions = defaultImpl;
25
+ }
26
+ }
27
+ };
28
+
29
+ // Provide immediately at module load
30
+ provideCheckUserHasPermissions();
31
+
32
+ const Initializer = () => {
33
+ const hasInitialized = useRef(false);
34
+
35
+ useEffect(() => {
36
+ if (!hasInitialized.current) {
37
+ hasInitialized.current = true;
38
+
39
+ // Ensure the function is available after React hydration
40
+ provideCheckUserHasPermissions();
41
+
42
+ // Also try to patch it into Strapi's app context if available
43
+ try {
44
+ // This will be called when the app context is available
45
+ const checkInterval = setInterval(() => {
46
+ if (window.strapi?.app && typeof window.strapi.app.checkUserHasPermissions === 'undefined') {
47
+ window.strapi.app.checkUserHasPermissions = async () => true;
48
+ clearInterval(checkInterval);
49
+ }
50
+ }, 100);
51
+
52
+ // Clear after 5 seconds to avoid infinite checking
53
+ setTimeout(() => clearInterval(checkInterval), 5000);
54
+ } catch (error) {
55
+ // Silently fail if we can't access Strapi context
56
+ }
57
+ }
58
+ }, []);
59
+
60
+ return null;
61
+ };
62
+
63
+ export default Initializer;
64
+
@@ -0,0 +1,280 @@
1
+ 'use strict';
2
+
3
+ import React, { useEffect, useState } from 'react';
4
+ import { useIntl } from 'react-intl';
5
+ import {
6
+ Box,
7
+ Flex,
8
+ Typography,
9
+ Button,
10
+ } from '@strapi/design-system';
11
+ import { useFetchClient } from '@strapi/admin/strapi-admin';
12
+ import { PLUGIN_ID } from '../pluginId';
13
+
14
+ const LoginRegisterContent = () => {
15
+ const { formatMessage } = useIntl();
16
+ const fetchClient = useFetchClient();
17
+ const [loading, setLoading] = useState(true);
18
+ const [saving, setSaving] = useState(false);
19
+ const [method, setMethod] = useState('default'); // 'default', 'otp', or 'both'
20
+ const [error, setError] = useState(null);
21
+ const [success, setSuccess] = useState(false);
22
+
23
+ useEffect(() => {
24
+ loadSettings();
25
+ }, []);
26
+
27
+ const loadSettings = async () => {
28
+ try {
29
+ setLoading(true);
30
+ setError(null);
31
+ const { data } = await fetchClient.get(`/webbycommerce/settings`);
32
+ if (data.loginRegisterMethod) {
33
+ setMethod(data.loginRegisterMethod);
34
+ }
35
+ } catch (err) {
36
+ console.error('Failed to load login/register settings:', err);
37
+ setError(
38
+ formatMessage({
39
+ id: `${PLUGIN_ID}.settings.loginRegister.load.error`,
40
+ defaultMessage: 'Failed to load settings.',
41
+ })
42
+ );
43
+ } finally {
44
+ setLoading(false);
45
+ }
46
+ };
47
+
48
+ const handleSave = async () => {
49
+ try {
50
+ setSaving(true);
51
+ setError(null);
52
+ setSuccess(false);
53
+
54
+ await fetchClient.put(`/webbycommerce/settings`, {
55
+ loginRegisterMethod: method,
56
+ });
57
+
58
+ setSuccess(true);
59
+ setTimeout(() => setSuccess(false), 3000);
60
+ } catch (err) {
61
+ console.error('Failed to save login/register settings:', err);
62
+ setError(
63
+ formatMessage({
64
+ id: `${PLUGIN_ID}.settings.loginRegister.save.error`,
65
+ defaultMessage: 'Failed to save settings.',
66
+ })
67
+ );
68
+ } finally {
69
+ setSaving(false);
70
+ }
71
+ };
72
+
73
+ if (loading) {
74
+ return (
75
+ <Box paddingTop={6}>
76
+ <Typography variant="omega" textColor="neutral600">
77
+ {formatMessage({
78
+ id: `${PLUGIN_ID}.settings.loginRegister.loading`,
79
+ defaultMessage: 'Loading settings...',
80
+ })}
81
+ </Typography>
82
+ </Box>
83
+ );
84
+ }
85
+
86
+ return (
87
+ <Box paddingTop={6}>
88
+ <Typography variant="delta" textColor="neutral800" fontWeight="bold" marginBottom={4}>
89
+ {formatMessage({
90
+ id: `${PLUGIN_ID}.settings.loginRegister.title`,
91
+ defaultMessage: 'Login/Register Configuration',
92
+ })}
93
+ </Typography>
94
+
95
+ <Typography variant="omega" textColor="neutral600" marginBottom={6} display="block">
96
+ {formatMessage({
97
+ id: `${PLUGIN_ID}.settings.loginRegister.description`,
98
+ defaultMessage:
99
+ 'Choose the authentication method for user login and registration. Default uses Strapi\'s standard email/password authentication. OTP allows users to login or register using email or mobile number with OTP verification.',
100
+ })}
101
+ </Typography>
102
+
103
+ <Box
104
+ background="neutral0"
105
+ hasRadius
106
+ shadow="filterShadow"
107
+ padding={4}
108
+ style={{ maxWidth: '640px' }}
109
+ >
110
+ <Typography variant="delta" textColor="neutral800" fontWeight="semiBold">
111
+ {formatMessage({
112
+ id: `${PLUGIN_ID}.settings.loginRegister.method.label`,
113
+ defaultMessage: 'Authentication Method',
114
+ })}
115
+ </Typography>
116
+ <Box marginTop={1}>
117
+ <Typography variant="pi" textColor="neutral600">
118
+ {formatMessage({
119
+ id: `${PLUGIN_ID}.settings.loginRegister.method.hint`,
120
+ defaultMessage: 'Select the method users will use to login and register.',
121
+ })}
122
+ </Typography>
123
+ </Box>
124
+ <Box marginTop={4}>
125
+ <Flex direction="column" gap={3} alignItems="start">
126
+ <Box
127
+ as="label"
128
+ padding={3}
129
+ background={method === 'default' ? 'primary100' : 'neutral0'}
130
+ hasRadius
131
+ style={{
132
+ cursor: 'pointer',
133
+ border: `1px solid ${method === 'default' ? '#4945ff' : '#dcdce4'}`,
134
+ }}
135
+ >
136
+ <Flex gap={2} alignItems="flex-start">
137
+ <input
138
+ type="radio"
139
+ name="loginMethod"
140
+ value="default"
141
+ checked={method === 'default'}
142
+ onChange={(e) => setMethod(e.target.value)}
143
+ style={{
144
+ marginTop: '4px',
145
+ cursor: 'pointer',
146
+ }}
147
+ />
148
+ <Box>
149
+ <Typography variant="omega" textColor="neutral800" fontWeight="semiBold">
150
+ {formatMessage({
151
+ id: `${PLUGIN_ID}.settings.loginRegister.method.default`,
152
+ defaultMessage: 'Default (Email/Password)',
153
+ })}
154
+ </Typography>
155
+ <Typography variant="pi" textColor="neutral600" marginTop={1} display="block">
156
+ {formatMessage({
157
+ id: `${PLUGIN_ID}.settings.loginRegister.method.default.description`,
158
+ defaultMessage:
159
+ 'Uses Strapi\'s built-in authentication system with email and password.',
160
+ })}
161
+ </Typography>
162
+ </Box>
163
+ </Flex>
164
+ </Box>
165
+
166
+ <Box
167
+ as="label"
168
+ padding={3}
169
+ background={method === 'otp' ? 'primary100' : 'neutral0'}
170
+ hasRadius
171
+ style={{
172
+ cursor: 'pointer',
173
+ border: `1px solid ${method === 'otp' ? '#4945ff' : '#dcdce4'}`,
174
+ }}
175
+ >
176
+ <Flex gap={2} alignItems="flex-start">
177
+ <input
178
+ type="radio"
179
+ name="loginMethod"
180
+ value="otp"
181
+ checked={method === 'otp'}
182
+ onChange={(e) => setMethod(e.target.value)}
183
+ style={{
184
+ marginTop: '4px',
185
+ cursor: 'pointer',
186
+ }}
187
+ />
188
+ <Box>
189
+ <Typography variant="omega" textColor="neutral800" fontWeight="semiBold">
190
+ {formatMessage({
191
+ id: `${PLUGIN_ID}.settings.loginRegister.method.otp`,
192
+ defaultMessage: 'OTP (Email/Mobile Verification)',
193
+ })}
194
+ </Typography>
195
+ <Typography variant="pi" textColor="neutral600" marginTop={1} display="block">
196
+ {formatMessage({
197
+ id: `${PLUGIN_ID}.settings.loginRegister.method.otp.description`,
198
+ defaultMessage:
199
+ 'Users can login or register using email or mobile number. An OTP (One-Time Password) will be sent for verification.',
200
+ })}
201
+ </Typography>
202
+ </Box>
203
+ </Flex>
204
+ </Box>
205
+
206
+ <Box
207
+ as="label"
208
+ padding={3}
209
+ background={method === 'both' ? 'primary100' : 'neutral0'}
210
+ hasRadius
211
+ style={{
212
+ cursor: 'pointer',
213
+ border: `1px solid ${method === 'both' ? '#4945ff' : '#dcdce4'}`,
214
+ }}
215
+ >
216
+ <Flex gap={2} alignItems="flex-start">
217
+ <input
218
+ type="radio"
219
+ name="loginMethod"
220
+ value="both"
221
+ checked={method === 'both'}
222
+ onChange={(e) => setMethod(e.target.value)}
223
+ style={{
224
+ marginTop: '4px',
225
+ cursor: 'pointer',
226
+ }}
227
+ />
228
+ <Box>
229
+ <Typography variant="omega" textColor="neutral800" fontWeight="semiBold">
230
+ {formatMessage({
231
+ id: `${PLUGIN_ID}.settings.loginRegister.method.both`,
232
+ defaultMessage: 'Both Methods (Unified)',
233
+ })}
234
+ </Typography>
235
+ <Typography variant="pi" textColor="neutral600" marginTop={1} display="block">
236
+ {formatMessage({
237
+ id: `${PLUGIN_ID}.settings.loginRegister.method.both.description`,
238
+ defaultMessage:
239
+ 'Users can choose between OTP or email/password authentication. Use the unified /auth/unified endpoint to support both methods simultaneously.',
240
+ })}
241
+ </Typography>
242
+ </Box>
243
+ </Flex>
244
+ </Box>
245
+ </Flex>
246
+ </Box>
247
+ {error && (
248
+ <Box marginTop={4} padding={3} background="danger100" hasRadius>
249
+ <Typography variant="omega" textColor="danger700">
250
+ {error}
251
+ </Typography>
252
+ </Box>
253
+ )}
254
+
255
+ {success && (
256
+ <Box marginTop={4} padding={3} background="success100" hasRadius>
257
+ <Typography variant="omega" textColor="success700">
258
+ {formatMessage({
259
+ id: `${PLUGIN_ID}.settings.loginRegister.save.success`,
260
+ defaultMessage: 'Settings updated successfully.',
261
+ })}
262
+ </Typography>
263
+ </Box>
264
+ )}
265
+
266
+ <Flex marginTop={6} gap={2}>
267
+ <Button onClick={handleSave} loading={saving} disabled={saving}>
268
+ {formatMessage({
269
+ id: `${PLUGIN_ID}.settings.loginRegister.save`,
270
+ defaultMessage: 'Save settings',
271
+ })}
272
+ </Button>
273
+ </Flex>
274
+ </Box>
275
+ </Box>
276
+ );
277
+ };
278
+
279
+ export default LoginRegisterContent;
280
+
@@ -0,0 +1,6 @@
1
+ import { ShoppingCart } from '@strapi/icons';
2
+
3
+ const PluginIcon = () => <ShoppingCart />;
4
+
5
+ export default PluginIcon;
6
+
@@ -0,0 +1,230 @@
1
+ 'use strict';
2
+
3
+ import React, { useEffect, useState } from 'react';
4
+ import { useIntl } from 'react-intl';
5
+ import {
6
+ Box,
7
+ Flex,
8
+ Typography,
9
+ Button,
10
+ } from '@strapi/design-system';
11
+ import { useFetchClient } from '@strapi/admin/strapi-admin';
12
+ import { PLUGIN_ID } from '../pluginId';
13
+
14
+ const ShippingTypeContent = () => {
15
+ const { formatMessage } = useIntl();
16
+ const fetchClient = useFetchClient();
17
+ const [loading, setLoading] = useState(true);
18
+ const [saving, setSaving] = useState(false);
19
+ const [shippingType, setShippingType] = useState('single'); // 'single' or 'multiple'
20
+ const [error, setError] = useState(null);
21
+ const [success, setSuccess] = useState(false);
22
+
23
+ useEffect(() => {
24
+ loadSettings();
25
+ }, []);
26
+
27
+ const loadSettings = async () => {
28
+ try {
29
+ setLoading(true);
30
+ setError(null);
31
+ const { data } = await fetchClient.get(`/webbycommerce/settings`);
32
+ if (data.shippingType) {
33
+ setShippingType(data.shippingType);
34
+ }
35
+ } catch (err) {
36
+ console.error('Failed to load shipping type settings:', err);
37
+ setError(
38
+ formatMessage({
39
+ id: `${PLUGIN_ID}.settings.shippingType.load.error`,
40
+ defaultMessage: 'Failed to load settings.',
41
+ })
42
+ );
43
+ } finally {
44
+ setLoading(false);
45
+ }
46
+ };
47
+
48
+ const handleSave = async () => {
49
+ try {
50
+ setSaving(true);
51
+ setError(null);
52
+ setSuccess(false);
53
+
54
+ const { data } = await fetchClient.put(`/webbycommerce/settings`, {
55
+ shippingType,
56
+ });
57
+
58
+ if (data.shippingType) {
59
+ setShippingType(data.shippingType);
60
+ }
61
+
62
+ setSuccess(true);
63
+ setTimeout(() => setSuccess(false), 3000);
64
+ } catch (err) {
65
+ console.error('Failed to save shipping type settings:', err);
66
+ setError(
67
+ formatMessage({
68
+ id: `${PLUGIN_ID}.settings.shippingType.save.error`,
69
+ defaultMessage: 'Failed to save settings. Please try again.',
70
+ })
71
+ );
72
+ } finally {
73
+ setSaving(false);
74
+ }
75
+ };
76
+
77
+ if (loading) {
78
+ return (
79
+ <Box padding={4}>
80
+ <Typography>
81
+ {formatMessage({
82
+ id: `${PLUGIN_ID}.settings.shippingType.loading`,
83
+ defaultMessage: 'Loading settings...',
84
+ })}
85
+ </Typography>
86
+ </Box>
87
+ );
88
+ }
89
+
90
+ return (
91
+ <Box>
92
+ <Box paddingBottom={4}>
93
+ <Typography variant="beta" as="h2">
94
+ {formatMessage({
95
+ id: `${PLUGIN_ID}.settings.shippingType.title`,
96
+ defaultMessage: 'Shipping Type Settings',
97
+ })}
98
+ </Typography>
99
+ <Typography variant="omega" textColor="neutral600" as="p" style={{ marginTop: '8px' }}>
100
+ {formatMessage({
101
+ id: `${PLUGIN_ID}.settings.shippingType.description`,
102
+ defaultMessage: 'Configure how users can manage their billing and shipping addresses.',
103
+ })}
104
+ </Typography>
105
+ </Box>
106
+
107
+ <Box paddingBottom={6}>
108
+ <Box paddingBottom={3}>
109
+ <Typography variant="omega" fontWeight="semiBold" as="label" style={{ display: 'block', marginBottom: '8px' }}>
110
+ {formatMessage({
111
+ id: `${PLUGIN_ID}.settings.shippingType.method.label`,
112
+ defaultMessage: 'Address Management Type',
113
+ })}
114
+ </Typography>
115
+ </Box>
116
+
117
+ <Flex direction="column" gap={3} alignItems="start">
118
+ <Box
119
+ as="label"
120
+ padding={4}
121
+ borderColor={shippingType === 'single' ? 'primary600' : 'neutral200'}
122
+ borderStyle="solid"
123
+ borderWidth="1px"
124
+ hasRadius
125
+ style={{
126
+ cursor: 'pointer',
127
+ backgroundColor: shippingType === 'single' ? 'rgba(0, 122, 255, 0.05)' : 'transparent',
128
+ }}
129
+ onClick={() => setShippingType('single')}
130
+ >
131
+ <Flex gap={2} alignItems="center">
132
+ <input
133
+ type="radio"
134
+ name="shippingType"
135
+ value="single"
136
+ checked={shippingType === 'single'}
137
+ onChange={() => setShippingType('single')}
138
+ style={{ margin: 0 }}
139
+ />
140
+ <Box>
141
+ <Typography variant="omega" fontWeight="semiBold">
142
+ {formatMessage({
143
+ id: `${PLUGIN_ID}.settings.shippingType.single.label`,
144
+ defaultMessage: 'Single Address Mode',
145
+ })}
146
+ </Typography>
147
+ <Typography variant="omega" textColor="neutral600" style={{ marginTop: '4px' }} display="block">
148
+ {formatMessage({
149
+ id: `${PLUGIN_ID}.settings.shippingType.single.description`,
150
+ defaultMessage: 'Users can have one billing address and one shipping address. Suitable for simple e-commerce sites.',
151
+ })}
152
+ </Typography>
153
+ </Box>
154
+ </Flex>
155
+ </Box>
156
+
157
+ <Box
158
+ as="label"
159
+ padding={4}
160
+ borderColor={shippingType === 'multiple' ? 'primary600' : 'neutral200'}
161
+ borderStyle="solid"
162
+ borderWidth="1px"
163
+ hasRadius
164
+ style={{
165
+ cursor: 'pointer',
166
+ backgroundColor: shippingType === 'multiple' ? 'rgba(0, 122, 255, 0.05)' : 'transparent',
167
+ }}
168
+ onClick={() => setShippingType('multiple')}
169
+ >
170
+ <Flex gap={2} alignItems="center">
171
+ <input
172
+ type="radio"
173
+ name="shippingType"
174
+ value="multiple"
175
+ checked={shippingType === 'multiple'}
176
+ onChange={() => setShippingType('multiple')}
177
+ style={{ margin: 0 }}
178
+ />
179
+ <Box>
180
+ <Typography variant="omega" fontWeight="semiBold">
181
+ {formatMessage({
182
+ id: `${PLUGIN_ID}.settings.shippingType.multiple.label`,
183
+ defaultMessage: 'Multiple Address Mode',
184
+ })}
185
+ </Typography>
186
+ <Typography variant="omega" textColor="neutral600" style={{ marginTop: '4px' }} display="block">
187
+ {formatMessage({
188
+ id: `${PLUGIN_ID}.settings.shippingType.multiple.description`,
189
+ defaultMessage: 'Users can have multiple billing and shipping addresses. They can select any address during checkout. Similar to Amazon/Flipkart.',
190
+ })}
191
+ </Typography>
192
+ </Box>
193
+ </Flex>
194
+ </Box>
195
+ </Flex>
196
+ </Box>
197
+
198
+ {error && (
199
+ <Box paddingBottom={4}>
200
+ <Typography textColor="danger600" variant="omega">
201
+ {error}
202
+ </Typography>
203
+ </Box>
204
+ )}
205
+
206
+ {success && (
207
+ <Box paddingBottom={4}>
208
+ <Typography textColor="success600" variant="omega">
209
+ {formatMessage({
210
+ id: `${PLUGIN_ID}.settings.shippingType.save.success`,
211
+ defaultMessage: 'Settings saved successfully!',
212
+ })}
213
+ </Typography>
214
+ </Box>
215
+ )}
216
+
217
+ <Flex justifyContent="flex-start">
218
+ <Button onClick={handleSave} loading={saving} disabled={saving}>
219
+ {formatMessage({
220
+ id: `${PLUGIN_ID}.settings.shippingType.save.button`,
221
+ defaultMessage: 'Save Settings',
222
+ })}
223
+ </Button>
224
+ </Flex>
225
+ </Box>
226
+ );
227
+ };
228
+
229
+ export default ShippingTypeContent;
230
+