@ikas/code-components-mcp 1.4.0-beta.139 → 1.4.0-beta.140

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 (28) hide show
  1. package/data/framework.json +2 -32
  2. package/data/section-templates/add-to-cart/index.tsx +2 -16
  3. package/data/section-templates/add-to-cart/sub-components/QuantitySelector/index.tsx +3 -11
  4. package/data/section-templates/cart-section/sub-components/CartItem/index.tsx +24 -57
  5. package/data/section-templates/cart-section/sub-components/CartItem/styles.css +0 -14
  6. package/data/section-templates/cart-section/sub-components/QuantitySelector/index.tsx +3 -11
  7. package/data/section-templates/header-section/sub-components/CartItem/index.tsx +24 -57
  8. package/data/section-templates/header-section/sub-components/CartItem/styles.css +0 -14
  9. package/data/section-templates/header-section/sub-components/QuantitySelector/index.tsx +3 -11
  10. package/data/section-templates/image-handling/ikas-config-snippet.json +0 -1
  11. package/data/section-templates/navigation/sub-components/CartItem/index.tsx +24 -57
  12. package/data/section-templates/navigation/sub-components/CartItem/styles.css +0 -14
  13. package/data/section-templates/navigation/sub-components/QuantitySelector/index.tsx +3 -11
  14. package/data/section-templates/product-detail-section/children/ProductDetailAddToCart/index.tsx +2 -16
  15. package/data/section-templates/product-detail-section/ikas-config-snippet.json +0 -1
  16. package/data/section-templates/product-detail-section/sub-components/QuantitySelector/index.tsx +3 -11
  17. package/data/storefront-api.json +1 -1
  18. package/data/storefront-types.json +1 -1
  19. package/package.json +1 -1
  20. package/data/section-templates/add-to-cart/utils/cartLimits.ts +0 -23
  21. package/data/section-templates/product-detail-section/children/ProductDetailBackInStock/components/BackInStockForm/index.tsx +0 -92
  22. package/data/section-templates/product-detail-section/children/ProductDetailBackInStock/ikas-config-snippet.json +0 -159
  23. package/data/section-templates/product-detail-section/children/ProductDetailBackInStock/index.tsx +0 -141
  24. package/data/section-templates/product-detail-section/children/ProductDetailBackInStock/styles.css +0 -47
  25. package/data/section-templates/product-detail-section/children/ProductDetailBackInStock/types.ts +0 -25
  26. package/data/section-templates/product-detail-section/sub-components/Modal/index.tsx +0 -120
  27. package/data/section-templates/product-detail-section/sub-components/Modal/styles.css +0 -70
  28. package/data/section-templates/product-detail-section/utils/cartLimits.ts +0 -23
@@ -14,8 +14,6 @@ import {
14
14
  getOrderLineItemFormattedOverridenPriceWithQuantity,
15
15
  hasOrderLineItemDiscount,
16
16
  getOrderLineItemOverridenPriceWithQuantity,
17
- cartStore,
18
- isOrderLineItemAutoCreated,
19
17
  } from "@ikas/bp-storefront";
20
18
  import { resolveAspectRatio, resolveObjectFit } from "../../utils/media";
21
19
  import { cx } from "../../utils/cx";
@@ -27,7 +25,6 @@ interface CartItemProps {
27
25
  variant?: "sidebar" | "page";
28
26
  aspectRatio?: AspectRatio;
29
27
  objectFit?: ObjectFit;
30
- giftLabel?: string;
31
28
  }
32
29
 
