@jay-framework/wix-stores 0.19.5 → 0.19.7

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.
@@ -25,19 +25,33 @@ items:
25
25
  Script key: p
26
26
  Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
27
27
 
28
- - id: wix-stores:related-products
29
- title: Related products
28
+ - id: wix-stores:category-products
29
+ title: Category products
30
30
  category: Store
31
31
  subCategory: Components
32
32
  pluginName: wix-stores
33
33
  packageName: "@jay-framework/wix-stores"
34
- thumbnail: thumbnails/wix-stores/related-products.png
34
+ thumbnail: thumbnails/wix-stores/category-products.png
35
35
  prompt: |
36
- Use headless component @jay-framework/wix-stores / contract related-products.
37
- Read agent-kit/materialized-contracts/wix-stores/related-products.jay-contract.
36
+ Use headless component @jay-framework/wix-stores / contract category-products.
37
+ Read agent-kit/materialized-contracts/wix-stores/category-products.jay-contract.
38
38
  Script key: r
39
39
  Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
40
- Use alongside product-page; pass productId and categorySlug as instance props.
40
+ Pass categorySlug to show products from a category. Pass productId to exclude a product (e.g. on a product page).
41
+
42
+ - id: wix-stores:product-spotlight
43
+ title: Product spotlight
44
+ category: Store
45
+ subCategory: Components
46
+ pluginName: wix-stores
47
+ packageName: "@jay-framework/wix-stores"
48
+ thumbnail: thumbnails/wix-stores/product-spotlight.png
49
+ prompt: |
50
+ Use headless component @jay-framework/wix-stores / contract product-spotlight.
51
+ Read agent-kit/materialized-contracts/wix-stores/product-spotlight.jay-contract.
52
+ Script key: spotlight
53
+ Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md.
54
+ Pass slug as a prop to specify which product to display.
41
55
 
42
56
  - id: wix-stores:category-list
43
57
  title: Category list
@@ -0,0 +1,31 @@
1
+ name: category-products
2
+ description: Products from a specific category. Use for category product showcases, or as related products on a product page (pass productId to exclude the current product).
3
+ props:
4
+ - name: productId
5
+ type: string
6
+ description: Product ID to exclude from results (optional, use for related products)
7
+ - name: categorySlug
8
+ type: string
9
+ description: Category slug to filter products by
10
+ - name: limit
11
+ type: number
12
+ description: Maximum number of products to show (default 4)
13
+ tags:
14
+ - tag: products
15
+ type: sub-contract
16
+ repeated: true
17
+ trackBy: _id
18
+ phase: fast+interactive
19
+ description: Product cards from the category
20
+ link: ./product-card
21
+
22
+ - tag: hasProducts
23
+ type: variant
24
+ dataType: boolean
25
+ phase: fast+interactive
26
+ description: Whether there are products to show
27
+
28
+ - tag: categoryName
29
+ type: data
30
+ dataType: string
31
+ description: Name of the category these products belong to
@@ -0,0 +1,36 @@
1
+ import {JayContract} from "@jay-framework/runtime";
2
+ import {ProductCardViewState, ProductCardRefs, ProductCardRepeatedRefs} from "./product-card.jay-contract";
3
+
4
+ export interface CategoryProductsViewState {
5
+ products: Array<ProductCardViewState>,
6
+ hasProducts: boolean,
7
+ categoryName: string
8
+ }
9
+
10
+ export type CategoryProductsSlowViewState = Pick<CategoryProductsViewState, 'categoryName'>;
11
+
12
+ export type CategoryProductsFastViewState = Pick<CategoryProductsViewState, 'hasProducts'> & {
13
+ products: Array<CategoryProductsViewState['products'][number]>;
14
+ };
15
+
16
+ export type CategoryProductsInteractiveViewState = Pick<CategoryProductsViewState, 'hasProducts'> & {
17
+ products: Array<CategoryProductsViewState['products'][number]>;
18
+ };
19
+
20
+
21
+ export interface CategoryProductsRefs {
22
+ products: ProductCardRepeatedRefs
23
+ }
24
+
25
+
26
+ export interface CategoryProductsRepeatedRefs {
27
+ products: ProductCardRepeatedRefs
28
+ }
29
+
30
+ export interface CategoryProductsProps {
31
+ productId?: string;
32
+ categorySlug?: string;
33
+ limit?: number;
34
+ }
35
+
36
+ export type CategoryProductsContract = JayContract<CategoryProductsViewState, CategoryProductsRefs, CategoryProductsSlowViewState, CategoryProductsFastViewState, CategoryProductsInteractiveViewState, CategoryProductsProps>
@@ -0,0 +1,16 @@
1
+ name: product-spotlight
2
+ description: A single product card by slug. Use to feature a specific product on any page (homepage, content pages, etc.).
3
+ props:
4
+ - name: slug
5
+ type: string
6
+ description: Product slug to display
7
+ tags:
8
+ - tag: product
9
+ type: sub-contract
10
+ description: The product card
11
+ link: ./product-card
12
+
13
+ - tag: hasProduct
14
+ type: variant
15
+ dataType: boolean
16
+ description: Whether the product was found
@@ -0,0 +1,61 @@
1
+ import {JayContract} from "@jay-framework/runtime";
2
+ import {ProductCardViewState, ProductCardRefs, ProductCardRepeatedRefs} from "./product-card.jay-contract";
3
+
4
+ export interface ProductSpotlightViewState {
5
+ product: ProductCardViewState,
6
+ hasProduct: boolean
7
+ }
8
+
9
+ export type ProductSpotlightSlowViewState = Pick<ProductSpotlightViewState, 'hasProduct'> & {
10
+ product: Pick<ProductSpotlightViewState['product'], '_id' | 'name' | 'slug' | 'productUrl' | 'categoryPrefix' | 'hasDiscount' | 'hasRibbon' | 'productType' | 'quickAddType'> & {
11
+ mainMedia: ProductSpotlightViewState['product']['mainMedia'];
12
+ thumbnail: ProductSpotlightViewState['product']['thumbnail'];
13
+ inventory: ProductSpotlightViewState['product']['inventory'];
14
+ ribbon: ProductSpotlightViewState['product']['ribbon'];
15
+ brand: ProductSpotlightViewState['product']['brand'];
16
+ quickOption: Pick<ProductSpotlightViewState['product']['quickOption'], '_id' | 'name' | 'optionRenderType'> & {
17
+ choices: Array<Pick<ProductSpotlightViewState['product']['quickOption']['choices'][number], 'choiceId' | 'name' | 'choiceType' | 'colorCode'>>;
18
+ };
19
+ secondQuickOption: Pick<ProductSpotlightViewState['product']['secondQuickOption'], '_id' | 'name' | 'optionRenderType'> & {
20
+ choices: Array<Pick<ProductSpotlightViewState['product']['secondQuickOption']['choices'][number], 'choiceId' | 'name' | 'choiceType' | 'colorCode'>>;
21
+ };
22
+ };
23
+ };
24
+
25
+ export type ProductSpotlightFastViewState = {
26
+ product: Pick<ProductSpotlightViewState['product'], 'price' | 'strikethroughPrice' | 'isAddingToCart'> & {
27
+ quickOption: {
28
+ choices: Array<Pick<ProductSpotlightViewState['product']['quickOption']['choices'][number], 'choiceId' | 'inStock' | 'isSelected'>>;
29
+ };
30
+ secondQuickOption: {
31
+ choices: Array<Pick<ProductSpotlightViewState['product']['secondQuickOption']['choices'][number], 'choiceId' | 'inStock' | 'isSelected'>>;
32
+ };
33
+ };
34
+ };
35
+
36
+ export type ProductSpotlightInteractiveViewState = {
37
+ product: Pick<ProductSpotlightViewState['product'], 'price' | 'strikethroughPrice' | 'isAddingToCart'> & {
38
+ quickOption: {
39
+ choices: Array<Pick<ProductSpotlightViewState['product']['quickOption']['choices'][number], 'choiceId' | 'inStock' | 'isSelected'>>;
40
+ };
41
+ secondQuickOption: {
42
+ choices: Array<Pick<ProductSpotlightViewState['product']['secondQuickOption']['choices'][number], 'choiceId' | 'inStock' | 'isSelected'>>;
43
+ };
44
+ };
45
+ };
46
+
47
+
48
+ export interface ProductSpotlightRefs {
49
+ product: ProductCardRefs
50
+ }
51
+
52
+
53
+ export interface ProductSpotlightRepeatedRefs {
54
+ product: ProductCardRepeatedRefs
55
+ }
56
+
57
+ export interface ProductSpotlightProps {
58
+ slug?: string;
59
+ }
60
+
61
+ export type ProductSpotlightContract = JayContract<ProductSpotlightViewState, ProductSpotlightRefs, ProductSpotlightSlowViewState, ProductSpotlightFastViewState, ProductSpotlightInteractiveViewState, ProductSpotlightProps>
@@ -886,7 +886,7 @@ function ProductSearchInteractive(props, refs, viewStateSignals, fastCarryForwar
886
886
  };
