@webbycrown/webbycommerce 1.0.0 → 1.0.2

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 CHANGED
@@ -46,8 +46,40 @@ module.exports = ({ env }) => ({
46
46
  });
47
47
  ```
48
48
 
49
+
49
50
  ## ⚙️ Initial Setup
50
51
 
52
+ ### 0. (Optional) Seed Demo Data
53
+
54
+ You can populate your store with sample products/categories/etc using any of the following options:
55
+
56
+ #### Option A: Run the setup and answer `y`
57
+
58
+ ```bash
59
+ npx strapi-ecommerce-setup
60
+ ```
61
+
62
+ When prompted **"Would you like to seed example data? (y/n):"**, answer **`y`**.
63
+
64
+ #### Option B: Seed from Strapi Admin (works even if you answered `n`)
65
+
66
+ - Go to **Strapi Admin → Settings → Advanced Ecommerce**
67
+ - Click **"Seed Demo Data"**
68
+
69
+ #### Option C: Seed via `.env` flag (programmatic / CI-friendly)
70
+
71
+ Add this to your Strapi project’s `.env` and then start Strapi:
72
+
73
+ ```bash
74
+ STRAPI_PLUGIN_ADVANCED_ECOMMERCE_SEED_DATA=true
75
+ ```
76
+
77
+ ```bash
78
+ npm run develop
79
+ ```
80
+
81
+ After the demo data is seeded once, set it back to `false` (or remove it) to avoid reseeding on every startup.
82
+
51
83
  ### 1. Enable Permissions
52
84
 
53
85
  After installation, navigate to **Settings → Users & Permissions → Roles** and select the **Public** role (or any role you want to grant access).
@@ -77,7 +109,7 @@ Navigate to **Settings → WebbyCommerce** in the Strapi admin panel. You'll fin
77
109
 
78
110
  ### 3. User Schema Extension
79
111
 
80
- The plugin automatically extends the user schema with ecommerce-specific fields. Ensure your user schema includes:
112
+ The plugin automatically extends the user schema with ecommerce-specific fields. The plugin will attempt to automatically add OTP fields to the user schema when it starts up.
81
113
 
82
114
  **Required Fields:**
83
115
  - `username` (string, required, unique)
@@ -90,11 +122,53 @@ The plugin automatically extends the user schema with ecommerce-specific fields.
90
122
  - `display_name` (string)
91
123
  - `company_name` (string)
92
124
 
93
- **OTP Fields (if using OTP authentication):**
94
- - `otp` (integer)
95
- - `isOtpVerified` (boolean, default: false)
96
-
97
- The plugin includes a schema extension file at `src/extensions/users-permissions/content-types/user/schema.json` that adds these fields automatically.
125
+ **OTP Fields (required if using OTP authentication):**
126
+ - `otp` (integer, nullable) - Stores the OTP code
127
+ - `isOtpVerified` (boolean, default: false) - Tracks if OTP has been verified
128
+
129
+ #### Automatic Schema Extension
130
+
131
+ The plugin automatically adds OTP fields to the user schema on startup. If you see an error about OTP fields not being available, you may need to manually extend the schema.
132
+
133
+ #### Manual Schema Extension (if automatic extension fails)
134
+
135
+ If the automatic schema extension doesn't work, create a schema extension file in your main Strapi project:
136
+
137
+ 1. Create the directory structure:
138
+ ```
139
+ src/extensions/users-permissions/content-types/user/
140
+ ```
141
+
142
+ 2. Create `schema.json` in that directory with the following content:
143
+ ```json
144
+ {
145
+ "kind": "collectionType",
146
+ "collectionName": "up_users",
147
+ "info": {
148
+ "name": "user",
149
+ "description": "",
150
+ "singularName": "user",
151
+ "pluralName": "users"
152
+ },
153
+ "options": {},
154
+ "pluginOptions": {},
155
+ "attributes": {
156
+ "otp": {
157
+ "type": "integer",
158
+ "required": false,
159
+ "private": true
160
+ },
161
+ "isOtpVerified": {
162
+ "type": "boolean",
163
+ "default": false,
164
+ "required": false,
165
+ "private": true
166
+ }
167
+ }
168
+ }
169
+ ```
170
+
171
+ 3. Restart Strapi to apply the schema changes.
98
172
 
99
173
  ### 4. Address Content Type
100
174
 
@@ -506,7 +580,20 @@ Authorization: Bearer YOUR_JWT_TOKEN
506
580
 
507
581
  ## 📜 Changelog
508
582
 
509
- ### [1.0.0] – Initial Stable Release
583
+ ## [1.0.2] – Patch Release
584
+
585
+ ### Fixed
586
+ - Fix critical bugs
587
+
588
+ ## [1.0.1] – Patch Release
589
+
590
+ ### Changed
591
+ - Updated README documentation
592
+
593
+ ### Fixed
594
+ - Resolved reported bugs
595
+
596
+ ## [1.0.0] – Initial Stable Release
510
597
 
511
598
  🎉 First production-ready release of WebbyCommerce, a complete ecommerce backend plugin for Strapi CMS.
512
599
 
@@ -2,8 +2,8 @@ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import React__default, { useContext, useRef, useCallback, useDebugValue, useMemo, useState, useEffect, useLayoutEffect, createContext as createContext$1, createElement, useReducer } from "react";
4
4
  import { useIntl, FormattedMessage } from "react-intl";
5
- import { Flex, Box, Popover, Typography, Button, LinkButton, Link as Link$1, Portal, Alert, Field, SubNav, Badge, TextInput, Pagination, PreviousLink, Dots, PageLink, NextLink, Modal, DesignSystemProvider, Main } from "@strapi/design-system";
6
- import { P as PLUGIN_ID } from "./index-BFH1VuAA.mjs";
5
+ import { Flex, Box, Popover, Typography, Button, LinkButton, Link as Link$1, Portal, Alert, Field, SubNav, Badge, TextInput, Pagination, PreviousLink, Dots, PageLink, NextLink, Modal, Main } from "@strapi/design-system";
6
+ import { P as PLUGIN_ID } from "./index-Cgnv1DYN.mjs";
7
7
  import { WarningCircle, CaretDown, Trash } from "@strapi/icons";
8
8
  import "react-dom/client";
9
9
  import ReactDOM, { unstable_batchedUpdates as unstable_batchedUpdates$1 } from "react-dom";
@@ -19103,6 +19103,7 @@ new QueryClient({
19103
19103
  });
19104
19104
  const HEIGHT_TOP_NAVIGATION = "6.4rem";
19105
19105
  const HEIGHT_TOP_NAVIGATION_MEDIUM = "5.6rem";
19106
+ const WIDTH_SIDE_NAVIGATION = "23.2rem";
19106
19107
  styled(Alert)`