33
30
  const CartItem = observer(function CartItem({
@@ -36,25 +33,9 @@ const CartItem = observer(function CartItem({
36
33
  variant = "sidebar",
37
34
  aspectRatio,
38
35
  objectFit,
39
- giftLabel = "Gift",
40
36
  }: CartItemProps) {
41
37
  const isPage = variant === "page";
42
38
 
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
-
58
39
  const mediaStyle: Record<string, string> = {
59
40
  aspectRatio: resolveAspectRatio(aspectRatio),
60
41
  objectFit: resolveObjectFit(objectFit),
@@ -122,7 +103,6 @@ const CartItem = observer(function CartItem({
122
103
  <a href={href} className={nameClass}>
123
104
  {item.variant?.name}
124
105
  </a>
125
- {giftBadge}
126
106
  {variantValues.length > 0 && (
127
107
  <p className="kombos-cart-item__variant-text text-sm-regular">
128
108
  {variantValues
@@ -133,22 +113,19 @@ const CartItem = observer(function CartItem({
133
113
  </p>
134
114
  )}
135
115
  </div>
136
- {!isAutoCreated && (
137
- <button
138
- className="kombos-cart-item__remove"
139
- onClick={() => onRemove(item)}
140
- aria-label="Remove"
141
- >
142
- <TrashSVG />
143
- </button>
144
- )}
116
+ <button
117
+ className="kombos-cart-item__remove"
118
+ onClick={() => onRemove(item)}
119
+ aria-label="Remove"
120
+ >
121
+ <TrashSVG />
122
+ </button>
145
123
  </div>
146
124
  ) : (
147
125
  <>
148
126
  <a href={href} className={nameClass}>
149
127
  {item.variant?.name}
150
128
  </a>
151
- {giftBadge}
152
129
 
153
130
  {variantValues.length > 0 && (
154
131
  <div className="kombos-cart-item__variants">
@@ -174,15 +151,11 @@ const CartItem = observer(function CartItem({
174
151
 
175
152
  {isPage ? (
176
153
  <div className="kombos-cart-item__bottom">
177
- {isAutoCreated ? (
178
- staticQuantity
179
- ) : (
180
- <QuantitySelector
181
- size="sm"
182
- value={item.quantity}
183
- onChange={(qty) => changeItemQuantity(item, qty)}
184
- />
185
- )}
154
+ <QuantitySelector
155
+ size="sm"
156
+ value={item.quantity}
157
+ onChange={(qty) => changeItemQuantity(item, qty)}
158
+ />
186
159
  <div className="kombos-cart-item__prices">
187
160
  {hasDiscount && (
188
161
  <span className="kombos-cart-item__original-price text-sm-regular-strike">
@@ -220,24 +193,18 @@ const CartItem = observer(function CartItem({
220
193
  </div>
221
194
 
222
195
  <div className="kombos-cart-item__actions">
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
- )}
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>
241
208
  </div>
242
209
  </>
243
210
  )}
@@ -168,17 +168,3 @@
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,27 +5,20 @@ interface Props {
5
5
  value: number;
6
6
  onChange: (value: number) => void;
7
7
  size?: "default" | "sm";
8
- min?: number;
9
- max?: number;
10
8
  }
11
9
 
12
10
  export default function QuantitySelector({
13
11
  value,
14
12
  onChange,
15
13
  size = "default",
16
- min = 1,
17
- max,
18
14
  }: Props) {
19
- const canDecrease = value > min;
20
- const canIncrease = max == null || value < max;
21
-
22
15
  return (
23
16
  <div className={cx("kombos-pd__qty", size === "sm" && "kombos-pd__qty--sm")}>
24
17
  <button
25
18
  type="button"
26
19
  className="kombos-pd__qty-btn"
27
- onClick={() => onChange(Math.max(min, value - 1))}
28
- disabled={!canDecrease}
20
+ onClick={() => onChange(Math.max(1, value - 1))}
21
+ disabled={value <= 1}
29
22
  aria-label="Decrease quantity"
30
23
  >
31
24
  <MinusSVG />
@@ -38,8 +31,7 @@ export default function QuantitySelector({
38
31
  <button
39
32
  type="button"
40
33
  className="kombos-pd__qty-btn"
41
- onClick={() => onChange(max == null ? value + 1 : Math.min(max, value + 1))}
42
- disabled={!canIncrease}
34
+ onClick={() => onChange(value + 1)}
43
35
  aria-label="Increase quantity"
44
36
  >
45
37
  <PlusSVG />
@@ -10,14 +10,13 @@ import {
10
10
  IkasBundleSettings,
11
11
  IkasStorefrontConfig,
12
12
  } from "@ikas/bp-storefront";
13
- import { useEffect, useState } from "preact/hooks";
13
+ import { 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";
21
20
  import { showToast } from "../../utils/toast";
22
21
 
23
22
  export function ProductDetailAddToCart({
@@ -38,12 +37,6 @@ export function ProductDetailAddToCart({
38
37
  const [quantity, setQuantity] = useState(1);
39
38
  const [isAddingToCart, setIsAddingToCart] = useState(false);
40
39
 
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
-
47
40
  if (!product) return null;
48
41
 
49
42
  const editLineID =
@@ -66,8 +59,6 @@ export function ProductDetailAddToCart({
66
59
 
67
60
  const isDisabled = isOutOfStock || isAddingToCart;
68
61
 
69
- const { min: minQuantity, max: maxQuantity } = getProductCartLimits(product);
70
-
71
62
  const payWithIkasUrl = IkasStorefrontConfig.getPayWithIkasUrl();
72
63
  const routing = IkasStorefrontConfig.getCurrentRouting();
73
64
  const showPayWithIkas =
@@ -128,12 +119,7 @@ export function ProductDetailAddToCart({
128
119
  >
129
120
  <div className="kombos-pd-atc__actions">
130
121
  {!hideQuantityInput && !isOutOfStock && (
131
- <QuantitySelector
132
- value={quantity}
133
- onChange={setQuantity}
134
- min={minQuantity}
135
- max={maxQuantity}
136
- />
122
+ <QuantitySelector value={quantity} onChange={setQuantity} />
137
123
  )}
138
124
  <Button
139
125
  variant="primary"
@@ -39,7 +39,6 @@
39
39
  "<id-of-ProductDetailProductGroup>",
40
40
  "<id-of-ProductDetailVariant>",
41
41
  "<id-of-ProductDetailAddToCart>",
42
- "<id-of-ProductDetailBackInStock>",
43
42
  "<id-of-ProductDetailFeatures>",
44
43
  "<id-of-ProductDetailDescription>",
45
44
  "<id-of-ProductDetailBundleProduct>",
@@ -5,27 +5,20 @@ interface Props {
5
5
  value: number;
6
6
  onChange: (value: number) => void;
7
7
  size?: "default" | "sm";
8
- min?: number;
9
- max?: number;
10
8
  }
11
9
 
12
10
  export default function QuantitySelector({
13
11
  value,
14
12
  onChange,
15
13
  size = "default",
16
- min = 1,
17
- max,
18
14
  }: Props) {
19
- const canDecrease = value > min;
20
- const canIncrease = max == null || value < max;
21
-
22
15
  return (
23
16
  <div className={cx("kombos-pd__qty", size === "sm" && "kombos-pd__qty--sm")}>
24
17
  <button
25
18
  type="button"
26
19
  className="kombos-pd__qty-btn"
27
- onClick={() => onChange(Math.max(min, value - 1))}
28
- disabled={!canDecrease}
20
+ onClick={() => onChange(Math.max(1, value - 1))}
21
+ disabled={value <= 1}
29
22
  aria-label="Decrease quantity"
30
23
  >
31
24
  <MinusSVG />
@@ -38,8 +31,7 @@ export default function QuantitySelector({
38
31
  <button
39
32
  type="button"
40
33
  className="kombos-pd__qty-btn"
41
- onClick={() => onChange(max == null ? value + 1 : Math.min(max, value + 1))}
42
- disabled={!canIncrease}
34
+ onClick={() => onChange(value + 1)}
43
35
  aria-label="Increase quantity"
44
36
  >
45
37
  <PlusSVG />
@@ -1,5 +1,5 @@
1
1
  {
2
- "generatedAt": "2026-07-24T12:21:21.621Z",
2
+ "generatedAt": "2026-07-24T12:41:17.826Z",
3
3
  "functions": [
4
4
  {
5
5
  "name": "apiListProductBrand",
@@ -1,5 +1,5 @@
1
1
  {
2
- "generatedAt": "2026-07-24T12:21:21.652Z",
2
+ "generatedAt": "2026-07-24T12:41:17.851Z",
3
3
  "types": [
4
4
  {
5
5
  "name": "TextElementTagTypes",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikas/code-components-mcp",
3
- "version": "1.4.0-beta.139",
3
+ "version": "1.4.0-beta.140",
4
4
  "description": "MCP server for ikas code components documentation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,23 +0,0 @@
1
- import { IkasProduct, IkasStorefrontConfig } from "@ikas/bp-storefront";
2
-
3
- export interface CartQuantityLimits {
4
- min: number;
5
- max?: number;
6
- }
7
-
8
- /**
9
- * Resolve the per-cart quantity limits for a product from its active sales
10
- * channel. Falls back to a minimum of 1 and no maximum when the channel or its
11
- * limits are not set. `addItemToCart` clamps to the same limits internally, so
12
- * applying these in the stepper keeps the UI honest instead of letting the
13
- * quantity snap silently.
14
- */
15
- export function getProductCartLimits(product: IkasProduct): CartQuantityLimits {
16
- const channel = product.salesChannels?.find(
17
- (sc) => sc.id === IkasStorefrontConfig.salesChannelId,
18
- );
19
- return {
20
- min: channel?.minQuantityPerCart ?? 1,
21
- max: channel?.maxQuantityPerCart ?? undefined,
22
- };
23
- }
@@ -1,92 +0,0 @@
1
- import {
2
- IkasProductVariant,
3
- getProductVariantBackInStockForm,
4
- initBackInStockNotificationForm,
5
- setBackInStockNotificationFormEmail,
6
- submitBackInStockNotificationForm,
7
- } from "@ikas/bp-storefront";
8
- import { observer } from "@ikas/component-utils";
9
- import { useEffect } from "preact/hooks";
10
- import Button from "../../../../sub-components/Button";
11
- import Input from "../../../../sub-components/Input";
12
- import FormItem from "../../../../sub-components/FormItem";
13
-
14
- interface Props {
15
- variant: IkasProductVariant;
16
- description: string;
17
- emailLabel: string;
18
- submitButtonText: string;
19
- submittingText: string;
20
- errorMessage: string;
21
- onSaved: () => void;
22
- }
23
-
24
- // observer() because it reads the MobX-backed notification form and must
25
- // re-render as the customer types and as the submit status flags change.
26
- const BackInStockForm = observer(function BackInStockForm({
27
- variant,
28
- description,
29
- emailLabel,
30
- submitButtonText,
31
- submittingText,
32
- errorMessage,
33
- onSaved,
34
- }: Props) {
35
- const form = getProductVariantBackInStockForm(variant);
36
-
37
- // Initialize once per variant (resets fields + validation flags).
38
- useEffect(() => {
39
- initBackInStockNotificationForm(form, variant);
40
- }, [form, variant]);
41
-
42
- const emailHasError = !!form.email?.hasError;
43
-
44
- const handleSubmit = async (e: Event) => {
45
- e.preventDefault();
46
- if (await submitBackInStockNotificationForm(form)) onSaved();
47
- };
48
-
49
- const handleEmailInput = (e: Event) =>
50
- setBackInStockNotificationFormEmail(
51
- form,
52
- (e.target as HTMLInputElement).value,
53
- );
54
-
55
- return (
56
- <form className="kombos-pd-bis__form" onSubmit={handleSubmit}>
57
- {description && <p className="kombos-pd-bis__desc">{description}</p>}
58
- <FormItem
59
- label={emailLabel}
60
- htmlFor="back-in-stock-email"
61
- status={emailHasError ? "error" : "default"}
62
- helper={emailHasError ? form.email?.message : undefined}
63
- >
64
- <Input
65
- id="back-in-stock-email"
66
- type="email"
67
- value={form.email?.value ?? ""}
68
- placeholder={form.email?.placeholder}
69
- status={emailHasError ? "error" : "default"}
70
- onInput={handleEmailInput}
71
- />
72
- </FormItem>
73
- {form.isFailure && (
74
- <p className="kombos-pd-bis__error">
75
- {form.responseMessage || errorMessage}
76
- </p>
77
- )}
78
- <Button
79
- variant="primary"
80
- size="s"
81
- type="submit"
82
- className="kombos-pd-bis__submit"
83
- loading={form.isSubmitting}
84
- disabled={form.isSubmitting}
85
- >
86
- {form.isSubmitting ? submittingText : submitButtonText}
87
- </Button>
88
- </form>
89
- );
90
- });
91
-
92
- export default BackInStockForm;
@@ -1,159 +0,0 @@
1
- {
2
- "id": "{{PROJECT_ID}}-product-detail-back-in-stock",
3
- "name": "ProductDetailBackInStock",
4
- "type": "component",
5
- "entry": "./src/components/ProductDetailBackInStock/index.tsx",
6
- "styles": "./src/components/ProductDetailBackInStock/styles.css",
7
- "props": [
8
- {
9
- "name": "product",
10
- "displayName": "Product",
11
- "type": "PRODUCT",
12
- "required": false
13
- },
14
- {
15
- "name": "notifyButtonText",
16
- "displayName": "Notify Button Text",
17
- "type": "TEXT",
18
- "required": false,
19
- "defaultValue": "Notify Me When Available",
20
- "groupId": "texts"
21
- },
22
- {
23
- "name": "reminderSavedText",
24
- "displayName": "Reminder Saved Text",
25
- "type": "TEXT",
26
- "required": false,
27
- "defaultValue": "We'll notify you",
28
- "groupId": "texts"
29
- },
30
- {
31
- "name": "alreadySavedMessage",
32
- "displayName": "Already Saved Message",
33
- "type": "TEXT",
34
- "required": false,
35
- "defaultValue": "You're already on the list — we'll email you when it's back.",
36
- "groupId": "texts"
37
- },
38
- {
39
- "name": "savedMessage",
40
- "displayName": "Saved Success Message",
41
- "type": "TEXT",
42
- "required": false,
43
- "defaultValue": "Great! We'll email you when this product is back in stock.",
44
- "groupId": "texts"
45
- },
46
- {
47
- "name": "loginRequiredMessage",
48
- "displayName": "Login Required Message",
49
- "type": "TEXT",
50
- "required": false,
51
- "defaultValue": "Please sign in so we can notify you.",
52
- "groupId": "texts"
53
- },
54
- {
55
- "name": "errorMessage",
56
- "displayName": "Error Message",
57
- "type": "TEXT",
58
- "required": false,
59
- "defaultValue": "Something went wrong. Please try again.",
60
- "groupId": "texts"
61
- },
62
- {
63
- "name": "modalTitle",
64
- "displayName": "Email Modal Title",
65
- "type": "TEXT",
66
- "required": false,
67
- "defaultValue": "Notify me when available",
68
- "groupId": "texts"
69
- },
70
- {
71
- "name": "modalDescription",
72
- "displayName": "Email Modal Description",
73
- "type": "TEXT",
74
- "required": false,
75
- "defaultValue": "Enter your email and we'll let you know the moment this product is back in stock.",
76
- "groupId": "texts"
77
- },
78
- {
79
- "name": "emailLabel",
80
- "displayName": "Email Field Label",
81
- "type": "TEXT",
82
- "required": false,
83
- "defaultValue": "Email",
84
- "groupId": "texts"
85
- },
86
- {
87
- "name": "submitButtonText",
88
- "displayName": "Submit Button Text",
89
- "type": "TEXT",
90
- "required": false,
91
- "defaultValue": "Notify Me",
92
- "groupId": "texts"
93
- },
94
- {
95
- "name": "submittingText",
96
- "displayName": "Submitting Text",
97
- "type": "TEXT",
98
- "required": false,
99
- "defaultValue": "Saving...",
100
- "groupId": "texts"
101
- },
102
- {
103
- "name": "mobileMarginTop",
104
- "displayName": "Top Spacing",
105
- "type": "TYPE",
106
- "required": false,
107
- "groupId": "mobile",
108
- "typeId": "@ikas/bp-storefront-models-MarginTopStyleType"
109
- },
110
- {
111
- "name": "mobileMarginBottom",
112
- "displayName": "Bottom Spacing",
113
- "type": "TYPE",
114
- "required": false,
115
- "groupId": "mobile",
116
- "typeId": "@ikas/bp-storefront-models-MarginBottomStyleType"
117
- },
118
- {
119
- "name": "desktopMarginTop",
120
- "displayName": "Top Spacing",
121
- "type": "TYPE",
122
- "required": false,
123
- "groupId": "desktop",
124
- "typeId": "@ikas/bp-storefront-models-MarginTopStyleType"
125
- },
126
- {
127
- "name": "desktopMarginBottom",
128
- "displayName": "Bottom Spacing",
129
- "type": "TYPE",
130
- "required": false,
131
- "groupId": "desktop",
132
- "typeId": "@ikas/bp-storefront-models-MarginBottomStyleType"
133
- }
134
- ],
135
- "propGroups": [
136
- {
137
- "id": "appearance",
138
- "name": "Appearance Settings",
139
- "children": [
140
- {
141
- "id": "mobile",
142
- "name": "Mobile View",
143
- "description": "Margin settings for mobile devices"
144
- },
145
- {
146
- "id": "desktop",
147
- "name": "Desktop View",
148
- "description": "Margin settings for desktop devices"
149
- }
150
- ],
151
- "description": "Component mobile and desktop view settings"
152
- },
153
- {
154
- "id": "texts",
155
- "name": "Texts",
156
- "description": "Button, status and modal texts"
157
- }
158
- ]
159
- }