@ikas/code-components-mcp 1.4.0-beta.16 → 1.4.0-beta.160

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 (72) hide show
  1. package/data/framework.json +103 -16
  2. package/data/migration.json +10 -4
  3. package/data/section-templates/account-info-section/children/AccountFavorites/ikas-config-snippet.json +13 -0
  4. package/data/section-templates/account-info-section/ikas-config-snippet.json +19 -0
  5. package/data/section-templates/add-to-cart/ikas-config-snippet.json +17 -0
  6. package/data/section-templates/add-to-cart/index.tsx +16 -2
  7. package/data/section-templates/add-to-cart/sub-components/QuantitySelector/index.tsx +11 -3
  8. package/data/section-templates/add-to-cart/utils/cartLimits.ts +23 -0
  9. package/data/section-templates/blog-home-section/ikas-config-snippet.json +2 -0
  10. package/data/section-templates/blog-post-section/ikas-config-snippet.json +2 -0
  11. package/data/section-templates/bundle-products/hooks/useBundleProducts.ts +7 -6
  12. package/data/section-templates/bundle-products/ikas-config-snippet.json +20 -0
  13. package/data/section-templates/cart-section/sub-components/CartItem/index.tsx +57 -24
  14. package/data/section-templates/cart-section/sub-components/CartItem/styles.css +14 -0
  15. package/data/section-templates/cart-section/sub-components/QuantitySelector/index.tsx +11 -3
  16. package/data/section-templates/category-images-section/children/CategoryImageItem/ikas-config-snippet.json +4 -0
  17. package/data/section-templates/category-images-section/ikas-config-snippet.json +15 -0
  18. package/data/section-templates/category-list-section/children/CardProductName/ikas-config-snippet.json +2 -1
  19. package/data/section-templates/category-list-section/ikas-config-snippet.json +21 -0
  20. package/data/section-templates/component-renderer/ikas-config-snippet.json +13 -0
  21. package/data/section-templates/favorites/ikas-config-snippet.json +18 -1
  22. package/data/section-templates/features-section/ikas-config-snippet.json +13 -0
  23. package/data/section-templates/footer-section/ikas-config-snippet.json +15 -0
  24. package/data/section-templates/header-section/children/Announcements/ikas-config-snippet.json +7 -0
  25. package/data/section-templates/header-section/children/CookieBar/ikas-config-snippet.json +3 -0
  26. package/data/section-templates/header-section/children/Navbar/ikas-config-snippet.json +27 -0
  27. package/data/section-templates/header-section/ikas-config-snippet.json +13 -0
  28. package/data/section-templates/header-section/sub-components/CartItem/index.tsx +57 -24
  29. package/data/section-templates/header-section/sub-components/CartItem/styles.css +14 -0
  30. package/data/section-templates/header-section/sub-components/QuantitySelector/index.tsx +11 -3
  31. package/data/section-templates/hero-slider-section/children/HeroSliderItem/ikas-config-snippet.json +18 -0
  32. package/data/section-templates/hero-slider-section/ikas-config-snippet.json +13 -0
  33. package/data/section-templates/image-handling/ikas-config-snippet.json +53 -0
  34. package/data/section-templates/login-section/ikas-config-snippet.json +2 -0
  35. package/data/section-templates/navigation/ikas-config-snippet.json +27 -0
  36. package/data/section-templates/navigation/sub-components/CartItem/index.tsx +57 -24
  37. package/data/section-templates/navigation/sub-components/CartItem/styles.css +14 -0
  38. package/data/section-templates/navigation/sub-components/QuantitySelector/index.tsx +11 -3
  39. package/data/section-templates/product-detail-section/children/ProductDetailAddToCart/ikas-config-snippet.json +17 -0
  40. package/data/section-templates/product-detail-section/children/ProductDetailAddToCart/index.tsx +16 -2
  41. package/data/section-templates/product-detail-section/children/ProductDetailBackInStock/components/BackInStockForm/index.tsx +92 -0
  42. package/data/section-templates/product-detail-section/children/ProductDetailBackInStock/ikas-config-snippet.json +175 -0
  43. package/data/section-templates/product-detail-section/children/ProductDetailBackInStock/index.tsx +141 -0
  44. package/data/section-templates/product-detail-section/children/ProductDetailBackInStock/styles.css +47 -0
  45. package/data/section-templates/product-detail-section/children/ProductDetailBackInStock/types.ts +25 -0
  46. package/data/section-templates/product-detail-section/children/ProductDetailBundleFurniture/ikas-config-snippet.json +18 -0
  47. package/data/section-templates/product-detail-section/children/ProductDetailBundleProduct/ikas-config-snippet.json +20 -0
  48. package/data/section-templates/product-detail-section/children/ProductDetailDescription/ikas-config-snippet.json +27 -0
  49. package/data/section-templates/product-detail-section/children/ProductDetailFeatures/ikas-config-snippet.json +29 -0
  50. package/data/section-templates/product-detail-section/children/ProductDetailNameFavorite/ikas-config-snippet.json +18 -1
  51. package/data/section-templates/product-detail-section/children/ProductDetailOffer/ikas-config-snippet.json +20 -0
  52. package/data/section-templates/product-detail-section/children/ProductDetailOptionSet/ikas-config-snippet.json +16 -0
  53. package/data/section-templates/product-detail-section/children/ProductDetailPrices/ikas-config-snippet.json +16 -0
  54. package/data/section-templates/product-detail-section/children/ProductDetailProductGroup/ikas-config-snippet.json +16 -0
  55. package/data/section-templates/product-detail-section/children/ProductDetailSku/ikas-config-snippet.json +16 -0
  56. package/data/section-templates/product-detail-section/children/ProductDetailVariant/ikas-config-snippet.json +17 -0
  57. package/data/section-templates/product-detail-section/hooks/useBundleProducts.ts +7 -6
  58. package/data/section-templates/product-detail-section/ikas-config-snippet.json +53 -0
  59. package/data/section-templates/product-detail-section/sub-components/Modal/index.tsx +120 -0
  60. package/data/section-templates/product-detail-section/sub-components/Modal/styles.css +70 -0
  61. package/data/section-templates/product-detail-section/sub-components/QuantitySelector/index.tsx +11 -3
  62. package/data/section-templates/product-detail-section/utils/cartLimits.ts +23 -0
  63. package/data/section-templates/product-pricing/ikas-config-snippet.json +16 -0
  64. package/data/section-templates/product-slider-section/children/CardProductName/ikas-config-snippet.json +2 -1
  65. package/data/section-templates/product-slider-section/ikas-config-snippet.json +17 -0
  66. package/data/section-templates/register-section/ikas-config-snippet.json +2 -0
  67. package/data/section-templates/variant-selection/ikas-config-snippet.json +17 -0
  68. package/data/storefront-api.json +239 -27
  69. package/data/storefront-types.json +9071 -5740
  70. package/dist/index.js +1242 -28
  71. package/dist/index.js.map +1 -1
  72. package/package.json +1 -1