887
887
  }
888
888
  const productSearch = makeJayStackComponent().withProps().withContexts(WIX_STORES_CONTEXT).withInteractive(ProductSearchInteractive);
889
- function RelatedProductsInteractive(_props, refs, viewStateSignals, _fastCarryForward, storesContext) {
889
+ function CategoryProductsInteractive(_props, refs, viewStateSignals, _fastCarryForward, storesContext) {
890
890
  const { products: [products, setProducts], hasProducts: [hasProducts] } = viewStateSignals;
891
891
  setupCardInteractions(refs.products, { get: products, set: setProducts }, storesContext);
892
892
  return {
@@ -896,7 +896,34 @@ function RelatedProductsInteractive(_props, refs, viewStateSignals, _fastCarryFo
896
896
  })
897
897
  };
898
898
  }
899
- const relatedProducts = makeJayStackComponent().withProps().withContexts(WIX_STORES_CONTEXT).withInteractive(RelatedProductsInteractive);
899
+ const categoryProducts = makeJayStackComponent().withProps().withContexts(WIX_STORES_CONTEXT).withInteractive(CategoryProductsInteractive);
900
+ function ProductSpotlightInteractive(_props, refs, viewStateSignals, fastCarryForward, storesContext) {
901
+ const { product: [product, setProduct] } = viewStateSignals;
902
+ const fullProduct = fastCarryForward.product;
903
+ refs.product.addToCartButton.onclick(async () => {
904
+ if (!fullProduct._id || fullProduct.quickAddType !== QuickAddType.SIMPLE)
905
+ return;
906
+ setProduct({ ...product(), isAddingToCart: true });
907
+ try {
908
+ await storesContext.addToCart(fullProduct._id, 1);
909
+ } catch (error) {
910
+ console.error("Failed to add to cart:", error);
911
+ } finally {
912
+ setProduct({ ...product(), isAddingToCart: false });
913
+ }
914
+ });
915
+ refs.product.viewOptionsButton.onclick(() => {
916
+ if (fullProduct.productUrl) {
917
+ window.location.href = fullProduct.productUrl;
918
+ }
919
+ });
920
+ return {
921
+ render: () => ({
922
+ product: product()
923
+ })
924
+ };
925
+ }
926
+ const productSpotlight = makeJayStackComponent().withProps().withContexts(WIX_STORES_CONTEXT).withInteractive(ProductSpotlightInteractive);
900
927
  const categoryList = makeJayStackComponent().withProps();
