@gfed-medusa/sf-lib-products 1.9.5 → 1.10.1

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 (60) hide show
  1. package/dist/components/behavior-tracker/index.d.ts.map +1 -1
  2. package/dist/components/behavior-tracker/index.js +39 -4
  3. package/dist/components/behavior-tracker/index.js.map +1 -1
  4. package/dist/components/browse-product-preview/index.d.ts +2 -4
  5. package/dist/components/browse-product-preview/index.d.ts.map +1 -1
  6. package/dist/components/browse-product-preview/index.js +26 -3
  7. package/dist/components/browse-product-preview/index.js.map +1 -1
  8. package/dist/components/image-gallery/index.d.ts +3 -1
  9. package/dist/components/image-gallery/index.d.ts.map +1 -1
  10. package/dist/components/image-gallery/index.js +12 -2
  11. package/dist/components/image-gallery/index.js.map +1 -1
  12. package/dist/components/pagination/index.d.ts +2 -2
  13. package/dist/components/product-actions/index.d.ts +2 -2
  14. package/dist/components/product-actions/index.js +1 -1
  15. package/dist/components/product-onboarding-cta/index.d.ts +2 -2
  16. package/dist/components/product-price/index.d.ts +2 -2
  17. package/dist/components/product-price/index.d.ts.map +1 -1
  18. package/dist/components/product-tabs/index.d.ts +2 -2
  19. package/dist/components/product-tabs/index.d.ts.map +1 -1
  20. package/dist/components/product-tabs/index.js +28 -7
  21. package/dist/components/product-tabs/index.js.map +1 -1
  22. package/dist/components/refinement-list/index.d.ts +2 -2
  23. package/dist/components/refinement-list/index.d.ts.map +1 -1
  24. package/dist/components/refinement-list/index.js +7 -1
  25. package/dist/components/refinement-list/index.js.map +1 -1
  26. package/dist/components/refinement-list/sort-products/index.d.ts +2 -2
  27. package/dist/components/refinement-list/sort-products/index.d.ts.map +1 -1
  28. package/dist/components/refinement-list/sort-products/index.js +3 -0
  29. package/dist/components/refinement-list/sort-products/index.js.map +1 -1
  30. package/dist/components/related-products/index.d.ts +2 -2
  31. package/dist/components/skeleton-product-grid/index.d.ts +2 -2
  32. package/dist/components/skeleton-related-products/index.d.ts +2 -2
  33. package/dist/lib/data/cart.d.ts +1 -1
  34. package/dist/lib/data/cart.d.ts.map +1 -1
  35. package/dist/lib/data/cart.js +15 -5
  36. package/dist/lib/data/cart.js.map +1 -1
  37. package/dist/lib/gql/fragments/cart.d.ts +9 -9
  38. package/dist/lib/gql/fragments/product.d.ts.map +1 -1
  39. package/dist/lib/gql/fragments/product.js +1 -0
  40. package/dist/lib/gql/fragments/product.js.map +1 -1
  41. package/dist/lib/gql/mutations/cart.d.ts +10 -10
  42. package/dist/lib/gql/queries/cart.js +1 -1
  43. package/dist/lib/gql/queries/cart.js.map +1 -1
  44. package/dist/templates/paginated-products/client.d.ts +2 -2
  45. package/dist/templates/paginated-products/client.d.ts.map +1 -1
  46. package/dist/templates/paginated-products/client.js +0 -1
  47. package/dist/templates/paginated-products/client.js.map +1 -1
  48. package/dist/templates/paginated-products/index.d.ts +2 -2
  49. package/dist/templates/paginated-products/index.d.ts.map +1 -1
  50. package/dist/templates/product-actions-wrapper/index.d.ts +2 -2
  51. package/dist/templates/product-actions-wrapper/index.d.ts.map +1 -1
  52. package/dist/templates/product-info/index.d.ts +2 -2
  53. package/dist/templates/product-template/index.js +5 -2
  54. package/dist/templates/product-template/index.js.map +1 -1
  55. package/dist/templates/store-template/index.d.ts +2 -2
  56. package/dist/types/graphql.d.ts +203 -3
  57. package/dist/types/graphql.d.ts.map +1 -1
  58. package/dist/types/graphql.js +56 -2
  59. package/dist/types/graphql.js.map +1 -1
  60. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/behavior-tracker/index.tsx"],"sourcesContent":[],"mappings":";;;UAmHiB,oBAAA;WACN;AADX;AAIgB,iBAAA,eAAA,CAAkB;EAAA;AAA+B,CAApB,EAAA,oBAAoB,CAAA,EAAA,IAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/behavior-tracker/index.tsx"],"sourcesContent":[],"mappings":";;;UAuHiB,oBAAA;WACN;AADX;AAIgB,iBAAA,eAAA,CAAkB;EAAA;AAA+B,CAApB,EAAA,oBAAoB,CAAA,EAAA,IAAA"}
@@ -1,14 +1,17 @@
1
1
  'use client';
2
2
 
3
3
  import { useEffect, useRef } from "react";
4
+ import { sendClientSignal } from "@gfed-medusa/sf-lib-common/lib/personalization/client-signal";
5
+ import { SignalType } from "@gfed-medusa/sf-lib-common/types/graphql";
4
6
  import { PERSONALIZATION_CONFIG, getSegmentIdFromCollection } from "@gfed-medusa/sf-lib-common/lib/personalization/config";
5
7
  import { getSegmentCookie, setSegmentCookie } from "@gfed-medusa/sf-lib-common/lib/personalization/behavior-tracker";
6
8
  import { useStorefrontContext } from "@gfed-medusa/sf-lib-common/lib/data/context";
9
+ import { useTimeOnPage } from "@gfed-medusa/sf-lib-common/lib/hooks/use-time-on-page";
7
10
 
8
11
  //#region src/components/behavior-tracker/index.tsx
9
12
  const PDP_HESITATION_MS = PERSONALIZATION_CONFIG.pdpHesitationMs;
10
13
  const HIGH_SCROLL_THRESHOLD = PERSONALIZATION_CONFIG.highScrollThreshold;