@@ -25,6 +25,7 @@
25
25
  "displayName": "Image Fit",
26
26
  "type": "ENUM",
27
27
  "required": false,
28
+ "defaultValue": "Cover",
28
29
  "groupId": "desktop",
29
30
  "enumTypeId": "{{ENUM_ID:ObjectFit}}"
30
31
  },
@@ -40,6 +41,7 @@
40
41
  "displayName": "Title Alignment",
41
42
  "type": "ENUM",
42
43
  "required": false,
44
+ "defaultValue": "Middle",
43
45
  "groupId": "desktop",
44
46
  "enumTypeId": "{{ENUM_ID:VerticalAlignment}}"
45
47
  },
@@ -48,6 +50,7 @@
48
50
  "displayName": "Text Color",
49
51
  "type": "COLOR",
50
52
  "required": false,
53
+ "defaultValue": "#FFFFFF",
51
54
  "groupId": "desktop"
52
55
  },
53
56
  {
@@ -55,6 +58,7 @@
55
58
  "displayName": "Overlay",
56
59
  "type": "BOOLEAN",
57
60
  "required": false,
61
+ "defaultValue": false,
58
62
  "groupId": "desktop"
59
63
  },
60
64
  {
@@ -62,6 +66,7 @@
62
66
  "displayName": "Rounded Corners",
63
67
  "type": "BOOLEAN",
64
68
  "required": false,
69
+ "defaultValue": false,
65
70
  "groupId": "desktop"
66
71
  },