901
928
  const init = makeJayInit().withClient(async (data) => {
902
929
  console.log("[wix-stores] Initializing client-side stores context...");
@@ -909,9 +936,10 @@ export {
909
936
  cartIndicator,
910
937
  cartPage,
911
938
  categoryList,
939
+ categoryProducts,
912
940
  init,
913
941
  productPage,
914
942
  productSearch,
915
- provideWixStoresContext,
916
- relatedProducts
943
+ productSpotlight,
944
+ provideWixStoresContext
917
945
  };
package/dist/index.d.ts CHANGED
@@ -41,6 +41,13 @@ interface ProductOptionsViewState {
41
41
  }
42
42
 
43
43
 
44
+ interface ProductOptionsRefs {
45
+ choices: {
46
+ choiceButton: HTMLElementCollectionProxy<ChoiceOfProductOptionsViewState, HTMLButtonElement>
47
+ }
48
+ }
49
+
50
+
44
51
  interface ProductOptionsRepeatedRefs {
45
52
  choices: {
46
53
  choiceButton: HTMLElementCollectionProxy<ChoiceOfProductOptionsViewState, HTMLButtonElement>
@@ -127,6 +134,16 @@ interface ProductCardViewState {
127
134
  }
128
135
 
129
136
 
137
+ interface ProductCardRefs {
138
+ productLink: HTMLElementProxy<ProductCardViewState, HTMLAnchorElement>,
139
+ addToCartButton: HTMLElementProxy<ProductCardViewState, HTMLButtonElement>,
140
+ cardContainer: HTMLElementProxy<ProductCardViewState, HTMLElement>,
141
+ viewOptionsButton: HTMLElementProxy<ProductCardViewState, HTMLButtonElement>,
142
+ quickOption: ProductOptionsRefs,
143
+ secondQuickOption: ProductOptionsRefs
144
+ }
145
+
146
+
130
147
  interface ProductCardRepeatedRefs {
131
148
  productLink: HTMLElementCollectionProxy<ProductCardViewState, HTMLAnchorElement>,
132
149
  addToCartButton: HTMLElementCollectionProxy<ProductCardViewState, HTMLButtonElement>,
@@ -865,36 +882,92 @@ interface ProductFastCarryForward {
865
882
  */
866
883
  declare const productPage: _jay_framework_fullstack_component.JayStackComponentDefinition<ProductPageRefs, ProductPageSlowViewState, ProductPageFastViewState, ProductPageInteractiveViewState, [ProductSlowCarryForward, WixStoresService], [Signals<ProductPageFastViewState>, ProductFastCarryForward, WixStoresContext], PageProps & ProductPageParams, ProductPageParams, _jay_framework_component.JayComponentCore<PageProps & ProductPageParams, ProductPageInteractiveViewState>>;
867
884
 
868
- interface RelatedProductsViewState {
885
+ interface CategoryProductsViewState {
869
886
  products: Array<ProductCardViewState>,
870
887
  hasProducts: boolean,
871
888
  categoryName: string
872
889
  }
873
890
 
874
- type RelatedProductsSlowViewState = Pick<RelatedProductsViewState, 'categoryName'>;
891
+ type CategoryProductsSlowViewState = Pick<CategoryProductsViewState, 'categoryName'>;
875
892
 
876
- type RelatedProductsFastViewState = Pick<RelatedProductsViewState, 'hasProducts'> & {
877
- products: Array<RelatedProductsViewState['products'][number]>;
893
+ type CategoryProductsFastViewState = Pick<CategoryProductsViewState, 'hasProducts'> & {
894
+ products: Array<CategoryProductsViewState['products'][number]>;
878
895
  };
879
896
 
880
- type RelatedProductsInteractiveViewState = Pick<RelatedProductsViewState, 'hasProducts'> & {
881
- products: Array<RelatedProductsViewState['products'][number]>;
897
+ type CategoryProductsInteractiveViewState = Pick<CategoryProductsViewState, 'hasProducts'> & {
898
+ products: Array<CategoryProductsViewState['products'][number]>;
882
899
  };
883
900
 
884
901
 
885
- interface RelatedProductsRefs {
902
+ interface CategoryProductsRefs {
886
903
  products: ProductCardRepeatedRefs
887
904
  }
888
905
 
889
- interface RelatedProductsProps {
890
- productId: string;
906
+ interface CategoryProductsProps {
907
+ productId?: string;
891
908
  categorySlug?: string;
892
909
  limit?: number;
893
910
  }
894
- interface RelatedSlowCarryForward {
911
+ interface SlowCarryForward {
895
912
  products: ProductCardViewState[];
896
913
  }
897
- declare const relatedProducts: _jay_framework_fullstack_component.JayStackComponentDefinition<RelatedProductsRefs, RelatedProductsSlowViewState, RelatedProductsFastViewState, RelatedProductsInteractiveViewState, [RelatedSlowCarryForward, WixStoresService], [Signals<RelatedProductsFastViewState>, Record<string, never>, WixStoresContext], PageProps & RelatedProductsProps, {}, _jay_framework_component.JayComponentCore<PageProps & RelatedProductsProps, RelatedProductsInteractiveViewState>>;
914
+ declare const categoryProducts: _jay_framework_fullstack_component.JayStackComponentDefinition<CategoryProductsRefs, CategoryProductsSlowViewState, CategoryProductsFastViewState, CategoryProductsInteractiveViewState, [SlowCarryForward, WixStoresService], [Signals<CategoryProductsFastViewState>, Record<string, never>, WixStoresContext], PageProps & CategoryProductsProps, {}, _jay_framework_component.JayComponentCore<PageProps & CategoryProductsProps, CategoryProductsInteractiveViewState>>;
915
+
916
+ interface ProductSpotlightViewState {
917
+ product: ProductCardViewState,
918
+ hasProduct: boolean
919
+ }
920
+
921
+ type ProductSpotlightSlowViewState = Pick<ProductSpotlightViewState, 'hasProduct'> & {
922
+ product: Pick<ProductSpotlightViewState['product'], '_id' | 'name' | 'slug' | 'productUrl' | 'categoryPrefix' | 'hasDiscount' | 'hasRibbon' | 'productType' | 'quickAddType'> & {
923
+ mainMedia: ProductSpotlightViewState['product']['mainMedia'];
924
+ thumbnail: ProductSpotlightViewState['product']['thumbnail'];
925
+ inventory: ProductSpotlightViewState['product']['inventory'];
926
+ ribbon: ProductSpotlightViewState['product']['ribbon'];
927
+ brand: ProductSpotlightViewState['product']['brand'];
928
+ quickOption: Pick<ProductSpotlightViewState['product']['quickOption'], '_id' | 'name' | 'optionRenderType'> & {
929
+ choices: Array<Pick<ProductSpotlightViewState['product']['quickOption']['choices'][number], 'choiceId' | 'name' | 'choiceType' | 'colorCode'>>;
930
+ };
931
+ secondQuickOption: Pick<ProductSpotlightViewState['product']['secondQuickOption'], '_id' | 'name' | 'optionRenderType'> & {
932
+ choices: Array<Pick<ProductSpotlightViewState['product']['secondQuickOption']['choices'][number], 'choiceId' | 'name' | 'choiceType' | 'colorCode'>>;
933
+ };
934
+ };
935
+ };
936
+
937
+ type ProductSpotlightFastViewState = {
938
+ product: Pick<ProductSpotlightViewState['product'], 'price' | 'strikethroughPrice' | 'isAddingToCart'> & {
939
+ quickOption: {
940
+ choices: Array<Pick<ProductSpotlightViewState['product']['quickOption']['choices'][number], 'choiceId' | 'inStock' | 'isSelected'>>;
941
+ };
942
+ secondQuickOption: {
943
+ choices: Array<Pick<ProductSpotlightViewState['product']['secondQuickOption']['choices'][number], 'choiceId' | 'inStock' | 'isSelected'>>;
944
+ };
945
+ };
946
+ };
947
+
948
+ type ProductSpotlightInteractiveViewState = {
949
+ product: Pick<ProductSpotlightViewState['product'], 'price' | 'strikethroughPrice' | 'isAddingToCart'> & {
950
+ quickOption: {
951
+ choices: Array<Pick<ProductSpotlightViewState['product']['quickOption']['choices'][number], 'choiceId' | 'inStock' | 'isSelected'>>;
952
+ };
953
+ secondQuickOption: {
954
+ choices: Array<Pick<ProductSpotlightViewState['product']['secondQuickOption']['choices'][number], 'choiceId' | 'inStock' | 'isSelected'>>;
955
+ };
956
+ };
957
+ };
958
+
959
+
960
+ interface ProductSpotlightRefs {
961
+ product: ProductCardRefs
962
+ }
963
+
964
+ interface ProductSpotlightProps {
965
+ slug: string;
966
+ }
967
+ interface SpotlightCarryForward {
968
+ product: ProductCardViewState;
969
+ }
970
+ declare const productSpotlight: _jay_framework_fullstack_component.JayStackComponentDefinition<ProductSpotlightRefs, ProductSpotlightSlowViewState, ProductSpotlightFastViewState, ProductSpotlightInteractiveViewState, [SpotlightCarryForward, WixStoresService], [Signals<ProductSpotlightFastViewState>, SpotlightCarryForward, WixStoresContext], PageProps & ProductSpotlightProps, {}, _jay_framework_component.JayComponentCore<PageProps & ProductSpotlightProps, ProductSpotlightInteractiveViewState>>;
898
971
 
899
972
  interface CategoryOfCategoryListViewState {
900
973
  _id: string,
@@ -1065,4 +1138,4 @@ declare function generateWixStoresReferences(ctx: PluginReferencesContext): Prom
1065
1138
  */
1066
1139
  declare const generator: _jay_framework_fullstack_component.DynamicContractGenerator<[WixStoresService]>;
1067
1140
 
1068
- export { type CategoryListParams, type CategoryTree, type GetProductBySlugInput, type ProductPageParams, type ProductSearchParams, type RelatedProductsProps, WIX_STORES_CONTEXT, WIX_STORES_SERVICE_MARKER, type WixStoresContext, type WixStoresInitData, type WixStoresService, type WixStoresServiceOptions, buildCategoryUrl, buildProductUrl, categoryList, findCategoryImage, findRootCategoryId, findRootCategorySlug, generateWixStoresReferences, getCategories, getProductBySlug, getVariantStock, init, productPage, generator as productPageContractGenerator, productSearch, provideWixStoresService, relatedProducts, searchProducts, setupWixStores };
1141
+ export { type CategoryListParams, type CategoryProductsProps, type CategoryTree, type GetProductBySlugInput, type ProductPageParams, type ProductSearchParams, type ProductSpotlightProps, WIX_STORES_CONTEXT, WIX_STORES_SERVICE_MARKER, type WixStoresContext, type WixStoresInitData, type WixStoresService, type WixStoresServiceOptions, buildCategoryUrl, buildProductUrl, categoryList, categoryProducts, findCategoryImage, findRootCategoryId, findRootCategorySlug, generateWixStoresReferences, getCategories, getProductBySlug, getVariantStock, init, productPage, generator as productPageContractGenerator, productSearch, productSpotlight, provideWixStoresService, searchProducts, setupWixStores };
package/dist/index.js CHANGED
@@ -716,6 +716,7 @@ const getProductBySlug = makeJayQuery("wixStores.getProductBySlug").withServices
716
716
  const fields = [
717
717
  "MEDIA_ITEMS_INFO",
718
718
  "VARIANT_OPTION_CHOICE_NAMES",
719
+ "CURRENCY",
719
720
  ...needsCategoryInfo(wixStores) ? ["ALL_CATEGORIES_INFO"] : []
720
721
  ];
721
722
  const result = await wixStores.products.getProductBySlug(slug, {
@@ -955,7 +956,7 @@ async function buildCategoryHeader(wixStoreService, category, categoryUrlTemplat
955
956
  }
956
957
  return header;
957
958
  }
958
- async function renderSlowlyChanging$3(props, wixStores) {
959
+ async function renderSlowlyChanging$4(props, wixStores) {
959
960
  const Pipeline = RenderPipeline.for();
960
961
  const categorySlug = props.category ?? null;
961
962
  const prefixSlug = props.prefix ?? null;
@@ -1028,7 +1029,7 @@ async function renderSlowlyChanging$3(props, wixStores) {
1028
1029
  };
1029
1030
  });
1030
1031
  }
1031
- async function renderFastChanging$2(props, slowCarryForward, _wixStores) {
1032
+ async function renderFastChanging$3(props, slowCarryForward, _wixStores) {
1032
1033
  const Pipeline = RenderPipeline.for();
1033
1034
  const urlFilters = parseUrlFilters(props.url);
1034
1035
  const initialSort = parseSortParam(urlFilters.sort);
@@ -1215,7 +1216,7 @@ async function* loadSearchParams([wixStores]) {
1215
1216
  yield [];
1216
1217
  }
1217
1218
  }
1218
- const productSearch = makeJayStackComponent().withProps().withServices(WIX_STORES_SERVICE_MARKER).withLoadParams(loadSearchParams).withSlowlyRender(renderSlowlyChanging$3).withFastRender(renderFastChanging$2);
1219
+ const productSearch = makeJayStackComponent().withProps().withServices(WIX_STORES_SERVICE_MARKER).withLoadParams(loadSearchParams).withSlowlyRender(renderSlowlyChanging$4).withFastRender(renderFastChanging$3);
1219
1220
  var ProductType = /* @__PURE__ */ ((ProductType2) => {
1220
1221
  ProductType2[ProductType2["PHYSICAL"] = 0] = "PHYSICAL";
1221
1222
  ProductType2[ProductType2["DIGITAL"] = 1] = "DIGITAL";
@@ -1314,15 +1315,22 @@ function mapInfoSections(infoSections) {
1314
1315
  uniqueName: infoSection.uniqueName || ""
1315
1316
  }));
1316
1317
  }
1317
- function mapSeoHeadTags(seoData) {
1318
- if (!seoData)
1319
- return [];
1320
- const headTags = (seoData.tags || []).map((tag) => ({
1318
+ function mapSeoHeadTags(product, seoData) {
1319
+ const headTags = (seoData?.tags || []).map((tag) => ({
1321
1320
  tag: tag.type || "meta",
1322
1321
  attrs: Object.fromEntries(Object.entries(tag.props || {}).map(([key, value]) => [key, value])),
1323
1322
  children: tag.children || void 0
1324
1323
  }));
1325
- const keywords = seoData.settings?.keywords;
1324
+ if (!headTags.some((t) => t.tag === "title") && product.name) {
1325
+ headTags.push({ tag: "title", children: product.name });
1326
+ }
1327
+ if (!headTags.some((t) => t.tag === "meta" && t.attrs?.name === "description") && product.plainDescription) {
1328
+ headTags.push({
1329
+ tag: "meta",
1330
+ attrs: { name: "description", content: product.plainDescription }
1331
+ });
1332
+ }
1333
+ const keywords = seoData?.settings?.keywords;
1326
1334
  if (keywords?.length) {
1327
1335
  const terms = keywords.map((k) => k.term).filter(Boolean);
1328
1336
  if (terms.length) {
@@ -1442,7 +1450,7 @@ function mapVariants(variantsInfo) {
1442
1450
  strikethroughPrice: variant.price.compareAtPrice?.formattedAmount || ""
1443
1451
  })) || [];
1444
1452
  }
1445
- async function renderSlowlyChanging$2(props, wixStores) {
1453
+ async function renderSlowlyChanging$3(props, wixStores) {
1446
1454
  const Pipeline = RenderPipeline.for();
1447
1455
  const template = wixStores.urls.product;
1448
1456
  const needsCategories = template.includes("{category}") || template.includes("{prefix}");
@@ -1482,7 +1490,7 @@ async function renderSlowlyChanging$2(props, wixStores) {
1482
1490
  modifiers: mapModifiersToSlowVS(modifiers),
1483
1491
  extendedFields: mapExtendedFields(product)
1484
1492
  },
1485
- headTags: mapSeoHeadTags(seoData),
1493
+ headTags: mapSeoHeadTags({ name, plainDescription }, seoData),
1486
1494
  carryForward: {
1487
1495
  productId: _id,
1488
1496
  mediaGallery: mapMedia(media),
@@ -1498,7 +1506,7 @@ async function renderSlowlyChanging$2(props, wixStores) {
1498
1506
  };
1499
1507
  });
1500
1508
  }
1501
- async function renderFastChanging$1(props, slowCarryForward, wixStores) {
1509
+ async function renderFastChanging$2(props, slowCarryForward, wixStores) {
1502
1510
  const Pipeline = RenderPipeline.for();
1503
1511
  const { variants } = slowCarryForward;
1504
1512
  const defaultVariant = variants.find((v) => v.inventoryStatus === StockStatus.IN_STOCK) || variants[0];
@@ -1548,9 +1556,9 @@ async function renderFastChanging$1(props, slowCarryForward, wixStores) {
1548
1556
  }
1549
1557
  }));
1550
1558
  }
1551
- const productPage = makeJayStackComponent().withProps().withServices(WIX_STORES_SERVICE_MARKER).withLoadParams(loadProductParams).withSlowlyRender(renderSlowlyChanging$2).withFastRender(renderFastChanging$1);
1559
+ const productPage = makeJayStackComponent().withProps().withServices(WIX_STORES_SERVICE_MARKER).withLoadParams(loadProductParams).withSlowlyRender(renderSlowlyChanging$3).withFastRender(renderFastChanging$2);
1552
1560
  const DEFAULT_LIMIT = 4;
1553
- async function renderSlowlyChanging$1(props, wixStores) {
1561
+ async function renderSlowlyChanging$2(props, wixStores) {
1554
1562
  const Pipeline = RenderPipeline.for();
1555
1563
  const limit = props.limit ?? DEFAULT_LIMIT;
1556
1564
  const categorySlug = props.categorySlug;
@@ -1594,7 +1602,7 @@ async function renderSlowlyChanging$1(props, wixStores) {
1594
1602
  carryForward: { products }
1595
1603
  }));
1596
1604
  }
1597
- async function renderFastChanging(_props, slowCarryForward, _wixStores) {
1605
+ async function renderFastChanging$1(_props, slowCarryForward, _wixStores) {
1598
1606
  const Pipeline = RenderPipeline.for();
1599
1607
  const { products } = slowCarryForward;
1600
1608
  return Pipeline.ok({
@@ -1602,7 +1610,95 @@ async function renderFastChanging(_props, slowCarryForward, _wixStores) {
1602
1610
  hasProducts: products.length > 0
1603
1611
  }).toPhaseOutput((viewState) => ({ viewState, carryForward: {} }));
1604
1612
  }
1605
- const relatedProducts = makeJayStackComponent().withProps().withServices(WIX_STORES_SERVICE_MARKER).withSlowlyRender(renderSlowlyChanging$1).withFastRender(renderFastChanging);
1613
+ const categoryProducts = makeJayStackComponent().withProps().withServices(WIX_STORES_SERVICE_MARKER).withSlowlyRender(renderSlowlyChanging$2).withFastRender(renderFastChanging$1);
1614
+ async function renderSlowlyChanging$1(props, _wixStores) {
1615
+ const Pipeline = RenderPipeline.for();
1616
+ return Pipeline.try(async () => {
1617
+ if (!props.slug) {
1618
+ throw new Error("No product slug provided");
1619
+ }
1620
+ const card = await getProductBySlug({ slug: props.slug });
1621
+ if (!card) {
1622
+ throw new Error(`Product not found: ${props.slug}`);
1623
+ }
1624
+ return card;
1625
+ }).recover((error) => {
1626
+ return handleError(error);
1627
+ }).toPhaseOutput((product) => {
1628
+ return {
1629
+ viewState: {
1630
+ hasProduct: true,
1631
+ product: {
1632
+ _id: product._id,
1633
+ name: product.name,
1634
+ slug: product.slug,
1635
+ productUrl: product.productUrl,
1636
+ categoryPrefix: product.categoryPrefix,
1637
+ mainMedia: product.mainMedia,
1638
+ thumbnail: product.thumbnail,
1639
+ hasDiscount: product.hasDiscount,
1640
+ inventory: product.inventory,
1641
+ ribbon: product.ribbon,
1642
+ hasRibbon: product.hasRibbon,
1643
+ brand: product.brand,
1644
+ productType: product.productType,
1645
+ quickAddType: product.quickAddType,
1646
+ quickOption: {
1647
+ _id: product.quickOption?._id,
1648
+ name: product.quickOption?.name,
1649
+ optionRenderType: product.quickOption?.optionRenderType,
1650
+ choices: (product.quickOption?.choices ?? []).map((c) => ({
1651
+ choiceId: c.choiceId,
1652
+ name: c.name,
1653
+ choiceType: c.choiceType,
1654
+ colorCode: c.colorCode
1655
+ }))
1656
+ },
1657
+ secondQuickOption: {
1658
+ _id: product.secondQuickOption?._id,
1659
+ name: product.secondQuickOption?.name,
1660
+ optionRenderType: product.secondQuickOption?.optionRenderType,
1661
+ choices: (product.secondQuickOption?.choices ?? []).map((c) => ({
1662
+ choiceId: c.choiceId,
1663
+ name: c.name,
1664
+ choiceType: c.choiceType,
1665
+ colorCode: c.colorCode
1666
+ }))
1667
+ }
1668
+ }
1669
+ },
1670
+ carryForward: { product }
1671
+ };
1672
+ });
1673
+ }
1674
+ async function renderFastChanging(_props, carryForward, _wixStores) {
1675
+ const Pipeline = RenderPipeline.for();
1676
+ const { product } = carryForward;
1677
+ return Pipeline.ok({
1678
+ product: {
1679
+ price: product.price,
1680
+ strikethroughPrice: product.strikethroughPrice,
1681
+ isAddingToCart: false,
1682
+ quickOption: {
1683
+ choices: (product.quickOption?.choices ?? []).map((c) => ({
1684
+ choiceId: c.choiceId,
1685
+ inStock: c.inStock,
1686
+ isSelected: c.isSelected
1687
+ }))
1688
+ },
1689
+ secondQuickOption: {
1690
+ choices: (product.secondQuickOption?.choices ?? []).map((c) => ({
1691
+ choiceId: c.choiceId,
1692
+ inStock: c.inStock,
1693
+ isSelected: c.isSelected
1694
+ }))
1695
+ }
1696
+ }
1697
+ }).toPhaseOutput((viewState) => {
1698
+ return { viewState, carryForward: { product } };
1699
+ });
1700
+ }
1701
+ const productSpotlight = makeJayStackComponent().withProps().withServices(WIX_STORES_SERVICE_MARKER).withSlowlyRender(renderSlowlyChanging$1).withFastRender(renderFastChanging);
1606
1702
  async function findCategoryBySlug(categoriesClient, slug) {
1607
1703
  const result = await categoriesClient.queryCategories({ treeReference: { appNamespace: "@wix/stores" } }).eq("slug", slug).eq("visible", true).limit(1).find();
1608
1704
  return result.items?.[0] ?? null;
@@ -1691,15 +1787,19 @@ const init = makeJayInit().withServer(async () => {
1691
1787
  enableClientSearch: true
1692
1788
  };
1693
1789
  });
1694
- function flattenCategoryTree(roots, parentNames = [], parentSlugs = [], rootSlug = null) {
1790
+ function flattenCategoryTree(roots, parentNames = [], parentSlugs = [], rootSlug = null, rootName = null, rootHasChildren = null) {
1695
1791
  const entries = [];
1696
1792
  for (const node of roots) {
1697
1793
  const entryRootSlug = rootSlug ?? node.slug;
1794
+ const entryRootName = rootName ?? node.name;
1795
+ const entryRootHasChildren = rootHasChildren ?? node.children.length > 0;
1698
1796
  entries.push({
1699
1797
  node,
1700
1798
  breadcrumbNames: parentNames,
1701
1799
  breadcrumbSlugs: parentSlugs,
1702
1800
  rootSlug: entryRootSlug,
1801
+ rootName: entryRootName,
1802
+ rootHasChildren: entryRootHasChildren,
1703
1803
  parentSlug: parentSlugs.length > 0 ? parentSlugs[parentSlugs.length - 1] : null
1704
1804
  });
1705
1805
  if (node.children.length > 0) {
@@ -1708,7 +1808,9 @@ function flattenCategoryTree(roots, parentNames = [], parentSlugs = [], rootSlug
1708
1808
  node.children,
1709
1809
  [...parentNames, node.name],
1710
1810
  [...parentSlugs, node.slug],
1711
- entryRootSlug
1811
+ entryRootSlug,
1812
+ entryRootName,
1813
+ entryRootHasChildren
1712
1814
  )
1713
1815
  );
1714
1816
  }
@@ -1794,12 +1896,12 @@ function buildCategoryPrompt(config, entry) {
1794
1896
  );
1795
1897
  }
1796
1898
  lines.push(
1797
- ` related-products — pass categorySlug="${node.slug}" alongside a productId when showing related items in this category`,
1899
+ ` category-products — pass categorySlug="${node.slug}" to show products from this category; optionally pass productId to exclude a product`,
1798
1900
  "",
1799
1901
  "Contracts:",
1800
1902
  " agent-kit/materialized-contracts/wix-stores/product-search.jay-contract",
1801
1903
  " agent-kit/materialized-contracts/wix-stores/category-list.jay-contract",
1802
- " agent-kit/materialized-contracts/wix-stores/related-products.jay-contract",
1904
+ " agent-kit/materialized-contracts/wix-stores/category-products.jay-contract",
1803
1905
  "",
1804
1906
  "Bind ViewState and refs per agent-kit/designer/INSTRUCTIONS.md."
1805
1907
  );
@@ -1811,6 +1913,12 @@ function buildCategoryPrompt(config, entry) {
1811
1913
  }
1812
1914
  return lines.join("\n");
1813
1915
  }
1916
+ function resolveCategorySubCategory(entry) {
1917
+ if (entry.rootHasChildren) {
1918
+ return entry.rootName;
1919
+ }
1920
+ return "Categories";
1921
+ }
1814
1922
  function buildCategoryAddMenuItems(roots, config) {
1815
1923
  const usedIds = /* @__PURE__ */ new Set();
1816
1924
  const entries = flattenCategoryTree(roots);
@@ -1818,7 +1926,7 @@ function buildCategoryAddMenuItems(roots, config) {
1818
1926
  id: uniqueCategoryItemId(entry.node.slug, entry.node._id, usedIds),
1819
1927
  title: formatCategoryTitle(entry),
1820
1928
  category: "Store",
1821
- subCategory: "Categories",
1929
+ subCategory: resolveCategorySubCategory(entry),
1822
1930
  pluginName: "wix-stores",
1823
1931
  packageName: "@jay-framework/wix-stores",
1824
1932
  prompt: buildCategoryPrompt(config, entry)
@@ -2257,6 +2365,7 @@ export {
2257
2365
  cartIndicator,
2258
2366
  cartPage,
2259
2367
  categoryList,
2368
+ categoryProducts,
2260
2369
  findCategoryImage,
2261
2370
  findRootCategoryId,
2262
2371
  findRootCategorySlug,
@@ -2268,10 +2377,10 @@ export {
2268
2377
  productPage,
2269
2378
  generator as productPageContractGenerator,
2270
2379
  productSearch,
2380
+ productSpotlight,
2271
2381
  provideWixCartContext,
2272
2382
  provideWixCartService,
2273
2383
  provideWixStoresService,
2274
- relatedProducts,
2275
2384
  searchProducts,
2276
2385
  setupWixStores
2277
2386
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/wix-stores",
3
- "version": "0.19.5",
3
+ "version": "0.19.7",
4
4
  "type": "module",
5
5
  "description": "Wix Stores API client for Jay Framework",
6
6
  "license": "Apache-2.0",
@@ -17,7 +17,8 @@
17
17
  "./media.jay-contract": "./dist/contracts/media.jay-contract",
18
18
  "./media-gallery.jay-contract": "./dist/contracts/media-gallery.jay-contract",
19
19
  "./product-search.jay-contract": "./dist/contracts/product-search.jay-contract",
20
- "./related-products.jay-contract": "./dist/contracts/related-products.jay-contract",
20
+ "./category-products.jay-contract": "./dist/contracts/category-products.jay-contract",
21
+ "./product-spotlight.jay-contract": "./dist/contracts/product-spotlight.jay-contract",
21
22
  "./category-list.jay-contract": "./dist/contracts/category-list.jay-contract",
22
23
  "./search-products.jay-action": "./dist/actions/search-products.jay-action",
23
24
  "./get-product-by-slug.jay-action": "./dist/actions/get-product-by-slug.jay-action",
@@ -38,16 +39,16 @@
38
39
  "test": "vitest run"
39
40
  },
40
41
  "dependencies": {
41
- "@jay-framework/component": "^0.19.5",
42
- "@jay-framework/fullstack-component": "^0.19.5",
43
- "@jay-framework/reactive": "^0.19.5",
44
- "@jay-framework/runtime": "^0.19.5",
45
- "@jay-framework/secure": "^0.19.5",
46
- "@jay-framework/stack-client-runtime": "^0.19.5",
47
- "@jay-framework/stack-server-runtime": "^0.19.5",
48
- "@jay-framework/wix-cart": "^0.19.5",
49
- "@jay-framework/wix-server-client": "^0.19.5",
50
- "@jay-framework/wix-utils": "^0.19.5",
42
+ "@jay-framework/component": "^0.19.7",
43
+ "@jay-framework/fullstack-component": "^0.19.7",
44
+ "@jay-framework/reactive": "^0.19.7",
45
+ "@jay-framework/runtime": "^0.19.7",
46
+ "@jay-framework/secure": "^0.19.7",
47
+ "@jay-framework/stack-client-runtime": "^0.19.7",
48
+ "@jay-framework/stack-server-runtime": "^0.19.7",
49
+ "@jay-framework/wix-cart": "^0.19.7",
50
+ "@jay-framework/wix-server-client": "^0.19.7",
51
+ "@jay-framework/wix-utils": "^0.19.7",
51
52
  "@wix/categories": "^1.0.185",
52
53
  "@wix/data-extension-schema": "^1.0.221",
53
54
  "@wix/sdk": "^1.21.5",
@@ -59,10 +60,10 @@
59
60
  "@babel/core": "^7.23.7",
60
61
  "@babel/preset-env": "^7.23.8",
61
62
  "@babel/preset-typescript": "^7.23.3",
62
- "@jay-framework/compiler-jay-stack": "^0.19.5",
63
- "@jay-framework/jay-cli": "^0.19.5",
64
- "@jay-framework/jay-stack-cli": "^0.19.5",
65
- "@jay-framework/vite-plugin": "^0.19.5",
63
+ "@jay-framework/compiler-jay-stack": "^0.19.7",
64
+ "@jay-framework/jay-cli": "^0.19.7",
65
+ "@jay-framework/jay-stack-cli": "^0.19.7",
66
+ "@jay-framework/vite-plugin": "^0.19.7",
66
67
  "nodemon": "^3.0.3",
67
68
  "rimraf": "^5.0.5",
68
69
  "tslib": "^2.6.2",
package/plugin.yaml CHANGED
@@ -7,10 +7,14 @@ contracts:
7
7
  component: productSearch
8
8
  description: Headless product search page
9
9
  # category-page removed — use product-search with prefix/category params instead
10
- - name: related-products
11
- contract: related-products.jay-contract
12
- component: relatedProducts
13
- description: Related products from the same category
10
+ - name: category-products
11
+ contract: category-products.jay-contract
12
+ component: categoryProducts
13
+ description: Products from a category — use for showcases or as related products (exclude current product via productId)
14
+ - name: product-spotlight
15
+ contract: product-spotlight.jay-contract
16
+ component: productSpotlight
17
+ description: A single product card by product ID — use to feature a product on any page
14
18
  - name: category-list
15
19
  contract: category-list.jay-contract
16
20
  component: categoryList
@@ -22,6 +26,9 @@ dynamic_contracts:
22
26
  - prefix: 'product-page'
23
27
  component: productPage
24
28
  generator: productPageContractGenerator
29
+ headTags:
30
+ - title
31
+ - meta:description
25
32
 
26
33
  # Server actions (named exports from main module)
27
34
  actions:
@@ -1,31 +0,0 @@
1
- name: related-products
2
- description: Related products grid showing products from the same category. Use on product pages alongside the product-page component.
3
- props:
4
- - name: productId
5
- type: string
6
- description: Product ID to exclude from results
7
- - name: categorySlug
8
- type: string
9
- description: Category slug to filter related products by
10
- - name: limit
11
- type: number
12
- description: Maximum number of related products to show (default 4)
13
- tags:
14
- - tag: products
15
- type: sub-contract
16
- repeated: true
17
- trackBy: _id
18
- phase: fast+interactive
19
- description: Related product cards
20
- link: ./product-card
21
-
22
- - tag: hasProducts
23
- type: variant
24
- dataType: boolean
25
- phase: fast+interactive
26
- description: Whether there are related products to show
27
-
28
- - tag: categoryName
29
- type: data
30
- dataType: string
31
- description: Name of the category these products belong to
@@ -1,36 +0,0 @@
1
- import {JayContract} from "@jay-framework/runtime";
2
- import {ProductCardViewState, ProductCardRefs, ProductCardRepeatedRefs} from "./product-card.jay-contract";
3
-
4
- export interface RelatedProductsViewState {
5
- products: Array<ProductCardViewState>,
6
- hasProducts: boolean,
7
- categoryName: string
8
- }
9
-
10
- export type RelatedProductsSlowViewState = Pick<RelatedProductsViewState, 'categoryName'>;
11
-
12
- export type RelatedProductsFastViewState = Pick<RelatedProductsViewState, 'hasProducts'> & {
13
- products: Array<RelatedProductsViewState['products'][number]>;
14
- };
15
-
16
- export type RelatedProductsInteractiveViewState = Pick<RelatedProductsViewState, 'hasProducts'> & {
17
- products: Array<RelatedProductsViewState['products'][number]>;
18
- };
19
-
20
-
21
- export interface RelatedProductsRefs {
22
- products: ProductCardRepeatedRefs
23
- }
24
-
25
-
26
- export interface RelatedProductsRepeatedRefs {
27
- products: ProductCardRepeatedRefs
28
- }
29
-
30
- export interface RelatedProductsProps {
31
- productId?: string;
32
- categorySlug?: string;
33
- limit?: number;
34
- }
35
-
36
- export type RelatedProductsContract = JayContract<RelatedProductsViewState, RelatedProductsRefs, RelatedProductsSlowViewState, RelatedProductsFastViewState, RelatedProductsInteractiveViewState, RelatedProductsProps>