19107
19108
  & > div:first-child {
19108
19109
  display: none;
@@ -21337,6 +21338,8 @@ flatRest(function(object2, paths) {
21337
21338
  styled(Field.Root)`
21338
21339
  height: 3.2rem;
21339
21340
  width: 3.2rem;
21341
+ align-items: center;
21342
+ justify-content: center;
21340
21343
 
21341
21344
  > label,
21342
21345
  ~ input {
@@ -21387,21 +21390,30 @@ create().shape({
21387
21390
  if (!value || typeof value !== "string") return true;
21388
21391
  const byteSize = getByteSize(value);
21389
21392
  return byteSize <= 72;
21390
- }).matches(/[a-z]/, {
21393
+ }).test("lowercase", {
21391
21394
  message: {
21392
21395
  id: "components.Input.error.contain.lowercase",
21393
21396
  defaultMessage: "Password must contain at least 1 lowercase letter"
21394
21397
  }
21395
- }).matches(/[A-Z]/, {
21398
+ }, (value) => {
21399
+ if (!value) return true;
21400
+ return /[a-z]/.test(value);
21401
+ }).test("uppercase", {
21396
21402
  message: {
21397
21403
  id: "components.Input.error.contain.uppercase",
21398
21404
  defaultMessage: "Password must contain at least 1 uppercase letter"
21399
21405
  }
21400
- }).matches(/\d/, {
21406
+ }, (value) => {
21407
+ if (!value) return true;
21408
+ return /[A-Z]/.test(value);
21409
+ }).test("number", {
21401
21410
  message: {
21402
21411
  id: "components.Input.error.contain.number",
21403
21412
  defaultMessage: "Password must contain at least 1 number"
21404
21413
  }
21414
+ }, (value) => {
21415
+ if (!value) return true;
21416
+ return /\d/.test(value);
21405
21417
  }).required({
21406
21418
  id: errorsTrads.required.id,
21407
21419
  defaultMessage: "Password is required"
@@ -21439,21 +21451,30 @@ create().shape({
21439
21451
  }, function(value) {
21440
21452
  if (!value) return true;
21441
21453
  return new TextEncoder().encode(value).length <= 72;
21442
- }).matches(/[a-z]/, {
21454
+ }).test("lowercase", {
21443
21455
  message: {
21444
21456
  id: "components.Input.error.contain.lowercase",
21445
21457
  defaultMessage: "Password must contain at least 1 lowercase letter"
21446
21458
  }
21447
- }).matches(/[A-Z]/, {
21459
+ }, (value) => {
21460
+ if (!value) return true;
21461
+ return /[a-z]/.test(value);
21462
+ }).test("uppercase", {
21448
21463
  message: {
21449
21464
  id: "components.Input.error.contain.uppercase",
21450
21465
  defaultMessage: "Password must contain at least 1 uppercase letter"
21451
21466
  }
21452
- }).matches(/\d/, {
21467
+ }, (value) => {
21468
+ if (!value) return true;
21469
+ return /[A-Z]/.test(value);
21470
+ }).test("number", {
21453
21471
  message: {
21454
21472
  id: "components.Input.error.contain.number",
21455
21473
  defaultMessage: "Password must contain at least 1 number"
21456
21474
  }
21475
+ }, (value) => {
21476
+ if (!value) return true;
21477
+ return /\d/.test(value);
21457
21478
  }).required({
21458
21479
  id: errorsTrads.required.id,
21459
21480
  defaultMessage: "Password is required"
@@ -21496,21 +21517,30 @@ create().shape({
21496
21517
  if (!value || typeof value !== "string") return true;
21497
21518
  const byteSize = getByteSize(value);
21498
21519
  return byteSize <= 72;
21499
- }).matches(/[a-z]/, {
21520
+ }).test("lowercase", {
21500
21521
  message: {
21501
21522
  id: "components.Input.error.contain.lowercase",
21502
21523
  defaultMessage: "Password must contain at least 1 lowercase letter"
21503
21524
  }
21504
- }).matches(/[A-Z]/, {
21525
+ }, (value) => {
21526
+ if (!value) return true;
21527
+ return /[a-z]/.test(value);
21528
+ }).test("uppercase", {
21505
21529
  message: {
21506
21530
  id: "components.Input.error.contain.uppercase",
21507
21531
  defaultMessage: "Password must contain at least 1 uppercase letter"
21508
21532
  }
21509
- }).matches(/\d/, {
21533
+ }, (value) => {
21534
+ if (!value) return true;
21535
+ return /[A-Z]/.test(value);
21536
+ }).test("number", {
21510
21537
  message: {
21511
21538
  id: "components.Input.error.contain.number",
21512
21539
  defaultMessage: "Password must contain at least 1 number"
21513
21540
  }
21541
+ }, (value) => {
21542
+ if (!value) return true;
21543
+ return /\d/.test(value);
21514
21544
  }).required({
21515
21545
  id: errorsTrads.required.id,
21516
21546
  defaultMessage: "Password is required"
@@ -21570,7 +21600,7 @@ const MainSubNav = styled(SubNav)`
21570
21600
  z-index: 2;
21571
21601
 
21572
21602
  ${({ theme }) => theme.breakpoints.medium} {
21573
- width: 23.2rem;
21603
+ width: ${WIDTH_SIDE_NAVIGATION};
21574
21604
  position: sticky;
21575
21605
  top: 0;
21576
21606
  border-right: 1px solid ${({ theme }) => theme.colors.neutral150};
@@ -21663,12 +21693,28 @@ styled(Box)`
21663
21693
  }
21664
21694
  `;
21665
21695
  styled(Badge)`
21696
+ width: 100%;
21666
21697
  background: linear-gradient(
21667
21698
  90deg,
21668
21699
  ${({ theme }) => theme.colors.primary600} 0%,
21669
21700
  ${({ theme }) => theme.colors.alternative600} 121.48%
21670
21701
  );
21671
- padding: 0.4rem 1rem;
21702
+ padding: 1.1rem 1rem;
21703
+
21704
+ ${({ theme }) => theme.breakpoints.small} {
21705
+ padding: 1.2rem 1rem;
21706
+ }
21707
+ ${({ theme }) => theme.breakpoints.medium} {
21708
+ padding: 0.4rem 1rem;
21709
+ }
21710
+ `;
21711
+ styled(Typography)`
21712
+ font-size: 1.2rem;
21713
+
21714
+ ${({ theme }) => theme.breakpoints.small} {
21715
+ font-size: 1.4rem;
21716
+ line-height: 1.6rem;
21717
+ }
21672
21718
  `;
21673
21719
  const useFetchClient = () => {
21674
21720
  const controller = React.useRef(null);
@@ -31137,7 +31183,7 @@ const Settings = () => {
31137
31183
  const { formatMessage } = useIntl();
31138
31184
  const [activeView, setActiveView] = useState("configure");
31139
31185
  const titleId = `${PLUGIN_ID}-settings-title`;
31140
- return /* @__PURE__ */ jsx(DesignSystemProvider, { children: /* @__PURE__ */ jsx(Main, { labelledBy: titleId, children: /* @__PURE__ */ jsxs(Box, { padding: 8, background: "neutral100", children: [
31186
+ return /* @__PURE__ */ jsx(Main, { labelledBy: titleId, children: /* @__PURE__ */ jsxs(Box, { padding: 8, background: "neutral100", children: [
31141
31187
  /* @__PURE__ */ jsx(Box, { paddingBottom: 4, children: /* @__PURE__ */ jsx(Typography, { id: titleId, variant: "alpha", as: "h1", children: formatMessage({
31142
31188
  id: `${PLUGIN_ID}.settings.section`,
31143
31189
  defaultMessage: "Advanced Ecommerce"
@@ -31206,7 +31252,7 @@ const Settings = () => {
31206
31252
  activeView === "smtp" && /* @__PURE__ */ jsx(SmtpContent, {}),
31207
31253
  activeView === "api-collections" && /* @__PURE__ */ jsx(ApiCollectionsContent, {})
31208
31254
  ] })
31209
- ] }) }) });
31255
+ ] }) });
31210
31256
  };
31211
31257
  export {
31212
31258
  Settings as default
@@ -4,7 +4,7 @@ const jsxRuntime = require("react/jsx-runtime");
4
4
  const React = require("react");
5
5
  const reactIntl = require("react-intl");
6
6
  const designSystem = require("@strapi/design-system");
7
- const index = require("./index-DXM6qeJr.js");
7
+ const index = require("./index-D0Y_VvWO.js");
8
8
  const icons = require("@strapi/icons");
9
9
  require("react-dom/client");
10
10
  const ReactDOM = require("react-dom");
@@ -19145,6 +19145,7 @@ new QueryClient({
19145
19145
  });
19146
19146
  const HEIGHT_TOP_NAVIGATION = "6.4rem";
19147
19147
  const HEIGHT_TOP_NAVIGATION_MEDIUM = "5.6rem";
19148
+ const WIDTH_SIDE_NAVIGATION = "23.2rem";
19148
19149
  styled.styled(designSystem.Alert)`
19149
19150
  & > div:first-child {
19150
19151
  display: none;
@@ -21379,6 +21380,8 @@ flatRest(function(object2, paths) {
21379
21380
  styled.styled(designSystem.Field.Root)`
21380
21381
  height: 3.2rem;
21381
21382
  width: 3.2rem;
21383
+ align-items: center;
21384
+ justify-content: center;
21382
21385
 
21383
21386
  > label,
21384
21387
  ~ input {
@@ -21429,21 +21432,30 @@ create().shape({
21429
21432
  if (!value || typeof value !== "string") return true;
21430
21433
  const byteSize = getByteSize(value);
21431
21434
  return byteSize <= 72;
21432
- }).matches(/[a-z]/, {
21435
+ }).test("lowercase", {
21433
21436
  message: {
21434
21437
  id: "components.Input.error.contain.lowercase",
21435
21438
  defaultMessage: "Password must contain at least 1 lowercase letter"
21436
21439
  }
21437
- }).matches(/[A-Z]/, {
21440
+ }, (value) => {
21441
+ if (!value) return true;
21442
+ return /[a-z]/.test(value);
21443
+ }).test("uppercase", {
21438
21444
  message: {
21439
21445
  id: "components.Input.error.contain.uppercase",
21440
21446
  defaultMessage: "Password must contain at least 1 uppercase letter"
21441
21447
  }
21442
- }).matches(/\d/, {
21448
+ }, (value) => {
21449
+ if (!value) return true;
21450
+ return /[A-Z]/.test(value);
21451
+ }).test("number", {
21443
21452
  message: {
21444
21453
  id: "components.Input.error.contain.number",
21445
21454
  defaultMessage: "Password must contain at least 1 number"
21446
21455
  }
21456
+ }, (value) => {
21457
+ if (!value) return true;
21458
+ return /\d/.test(value);
21447
21459
  }).required({
21448
21460
  id: errorsTrads.required.id,
21449
21461
  defaultMessage: "Password is required"
@@ -21481,21 +21493,30 @@ create().shape({
21481
21493
  }, function(value) {
21482
21494
  if (!value) return true;
21483
21495
  return new TextEncoder().encode(value).length <= 72;
21484
- }).matches(/[a-z]/, {
21496
+ }).test("lowercase", {
21485
21497
  message: {
21486
21498
  id: "components.Input.error.contain.lowercase",
21487
21499
  defaultMessage: "Password must contain at least 1 lowercase letter"
21488
21500
  }
21489
- }).matches(/[A-Z]/, {
21501
+ }, (value) => {
21502
+ if (!value) return true;
21503
+ return /[a-z]/.test(value);
21504
+ }).test("uppercase", {
21490
21505
  message: {
21491
21506
  id: "components.Input.error.contain.uppercase",
21492
21507
  defaultMessage: "Password must contain at least 1 uppercase letter"
21493
21508
  }
21494
- }).matches(/\d/, {
21509
+ }, (value) => {
21510
+ if (!value) return true;
21511
+ return /[A-Z]/.test(value);
21512
+ }).test("number", {
21495
21513
  message: {
21496
21514
  id: "components.Input.error.contain.number",
21497
21515
  defaultMessage: "Password must contain at least 1 number"
21498
21516
  }
21517
+ }, (value) => {
21518
+ if (!value) return true;
21519
+ return /\d/.test(value);
21499
21520
  }).required({
21500
21521
  id: errorsTrads.required.id,
21501
21522
  defaultMessage: "Password is required"
@@ -21538,21 +21559,30 @@ create().shape({
21538
21559
  if (!value || typeof value !== "string") return true;
21539
21560
  const byteSize = getByteSize(value);
21540
21561
  return byteSize <= 72;
21541
- }).matches(/[a-z]/, {
21562
+ }).test("lowercase", {
21542
21563
  message: {
21543
21564
  id: "components.Input.error.contain.lowercase",
21544
21565
  defaultMessage: "Password must contain at least 1 lowercase letter"
21545
21566
  }
21546
- }).matches(/[A-Z]/, {
21567
+ }, (value) => {
21568
+ if (!value) return true;
21569
+ return /[a-z]/.test(value);
21570
+ }).test("uppercase", {
21547
21571
  message: {
21548
21572
  id: "components.Input.error.contain.uppercase",
21549
21573
  defaultMessage: "Password must contain at least 1 uppercase letter"
21550
21574
  }
21551
- }).matches(/\d/, {
21575
+ }, (value) => {
21576
+ if (!value) return true;
21577
+ return /[A-Z]/.test(value);
21578
+ }).test("number", {
21552
21579
  message: {
21553
21580
  id: "components.Input.error.contain.number",
21554
21581
  defaultMessage: "Password must contain at least 1 number"
21555
21582
  }
21583
+ }, (value) => {
21584
+ if (!value) return true;
21585
+ return /\d/.test(value);
21556
21586
  }).required({
21557
21587
  id: errorsTrads.required.id,
21558
21588
  defaultMessage: "Password is required"
@@ -21612,7 +21642,7 @@ const MainSubNav = styled.styled(designSystem.SubNav)`
21612
21642
  z-index: 2;
21613
21643
 
21614
21644
  ${({ theme }) => theme.breakpoints.medium} {
21615
- width: 23.2rem;
21645
+ width: ${WIDTH_SIDE_NAVIGATION};
21616
21646
  position: sticky;
21617
21647
  top: 0;
21618
21648
  border-right: 1px solid ${({ theme }) => theme.colors.neutral150};
@@ -21705,12 +21735,28 @@ styled.styled(designSystem.Box)`
21705
21735
  }
21706
21736
  `;
21707
21737
  styled.styled(designSystem.Badge)`
21738
+ width: 100%;
21708
21739
  background: linear-gradient(
21709
21740
  90deg,
21710
21741
  ${({ theme }) => theme.colors.primary600} 0%,
21711
21742
  ${({ theme }) => theme.colors.alternative600} 121.48%
21712
21743
  );
21713
- padding: 0.4rem 1rem;
21744
+ padding: 1.1rem 1rem;
21745
+
21746
+ ${({ theme }) => theme.breakpoints.small} {
21747
+ padding: 1.2rem 1rem;
21748
+ }
21749
+ ${({ theme }) => theme.breakpoints.medium} {
21750
+ padding: 0.4rem 1rem;
21751
+ }
21752
+ `;
21753
+ styled.styled(designSystem.Typography)`
21754
+ font-size: 1.2rem;
21755
+
21756
+ ${({ theme }) => theme.breakpoints.small} {
21757
+ font-size: 1.4rem;
21758
+ line-height: 1.6rem;
21759
+ }
21714
21760
  `;
21715
21761
  const useFetchClient = () => {
21716
21762
  const controller = React__namespace.useRef(null);
@@ -31179,7 +31225,7 @@ const Settings = () => {
31179
31225
  const { formatMessage } = reactIntl.useIntl();
31180
31226
  const [activeView, setActiveView] = React.useState("configure");
31181
31227
  const titleId = `${index.PLUGIN_ID}-settings-title`;
31182
- return /* @__PURE__ */ jsxRuntime.jsx(designSystem.DesignSystemProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { labelledBy: titleId, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 8, background: "neutral100", children: [
31228
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Main, { labelledBy: titleId, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 8, background: "neutral100", children: [
31183
31229
  /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { id: titleId, variant: "alpha", as: "h1", children: formatMessage({
31184
31230
  id: `${index.PLUGIN_ID}.settings.section`,
31185
31231
  defaultMessage: "Advanced Ecommerce"
@@ -31248,6 +31294,6 @@ const Settings = () => {
31248
31294
  activeView === "smtp" && /* @__PURE__ */ jsxRuntime.jsx(SmtpContent, {}),
31249
31295
  activeView === "api-collections" && /* @__PURE__ */ jsxRuntime.jsx(ApiCollectionsContent, {})
31250
31296
  ] })
31251
- ] }) }) });
31297
+ ] }) });
31252
31298
  };
31253
31299
  exports.default = Settings;
@@ -385,11 +385,13 @@ const en = {
385
385
  "webbycommerce.settings.apiCollections.products.getAttributes.title": "List Product Attributes",
386
386
  "webbycommerce.settings.apiCollections.products.getAttributes.summary": "Get all product attributes with optional filtering.",
387
387
  "webbycommerce.settings.apiCollections.cart.applyCoupon.title": "Apply Coupon to Cart",
388
- "webbycommerce.settings.apiCollections.cart.applyCoupon.summary": "Apply a discount coupon to the authenticated user's cart.",
388
+ "webbycommerce.settings.apiCollections.cart.applyCoupon.summary": "Apply a discount coupon to the authenticated user's cart. Validates coupon code, expiry, usage limits, and minimum order amount.",
389
+ "webbycommerce.settings.apiCollections.cart.applyCoupon.auth": "Auth: requires JWT token (Authorization: Bearer <token>) and ecommerce permission enabled.",
389
390
  "webbycommerce.settings.apiCollections.cart.removeCoupon.title": "Remove Coupon from Cart",
390
- "webbycommerce.settings.apiCollections.cart.removeCoupon.summary": "Remove any applied coupon from the authenticated user's cart.",
391
+ "webbycommerce.settings.apiCollections.cart.removeCoupon.summary": "Remove any applied coupon from the authenticated user's cart and recalculate totals.",
392
+ "webbycommerce.settings.apiCollections.cart.removeCoupon.auth": "Auth: requires JWT token (Authorization: Bearer <token>) and ecommerce permission enabled.",
391
393
  "webbycommerce.settings.apiCollections.cart.getTotals.title": "Get Cart Totals",
392
- "webbycommerce.settings.apiCollections.cart.getTotals.summary": "Calculate and return the authenticated user's cart totals.",
394
+ "webbycommerce.settings.apiCollections.cart.getTotals.summary": "Calculate and return the authenticated user's cart totals including subtotal, tax, shipping, discounts, and final total.",
393
395
  "webbycommerce.settings.apiCollections.wishlist.moveToCart.title": "Move Wishlist Item to Cart",
394
396
  "webbycommerce.settings.apiCollections.wishlist.moveToCart.summary": "Move a product from the authenticated user's wishlist to their cart.",
395
397
  "webbycommerce.settings.apiCollections.products.getRelated.usage.product": "Replace :id with the actual product ID.",
@@ -400,11 +402,15 @@ const en = {
400
402
  "webbycommerce.settings.apiCollections.products.getTags.usage.pagination": "Returns paginated list of product tags.",
401
403
  "webbycommerce.settings.apiCollections.products.getAttributes.usage.filter": "Optional query parameters: ?is_variation=true&limit=20&start=0",
402
404
  "webbycommerce.settings.apiCollections.products.getAttributes.usage.variation": "Returns product attributes for variations.",
403
- "webbycommerce.settings.apiCollections.cart.applyCoupon.usage.code": "Provide valid coupon code to apply discount.",
404
- "webbycommerce.settings.apiCollections.cart.applyCoupon.usage.validation": "Validates coupon expiry, usage limits, and minimum order amount.",
405
- "webbycommerce.settings.apiCollections.cart.applyCoupon.usage.totals": "Returns updated cart totals with discount applied.",
405
+ "webbycommerce.settings.apiCollections.cart.applyCoupon.usage.code": 'Provide valid coupon code in the request body: {"coupon_code": "SAVE10"}.',
406
+ "webbycommerce.settings.apiCollections.cart.applyCoupon.usage.validation": "Validates coupon expiry date, usage limits, minimum order amount, and active status.",
407
+ "webbycommerce.settings.apiCollections.cart.applyCoupon.usage.totals": "Returns updated cart totals with discount applied. Discount is calculated based on coupon type (percentage or fixed amount).",
408
+ "webbycommerce.settings.apiCollections.cart.applyCoupon.usage.errors": "Returns error messages for invalid codes, expired coupons, usage limit exceeded, or minimum order amount not met.",
409
+ "webbycommerce.settings.apiCollections.cart.applyCoupon.usage.case": "Coupon code lookup is case-insensitive (tries exact match, uppercase, and lowercase).",
410
+ "webbycommerce.settings.apiCollections.cart.applyCoupon.usage.types": "Supports percentage discounts (e.g., 10% off) and fixed amount discounts (e.g., $5 off).",
406
411
  "webbycommerce.settings.apiCollections.cart.removeCoupon.usage.active": "Removes any active coupon from the user's cart.",
407
- "webbycommerce.settings.apiCollections.cart.removeCoupon.usage.reset": "Resets cart totals to original values.",
412
+ "webbycommerce.settings.apiCollections.cart.removeCoupon.usage.reset": "Resets cart totals to original values without discount.",
413
+ "webbycommerce.settings.apiCollections.cart.removeCoupon.usage.response": "Returns updated cart with recalculated totals after coupon removal.",
408
414
  "webbycommerce.settings.apiCollections.cart.getTotals.usage.calculate": "Calculates subtotal, tax, shipping, and discounts.",
409
415
  "webbycommerce.settings.apiCollections.cart.getTotals.usage.comprehensive": "Returns complete cart totals breakdown.",
410
416
  "webbycommerce.settings.apiCollections.wishlist.moveToCart.usage.product": "Replace :id with the actual product ID from wishlist.",
@@ -497,7 +503,54 @@ const en = {
497
503
  "webbycommerce.dashboard.quickActions.subtitle": "Common tasks and testing utilities",
498
504
  "webbycommerce.dashboard.quickActions.viewUsers": "View Users",
499
505
  "webbycommerce.dashboard.quickActions.viewAddresses": "View Addresses",
500
- "webbycommerce.dashboard.quickActions.apiDocs": "API Documentation"
506
+ "webbycommerce.dashboard.quickActions.apiDocs": "API Documentation",
507
+ "webbycommerce.settings.apiCollections.coupons.title": "Coupon Management",
508
+ "webbycommerce.settings.apiCollections.coupons.summary": "Manage discount coupons for the ecommerce system. Create, update, and track coupon usage.",
509
+ "webbycommerce.settings.apiCollections.coupons.auth": "Auth: public for reading, admin for writing.",
510
+ "webbycommerce.settings.apiCollections.coupons.authAdmin": "Auth: requires JWT token (Authorization: Bearer <token>) and administrator role.",
511
+ "webbycommerce.settings.apiCollections.coupons.getList.title": "Get All Coupons",
512
+ "webbycommerce.settings.apiCollections.coupons.getList.summary": "Retrieve all discount coupons with optional filtering by active status, expiry date, or usage limits.",
513
+ "webbycommerce.settings.apiCollections.coupons.getSingle.title": "Get Single Coupon",
514
+ "webbycommerce.settings.apiCollections.coupons.getSingle.summary": "Retrieve detailed information for a specific coupon by ID or code.",
515
+ "webbycommerce.settings.apiCollections.coupons.create.title": "Create Coupon",
516
+ "webbycommerce.settings.apiCollections.coupons.create.summary": "Create a new discount coupon with code, type (percentage or fixed), value, and validation rules.",
517
+ "webbycommerce.settings.apiCollections.coupons.update.title": "Update Coupon",
518
+ "webbycommerce.settings.apiCollections.coupons.update.summary": "Update an existing coupon's settings, usage limits, expiry date, or active status.",
519
+ "webbycommerce.settings.apiCollections.coupons.delete.title": "Delete Coupon",
520
+ "webbycommerce.settings.apiCollections.coupons.delete.summary": "Delete a coupon from the system. Cannot be deleted if currently applied to active carts.",
521
+ "webbycommerce.settings.apiCollections.coupons.getList.usage.filter": "Optional query parameters: ?is_active=true&expires_after=2024-12-31&limit=20&start=0",
522
+ "webbycommerce.settings.apiCollections.coupons.getList.usage.active": "Filter coupons by active status to show only currently usable coupons.",
523
+ "webbycommerce.settings.apiCollections.coupons.getList.usage.expiry": "Filter coupons by expiry date to find expired or upcoming expirations.",
524
+ "webbycommerce.settings.apiCollections.coupons.getSingle.usage.id": "Replace :id with the actual coupon ID or use code parameter.",
525
+ "webbycommerce.settings.apiCollections.coupons.getSingle.usage.code": "Can also retrieve by coupon code: ?code=SAVE10",
526
+ "webbycommerce.settings.apiCollections.coupons.create.usage.required": "Required fields: code (unique), type (percentage or fixed), value (decimal).",
527
+ "webbycommerce.settings.apiCollections.coupons.create.usage.optional": "Optional fields: description, usage_limit, minimum_order_amount, expires_at, is_active.",
528
+ "webbycommerce.settings.apiCollections.coupons.create.usage.types": "Type 'percentage' applies discount as percentage (e.g., 10 = 10% off). Type 'fixed' applies fixed amount discount (e.g., 5.00 = $5 off).",
529
+ "webbycommerce.settings.apiCollections.coupons.create.usage.unique": "Coupon code must be unique across all coupons.",
530
+ "webbycommerce.settings.apiCollections.coupons.update.usage.fields": "Update any coupon field: code, type, value, description, usage limits, expiry, or active status.",
531
+ "webbycommerce.settings.apiCollections.coupons.update.usage.impact": "Updating active coupons may affect carts that have already applied the coupon.",
532
+ "webbycommerce.settings.apiCollections.coupons.update.usage.usedCount": "used_count field is automatically incremented when coupons are applied to orders.",
533
+ "webbycommerce.settings.apiCollections.coupons.delete.usage.active": "Cannot delete coupons that are currently applied to active carts.",
534
+ "webbycommerce.settings.apiCollections.coupons.delete.usage.impact": "Deleting a coupon will prevent it from being used in future orders.",
535
+ "webbycommerce.coupon.errors.invalid": "Invalid coupon code",
536
+ "webbycommerce.coupon.errors.required": "Coupon code is required",
537
+ "webbycommerce.coupon.errors.inactive": "This coupon is not active",
538
+ "webbycommerce.coupon.errors.expired": "This coupon has expired",
539
+ "webbycommerce.coupon.errors.usageLimit": "This coupon has reached its usage limit",
540
+ "webbycommerce.coupon.errors.minimumOrder": "Minimum order amount of {amount} required for this coupon",
541
+ "webbycommerce.coupon.success.applied": "Coupon applied successfully",
542
+ "webbycommerce.coupon.success.removed": "Coupon removed successfully",
543
+ "webbycommerce.coupon.types.percentage": "Percentage",
544
+ "webbycommerce.coupon.types.fixed": "Fixed Amount",
545
+ "webbycommerce.coupon.fields.code": "Coupon Code",
546
+ "webbycommerce.coupon.fields.type": "Discount Type",
547
+ "webbycommerce.coupon.fields.value": "Discount Value",
548
+ "webbycommerce.coupon.fields.description": "Description",
549
+ "webbycommerce.coupon.fields.usageLimit": "Usage Limit",
550
+ "webbycommerce.coupon.fields.usedCount": "Times Used",
551
+ "webbycommerce.coupon.fields.minimumOrder": "Minimum Order Amount",
552
+ "webbycommerce.coupon.fields.expiresAt": "Expires At",
553
+ "webbycommerce.coupon.fields.isActive": "Active Status"
501
554
  };
502
555
  export {
503
556
  en as default