67
72
  {
@@ -84,6 +89,7 @@
84
89
  "displayName": "Image Fit",
85
90
  "type": "ENUM",
86
91
  "required": false,
92
+ "defaultValue": "Cover",
87
93
  "groupId": "mobile",
88
94
  "enumTypeId": "{{ENUM_ID:ObjectFit}}"
89
95
  },
@@ -99,6 +105,7 @@
99
105
  "displayName": "Title Alignment",
100
106
  "type": "ENUM",
101
107
  "required": false,
108
+ "defaultValue": "Top",
102
109
  "groupId": "mobile",
103
110
  "enumTypeId": "{{ENUM_ID:VerticalAlignment}}"
104
111
  },
@@ -107,6 +114,7 @@
107
114
  "displayName": "Text Color",
108
115
  "type": "COLOR",
109
116
  "required": false,
117
+ "defaultValue": "#FFFFFF",
110
118
  "groupId": "mobile"
111
119
  },
112
120
  {
@@ -114,6 +122,7 @@
114
122
  "displayName": "Overlay",
115
123
  "type": "BOOLEAN",
116
124
  "required": false,
125
+ "defaultValue": false,
117
126
  "groupId": "mobile"
118
127
  },
119
128
  {
@@ -121,6 +130,7 @@
121
130
  "displayName": "Rounded Corners",
122
131
  "type": "BOOLEAN",
123
132
  "required": false,
133
+ "defaultValue": false,
124
134
  "groupId": "mobile"
125
135
  },
126
136
  {
@@ -143,6 +153,7 @@
143
153
  "displayName": "Color",
144
154
  "type": "COLOR",
145
155
  "required": false,
156
+ "defaultValue": "#14141a",
146
157
  "groupId": "desktopButton"
147
158
  },
148
159
  {
@@ -150,6 +161,7 @@
150
161
  "displayName": "Background Color",
151
162
  "type": "COLOR",
152
163
  "required": false,
164
+ "defaultValue": "#ffffff",
153
165
  "groupId": "desktopButton"
154
166
  },
155
167
  {
@@ -157,6 +169,7 @@
157
169
  "displayName": "Color (Hover)",
158
170
  "type": "COLOR",
159
171
  "required": false,
172
+ "defaultValue": "#14141a",
160
173
  "groupId": "desktopButton"
161
174
  },
162
175
  {
@@ -164,6 +177,7 @@
164
177
  "displayName": "Background Color (Hover)",
165
178
  "type": "COLOR",
166
179
  "required": false,
180
+ "defaultValue": "#fafafa",
167
181
  "groupId": "desktopButton"
168
182
  },
169
183
  {
@@ -186,6 +200,7 @@
186
200
  "displayName": "Color",
187
201
  "type": "COLOR",
188
202
  "required": false,
203
+ "defaultValue": "#14141a",
189
204
  "groupId": "mobileButton"
190
205
  },
191
206
  {
@@ -193,6 +208,7 @@
193
208
  "displayName": "Background Color",
194
209
  "type": "COLOR",
195
210
  "required": false,
211
+ "defaultValue": "#ffffff",
196
212
  "groupId": "mobileButton"
197
213
  },
198
214
  {
@@ -200,6 +216,7 @@
200
216
  "displayName": "Color (Hover)",
201
217
  "type": "COLOR",
202
218
  "required": false,
219
+ "defaultValue": "#14141a",
203
220
  "groupId": "mobileButton"
204
221
  },
205
222
  {
@@ -207,6 +224,7 @@
207
224
  "displayName": "Background Color (Hover)",
208
225
  "type": "COLOR",
209
226
  "required": false,
227
+ "defaultValue": "#fafafa",
210
228
  "groupId": "mobileButton"
211
229
  }
212
230
  ],
@@ -18,6 +18,7 @@
18
18
  "displayName": "Autoplay",