11
- const PRICE_THRESHOLD_USD = PERSONALIZATION_CONFIG.priceThresholdUsd;
14
+ const PRICE_THRESHOLD = PERSONALIZATION_CONFIG.priceThreshold;
12
15
  function getScrollPercentage() {
13
16
  const scrollTop = window.scrollY;
14
17
  const docHeight = document.documentElement.scrollHeight - window.innerHeight;
@@ -72,6 +75,9 @@ function BehaviorTracker({ product }) {
72
75
  const scrollHandlerRef = useRef(null);
73
76
  const scrollTrackedRef = useRef(false);
74
77
  useRef(false);
78
+ const firedDepthRef = useRef(/* @__PURE__ */ new Set());
79
+ const exitIntentFiredRef = useRef(false);
80
+ useTimeOnPage("pdp", Boolean(product?.id));
75
81
  useEffect(() => {
76
82
  if (!product?.id) return;
77
83
  const collectionHandle = product.collection?.handle;
@@ -81,16 +87,35 @@ function BehaviorTracker({ product }) {
81
87
  trackRepeatedCategoryView(segment);
82
88
  }
83
89
  const productPrice = getCheapestVariantPrice(product);
90
+ sendClientSignal(SignalType.ProductView, {
91
+ productId: product.id,
92
+ productName: product.title,
93
+ productHandle: product.handle,
94
+ category: collectionHandle,
95
+ price: productPrice
96
+ });
84
97
  const hasCart = Boolean(cartId);
85
- if (productPrice && productPrice >= PRICE_THRESHOLD_USD && !hasCart) hesitationTimeoutRef.current = setTimeout(() => {
98
+ if (productPrice && productPrice >= PRICE_THRESHOLD && !hasCart) hesitationTimeoutRef.current = setTimeout(() => {
86
99
  emitSignal("pdp-hesitation", {
87
100
  productId: product.id,
88
101
  price: productPrice
89
102
  });
90
103
  }, PDP_HESITATION_MS);
91
104
  const handleScroll = throttle(() => {
92
- if (scrollTrackedRef.current) return;
93
- if (getScrollPercentage() >= HIGH_SCROLL_THRESHOLD && !hasCart) {
105
+ const scrollPct = Math.round(getScrollPercentage() * 100);
106
+ for (const threshold of [
107
+ 25,
108
+ 50,
109
+ 75,
110
+ 90
111
+ ]) if (scrollPct >= threshold && !firedDepthRef.current.has(threshold)) {
112
+ firedDepthRef.current.add(threshold);
113
+ sendClientSignal(SignalType.ScrollDepth, {
114
+ depth: threshold,
115
+ page: window.location.pathname
116
+ });
117
+ }
118
+ if (getScrollPercentage() >= HIGH_SCROLL_THRESHOLD && !hasCart && !scrollTrackedRef.current) {
94
119
  scrollTrackedRef.current = true;
95
120
  emitSignal("high-scroll-no-action", {
96
121
  productId: product.id,
@@ -112,6 +137,16 @@ function BehaviorTracker({ product }) {
112
137
  scrollTrackedRef.current = false;
113
138
  };
114
139
  }, [product, cartId]);
140
+ useEffect(() => {
141
+ const handleMouseLeave = (e) => {
142
+ if (e.clientY <= 0 && !exitIntentFiredRef.current) {
143
+ exitIntentFiredRef.current = true;
144
+ sendClientSignal(SignalType.ExitIntent, { page: window.location.pathname });
145
+ }
146
+ };
147
+ document.documentElement.addEventListener("mouseleave", handleMouseLeave);
148
+ return () => document.documentElement.removeEventListener("mouseleave", handleMouseLeave);
149
+ }, []);
115
150
  return null;
116
151
  }
117
152
  var behavior_tracker_default = BehaviorTracker;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["cheapest: number | null"],"sources":["../../../src/components/behavior-tracker/index.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef } from 'react';\n\nimport {\n PERSONALIZATION_CONFIG,\n getSegmentIdFromCollection,\n} from '@gfed-medusa/sf-lib-common/lib/personalization/config';\nimport {\n getSegmentCookie,\n setSegmentCookie,\n} from '@gfed-medusa/sf-lib-common/lib/personalization/behavior-tracker';\nimport { useStorefrontContext } from '@gfed-medusa/sf-lib-common/lib/data/context';\nimport { Product } from '@/types/graphql';\n\nconst PDP_HESITATION_MS = PERSONALIZATION_CONFIG.pdpHesitationMs;\nconst HIGH_SCROLL_THRESHOLD = PERSONALIZATION_CONFIG.highScrollThreshold;\nconst PRICE_THRESHOLD_USD = PERSONALIZATION_CONFIG.priceThresholdUsd;\n\nfunction getScrollPercentage(): number {\n const scrollTop = window.scrollY;\n const docHeight =\n document.documentElement.scrollHeight - window.innerHeight;\n return docHeight > 0 ? scrollTop / docHeight : 0;\n}\n\nfunction getCheapestVariantPrice(product: Product): number | null {\n const variants = product.variants;\n if (!variants || variants.length === 0) return null;\n\n let cheapest: number | null = null;\n\n for (const variant of variants) {\n const priceObj = variant.price;\n if (priceObj && typeof priceObj.amount === 'number') {\n if (cheapest === null || priceObj.amount < cheapest) {\n cheapest = priceObj.amount;\n }\n }\n }\n\n return cheapest;\n}\n\nfunction throttle<T extends (...args: unknown[]) => void>(\n fn: T,\n limit: number\n): T {\n let lastCall = 0;\n return ((...args: unknown[]) => {\n const now = Date.now();\n if (now - lastCall >= limit) {\n lastCall = now;\n fn(...args);\n }\n }) as T;\n}\n\nfunction emitSignal(type: string, payload?: unknown) {\n const data = getSegmentCookie();\n\n if (!data.signals || typeof data.signals !== 'object') {\n data.signals = {};\n }\n\n (data.signals as Record<string, unknown>)[type] = payload ?? true;\n\n setSegmentCookie(data);\n}\n\nconst REPEAT_CATEGORY_THRESHOLD = 3;\n\nfunction trackRepeatedCategoryView(segment: string) {\n try {\n const data = getSegmentCookie();\n\n if (!data.signals || typeof data.signals !== 'object') {\n data.signals = {};\n }\n\n const categoryCounts = (data.signals as Record<string, unknown>)['repeated-category-view'] as Record<string, number> ?? {};\n const currentCount = categoryCounts[segment] ?? 0;\n const newCount = currentCount + 1;\n\n categoryCounts[segment] = newCount;\n (data.signals as Record<string, unknown>)['repeated-category-view'] = categoryCounts;\n\n if (newCount >= REPEAT_CATEGORY_THRESHOLD) {\n emitSignal('repeated-category-view', { segment, count: newCount });\n }\n\n setSegmentCookie(data);\n } catch {\n // Ignore\n }\n}\n\nfunction addHistoryToCookie(segment: string): void {\n try {\n const data = getSegmentCookie();\n\n if (!data.history || !Array.isArray(data.history)) {\n data.history = [];\n }\n\n const history = [...(data.history as string[]), segment].slice(\n -PERSONALIZATION_CONFIG.historyMaxLength\n );\n data.history = history;\n\n setSegmentCookie(data);\n } catch {\n }\n}\n\nexport interface BehaviorTrackerProps {\n product: Product;\n}\n\nexport function BehaviorTracker({ product }: BehaviorTrackerProps) {\n const { cartId } = useStorefrontContext();\n const hesitationTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(\n null\n );\n const scrollHandlerRef = useRef<(() => void) | null>(null);\n const scrollTrackedRef = useRef(false);\n const historyTrackedRef = useRef(false);\n\n useEffect(() => {\n if (!product?.id) return;\n\n const collectionHandle = product.collection?.handle;\n const segment = getSegmentIdFromCollection(collectionHandle);\n\n if (segment) {\n addHistoryToCookie(segment);\n trackRepeatedCategoryView(segment);\n }\n\n const productPrice = getCheapestVariantPrice(product);\n const hasCart = Boolean(cartId);\n\n if (productPrice && productPrice >= PRICE_THRESHOLD_USD && !hasCart) {\n hesitationTimeoutRef.current = setTimeout(() => {\n emitSignal('pdp-hesitation', {\n productId: product.id,\n price: productPrice,\n });\n }, PDP_HESITATION_MS);\n }\n\n const handleScroll = throttle(() => {\n if (scrollTrackedRef.current) return;\n if (getScrollPercentage() >= HIGH_SCROLL_THRESHOLD && !hasCart) {\n scrollTrackedRef.current = true;\n emitSignal('high-scroll-no-action', {\n productId: product.id,\n scrollDepth: HIGH_SCROLL_THRESHOLD,\n });\n }\n }, 500);\n\n scrollHandlerRef.current = handleScroll;\n window.addEventListener('scroll', handleScroll, { passive: true });\n\n return () => {\n if (hesitationTimeoutRef.current !== null) {\n clearTimeout(hesitationTimeoutRef.current);\n hesitationTimeoutRef.current = null;\n }\n if (scrollHandlerRef.current !== null) {\n window.removeEventListener('scroll', scrollHandlerRef.current);\n scrollHandlerRef.current = null;\n }\n scrollTrackedRef.current = false;\n };\n }, [product, cartId]);\n\n return null;\n}\n\nexport default BehaviorTracker;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["cheapest: number | null"],"sources":["../../../src/components/behavior-tracker/index.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef } from 'react';\n\nimport { sendClientSignal } from '@gfed-medusa/sf-lib-common/lib/personalization/client-signal';\nimport { SignalType } from '@gfed-medusa/sf-lib-common/types/graphql';\n\nimport {\n PERSONALIZATION_CONFIG,\n getSegmentIdFromCollection,\n} from '@gfed-medusa/sf-lib-common/lib/personalization/config';\nimport {\n getSegmentCookie,\n setSegmentCookie,\n} from '@gfed-medusa/sf-lib-common/lib/personalization/behavior-tracker';\nimport { useStorefrontContext } from '@gfed-medusa/sf-lib-common/lib/data/context';\nimport { useTimeOnPage } from '@gfed-medusa/sf-lib-common/lib/hooks/use-time-on-page';\nimport { Product } from '@/types/graphql';\n\nconst PDP_HESITATION_MS = PERSONALIZATION_CONFIG.pdpHesitationMs;\nconst HIGH_SCROLL_THRESHOLD = PERSONALIZATION_CONFIG.highScrollThreshold;\nconst PRICE_THRESHOLD = PERSONALIZATION_CONFIG.priceThreshold;\n\nfunction getScrollPercentage(): number {\n const scrollTop = window.scrollY;\n const docHeight =\n document.documentElement.scrollHeight - window.innerHeight;\n return docHeight > 0 ? scrollTop / docHeight : 0;\n}\n\nfunction getCheapestVariantPrice(product: Product): number | null {\n const variants = product.variants;\n if (!variants || variants.length === 0) return null;\n\n let cheapest: number | null = null;\n\n for (const variant of variants) {\n const priceObj = variant.price;\n if (priceObj && typeof priceObj.amount === 'number') {\n if (cheapest === null || priceObj.amount < cheapest) {\n cheapest = priceObj.amount;\n }\n }\n }\n\n return cheapest;\n}\n\nfunction throttle<T extends (...args: unknown[]) => void>(\n fn: T,\n limit: number\n): T {\n let lastCall = 0;\n return ((...args: unknown[]) => {\n const now = Date.now();\n if (now - lastCall >= limit) {\n lastCall = now;\n fn(...args);\n }\n }) as T;\n}\n\nfunction emitSignal(type: string, payload?: unknown) {\n const data = getSegmentCookie();\n\n if (!data.signals || typeof data.signals !== 'object') {\n data.signals = {};\n }\n\n (data.signals as Record<string, unknown>)[type] = payload ?? true;\n\n setSegmentCookie(data);\n}\n\nconst REPEAT_CATEGORY_THRESHOLD = 3;\n\nfunction trackRepeatedCategoryView(segment: string) {\n try {\n const data = getSegmentCookie();\n\n if (!data.signals || typeof data.signals !== 'object') {\n data.signals = {};\n }\n\n const categoryCounts = (data.signals as Record<string, unknown>)['repeated-category-view'] as Record<string, number> ?? {};\n const currentCount = categoryCounts[segment] ?? 0;\n const newCount = currentCount + 1;\n\n categoryCounts[segment] = newCount;\n (data.signals as Record<string, unknown>)['repeated-category-view'] = categoryCounts;\n\n if (newCount >= REPEAT_CATEGORY_THRESHOLD) {\n emitSignal('repeated-category-view', { segment, count: newCount });\n }\n\n setSegmentCookie(data);\n } catch {\n // Ignore\n }\n}\n\nfunction addHistoryToCookie(segment: string): void {\n try {\n const data = getSegmentCookie();\n\n if (!data.history || !Array.isArray(data.history)) {\n data.history = [];\n }\n\n const history = [...(data.history as string[]), segment].slice(\n -PERSONALIZATION_CONFIG.historyMaxLength\n );\n data.history = history;\n\n setSegmentCookie(data);\n } catch {\n }\n}\n\nexport interface BehaviorTrackerProps {\n product: Product;\n}\n\nexport function BehaviorTracker({ product }: BehaviorTrackerProps) {\n const { cartId } = useStorefrontContext();\n const hesitationTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(\n null\n );\n const scrollHandlerRef = useRef<(() => void) | null>(null);\n const scrollTrackedRef = useRef(false);\n const historyTrackedRef = useRef(false);\n const firedDepthRef = useRef<Set<number>>(new Set());\n const exitIntentFiredRef = useRef(false);\n\n useTimeOnPage('pdp', Boolean(product?.id));\n\n useEffect(() => {\n if (!product?.id) return;\n\n const collectionHandle = product.collection?.handle;\n const segment = getSegmentIdFromCollection(collectionHandle);\n\n if (segment) {\n addHistoryToCookie(segment);\n trackRepeatedCategoryView(segment);\n }\n\n const productPrice = getCheapestVariantPrice(product);\n void sendClientSignal(SignalType.ProductView, {\n productId: product.id,\n productName: product.title,\n productHandle: product.handle,\n category: collectionHandle,\n price: productPrice,\n });\n\n const hasCart = Boolean(cartId);\n\n if (productPrice && productPrice >= PRICE_THRESHOLD && !hasCart) {\n hesitationTimeoutRef.current = setTimeout(() => {\n emitSignal('pdp-hesitation', {\n productId: product.id,\n price: productPrice,\n });\n }, PDP_HESITATION_MS);\n }\n\n const handleScroll = throttle(() => {\n // SCROLL_DEPTH analytics signal — always runs independently\n const scrollPct = Math.round(getScrollPercentage() * 100);\n const depthThresholds = [25, 50, 75, 90];\n for (const threshold of depthThresholds) {\n if (scrollPct >= threshold && !firedDepthRef.current.has(threshold)) {\n firedDepthRef.current.add(threshold);\n void sendClientSignal(SignalType.ScrollDepth, {\n depth: threshold,\n page: window.location.pathname,\n });\n }\n }\n\n if (getScrollPercentage() >= HIGH_SCROLL_THRESHOLD && !hasCart && !scrollTrackedRef.current) {\n scrollTrackedRef.current = true;\n emitSignal('high-scroll-no-action', {\n productId: product.id,\n scrollDepth: HIGH_SCROLL_THRESHOLD,\n });\n }\n }, 500);\n\n scrollHandlerRef.current = handleScroll;\n window.addEventListener('scroll', handleScroll, { passive: true });\n\n return () => {\n if (hesitationTimeoutRef.current !== null) {\n clearTimeout(hesitationTimeoutRef.current);\n hesitationTimeoutRef.current = null;\n }\n if (scrollHandlerRef.current !== null) {\n window.removeEventListener('scroll', scrollHandlerRef.current);\n scrollHandlerRef.current = null;\n }\n scrollTrackedRef.current = false;\n };\n }, [product, cartId]);\n\n useEffect(() => {\n const handleMouseLeave = (e: MouseEvent) => {\n if (e.clientY <= 0 && !exitIntentFiredRef.current) {\n exitIntentFiredRef.current = true;\n void sendClientSignal(SignalType.ExitIntent, {\n page: window.location.pathname,\n });\n }\n };\n document.documentElement.addEventListener('mouseleave', handleMouseLeave);\n return () => document.documentElement.removeEventListener('mouseleave', handleMouseLeave);\n }, []);\n\n return null;\n}\n\nexport default BehaviorTracker;\n"],"mappings":"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"}
@@ -1,19 +1,17 @@
1
1
  import { BrowseProductHitFragment } from "../../types/graphql.js";
2
- import * as react_jsx_runtime0 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime16 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/components/browse-product-preview/index.d.ts
5
5
  type BrowseProductPreviewProps = {
6
6
  product: BrowseProductHitFragment;
7
7
  isFeatured?: boolean;
8
- imagePriority?: boolean;
9
8
  imageFetchPriority?: 'auto' | 'high' | 'low';
10
9
  };
11
10
  declare const BrowseProductPreview: ({
12
11
  product,
13
12
  isFeatured,
14
- imagePriority,
15
13
  imageFetchPriority
16
- }: BrowseProductPreviewProps) => react_jsx_runtime0.JSX.Element;
14
+ }: BrowseProductPreviewProps) => react_jsx_runtime16.JSX.Element;
17
15
  //#endregion
18
16
  export { BrowseProductPreview };
19
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/browse-product-preview/index.tsx"],"sourcesContent":[],"mappings":";;;;KAUK,yBAAA;WACM;;EADN,aAAA,CAAA,EAAA,OAAA;EA0CC,kBAAA,CAAA,EAAA,MAiCL,GAAA,MAAA,GAAA,KAAA;CAjC6B;cAAxB,oBAAwB,EAAA,CAAA;EAAA,OAAA;EAAA,UAAA;EAAA,aAAA;EAAA;AAAA,CAAA,EAK3B,yBAL2B,EAAA,GAKF,kBAAA,CAAA,GAAA,CAAA,OALE"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/browse-product-preview/index.tsx"],"sourcesContent":[],"mappings":";;;;KAkBK,yBAAA;WACM;;EADN,kBAAA,CAAA,EAAA,MAAA,GAAyB,MAAA,GAAA,KACnB;AAAwB,CAAA;cAwC7B,oBAAwB,EAAA,CAAA;EAAA,OAAA;EAAA,UAAA;EAAA;AAAA,CAAA,EAI3B,yBAJ2B,EAAA,GAIF,mBAAA,CAAA,GAAA,CAAA,OAJE"}
@@ -1,3 +1,9 @@
1
+ 'use client';
2
+
3
+ import { useRef } from "react";
4
+ import { sendClientSignal } from "@gfed-medusa/sf-lib-common/lib/personalization/client-signal";
5
+ import { SignalType } from "@gfed-medusa/sf-lib-common/types/graphql";
6
+ import { useSearchParams } from "next/navigation";
1
7
  import { LocalizedClientLink } from "@gfed-medusa/sf-lib-common/components/localized-client-link";
2
8
  import { PreviewPrice } from "@gfed-medusa/sf-lib-common/components/preview-price";
3
9
  import { Thumbnail } from "@gfed-medusa/sf-lib-common/components/thumbnail";
@@ -27,19 +33,36 @@ const getBrowseProductPrice = (product) => {
27
33
  percentage_diff: isSale && originalPrice > 0 ? getPercentageDiff(originalPrice, product.priceAmount) : "0"
28
34
  };
29
35
  };
30
- const BrowseProductPreview = ({ product, isFeatured, imagePriority = false, imageFetchPriority }) => {
36
+ const BrowseProductPreview = ({ product, isFeatured, imageFetchPriority }) => {
31
37
  const price = getBrowseProductPrice(product);
38
+ const hoveredRef = useRef(false);
39
+ const query = useSearchParams()?.get("q") ?? "";
32
40
  return /* @__PURE__ */ jsx(LocalizedClientLink, {
33
- href: `/products/${product.handle}`,
41
+ href: `/products/${product.handle ?? ""}`,
34
42
  className: "group",
43
+ onClick: () => {
44
+ sendClientSignal(SignalType.SearchResultClick, {
45
+ productId: product.id ?? "",
46
+ productHandle: product.handle ?? "",
47
+ query
48
+ });
49
+ },
35
50
  children: /* @__PURE__ */ jsxs("div", {
36
51
  "data-testid": "product-wrapper",
52
+ onMouseEnter: () => {
53
+ if (!hoveredRef.current) {
54
+ hoveredRef.current = true;
55
+ sendClientSignal(SignalType.ProductHover, {
56
+ productId: product.id ?? "",
57
+ productHandle: product.handle ?? ""
58
+ });
59
+ }
60
+ },
37
61
  children: [/* @__PURE__ */ jsx(Thumbnail, {
38
62
  thumbnail: product.thumbnail,
39
63
  images: [],
40
64
  size: "full",
41
65
  isFeatured,
42
- imagePriority,
43
66
  imageFetchPriority
44
67
  }), /* @__PURE__ */ jsxs("div", {
45
68
  className: "txt-compact-medium mt-4 flex min-w-0 flex-col items-start gap-y-1 text-left",
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/browse-product-preview/index.tsx"],"sourcesContent":["import { LocalizedClientLink } from '@gfed-medusa/sf-lib-common/components/localized-client-link';\nimport { PreviewPrice } from '@gfed-medusa/sf-lib-common/components/preview-price';\nimport { Thumbnail } from '@gfed-medusa/sf-lib-common/components/thumbnail';\nimport { getPercentageDiff } from '@gfed-medusa/sf-lib-common/lib/utils/get-percentage-diff';\nimport { convertToLocale } from '@gfed-medusa/sf-lib-common/lib/utils/money';\nimport type { VariantPrice } from '@gfed-medusa/sf-lib-common/types/prices';\nimport { Text } from '@medusajs/ui';\n\nimport type { BrowseProductHitFragment } from '@/types/graphql';\n\ntype BrowseProductPreviewProps = {\n product: BrowseProductHitFragment;\n isFeatured?: boolean;\n imagePriority?: boolean;\n imageFetchPriority?: 'auto' | 'high' | 'low';\n};\n\nconst getBrowseProductPrice = (\n product: BrowseProductHitFragment\n): VariantPrice | null => {\n if (\n typeof product.priceAmount !== 'number' ||\n !product.currencyCode ||\n !product.currencyCode.trim()\n ) {\n return null;\n }\n\n const originalPrice = product.originalPriceAmount ?? product.priceAmount;\n const isSale =\n typeof originalPrice === 'number' && originalPrice > product.priceAmount;\n\n return {\n calculated_price_number: product.priceAmount,\n calculated_price: convertToLocale({\n amount: product.priceAmount,\n currency_code: product.currencyCode,\n }),\n original_price_number: originalPrice,\n original_price: convertToLocale({\n amount: originalPrice,\n currency_code: product.currencyCode,\n }),\n currency_code: product.currencyCode,\n price_type: isSale ? 'sale' : 'default',\n percentage_diff:\n isSale && originalPrice > 0\n ? getPercentageDiff(originalPrice, product.priceAmount)\n : '0',\n };\n};\n\nconst BrowseProductPreview = ({\n product,\n isFeatured,\n imagePriority = false,\n imageFetchPriority,\n}: BrowseProductPreviewProps) => {\n const price = getBrowseProductPrice(product);\n\n return (\n <LocalizedClientLink href={`/products/${product.handle}`} className=\"group\">\n <div data-testid=\"product-wrapper\">\n <Thumbnail\n thumbnail={product.thumbnail}\n images={[]}\n size=\"full\"\n isFeatured={isFeatured}\n imagePriority={imagePriority}\n imageFetchPriority={imageFetchPriority}\n />\n <div className=\"txt-compact-medium mt-4 flex min-w-0 flex-col items-start gap-y-1 text-left\">\n <Text\n className=\"text-ui-fg-subtle w-full min-w-0 overflow-hidden break-words whitespace-normal [display:-webkit-box] [-webkit-box-orient:vertical] [-webkit-line-clamp:2]\"\n data-testid=\"product-title\"\n >\n {product.title}\n </Text>\n <div className=\"flex w-full min-w-0 flex-wrap items-baseline gap-x-2 gap-y-1 text-left\">\n {price && <PreviewPrice price={price} />}\n </div>\n </div>\n </div>\n </LocalizedClientLink>\n );\n};\n\nexport { BrowseProductPreview };\n"],"mappings":";;;;;;;;;AAiBA,MAAM,yBACJ,YACwB;AACxB,KACE,OAAO,QAAQ,gBAAgB,YAC/B,CAAC,QAAQ,gBACT,CAAC,QAAQ,aAAa,MAAM,CAE5B,QAAO;CAGT,MAAM,gBAAgB,QAAQ,uBAAuB,QAAQ;CAC7D,MAAM,SACJ,OAAO,kBAAkB,YAAY,gBAAgB,QAAQ;AAE/D,QAAO;EACL,yBAAyB,QAAQ;EACjC,kBAAkB,gBAAgB;GAChC,QAAQ,QAAQ;GAChB,eAAe,QAAQ;GACxB,CAAC;EACF,uBAAuB;EACvB,gBAAgB,gBAAgB;GAC9B,QAAQ;GACR,eAAe,QAAQ;GACxB,CAAC;EACF,eAAe,QAAQ;EACvB,YAAY,SAAS,SAAS;EAC9B,iBACE,UAAU,gBAAgB,IACtB,kBAAkB,eAAe,QAAQ,YAAY,GACrD;EACP;;AAGH,MAAM,wBAAwB,EAC5B,SACA,YACA,gBAAgB,OAChB,yBAC+B;CAC/B,MAAM,QAAQ,sBAAsB,QAAQ;AAE5C,QACE,oBAAC;EAAoB,MAAM,aAAa,QAAQ;EAAU,WAAU;YAClE,qBAAC;GAAI,eAAY;cACf,oBAAC;IACC,WAAW,QAAQ;IACnB,QAAQ,EAAE;IACV,MAAK;IACO;IACG;IACK;KACpB,EACF,qBAAC;IAAI,WAAU;eACb,oBAAC;KACC,WAAU;KACV,eAAY;eAEX,QAAQ;MACJ,EACP,oBAAC;KAAI,WAAU;eACZ,SAAS,oBAAC,gBAAoB,QAAS;MACpC;KACF;IACF;GACc"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/browse-product-preview/index.tsx"],"sourcesContent":["'use client';\n\nimport { useRef } from 'react';\n\nimport { useSearchParams } from 'next/navigation';\n\nimport { LocalizedClientLink } from '@gfed-medusa/sf-lib-common/components/localized-client-link';\nimport { PreviewPrice } from '@gfed-medusa/sf-lib-common/components/preview-price';\nimport { Thumbnail } from '@gfed-medusa/sf-lib-common/components/thumbnail';\nimport { getPercentageDiff } from '@gfed-medusa/sf-lib-common/lib/utils/get-percentage-diff';\nimport { convertToLocale } from '@gfed-medusa/sf-lib-common/lib/utils/money';\nimport { sendClientSignal } from '@gfed-medusa/sf-lib-common/lib/personalization/client-signal';\nimport { SignalType } from '@gfed-medusa/sf-lib-common/types/graphql';\nimport type { VariantPrice } from '@gfed-medusa/sf-lib-common/types/prices';\nimport { Text } from '@medusajs/ui';\n\nimport type { BrowseProductHitFragment } from '@/types/graphql';\n\ntype BrowseProductPreviewProps = {\n product: BrowseProductHitFragment;\n isFeatured?: boolean;\n imageFetchPriority?: 'auto' | 'high' | 'low';\n};\n\nconst getBrowseProductPrice = (\n product: BrowseProductHitFragment\n): VariantPrice | null => {\n if (\n typeof product.priceAmount !== 'number' ||\n !product.currencyCode ||\n !product.currencyCode.trim()\n ) {\n return null;\n }\n\n const originalPrice = product.originalPriceAmount ?? product.priceAmount;\n const isSale =\n typeof originalPrice === 'number' && originalPrice > product.priceAmount;\n\n return {\n calculated_price_number: product.priceAmount,\n calculated_price: convertToLocale({\n amount: product.priceAmount,\n currency_code: product.currencyCode,\n }),\n original_price_number: originalPrice,\n original_price: convertToLocale({\n amount: originalPrice,\n currency_code: product.currencyCode,\n }),\n currency_code: product.currencyCode,\n price_type: isSale ? 'sale' : 'default',\n percentage_diff:\n isSale && originalPrice > 0\n ? getPercentageDiff(originalPrice, product.priceAmount)\n : '0',\n };\n};\n\nconst BrowseProductPreview = ({\n product,\n isFeatured,\n imageFetchPriority,\n}: BrowseProductPreviewProps) => {\n const price = getBrowseProductPrice(product);\n const hoveredRef = useRef(false);\n const searchParams = useSearchParams();\n const query = searchParams?.get('q') ?? '';\n\n return (\n <LocalizedClientLink\n href={`/products/${product.handle ?? ''}`}\n className=\"group\"\n onClick={() => {\n void sendClientSignal(SignalType.SearchResultClick, {\n productId: product.id ?? '',\n productHandle: product.handle ?? '',\n query,\n });\n }}\n >\n <div\n data-testid=\"product-wrapper\"\n onMouseEnter={() => {\n if (!hoveredRef.current) {\n hoveredRef.current = true;\n void sendClientSignal(SignalType.ProductHover, {\n productId: product.id ?? '',\n productHandle: product.handle ?? '',\n });\n }\n }}\n >\n <Thumbnail\n thumbnail={product.thumbnail}\n images={[]}\n size=\"full\"\n isFeatured={isFeatured}\n imageFetchPriority={imageFetchPriority}\n />\n <div className=\"txt-compact-medium mt-4 flex min-w-0 flex-col items-start gap-y-1 text-left\">\n <Text\n className=\"text-ui-fg-subtle w-full min-w-0 overflow-hidden break-words whitespace-normal [display:-webkit-box] [-webkit-box-orient:vertical] [-webkit-line-clamp:2]\"\n data-testid=\"product-title\"\n >\n {product.title}\n </Text>\n <div className=\"flex w-full min-w-0 flex-wrap items-baseline gap-x-2 gap-y-1 text-left\">\n {price && <PreviewPrice price={price} />}\n </div>\n </div>\n </div>\n </LocalizedClientLink>\n );\n};\n\nexport { BrowseProductPreview };\n"],"mappings":";;;;;;;;;;;;;;;AAwBA,MAAM,yBACJ,YACwB;AACxB,KACE,OAAO,QAAQ,gBAAgB,YAC/B,CAAC,QAAQ,gBACT,CAAC,QAAQ,aAAa,MAAM,CAE5B,QAAO;CAGT,MAAM,gBAAgB,QAAQ,uBAAuB,QAAQ;CAC7D,MAAM,SACJ,OAAO,kBAAkB,YAAY,gBAAgB,QAAQ;AAE/D,QAAO;EACL,yBAAyB,QAAQ;EACjC,kBAAkB,gBAAgB;GAChC,QAAQ,QAAQ;GAChB,eAAe,QAAQ;GACxB,CAAC;EACF,uBAAuB;EACvB,gBAAgB,gBAAgB;GAC9B,QAAQ;GACR,eAAe,QAAQ;GACxB,CAAC;EACF,eAAe,QAAQ;EACvB,YAAY,SAAS,SAAS;EAC9B,iBACE,UAAU,gBAAgB,IACtB,kBAAkB,eAAe,QAAQ,YAAY,GACrD;EACP;;AAGH,MAAM,wBAAwB,EAC5B,SACA,YACA,yBAC+B;CAC/B,MAAM,QAAQ,sBAAsB,QAAQ;CAC5C,MAAM,aAAa,OAAO,MAAM;CAEhC,MAAM,QADe,iBAAiB,EACV,IAAI,IAAI,IAAI;AAExC,QACE,oBAAC;EACC,MAAM,aAAa,QAAQ,UAAU;EACrC,WAAU;EACV,eAAe;AACb,GAAK,iBAAiB,WAAW,mBAAmB;IAClD,WAAW,QAAQ,MAAM;IACzB,eAAe,QAAQ,UAAU;IACjC;IACD,CAAC;;YAGJ,qBAAC;GACC,eAAY;GACZ,oBAAoB;AAClB,QAAI,CAAC,WAAW,SAAS;AACvB,gBAAW,UAAU;AACrB,KAAK,iBAAiB,WAAW,cAAc;MAC7C,WAAW,QAAQ,MAAM;MACzB,eAAe,QAAQ,UAAU;MAClC,CAAC;;;cAIN,oBAAC;IACC,WAAW,QAAQ;IACnB,QAAQ,EAAE;IACV,MAAK;IACO;IACQ;KACpB,EACF,qBAAC;IAAI,WAAU;eACb,oBAAC;KACC,WAAU;KACV,eAAY;eAEX,QAAQ;MACJ,EACP,oBAAC;KAAI,WAAU;eACZ,SAAS,oBAAC,gBAAoB,QAAS;MACpC;KACF;IACF;GACc"}
@@ -4,9 +4,11 @@ import * as react_jsx_runtime0 from "react/jsx-runtime";
4
4
  //#region src/components/image-gallery/index.d.ts
5
5
  type ImageGalleryProps = {
6
6
  images: ProductImage[];
7
+ productId: string;
7
8
  };
8
9
  declare const ImageGallery: ({
9
- images
10
+ images,
11
+ productId
10
12
  }: ImageGalleryProps) => react_jsx_runtime0.JSX.Element;
11
13
  //#endregion
12
14
  export { ImageGallery as default };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/image-gallery/index.tsx"],"sourcesContent":[],"mappings":";;;;KAMK,iBAAA;UACK;;AAHqC,cAMzC,YAJgB,EAAA,CAAA;EAAA;AACA,CAAZ,EAGwB,iBAHZ,EAAA,GAG6B,kBAAA,CAAA,GAAA,CAAA,OAH7B"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/image-gallery/index.tsx"],"sourcesContent":[],"mappings":";;;;KAUK,iBAAA;UACK;;AAHqC,CAAA;AAGzB,cAIhB,YAsCL,EAAA,CAAA;EAAA,MAAA;EAAA;AAAA,CAAA,EAtC4C,iBAsC5C,EAAA,GAtC6D,kBAAA,CAAA,GAAA,CAAA,OAsC7D"}
@@ -1,9 +1,13 @@
1
+ 'use client';
2
+
3
+ import { sendClientSignal } from "@gfed-medusa/sf-lib-common/lib/personalization/client-signal";
4
+ import { SignalType } from "@gfed-medusa/sf-lib-common/types/graphql";
1
5
  import { Container } from "@medusajs/ui";
2
6
  import { jsx } from "react/jsx-runtime";
3
7
  import Image from "next/image";
4
8
 
5
9
  //#region src/components/image-gallery/index.tsx
6
- const ImageGallery = ({ images }) => {
10
+ const ImageGallery = ({ images, productId }) => {
7
11
  return /* @__PURE__ */ jsx("div", {
8
12
  className: "relative flex items-start",
9
13
  children: /* @__PURE__ */ jsx("div", {
@@ -21,7 +25,13 @@ const ImageGallery = ({ images }) => {
21
25
  quality: 60,
22
26
  sizes: "(max-width: 576px) 280px, (max-width: 768px) 360px, (max-width: 992px) 480px, 800px",
23
27
  style: { objectFit: "cover" },
24
- ...index === 0 ? { fetchPriority: "high" } : {}
28
+ ...index === 0 ? { fetchPriority: "high" } : {},
29
+ onClick: () => {
30
+ sendClientSignal(SignalType.ImageZoom, {
31
+ productId,
32
+ imageUrl: image.url
33
+ });
34
+ }
25
35
  })
26
36
  }, image.id);
27
37
  })
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/image-gallery/index.tsx"],"sourcesContent":["import Image from 'next/image';\n\nimport { Container } from '@medusajs/ui';\n\nimport { ProductImage } from '@/types/graphql';\n\ntype ImageGalleryProps = {\n images: ProductImage[];\n};\n\nconst ImageGallery = ({ images }: ImageGalleryProps) => {\n return (\n <div className=\"relative flex items-start\">\n <div className=\"small:mx-16 flex flex-1 flex-col gap-y-4\">\n {images.map((image, index) => {\n return (\n <Container\n key={image.id}\n className=\"bg-ui-bg-subtle relative aspect-[29/34] w-full overflow-hidden\"\n id={image.id}\n >\n {!!image.url && (\n <Image\n src={image.url}\n priority={index <= 2 ? true : false}\n className=\"rounded-rounded absolute inset-0\"\n alt={`Product image ${index + 1}`}\n fill\n quality={60}\n sizes=\"(max-width: 576px) 280px, (max-width: 768px) 360px, (max-width: 992px) 480px, 800px\"\n style={{\n objectFit: 'cover',\n }}\n {...(index === 0 ? { fetchPriority: 'high' } : {})}\n />\n )}\n </Container>\n );\n })}\n </div>\n </div>\n );\n};\n\nexport default ImageGallery;\n"],"mappings":";;;;;AAUA,MAAM,gBAAgB,EAAE,aAAgC;AACtD,QACE,oBAAC;EAAI,WAAU;YACb,oBAAC;GAAI,WAAU;aACZ,OAAO,KAAK,OAAO,UAAU;AAC5B,WACE,oBAAC;KAEC,WAAU;KACV,IAAI,MAAM;eAET,CAAC,CAAC,MAAM,OACP,oBAAC;MACC,KAAK,MAAM;MACX,UAAU,SAAS,IAAI,OAAO;MAC9B,WAAU;MACV,KAAK,iBAAiB,QAAQ;MAC9B;MACA,SAAS;MACT,OAAM;MACN,OAAO,EACL,WAAW,SACZ;MACD,GAAK,UAAU,IAAI,EAAE,eAAe,QAAQ,GAAG,EAAE;OACjD;OAjBC,MAAM,GAmBD;KAEd;IACE;GACF;;AAIV,4BAAe"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/image-gallery/index.tsx"],"sourcesContent":["'use client';\n\nimport Image from 'next/image';\n\nimport { Container } from '@medusajs/ui';\n\nimport { sendClientSignal } from '@gfed-medusa/sf-lib-common/lib/personalization/client-signal';\nimport { SignalType } from '@gfed-medusa/sf-lib-common/types/graphql';\nimport { ProductImage } from '@/types/graphql';\n\ntype ImageGalleryProps = {\n images: ProductImage[];\n productId: string;\n};\n\nconst ImageGallery = ({ images, productId }: ImageGalleryProps) => {\n return (\n <div className=\"relative flex items-start\">\n <div className=\"small:mx-16 flex flex-1 flex-col gap-y-4\">\n {images.map((image, index) => {\n return (\n <Container\n key={image.id}\n className=\"bg-ui-bg-subtle relative aspect-[29/34] w-full overflow-hidden\"\n id={image.id}\n >\n {!!image.url && (\n <Image\n src={image.url}\n priority={index <= 2 ? true : false}\n className=\"rounded-rounded absolute inset-0\"\n alt={`Product image ${index + 1}`}\n fill\n quality={60}\n sizes=\"(max-width: 576px) 280px, (max-width: 768px) 360px, (max-width: 992px) 480px, 800px\"\n style={{\n objectFit: 'cover',\n }}\n {...(index === 0 ? { fetchPriority: 'high' } : {})}\n onClick={() => {\n void sendClientSignal(SignalType.ImageZoom, {\n productId,\n imageUrl: image.url,\n });\n }}\n />\n )}\n </Container>\n );\n })}\n </div>\n </div>\n );\n};\n\nexport default ImageGallery;\n"],"mappings":";;;;;;;;;AAeA,MAAM,gBAAgB,EAAE,QAAQ,gBAAmC;AACjE,QACE,oBAAC;EAAI,WAAU;YACb,oBAAC;GAAI,WAAU;aACZ,OAAO,KAAK,OAAO,UAAU;AAC5B,WACE,oBAAC;KAEC,WAAU;KACV,IAAI,MAAM;eAET,CAAC,CAAC,MAAM,OACP,oBAAC;MACC,KAAK,MAAM;MACX,UAAU,SAAS,IAAI,OAAO;MAC9B,WAAU;MACV,KAAK,iBAAiB,QAAQ;MAC9B;MACA,SAAS;MACT,OAAM;MACN,OAAO,EACL,WAAW,SACZ;MACD,GAAK,UAAU,IAAI,EAAE,eAAe,QAAQ,GAAG,EAAE;MACjD,eAAe;AACb,OAAK,iBAAiB,WAAW,WAAW;QAC1C;QACA,UAAU,MAAM;QACjB,CAAC;;OAEJ;OAvBC,MAAM,GAyBD;KAEd;IACE;GACF;;AAIV,4BAAe"}
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime1 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/components/pagination/index.d.ts
4
4
  declare function Pagination({
@@ -19,7 +19,7 @@ declare function Pagination({
19
19
  isLoading?: boolean;
20
20
  onLoadMore: () => void;
21
21
  'data-testid'?: string;
22
- }): react_jsx_runtime1.JSX.Element;
22
+ }): react_jsx_runtime0.JSX.Element;
23
23
  //#endregion
24
24
  export { Pagination };
25
25
  //# sourceMappingURL=index.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { ProductActionsProduct } from "../../types/index.js";
2
- import * as react_jsx_runtime2 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime1 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/components/product-actions/index.d.ts
5
5
  type ProductActionsProps = {
@@ -13,7 +13,7 @@ declare function ProductActions({
13
13
  regionId,
14
14
  disabled,
15
15
  enableMobileActions
16
- }: ProductActionsProps): react_jsx_runtime2.JSX.Element;
16
+ }: ProductActionsProps): react_jsx_runtime1.JSX.Element;
17
17
  //#endregion
18
18
  export { ProductActionsProps, ProductActions as default };
19
19
  //# sourceMappingURL=index.d.ts.map
@@ -7,9 +7,9 @@ import { useProductPrice } from "../../lib/hooks/use-product-price.js";
7
7
  import { mergeProductPricing } from "../../lib/utils/merge-product-pricing.js";
8
8
  import mobile_actions_default from "./mobile-actions.js";
9
9
  import { useEffect, useMemo, useRef, useState } from "react";
10
+ import { useParams } from "next/navigation";
10
11
  import { Button } from "@medusajs/ui";
11
12
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
12
- import { useParams } from "next/navigation";
13
13
  import { isEqual } from "lodash";
14
14
  import { ErrorMessage } from "@gfed-medusa/sf-lib-common/components/error-message";
15
15
  import { WebComponent } from "@gfed-medusa/sf-lib-common/components/web-component";
@@ -1,7 +1,7 @@
1
- import * as react_jsx_runtime3 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime2 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/components/product-onboarding-cta/index.d.ts
4
- declare function ProductOnboardingCta(): Promise<react_jsx_runtime3.JSX.Element | null>;
4
+ declare function ProductOnboardingCta(): Promise<react_jsx_runtime2.JSX.Element | null>;
5
5
  //#endregion
6
6
  export { ProductOnboardingCta as default };
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import { ProductVariant } from "../../types/graphql.js";
2
2
  import { ProductActionsProduct } from "../../types/index.js";
3
- import * as react_jsx_runtime4 from "react/jsx-runtime";
3
+ import * as react_jsx_runtime15 from "react/jsx-runtime";
4
4
 
5
5
  //#region src/components/product-price/index.d.ts
6
6
  declare function ProductPrice({
@@ -9,7 +9,7 @@ declare function ProductPrice({
9
9
  }: {
10
10
  product: ProductActionsProduct;
11
11
  variant?: ProductVariant;
12
- }): react_jsx_runtime4.JSX.Element;
12
+ }): react_jsx_runtime15.JSX.Element;
13
13
  //#endregion
14
14
  export { ProductPrice as default };
15
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/product-price/index.tsx"],"sourcesContent":[],"mappings":";;;;;iBAOwB,YAAA;;;AAFyB;WAMtC;YACC;AAPqC,CAAA,CAAA,EAQhD,kBAAA,CAAA,GAAA,CAAA,OANmC"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/product-price/index.tsx"],"sourcesContent":[],"mappings":";;;;;iBAOwB,YAAA;;;AAFyB;WAMtC;YACC;AAPqC,CAAA,CAAA,EAQhD,mBAAA,CAAA,GAAA,CAAA,OANmC"}
@@ -1,5 +1,5 @@
1
1
  import { Product } from "../../types/graphql.js";
2
- import * as react_jsx_runtime5 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime13 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/components/product-tabs/index.d.ts
5
5
  type ProductTabsProps = {
@@ -7,7 +7,7 @@ type ProductTabsProps = {
7
7
  };
8
8
  declare const ProductTabs: ({
9
9
  product
10
- }: ProductTabsProps) => react_jsx_runtime5.JSX.Element;
10
+ }: ProductTabsProps) => react_jsx_runtime13.JSX.Element;
11
11
  //#endregion
12
12
  export { ProductTabs as default };
13
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/product-tabs/index.tsx"],"sourcesContent":[],"mappings":";;;;KAUK,gBAAA;WACM;;AAL+B,cAQpC,WAJe,EAAA,CAAA;EAAA;AACH,CAAP,EAGuB,gBAHhB,EAAA,GAGgC,kBAAA,CAAA,GAAA,CAAA,OAHhC"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/product-tabs/index.tsx"],"sourcesContent":[],"mappings":";;;;KAcK,gBAAA;WACM;;AAL+B,cAQpC,WAJe,EAAA,CAAA;EAAA;AACH,CAAP,EAGuB,gBAHhB,EAAA,GAGgC,mBAAA,CAAA,GAAA,CAAA,OAHhC"}
@@ -1,6 +1,9 @@
1
1
  'use client';
2
2
 
3
3
  import accordion_default from "./accordion.js";
4
+ import { useEffect, useRef, useState } from "react";
5
+ import { sendClientSignal } from "@gfed-medusa/sf-lib-common/lib/personalization/client-signal";
6
+ import { SignalType } from "@gfed-medusa/sf-lib-common/types/graphql";
4
7
  import { jsx, jsxs } from "react/jsx-runtime";
5
8
  import { Back } from "@gfed-medusa/sf-lib-ui/icons/back";
6
9
  import { FastDelivery } from "@gfed-medusa/sf-lib-ui/icons/fast-delivery";
@@ -8,17 +11,35 @@ import { Refresh } from "@gfed-medusa/sf-lib-ui/icons/refresh";
8
11
 
9
12
  //#region src/components/product-tabs/index.tsx
10
13
  const ProductTabs = ({ product }) => {
14
+ const hasSentReviewsViewRef = useRef(false);
15
+ const productId = product.id;
16
+ const [openTabValues, setOpenTabValues] = useState([]);
17
+ const tabs = [{
18
+ label: "Product Information",
19
+ component: /* @__PURE__ */ jsx(ProductInfoTab, { product })
20
+ }, {
21
+ label: "Shipping & Returns",
22
+ component: /* @__PURE__ */ jsx(ShippingInfoTab, {})
23
+ }];
24
+ useEffect(() => {
25
+ if (openTabValues.includes("Shipping & Returns") && !hasSentReviewsViewRef.current) {
26
+ hasSentReviewsViewRef.current = true;
27
+ sendClientSignal(SignalType.ReviewsView, { productId });
28
+ }
29
+ }, [openTabValues, productId]);
11
30
  return /* @__PURE__ */ jsx("div", {
12
31
  className: "w-full",
13
32
  children: /* @__PURE__ */ jsx(accordion_default, {
14
33
  type: "multiple",
15
- children: [{
16
- label: "Product Information",
17
- component: /* @__PURE__ */ jsx(ProductInfoTab, { product })
18
- }, {
19
- label: "Shipping & Returns",
20
- component: /* @__PURE__ */ jsx(ShippingInfoTab, {})
21
- }].map((tab, i) => /* @__PURE__ */ jsx(accordion_default.Item, {
34
+ value: openTabValues,
35
+ onValueChange: (values) => {
36
+ setOpenTabValues(values);
37
+ sendClientSignal(SignalType.TabSwitch, {
38
+ activeTabs: values,
39
+ productId
40
+ });
41
+ },
42
+ children: tabs.map((tab, i) => /* @__PURE__ */ jsx(accordion_default.Item, {
22
43
  title: tab.label,
23
44
  headingSize: "medium",
24
45
  value: tab.label,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["Accordion"],"sources":["../../../src/components/product-tabs/index.tsx"],"sourcesContent":["'use client';\n\nimport { Back } from '@gfed-medusa/sf-lib-ui/icons/back';\nimport { FastDelivery } from '@gfed-medusa/sf-lib-ui/icons/fast-delivery';\nimport { Refresh } from '@gfed-medusa/sf-lib-ui/icons/refresh';\n\nimport { Product } from '@/types/graphql';\n\nimport Accordion from './accordion';\n\ntype ProductTabsProps = {\n product: Product;\n};\n\nconst ProductTabs = ({ product }: ProductTabsProps) => {\n const tabs = [\n {\n label: 'Product Information',\n component: <ProductInfoTab product={product} />,\n },\n {\n label: 'Shipping & Returns',\n component: <ShippingInfoTab />,\n },\n ];\n\n return (\n <div className=\"w-full\">\n <Accordion type=\"multiple\">\n {tabs.map((tab, i) => (\n <Accordion.Item\n key={i}\n title={tab.label}\n headingSize=\"medium\"\n value={tab.label}\n >\n {tab.component}\n </Accordion.Item>\n ))}\n </Accordion>\n </div>\n );\n};\n\nconst ProductInfoTab = ({ product }: ProductTabsProps) => {\n return (\n <div className=\"text-small-regular py-8\">\n <div className=\"grid grid-cols-2 gap-x-8\">\n <div className=\"flex flex-col gap-y-4\">\n <div>\n <span className=\"font-semibold\">Material</span>\n <p>{product.material ? product.material : '-'}</p>\n </div>\n <div>\n <span className=\"font-semibold\">Country of origin</span>\n <p>{product.originCountry ? product.originCountry : '-'}</p>\n </div>\n <div>\n <span className=\"font-semibold\">Type</span>\n <p>{product.type || '-'}</p>\n </div>\n </div>\n <div className=\"flex flex-col gap-y-4\">\n <div>\n <span className=\"font-semibold\">Weight</span>\n <p>{product.weight ? `${product.weight} g` : '-'}</p>\n </div>\n <div>\n <span className=\"font-semibold\">Dimensions</span>\n <p>\n {product.length && product.width && product.height\n ? `${product.length}L x ${product.width}W x ${product.height}H`\n : '-'}\n </p>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nconst ShippingInfoTab = () => {\n return (\n <div className=\"text-small-regular py-8\">\n <div className=\"grid grid-cols-1 gap-y-8\">\n <div className=\"flex items-start gap-x-2\">\n <FastDelivery />\n <div>\n <span className=\"font-semibold\">Fast delivery</span>\n <p className=\"max-w-sm\">\n Your package will arrive in 3-5 business days at your pick up\n location or in the comfort of your home.\n </p>\n </div>\n </div>\n <div className=\"flex items-start gap-x-2\">\n <Refresh />\n <div>\n <span className=\"font-semibold\">Simple exchanges</span>\n <p className=\"max-w-sm\">\n Is the fit not quite right? No worries - we&apos;ll exchange your\n product for a new one.\n </p>\n </div>\n </div>\n <div className=\"flex items-start gap-x-2\">\n <Back />\n <div>\n <span className=\"font-semibold\">Easy returns</span>\n <p className=\"max-w-sm\">\n Just return your product and we&apos;ll refund your money. No\n questions asked – we&apos;ll do our best to make sure your return\n is hassle-free.\n </p>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default ProductTabs;\n"],"mappings":";;;;;;;;;AAcA,MAAM,eAAe,EAAE,cAAgC;AAYrD,QACE,oBAAC;EAAI,WAAU;YACb,oBAACA;GAAU,MAAK;aAbP,CACX;IACE,OAAO;IACP,WAAW,oBAAC,kBAAwB,UAAW;IAChD,EACD;IACE,OAAO;IACP,WAAW,oBAAC,oBAAkB;IAC/B,CACF,CAKW,KAAK,KAAK,MACd,oBAACA,kBAAU;IAET,OAAO,IAAI;IACX,aAAY;IACZ,OAAO,IAAI;cAEV,IAAI;MALA,EAMU,CACjB;IACQ;GACR;;AAIV,MAAM,kBAAkB,EAAE,cAAgC;AACxD,QACE,oBAAC;EAAI,WAAU;YACb,qBAAC;GAAI,WAAU;cACb,qBAAC;IAAI,WAAU;;KACb,qBAAC,oBACC,oBAAC;MAAK,WAAU;gBAAgB;OAAe,EAC/C,oBAAC,iBAAG,QAAQ,WAAW,QAAQ,WAAW,MAAQ,IAC9C;KACN,qBAAC,oBACC,oBAAC;MAAK,WAAU;gBAAgB;OAAwB,EACxD,oBAAC,iBAAG,QAAQ,gBAAgB,QAAQ,gBAAgB,MAAQ,IACxD;KACN,qBAAC,oBACC,oBAAC;MAAK,WAAU;gBAAgB;OAAW,EAC3C,oBAAC,iBAAG,QAAQ,QAAQ,MAAQ,IACxB;;KACF,EACN,qBAAC;IAAI,WAAU;eACb,qBAAC,oBACC,oBAAC;KAAK,WAAU;eAAgB;MAAa,EAC7C,oBAAC,iBAAG,QAAQ,SAAS,GAAG,QAAQ,OAAO,MAAM,MAAQ,IACjD,EACN,qBAAC,oBACC,oBAAC;KAAK,WAAU;eAAgB;MAAiB,EACjD,oBAAC,iBACE,QAAQ,UAAU,QAAQ,SAAS,QAAQ,SACxC,GAAG,QAAQ,OAAO,MAAM,QAAQ,MAAM,MAAM,QAAQ,OAAO,KAC3D,MACF,IACA;KACF;IACF;GACF;;AAIV,MAAM,wBAAwB;AAC5B,QACE,oBAAC;EAAI,WAAU;YACb,qBAAC;GAAI,WAAU;;IACb,qBAAC;KAAI,WAAU;gBACb,oBAAC,iBAAe,EAChB,qBAAC,oBACC,oBAAC;MAAK,WAAU;gBAAgB;OAAoB,EACpD,oBAAC;MAAE,WAAU;gBAAW;OAGpB,IACA;MACF;IACN,qBAAC;KAAI,WAAU;gBACb,oBAAC,YAAU,EACX,qBAAC,oBACC,oBAAC;MAAK,WAAU;gBAAgB;OAAuB,EACvD,oBAAC;MAAE,WAAU;gBAAW;OAGpB,IACA;MACF;IACN,qBAAC;KAAI,WAAU;gBACb,oBAAC,SAAO,EACR,qBAAC,oBACC,oBAAC;MAAK,WAAU;gBAAgB;OAAmB,EACnD,oBAAC;MAAE,WAAU;gBAAW;OAIpB,IACA;MACF;;IACF;GACF;;AAIV,2BAAe"}
1
+ {"version":3,"file":"index.js","names":["Accordion"],"sources":["../../../src/components/product-tabs/index.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\n\nimport { Back } from '@gfed-medusa/sf-lib-ui/icons/back';\nimport { FastDelivery } from '@gfed-medusa/sf-lib-ui/icons/fast-delivery';\nimport { Refresh } from '@gfed-medusa/sf-lib-ui/icons/refresh';\n\nimport { sendClientSignal } from '@gfed-medusa/sf-lib-common/lib/personalization/client-signal';\nimport { SignalType } from '@gfed-medusa/sf-lib-common/types/graphql';\nimport { Product } from '@/types/graphql';\n\nimport Accordion from './accordion';\n\ntype ProductTabsProps = {\n product: Product;\n};\n\nconst ProductTabs = ({ product }: ProductTabsProps) => {\n const hasSentReviewsViewRef = useRef(false);\n const productId = product.id;\n const [openTabValues, setOpenTabValues] = useState<string[]>([]);\n\n const tabs = [\n {\n label: 'Product Information',\n component: <ProductInfoTab product={product} />,\n },\n {\n label: 'Shipping & Returns',\n component: <ShippingInfoTab />,\n },\n ];\n\n // Fire REVIEWS_VIEW when the \"Shipping & Returns\" tab is\n // open — whether initially (via defaultValue) or via user\n // interaction.\n useEffect(() => {\n if (\n openTabValues.includes('Shipping & Returns') &&\n !hasSentReviewsViewRef.current\n ) {\n hasSentReviewsViewRef.current = true;\n void sendClientSignal(SignalType.ReviewsView, { productId });\n }\n }, [openTabValues, productId]);\n\n return (\n <div className=\"w-full\">\n <Accordion\n type=\"multiple\"\n value={openTabValues}\n onValueChange={(values: string[]) => {\n setOpenTabValues(values);\n void sendClientSignal(SignalType.TabSwitch, {\n activeTabs: values,\n productId,\n });\n }}\n >\n {tabs.map((tab, i) => (\n <Accordion.Item\n key={i}\n title={tab.label}\n headingSize=\"medium\"\n value={tab.label}\n >\n {tab.component}\n </Accordion.Item>\n ))}\n </Accordion>\n </div>\n );\n};\n\nconst ProductInfoTab = ({ product }: ProductTabsProps) => {\n return (\n <div className=\"text-small-regular py-8\">\n <div className=\"grid grid-cols-2 gap-x-8\">\n <div className=\"flex flex-col gap-y-4\">\n <div>\n <span className=\"font-semibold\">Material</span>\n <p>{product.material ? product.material : '-'}</p>\n </div>\n <div>\n <span className=\"font-semibold\">Country of origin</span>\n <p>{product.originCountry ? product.originCountry : '-'}</p>\n </div>\n <div>\n <span className=\"font-semibold\">Type</span>\n <p>{product.type || '-'}</p>\n </div>\n </div>\n <div className=\"flex flex-col gap-y-4\">\n <div>\n <span className=\"font-semibold\">Weight</span>\n <p>{product.weight ? `${product.weight} g` : '-'}</p>\n </div>\n <div>\n <span className=\"font-semibold\">Dimensions</span>\n <p>\n {product.length && product.width && product.height\n ? `${product.length}L x ${product.width}W x ${product.height}H`\n : '-'}\n </p>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nconst ShippingInfoTab = () => {\n return (\n <div className=\"text-small-regular py-8\">\n <div className=\"grid grid-cols-1 gap-y-8\">\n <div className=\"flex items-start gap-x-2\">\n <FastDelivery />\n <div>\n <span className=\"font-semibold\">Fast delivery</span>\n <p className=\"max-w-sm\">\n Your package will arrive in 3-5 business days at your pick up\n location or in the comfort of your home.\n </p>\n </div>\n </div>\n <div className=\"flex items-start gap-x-2\">\n <Refresh />\n <div>\n <span className=\"font-semibold\">Simple exchanges</span>\n <p className=\"max-w-sm\">\n Is the fit not quite right? No worries - we&apos;ll exchange your\n product for a new one.\n </p>\n </div>\n </div>\n <div className=\"flex items-start gap-x-2\">\n <Back />\n <div>\n <span className=\"font-semibold\">Easy returns</span>\n <p className=\"max-w-sm\">\n Just return your product and we&apos;ll refund your money. No\n questions asked – we&apos;ll do our best to make sure your return\n is hassle-free.\n </p>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default ProductTabs;\n"],"mappings":";;;;;;;;;;;;AAkBA,MAAM,eAAe,EAAE,cAAgC;CACrD,MAAM,wBAAwB,OAAO,MAAM;CAC3C,MAAM,YAAY,QAAQ;CAC1B,MAAM,CAAC,eAAe,oBAAoB,SAAmB,EAAE,CAAC;CAEhE,MAAM,OAAO,CACX;EACE,OAAO;EACP,WAAW,oBAAC,kBAAwB,UAAW;EAChD,EACD;EACE,OAAO;EACP,WAAW,oBAAC,oBAAkB;EAC/B,CACF;AAKD,iBAAgB;AACd,MACE,cAAc,SAAS,qBAAqB,IAC5C,CAAC,sBAAsB,SACvB;AACA,yBAAsB,UAAU;AAChC,GAAK,iBAAiB,WAAW,aAAa,EAAE,WAAW,CAAC;;IAE7D,CAAC,eAAe,UAAU,CAAC;AAE9B,QACE,oBAAC;EAAI,WAAU;YACb,oBAACA;GACC,MAAK;GACL,OAAO;GACP,gBAAgB,WAAqB;AACnC,qBAAiB,OAAO;AACxB,IAAK,iBAAiB,WAAW,WAAW;KAC1C,YAAY;KACZ;KACD,CAAC;;aAGH,KAAK,KAAK,KAAK,MACd,oBAACA,kBAAU;IAET,OAAO,IAAI;IACX,aAAY;IACZ,OAAO,IAAI;cAEV,IAAI;MALA,EAMU,CACjB;IACQ;GACR;;AAIV,MAAM,kBAAkB,EAAE,cAAgC;AACxD,QACE,oBAAC;EAAI,WAAU;YACb,qBAAC;GAAI,WAAU;cACb,qBAAC;IAAI,WAAU;;KACb,qBAAC,oBACC,oBAAC;MAAK,WAAU;gBAAgB;OAAe,EAC/C,oBAAC,iBAAG,QAAQ,WAAW,QAAQ,WAAW,MAAQ,IAC9C;KACN,qBAAC,oBACC,oBAAC;MAAK,WAAU;gBAAgB;OAAwB,EACxD,oBAAC,iBAAG,QAAQ,gBAAgB,QAAQ,gBAAgB,MAAQ,IACxD;KACN,qBAAC,oBACC,oBAAC;MAAK,WAAU;gBAAgB;OAAW,EAC3C,oBAAC,iBAAG,QAAQ,QAAQ,MAAQ,IACxB;;KACF,EACN,qBAAC;IAAI,WAAU;eACb,qBAAC,oBACC,oBAAC;KAAK,WAAU;eAAgB;MAAa,EAC7C,oBAAC,iBAAG,QAAQ,SAAS,GAAG,QAAQ,OAAO,MAAM,MAAQ,IACjD,EACN,qBAAC,oBACC,oBAAC;KAAK,WAAU;eAAgB;MAAiB,EACjD,oBAAC,iBACE,QAAQ,UAAU,QAAQ,SAAS,QAAQ,SACxC,GAAG,QAAQ,OAAO,MAAM,QAAQ,MAAM,MAAM,QAAQ,OAAO,KAC3D,MACF,IACA;KACF;IACF;GACF;;AAIV,MAAM,wBAAwB;AAC5B,QACE,oBAAC;EAAI,WAAU;YACb,qBAAC;GAAI,WAAU;;IACb,qBAAC;KAAI,WAAU;gBACb,oBAAC,iBAAe,EAChB,qBAAC,oBACC,oBAAC;MAAK,WAAU;gBAAgB;OAAoB,EACpD,oBAAC;MAAE,WAAU;gBAAW;OAGpB,IACA;MACF;IACN,qBAAC;KAAI,WAAU;gBACb,oBAAC,YAAU,EACX,qBAAC,oBACC,oBAAC;MAAK,WAAU;gBAAgB;OAAuB,EACvD,oBAAC;MAAE,WAAU;gBAAW;OAGpB,IACA;MACF;IACN,qBAAC;KAAI,WAAU;gBACb,oBAAC,SAAO,EACR,qBAAC,oBACC,oBAAC;MAAK,WAAU;gBAAgB;OAAmB,EACnD,oBAAC;MAAE,WAAU;gBAAW;OAIpB,IACA;MACF;;IACF;GACF;;AAIV,2BAAe"}
@@ -1,5 +1,5 @@
1
1
  import { SortOptions } from "./sort-products/index.js";
2
- import * as react_jsx_runtime6 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime5 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/components/refinement-list/index.d.ts
5
5
  type RefinementListProps = {
@@ -12,7 +12,7 @@ declare const RefinementList: ({
12
12
  sortBy,
13
13
  "data-testid": dataTestId,
14
14
  className
15
- }: RefinementListProps) => react_jsx_runtime6.JSX.Element;
15
+ }: RefinementListProps) => react_jsx_runtime5.JSX.Element;
16
16
  //#endregion
17
17
  export { RefinementList as default };
18
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/refinement-list/index.tsx"],"sourcesContent":[],"mappings":";;;;KAUK,mBAAA;UACK;;EADL,aAAA,CAAA,EAAA,MAAmB;EAOlB,SAAA,CAAA,EAAA,MA8CL;CA9CuB;cAAlB,cAAkB,EAAA,CAAA;EAAA,MAAA;EAAA,aAAA,EAAA,UAAA;EAAA;AAAA,CAAA,EAIrB,mBAJqB,EAAA,GAIF,kBAAA,CAAA,GAAA,CAAA,OAJE"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/components/refinement-list/index.tsx"],"sourcesContent":[],"mappings":";;;;KAaK,mBAAA;UACK;;EADL,aAAA,CAAA,EAAA,MAAmB;EAOlB,SAAA,CAAA,EAAA,MAmDL;CAnDuB;cAAlB,cAAkB,EAAA,CAAA;EAAA,MAAA;EAAA,aAAA,EAAA,UAAA;EAAA;AAAA,CAAA,EAIrB,mBAJqB,EAAA,GAIF,kBAAA,CAAA,GAAA,CAAA,OAJE"}
@@ -2,9 +2,11 @@
2
2
 
3
3
  import sort_products_default from "./sort-products/index.js";
4
4
  import { useCallback } from "react";
5
+ import { sendClientSignal } from "@gfed-medusa/sf-lib-common/lib/personalization/client-signal";
6
+ import { SignalType } from "@gfed-medusa/sf-lib-common/types/graphql";
7
+ import { usePathname, useRouter, useSearchParams } from "next/navigation";
5
8
  import { clx } from "@medusajs/ui";
6
9
  import { jsx, jsxs } from "react/jsx-runtime";
7
- import { usePathname, useRouter, useSearchParams } from "next/navigation";
8
10
 
9
11
  //#region src/components/refinement-list/index.tsx
10
12
  const RefinementList = ({ sortBy, "data-testid": dataTestId, className }) => {
@@ -19,6 +21,10 @@ const RefinementList = ({ sortBy, "data-testid": dataTestId, className }) => {
19
21
  }, [searchParams]);
20
22
  const setQueryParams = (name, value) => {
21
23
  const query = createQueryString(name, value);
24
+ if (name !== "sortBy") sendClientSignal(SignalType.FilterApplied, {
25
+ filterName: name,
26
+ filterValue: value
27
+ });
22
28
  router.push(`${pathname}?${query}`);
23
29
  };
24
30
  return /* @__PURE__ */ jsxs("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["SortProducts"],"sources":["../../../src/components/refinement-list/index.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback } from 'react';\n\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation';\n\nimport { clx } from '@medusajs/ui';\n\nimport SortProducts, { SortOptions } from './sort-products';\n\ntype RefinementListProps = {\n sortBy: SortOptions;\n search?: boolean;\n 'data-testid'?: string;\n className?: string;\n};\n\nconst RefinementList = ({\n sortBy,\n 'data-testid': dataTestId,\n className,\n}: RefinementListProps) => {\n const router = useRouter();\n const pathname = usePathname();\n const searchParams = useSearchParams();\n\n const createQueryString = useCallback(\n (name: string, value: string) => {\n const params = new URLSearchParams(searchParams);\n params.set(name, value);\n if (name === 'sortBy') {\n params.delete('page');\n }\n\n return params.toString();\n },\n [searchParams]\n );\n\n const setQueryParams = (name: string, value: string) => {\n const query = createQueryString(name, value);\n router.push(`${pathname}?${query}`);\n };\n\n return (\n <div className={clx('w-full', className)}>\n <div className=\"small:hidden\">\n <SortProducts\n sortBy={sortBy}\n setQueryParams={setQueryParams}\n data-testid={dataTestId}\n variant=\"dropdown\"\n />\n </div>\n <div className=\"small:ml-[1.675rem] small:min-w-[250px] small:flex-col small:px-0 small:py-4 small:pr-6 small:block hidden\">\n <SortProducts\n sortBy={sortBy}\n setQueryParams={setQueryParams}\n data-testid={dataTestId}\n />\n </div>\n </div>\n );\n};\n\nexport default RefinementList;\n"],"mappings":";;;;;;;;;AAiBA,MAAM,kBAAkB,EACtB,QACA,eAAe,YACf,gBACyB;CACzB,MAAM,SAAS,WAAW;CAC1B,MAAM,WAAW,aAAa;CAC9B,MAAM,eAAe,iBAAiB;CAEtC,MAAM,oBAAoB,aACvB,MAAc,UAAkB;EAC/B,MAAM,SAAS,IAAI,gBAAgB,aAAa;AAChD,SAAO,IAAI,MAAM,MAAM;AACvB,MAAI,SAAS,SACX,QAAO,OAAO,OAAO;AAGvB,SAAO,OAAO,UAAU;IAE1B,CAAC,aAAa,CACf;CAED,MAAM,kBAAkB,MAAc,UAAkB;EACtD,MAAM,QAAQ,kBAAkB,MAAM,MAAM;AAC5C,SAAO,KAAK,GAAG,SAAS,GAAG,QAAQ;;AAGrC,QACE,qBAAC;EAAI,WAAW,IAAI,UAAU,UAAU;aACtC,oBAAC;GAAI,WAAU;aACb,oBAACA;IACS;IACQ;IAChB,eAAa;IACb,SAAQ;KACR;IACE,EACN,oBAAC;GAAI,WAAU;aACb,oBAACA;IACS;IACQ;IAChB,eAAa;KACb;IACE;GACF;;AAIV,8BAAe"}
1
+ {"version":3,"file":"index.js","names":["SortProducts"],"sources":["../../../src/components/refinement-list/index.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback } from 'react';\n\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation';\n\nimport { clx } from '@medusajs/ui';\n\nimport { sendClientSignal } from '@gfed-medusa/sf-lib-common/lib/personalization/client-signal';\nimport { SignalType } from '@gfed-medusa/sf-lib-common/types/graphql';\n\nimport SortProducts, { SortOptions } from './sort-products';\n\ntype RefinementListProps = {\n sortBy: SortOptions;\n search?: boolean;\n 'data-testid'?: string;\n className?: string;\n};\n\nconst RefinementList = ({\n sortBy,\n 'data-testid': dataTestId,\n className,\n}: RefinementListProps) => {\n const router = useRouter();\n const pathname = usePathname();\n const searchParams = useSearchParams();\n\n const createQueryString = useCallback(\n (name: string, value: string) => {\n const params = new URLSearchParams(searchParams);\n params.set(name, value);\n if (name === 'sortBy') {\n params.delete('page');\n }\n\n return params.toString();\n },\n [searchParams]\n );\n\n const setQueryParams = (name: string, value: string) => {\n const query = createQueryString(name, value);\n\n if (name !== 'sortBy') {\n void sendClientSignal(SignalType.FilterApplied, { filterName: name, filterValue: value });\n }\n\n router.push(`${pathname}?${query}`);\n };\n\n return (\n <div className={clx('w-full', className)}>\n <div className=\"small:hidden\">\n <SortProducts\n sortBy={sortBy}\n setQueryParams={setQueryParams}\n data-testid={dataTestId}\n variant=\"dropdown\"\n />\n </div>\n <div className=\"small:ml-[1.675rem] small:min-w-[250px] small:flex-col small:px-0 small:py-4 small:pr-6 small:block hidden\">\n <SortProducts\n sortBy={sortBy}\n setQueryParams={setQueryParams}\n data-testid={dataTestId}\n />\n </div>\n </div>\n );\n};\n\nexport default RefinementList;\n"],"mappings":";;;;;;;;;;;AAoBA,MAAM,kBAAkB,EACtB,QACA,eAAe,YACf,gBACyB;CACzB,MAAM,SAAS,WAAW;CAC1B,MAAM,WAAW,aAAa;CAC9B,MAAM,eAAe,iBAAiB;CAEtC,MAAM,oBAAoB,aACvB,MAAc,UAAkB;EAC/B,MAAM,SAAS,IAAI,gBAAgB,aAAa;AAChD,SAAO,IAAI,MAAM,MAAM;AACvB,MAAI,SAAS,SACX,QAAO,OAAO,OAAO;AAGvB,SAAO,OAAO,UAAU;IAE1B,CAAC,aAAa,CACf;CAED,MAAM,kBAAkB,MAAc,UAAkB;EACtD,MAAM,QAAQ,kBAAkB,MAAM,MAAM;AAE5C,MAAI,SAAS,SACX,CAAK,iBAAiB,WAAW,eAAe;GAAE,YAAY;GAAM,aAAa;GAAO,CAAC;AAG3F,SAAO,KAAK,GAAG,SAAS,GAAG,QAAQ;;AAGrC,QACE,qBAAC;EAAI,WAAW,IAAI,UAAU,UAAU;aACtC,oBAAC;GAAI,WAAU;aACb,oBAACA;IACS;IACQ;IAChB,eAAa;IACb,SAAQ;KACR;IACE,EACN,oBAAC;GAAI,WAAU;aACb,oBAACA;IACS;IACQ;IAChB,eAAa;KACb;IACE;GACF;;AAIV,8BAAe"}
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime13 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime14 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/components/refinement-list/sort-products/index.d.ts
4
4
  type SortOptions = 'price_asc' | 'price_desc' | 'created_at';
@@ -13,7 +13,7 @@ declare const SortProducts: ({
13
13
  sortBy,
14
14
  setQueryParams,
15
15
  variant
16
- }: SortProductsProps) => react_jsx_runtime13.JSX.Element;
16
+ }: SortProductsProps) => react_jsx_runtime14.JSX.Element;
17
17
  //#endregion
18
18
  export { SortOptions, SortProducts as default };
19
19
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../../src/components/refinement-list/sort-products/index.tsx"],"sourcesContent":[],"mappings":";;;KAKY,WAAA;KAEP,iBAAA;UACK;EAHE,cAAW,EAAA,CAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAIiB,WAJjB,EAAA,GAAA,IAAA;EAElB,OAAA,CAAA,EAAA,OAAA,GAAiB,UAAA;EAsBhB,aAAA,CAAA,EAwCL,MAAA;CAxCqB;cAAhB,YAAgB,EAAA,CAAA;EAAA,aAAA,EAAA,UAAA;EAAA,MAAA;EAAA,cAAA;EAAA;AAAA,CAAA,EAKnB,iBALmB,EAAA,GAKF,mBAAA,CAAA,GAAA,CAAA,OALE"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../../src/components/refinement-list/sort-products/index.tsx"],"sourcesContent":[],"mappings":";;;KAOY,WAAA;KAEP,iBAAA;UACK;EAHE,cAAW,EAAA,CAAA,IAAA,EAAA,MAAA,EAAA,KAAA,EAIiB,WAJjB,EAAA,GAAA,IAAA;EAElB,OAAA,CAAA,EAAA,OAAA,GAAiB,UAAA;EAsBhB,aAAA,CAAA,EAyCL,MAAA;CAzCqB;cAAhB,YAAgB,EAAA,CAAA;EAAA,aAAA,EAAA,UAAA;EAAA,MAAA;EAAA,cAAA;EAAA;AAAA,CAAA,EAKnB,iBALmB,EAAA,GAKF,mBAAA,CAAA,GAAA,CAAA,OALE"}
@@ -1,5 +1,7 @@
1
1
  'use client';
2
2
 
3
+ import { sendClientSignal } from "@gfed-medusa/sf-lib-common/lib/personalization/client-signal";
4
+ import { SignalType } from "@gfed-medusa/sf-lib-common/types/graphql";
3
5
  import { jsx } from "react/jsx-runtime";
4
6
  import { FilterRadioGroup } from "@gfed-medusa/sf-lib-ui/components/filter-radio-group";
5
7
  import NativeSelect from "@gfed-medusa/sf-lib-ui/components/native-select";
@@ -22,6 +24,7 @@ const sortOptions = [
22
24
  const SortProducts = ({ "data-testid": dataTestId, sortBy, setQueryParams, variant = "radio" }) => {
23
25
  const handleChange = (value) => {
24
26
  setQueryParams("sortBy", value);
27
+ sendClientSignal(SignalType.SortChanged, { sortBy: value });
25
28
  };
26
29
  if (variant === "dropdown") return /* @__PURE__ */ jsx("div", {
27
30
  className: "w-fit max-w-full",
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/refinement-list/sort-products/index.tsx"],"sourcesContent":["'use client';\n\nimport { FilterRadioGroup } from '@gfed-medusa/sf-lib-ui/components/filter-radio-group';\nimport NativeSelect from '@gfed-medusa/sf-lib-ui/components/native-select';\n\nexport type SortOptions = 'price_asc' | 'price_desc' | 'created_at';\n\ntype SortProductsProps = {\n sortBy: SortOptions;\n setQueryParams: (name: string, value: SortOptions) => void;\n variant?: 'radio' | 'dropdown';\n 'data-testid'?: string;\n};\n\nconst sortOptions = [\n {\n value: 'created_at',\n label: 'Latest Arrivals',\n },\n {\n value: 'price_asc',\n label: 'Price: Low -> High',\n },\n {\n value: 'price_desc',\n label: 'Price: High -> Low',\n },\n];\n\nconst SortProducts = ({\n 'data-testid': dataTestId,\n sortBy,\n setQueryParams,\n variant = 'radio',\n}: SortProductsProps) => {\n const handleChange = (value: SortOptions) => {\n setQueryParams('sortBy', value);\n };\n\n if (variant === 'dropdown') {\n return (\n <div className=\"w-fit max-w-full\">\n <NativeSelect\n aria-label=\"Sort products\"\n placeholder=\"Sort by\"\n value={sortBy}\n className=\"xsmall:w-[280px] w-[220px] max-w-full\"\n onChange={(event) => handleChange(event.target.value as SortOptions)}\n data-testid={dataTestId}\n >\n {sortOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </NativeSelect>\n </div>\n );\n }\n\n return (\n <FilterRadioGroup\n title=\"Sort by\"\n items={sortOptions}\n value={sortBy}\n handleChange={handleChange}\n data-testid={dataTestId}\n />\n );\n};\n\nexport default SortProducts;\n"],"mappings":";;;;;;;AAcA,MAAM,cAAc;CAClB;EACE,OAAO;EACP,OAAO;EACR;CACD;EACE,OAAO;EACP,OAAO;EACR;CACD;EACE,OAAO;EACP,OAAO;EACR;CACF;AAED,MAAM,gBAAgB,EACpB,eAAe,YACf,QACA,gBACA,UAAU,cACa;CACvB,MAAM,gBAAgB,UAAuB;AAC3C,iBAAe,UAAU,MAAM;;AAGjC,KAAI,YAAY,WACd,QACE,oBAAC;EAAI,WAAU;YACb,oBAAC;GACC,cAAW;GACX,aAAY;GACZ,OAAO;GACP,WAAU;GACV,WAAW,UAAU,aAAa,MAAM,OAAO,MAAqB;GACpE,eAAa;aAEZ,YAAY,KAAK,WAChB,oBAAC;IAA0B,OAAO,OAAO;cACtC,OAAO;MADG,OAAO,MAEX,CACT;IACW;GACX;AAIV,QACE,oBAAC;EACC,OAAM;EACN,OAAO;EACP,OAAO;EACO;EACd,eAAa;GACb;;AAIN,4BAAe"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/components/refinement-list/sort-products/index.tsx"],"sourcesContent":["'use client';\n\nimport { FilterRadioGroup } from '@gfed-medusa/sf-lib-ui/components/filter-radio-group';\nimport NativeSelect from '@gfed-medusa/sf-lib-ui/components/native-select';\nimport { sendClientSignal } from '@gfed-medusa/sf-lib-common/lib/personalization/client-signal';\nimport { SignalType } from '@gfed-medusa/sf-lib-common/types/graphql';\n\nexport type SortOptions = 'price_asc' | 'price_desc' | 'created_at';\n\ntype SortProductsProps = {\n sortBy: SortOptions;\n setQueryParams: (name: string, value: SortOptions) => void;\n variant?: 'radio' | 'dropdown';\n 'data-testid'?: string;\n};\n\nconst sortOptions = [\n {\n value: 'created_at',\n label: 'Latest Arrivals',\n },\n {\n value: 'price_asc',\n label: 'Price: Low -> High',\n },\n {\n value: 'price_desc',\n label: 'Price: High -> Low',\n },\n];\n\nconst SortProducts = ({\n 'data-testid': dataTestId,\n sortBy,\n setQueryParams,\n variant = 'radio',\n}: SortProductsProps) => {\n const handleChange = (value: SortOptions) => {\n setQueryParams('sortBy', value);\n void sendClientSignal(SignalType.SortChanged, { sortBy: value });\n };\n\n if (variant === 'dropdown') {\n return (\n <div className=\"w-fit max-w-full\">\n <NativeSelect\n aria-label=\"Sort products\"\n placeholder=\"Sort by\"\n value={sortBy}\n className=\"xsmall:w-[280px] w-[220px] max-w-full\"\n onChange={(event) => handleChange(event.target.value as SortOptions)}\n data-testid={dataTestId}\n >\n {sortOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </NativeSelect>\n </div>\n );\n }\n\n return (\n <FilterRadioGroup\n title=\"Sort by\"\n items={sortOptions}\n value={sortBy}\n handleChange={handleChange}\n data-testid={dataTestId}\n />\n );\n};\n\nexport default SortProducts;\n"],"mappings":";;;;;;;;;AAgBA,MAAM,cAAc;CAClB;EACE,OAAO;EACP,OAAO;EACR;CACD;EACE,OAAO;EACP,OAAO;EACR;CACD;EACE,OAAO;EACP,OAAO;EACR;CACF;AAED,MAAM,gBAAgB,EACpB,eAAe,YACf,QACA,gBACA,UAAU,cACa;CACvB,MAAM,gBAAgB,UAAuB;AAC3C,iBAAe,UAAU,MAAM;AAC/B,EAAK,iBAAiB,WAAW,aAAa,EAAE,QAAQ,OAAO,CAAC;;AAGlE,KAAI,YAAY,WACd,QACE,oBAAC;EAAI,WAAU;YACb,oBAAC;GACC,cAAW;GACX,aAAY;GACZ,OAAO;GACP,WAAU;GACV,WAAW,UAAU,aAAa,MAAM,OAAO,MAAqB;GACpE,eAAa;aAEZ,YAAY,KAAK,WAChB,oBAAC;IAA0B,OAAO,OAAO;cACtC,OAAO;MADG,OAAO,MAEX,CACT;IACW;GACX;AAIV,QACE,oBAAC;EACC,OAAM;EACN,OAAO;EACP,OAAO;EACO;EACd,eAAa;GACb;;AAIN,4BAAe"}