19
19
  "type": "BOOLEAN",
20
20
  "required": false,
21
+ "defaultValue": false,
21
22
  "groupId": "settings"
22
23
  },
23
24
  {
@@ -33,6 +34,7 @@
33
34
  "displayName": "Full Width",
34
35
  "type": "BOOLEAN",
35
36
  "required": false,
37
+ "defaultValue": false,
36
38
  "groupId": "settings"
37
39
  },
38
40
  {
@@ -40,6 +42,7 @@
40
42
  "displayName": "Show Arrows",
41
43
  "type": "BOOLEAN",
42
44
  "required": false,
45
+ "defaultValue": false,
43
46
  "groupId": "settings"
44
47
  },
45
48
  {
@@ -47,6 +50,16 @@
47
50
  "displayName": "Slides",
48
51
  "type": "COMPONENT_LIST",
49
52
  "required": false,
53
+ "defaultValue": {
54
+ "components": [
55
+ {
56
+ "codeComponentId": "nb34u3yu-hero-slide"
57
+ },
58
+ {
59
+ "codeComponentId": "nb34u3yu-hero-slide"
60
+ }
61
+ ]
62
+ },
50
63
  "filteredComponentIds": [
51
64
  "<id-of-HeroSliderItem>"
52
65
  ]
@@ -16,6 +16,7 @@
16
16
  "displayName": "Aspect Ratio",
17
17
  "type": "ENUM",
18
18
  "required": false,
19
+ "defaultValue": "Square",
19
20
  "groupId": "image-settings",
20
21
  "enumTypeId": "{{ENUM_ID:AspectRatio}}"
21
22
  },
@@ -24,6 +25,7 @@
24
25
  "displayName": "Image Fit",
25
26
  "type": "ENUM",
26
27
  "required": false,
28
+ "defaultValue": "Cover",
27
29
  "groupId": "image-settings",
28
30
  "enumTypeId": "{{ENUM_ID:ObjectFit}}"
29
31
  },
@@ -32,6 +34,46 @@
32
34
  "displayName": "Info Components",
33
35
  "type": "COMPONENT_LIST",
34
36
  "required": false,
37
+ "defaultValue": {
38
+ "components": [
39
+ {
40
+ "codeComponentId": "nb34u3yu-product-detail-name-favorite"
41
+ },
42
+ {
43
+ "codeComponentId": "nb34u3yu-product-detail-sku"
44
+ },
45
+ {
46
+ "codeComponentId": "nb34u3yu-product-detail-prices"
47
+ },
48
+ {
49
+ "codeComponentId": "nb34u3yu-product-detail-product-group"
50
+ },
51
+ {
52
+ "codeComponentId": "nb34u3yu-product-detail-bundle-product"
53
+ },
54
+ {
55
+ "codeComponentId": "nb34u3yu-product-detail-variant"
56
+ },
57
+ {
58
+ "codeComponentId": "nb34u3yu-product-detail-option-set"
59
+ },
60
+ {
61
+ "codeComponentId": "nb34u3yu-product-detail-add-to-cart"
62
+ },
63
+ {
64
+ "codeComponentId": "nb34u3yu-product-detail-back-in-stock"
65
+ },
66
+ {
67
+ "codeComponentId": "nb34u3yu-product-detail-offer"
68
+ },
69
+ {
70
+ "codeComponentId": "nb34u3yu-product-detail-features"
71
+ },
72
+ {
73
+ "codeComponentId": "nb34u3yu-product-detail-description"
74
+ }
75
+ ]
76
+ },
35
77
  "filteredComponentIds": [
36
78
  "<id-of-ProductDetailNameFavorite>",
37
79
  "<id-of-ProductDetailSku>",
@@ -39,6 +81,7 @@
39
81
  "<id-of-ProductDetailProductGroup>",
40
82
  "<id-of-ProductDetailVariant>",
41
83
  "<id-of-ProductDetailAddToCart>",
84
+ "<id-of-ProductDetailBackInStock>",
42
85
  "<id-of-ProductDetailFeatures>",
43
86
  "<id-of-ProductDetailDescription>",
44
87
  "<id-of-ProductDetailBundleProduct>",
@@ -51,6 +94,16 @@
51
94
  "displayName": "Sub Components",
52
95
  "type": "COMPONENT_LIST",
53
96
  "required": false,
97
+ "defaultValue": {
98
+ "components": [
99
+ {
100
+ "codeComponentId": "nb34u3yu-product-detail-bundle-furniture"
101
+ },
102
+ {
103
+ "codeComponentId": "nb34u3yu-product-detail-offer"
104
+ }
105
+ ]
106
+ },
54
107
  "filteredComponentIds": [
55
108
  "<id-of-ProductDetailBundleFurniture>",
56
109
  "<id-of-ProductDetailOffer>"
@@ -90,6 +90,7 @@
90
90
  "displayName": "Google Login",
91
91
  "type": "BOOLEAN",
92
92
  "required": false,
93
+ "defaultValue": false,
93
94
  "groupId": "google"
94
95
  },
95
96
  {
@@ -105,6 +106,7 @@
105
106
  "displayName": "Facebook Login",
106
107
  "type": "BOOLEAN",
107
108
  "required": false,
109
+ "defaultValue": false,
108
110
  "groupId": "facebook"
109
111
  },
110
112
  {
@@ -17,6 +17,10 @@
17
17
  "displayName": "Desktop Logo Size",
18
18
  "type": "TYPE",
19
19
  "required": false,
20
+ "defaultValue": {
21
+ "value": 60,
22
+ "unit": "px"
23
+ },
20
24
  "groupId": "logo-settings",
21
25
  "typeId": "@ikas/bp-storefront-models-HeightStyleType"
22
26
  },
@@ -25,6 +29,10 @@
25
29
  "displayName": "Mobile Logo Size",
26
30
  "type": "TYPE",
27
31
  "required": false,
32
+ "defaultValue": {
33
+ "value": 48,
34
+ "unit": "px"
35
+ },
28
36
  "groupId": "logo-settings",
29
37
  "typeId": "@ikas/bp-storefront-models-HeightStyleType"
30
38
  },
@@ -40,6 +48,7 @@
40
48
  "displayName": "Link Color",
41
49
  "type": "COLOR",
42
50
  "required": false,
51
+ "defaultValue": "#2e2e33",
43
52
  "groupId": "navigation"
44
53
  },
45
54
  {
@@ -54,6 +63,7 @@
54
63
  "displayName": "Link Color",
55
64
  "type": "COLOR",
56
65
  "required": false,
66
+ "defaultValue": "#2e2e33",
57
67
  "groupId": "colored-links"
58
68
  },
59
69
  {
@@ -140,6 +150,7 @@
140
150
  "displayName": "Hide Add to Cart Button",
141
151
  "type": "BOOLEAN",
142
152
  "required": false,
153
+ "defaultValue": false,
143
154
  "groupId": "search-card-settings"
144
155
  },
145
156
  {
@@ -219,6 +230,19 @@
219
230
  "displayName": "Components",
220
231
  "type": "COMPONENT_LIST",
221
232
  "required": false,
233
+ "defaultValue": {
234
+ "components": [
235
+ {
236
+ "codeComponentId": "nb34u3yu-card-product-name"
237
+ },
238
+ {
239
+ "codeComponentId": "nb34u3yu-card-product-variants"
240
+ },
241
+ {
242
+ "codeComponentId": "nb34u3yu-card-product-price"
243
+ }
244
+ ]
245
+ },
222
246
  "privateVarMap": {
223
247
  "product": {
224
248
  "id": "pvm_1772803767210_2",
@@ -236,6 +260,7 @@
236
260
  "displayName": "Image Ratio",
237
261
  "type": "ENUM",
238
262
  "required": false,
263
+ "defaultValue": "Square",
239
264
  "groupId": "search-image",
240
265
  "enumTypeId": "{{ENUM_ID:AspectRatio}}"
241
266
  },
@@ -244,6 +269,7 @@
244
269
  "displayName": "Image Fit",
245
270
  "type": "ENUM",
246
271
  "required": false,
272
+ "defaultValue": "Cover",
247
273
  "groupId": "search-image",
248
274
  "enumTypeId": "{{ENUM_ID:ObjectFit}}"
249
275
  },
@@ -260,6 +286,7 @@
260
286
  "displayName": "Enable Sticky",
261
287
  "type": "BOOLEAN",
262
288
  "required": false,
289
+ "defaultValue": false,
263
290
  "description": "When enabled, the navbar stays fixed at the top of the page and remains visible while scrolling",
264
291
  "groupId": "settings"
265
292
  },
@@ -14,6 +14,8 @@ import {
14
14
  getOrderLineItemFormattedOverridenPriceWithQuantity,
15
15
  hasOrderLineItemDiscount,
16
16
  getOrderLineItemOverridenPriceWithQuantity,
17
+ cartStore,
18
+ isOrderLineItemAutoCreated,
17
19
  } from "@ikas/bp-storefront";
18
20
  import { resolveAspectRatio, resolveObjectFit } from "../../utils/media";
19
21
  import { cx } from "../../utils/cx";
@@ -25,6 +27,7 @@ interface CartItemProps {
25
27
  variant?: "sidebar" | "page";
26
28
  aspectRatio?: AspectRatio;
27
29
  objectFit?: ObjectFit;
30
+ giftLabel?: string;
28
31
  }
29
32
 
30
33
  const CartItem = observer(function CartItem({
@@ -33,9 +36,25 @@ const CartItem = observer(function CartItem({
33
36
  variant = "sidebar",
34
37
  aspectRatio,
35
38
  objectFit,
39
+ giftLabel = "Gift",
36
40
  }: CartItemProps) {
37
41
  const isPage = variant === "page";
38
42
 
43
+ // Lines a campaign added automatically (e.g. a "buy 1 get 1" gift) cannot be
44
+ // edited or removed on their own, so render them read-only with a badge.
45
+ const isAutoCreated =
46
+ !!cartStore.cart && isOrderLineItemAutoCreated(cartStore.cart, item);
47
+ const giftBadge = isAutoCreated ? (
48
+ <span className="kombos-cart-item__gift-badge text-xs-medium">
49
+ {giftLabel}
50
+ </span>
51
+ ) : null;
52
+ const staticQuantity = (
53
+ <span className="kombos-cart-item__qty-static text-sm-medium">
54
+ ×{item.quantity}
55
+ </span>
56
+ );
57
+
39
58
  const mediaStyle: Record<string, string> = {
40
59
  aspectRatio: resolveAspectRatio(aspectRatio),
41
60
  objectFit: resolveObjectFit(objectFit),
@@ -103,6 +122,7 @@ const CartItem = observer(function CartItem({
103
122
  <a href={href} className={nameClass}>
104
123
  {item.variant?.name}
105
124
  </a>
125
+ {giftBadge}
106
126
  {variantValues.length > 0 && (
107
127
  <p className="kombos-cart-item__variant-text text-sm-regular">
108
128
  {variantValues
@@ -113,19 +133,22 @@ const CartItem = observer(function CartItem({
113
133
  </p>
114
134
  )}
115
135
  </div>
116
- <button
117
- className="kombos-cart-item__remove"
118
- onClick={() => onRemove(item)}
119
- aria-label="Remove"
120
- >
121
- <TrashSVG />
122
- </button>
136
+ {!isAutoCreated && (
137
+ <button
138
+ className="kombos-cart-item__remove"
139
+ onClick={() => onRemove(item)}
140
+ aria-label="Remove"
141
+ >
142
+ <TrashSVG />
143
+ </button>
144
+ )}
123
145
  </div>
124
146
  ) : (
125
147
  <>
126
148
  <a href={href} className={nameClass}>
127
149
  {item.variant?.name}
128
150
  </a>
151
+ {giftBadge}
129
152
 
130
153
  {variantValues.length > 0 && (
131
154
  <div className="kombos-cart-item__variants">
@@ -151,11 +174,15 @@ const CartItem = observer(function CartItem({
151
174
 
152
175
  {isPage ? (
153
176
  <div className="kombos-cart-item__bottom">
154
- <QuantitySelector
155
- size="sm"
156
- value={item.quantity}
157
- onChange={(qty) => changeItemQuantity(item, qty)}
158
- />
177
+ {isAutoCreated ? (
178
+ staticQuantity
179
+ ) : (
180
+ <QuantitySelector
181
+ size="sm"
182
+ value={item.quantity}
183
+ onChange={(qty) => changeItemQuantity(item, qty)}
184
+ />
185
+ )}
159
186
  <div className="kombos-cart-item__prices">
160
187
  {hasDiscount && (
161
188
  <span className="kombos-cart-item__original-price text-sm-regular-strike">
@@ -193,18 +220,24 @@ const CartItem = observer(function CartItem({
193
220
  </div>
194
221
 
195
222
  <div className="kombos-cart-item__actions">
196
- <QuantitySelector
197
- size="sm"
198
- value={item.quantity}
199
- onChange={(qty) => changeItemQuantity(item, qty)}
200
- />
201
- <button
202
- className="kombos-cart-item__remove"
203
- onClick={() => onRemove(item)}
204
- aria-label="Remove"
205
- >
206
- <TrashSVG />
207
- </button>
223
+ {isAutoCreated ? (
224
+ staticQuantity
225
+ ) : (
226
+ <>
227
+ <QuantitySelector
228
+ size="sm"
229
+ value={item.quantity}
230
+ onChange={(qty) => changeItemQuantity(item, qty)}
231
+ />
232
+ <button
233
+ className="kombos-cart-item__remove"
234
+ onClick={() => onRemove(item)}
235
+ aria-label="Remove"
236
+ >
237
+ <TrashSVG />
238
+ </button>
239
+ </>
240
+ )}
208
241
  </div>
209
242
  </>
210
243
  )}
@@ -168,3 +168,17 @@
168
168
  flex-shrink: 0;
169
169
  }
170
170
 
171
+ /* Auto-created (campaign gift) line: badge + read-only quantity */
172
+ .kombos-cart-item__gift-badge {
173
+ display: inline-flex;
174
+ align-self: flex-start;
175
+ padding: 0.125rem 0.5rem;
176
+ border-radius: 999px;
177
+ background: var(--kombos-gray-100);
178
+ color: var(--kombos-gray-500);
179
+ }
180
+
181
+ .kombos-cart-item__qty-static {
182
+ color: var(--kombos-gray-500);
183
+ }
184
+
@@ -5,20 +5,27 @@ interface Props {
5
5
  value: number;
6
6
  onChange: (value: number) => void;
7
7
  size?: "default" | "sm";
8
+ min?: number;
9
+ max?: number;
8
10
  }
9
11
 
10
12
  export default function QuantitySelector({
11
13
  value,
12
14
  onChange,
13
15
  size = "default",
16
+ min = 1,
17
+ max,
14
18
  }: Props) {
19
+ const canDecrease = value > min;
20
+ const canIncrease = max == null || value < max;
21
+
15
22
  return (
16
23
  <div className={cx("kombos-pd__qty", size === "sm" && "kombos-pd__qty--sm")}>
17
24
  <button
18
25
  type="button"
19
26
  className="kombos-pd__qty-btn"
20
- onClick={() => onChange(Math.max(1, value - 1))}
21
- disabled={value <= 1}
27
+ onClick={() => onChange(Math.max(min, value - 1))}
28
+ disabled={!canDecrease}
22
29
  aria-label="Decrease quantity"
23
30
  >
24
31
  <MinusSVG />
@@ -31,7 +38,8 @@ export default function QuantitySelector({
31
38
  <button
32
39
  type="button"
33
40
  className="kombos-pd__qty-btn"
34
- onClick={() => onChange(value + 1)}
41
+ onClick={() => onChange(max == null ? value + 1 : Math.min(max, value + 1))}
42
+ disabled={!canIncrease}
35
43
  aria-label="Increase quantity"
36
44
  >
37
45
  <PlusSVG />
@@ -40,6 +40,10 @@
40
40
  "displayName": "Top Spacing",
41
41
  "type": "TYPE",
42
42
  "required": false,
43
+ "defaultValue": {
44
+ "value": 0,
45
+ "unit": "px"
46
+ },
43
47
  "groupId": "mobile",
44
48
  "typeId": "@ikas/bp-storefront-models-MarginTopStyleType"
45
49
  },
@@ -48,6 +52,10 @@
48
52
  "displayName": "Bottom Spacing",
49
53
  "type": "TYPE",
50
54
  "required": false,
55
+ "defaultValue": {
56
+ "value": 0,
57
+ "unit": "px"
58
+ },
51
59
  "groupId": "mobile",
52
60
  "typeId": "@ikas/bp-storefront-models-MarginBottomStyleType"
53
61
  },
@@ -56,6 +64,10 @@
56
64
  "displayName": "Top Spacing",
57
65
  "type": "TYPE",
58
66
  "required": false,
67
+ "defaultValue": {
68
+ "value": 0,
69
+ "unit": "px"
70
+ },
59
71
  "groupId": "desktop",
60
72
  "typeId": "@ikas/bp-storefront-models-MarginTopStyleType"
61
73
  },
@@ -64,6 +76,10 @@
64
76
  "displayName": "Bottom Spacing",
65
77
  "type": "TYPE",
66
78
  "required": false,
79
+ "defaultValue": {
80
+ "value": 0,
81
+ "unit": "px"
82
+ },
67
83
  "groupId": "desktop",
68
84
  "typeId": "@ikas/bp-storefront-models-MarginBottomStyleType"
69
85
  },
@@ -72,6 +88,7 @@
72
88
  "displayName": "Hide Quantity Selector",
73
89
  "type": "BOOLEAN",
74
90
  "required": false,
91
+ "defaultValue": false,
75
92
  "groupId": "settings"
76
93
  },
77
94
  {
@@ -10,13 +10,14 @@ import {
10
10
  IkasBundleSettings,
11
11
  IkasStorefrontConfig,
12
12
  } from "@ikas/bp-storefront";
13
- import { useState } from "preact/hooks";
13
+ import { useEffect, useState } from "preact/hooks";
14
14
  import { Props } from "./types";
15
15
  import Button from "../../sub-components/Button";
16
16
  import QuantitySelector from "../../sub-components/QuantitySelector";
17
17
  import PayWithIkas from "./components/PayWithIkas";
18
18
  import { isBundleOutOfStock } from "../../utils/bundle";
19
19
  import { validateOptionSet } from "../../utils/optionSet";
20
+ import { getProductCartLimits } from "../../utils/cartLimits";
20
21
  import { showToast } from "../../utils/toast";
21
22
 
22
23
  export function ProductDetailAddToCart({
@@ -37,6 +38,12 @@ export function ProductDetailAddToCart({
37
38
  const [quantity, setQuantity] = useState(1);
38
39
  const [isAddingToCart, setIsAddingToCart] = useState(false);
39
40
 
41
+ // Seed the quantity to the product's minimum-per-cart when the product changes.
42
+ useEffect(() => {
43
+ if (!product) return;
44
+ setQuantity(getProductCartLimits(product).min);
45
+ }, [product?.id]);
46
+
40
47
  if (!product) return null;
41
48
 
42
49
  const editLineID =
@@ -59,6 +66,8 @@ export function ProductDetailAddToCart({
59
66
 
60
67
  const isDisabled = isOutOfStock || isAddingToCart;
61
68
 
69
+ const { min: minQuantity, max: maxQuantity } = getProductCartLimits(product);
70
+
62
71
  const payWithIkasUrl = IkasStorefrontConfig.getPayWithIkasUrl();
63
72
  const routing = IkasStorefrontConfig.getCurrentRouting();
64
73
  const showPayWithIkas =
@@ -119,7 +128,12 @@ export function ProductDetailAddToCart({
119
128
  >
120
129
  <div className="kombos-pd-atc__actions">
121
130
  {!hideQuantityInput && !isOutOfStock && (
122
- <QuantitySelector value={quantity} onChange={setQuantity} />
131
+ <QuantitySelector
132
+ value={quantity}
133
+ onChange={setQuantity}
134
+ min={minQuantity}
135
+ max={maxQuantity}
136
+ />
123
137
  )}
124
138
  <Button
125
139
  variant="primary"