@wix/headless-stores 0.0.18 → 0.0.20

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.
@@ -0,0 +1,35 @@
1
+ import { type ServiceFactoryConfig } from "@wix/services-definitions";
2
+ import { type Signal } from "@wix/services-definitions/core-services/signals";
3
+ export interface ProductOption {
4
+ id: string;
5
+ name: string;
6
+ choices: ProductChoice[];
7
+ optionRenderType?: string;
8
+ }
9
+ export interface ProductChoice {
10
+ id: string;
11
+ name: string;
12
+ colorCode?: string;
13
+ }
14
+ export interface CatalogPriceRange {
15
+ minPrice: number;
16
+ maxPrice: number;
17
+ }
18
+ export interface CatalogServiceAPI {
19
+ catalogOptions: Signal<ProductOption[] | null>;
20
+ catalogPriceRange: Signal<CatalogPriceRange | null>;
21
+ isLoading: Signal<boolean>;
22
+ error: Signal<string | null>;
23
+ loadCatalogData: (categoryId?: string) => Promise<void>;
24
+ }
25
+ export declare const CatalogServiceDefinition: string & {
26
+ __api: CatalogServiceAPI;
27
+ __config: {};
28
+ isServiceDefinition?: boolean;
29
+ } & CatalogServiceAPI;
30
+ export declare const CatalogService: import("@wix/services-definitions").ServiceFactory<string & {
31
+ __api: CatalogServiceAPI;
32
+ __config: {};
33
+ isServiceDefinition?: boolean;
34
+ } & CatalogServiceAPI, {}>;
35
+ export declare function loadCatalogServiceConfig(): Promise<ServiceFactoryConfig<typeof CatalogService>>;
@@ -9,6 +9,12 @@ const extractAggregationValues = (aggregationResponse, name) => {
9
9
  aggregationResponse.aggregationData?.results?.find((r) => r.name === name);
10
10
  return aggregation?.values?.results?.map((item) => item.value) || [];
11
11
  };
12
+ const extractScalarAggregationValue = (aggregationResponse, name) => {
13
+ const aggregation = aggregationResponse.aggregations?.[name] ||
14
+ aggregationResponse.aggregationData?.results?.find((r) => r.name === name);
15
+ const value = aggregation?.scalar?.value;
16
+ return value !== undefined && value !== null ? parseFloat(value) : null;
17
+ };
12
18
  const matchesAggregationName = (name, aggregationNames) => {
13
19
  return aggregationNames.some((aggName) => aggName.toLowerCase() === name.toLowerCase());
14
20
  };
@@ -37,19 +43,34 @@ const buildCategoryFilter = (categoryId) => {
37
43
  },
38
44
  };
39
45
  };
40
- export const CatalogOptionsServiceDefinition = defineService("catalogOptions");
41
- export const CatalogOptionsService = implementService.withConfig()(CatalogOptionsServiceDefinition, ({ getService }) => {
46
+ export const CatalogServiceDefinition = defineService("catalog");
47
+ export const CatalogService = implementService.withConfig()(CatalogServiceDefinition, ({ getService }) => {
42
48
  const signalsService = getService(SignalsServiceDefinition);
43
49
  const catalogOptions = signalsService.signal(null);
50
+ const catalogPriceRange = signalsService.signal(null);
44
51
  const isLoading = signalsService.signal(false);
45
52
  const error = signalsService.signal(null);
46
- const loadCatalogOptions = async (categoryId) => {
53
+ const loadCatalogData = async (categoryId) => {
47
54
  isLoading.set(true);
48
55
  error.set(null);
49
56
  try {
50
- // Step 1: Get unique option and choice names from catalog via aggregation (no products returned)
57
+ // Single aggregation request to get ALL catalog data at once
51
58
  const aggregationRequest = {
52
59
  aggregations: [
60
+ // Price range aggregations
61
+ {
62
+ name: "minPrice",
63
+ fieldPath: "actualPriceRange.minValue.amount",
64
+ type: "SCALAR",
65
+ scalar: { type: "MIN" },
66
+ },
67
+ {
68
+ name: "maxPrice",
69
+ fieldPath: "actualPriceRange.maxValue.amount",
70
+ type: "SCALAR",
71
+ scalar: { type: "MAX" },
72
+ },
73
+ // Options aggregations
53
74
  {
54
75
  name: "optionNames",
55
76
  fieldPath: "options.name",
@@ -85,16 +106,31 @@ export const CatalogOptionsService = implementService.withConfig()(CatalogOption
85
106
  includeProducts: false,
86
107
  cursorPaging: { limit: 0 },
87
108
  };
88
- const aggregationResponse = await productsV3.searchProducts(aggregationRequest);
109
+ // Make the single aggregation request
110
+ const [aggregationResponse, customizationsResponse] = await Promise.all([
111
+ productsV3.searchProducts(aggregationRequest),
112
+ customizationsV3.queryCustomizations().find(),
113
+ ]);
114
+ // Process price range data
115
+ const minPrice = extractScalarAggregationValue(aggregationResponse, "minPrice");
116
+ const maxPrice = extractScalarAggregationValue(aggregationResponse, "maxPrice");
117
+ if (minPrice !== null &&
118
+ maxPrice !== null &&
119
+ (minPrice > 0 || maxPrice > 0)) {
120
+ catalogPriceRange.set({
121
+ minPrice,
122
+ maxPrice,
123
+ });
124
+ }
125
+ else {
126
+ catalogPriceRange.set(null);
127
+ }
128
+ // Process options data
89
129
  const optionNames = extractAggregationValues(aggregationResponse, "optionNames");
90
130
  const choiceNames = extractAggregationValues(aggregationResponse, "choiceNames");
91
131
  const inventoryStatuses = extractAggregationValues(aggregationResponse, "inventoryStatus");
92
- // Step 2: Get option structure from customizations API
93
- const customizationsResponse = await customizationsV3
94
- .queryCustomizations()
95
- .find();
96
132
  const customizations = customizationsResponse.items || [];
97
- // Step 3: Build options by matching customizations with aggregation data
133
+ // Build options by matching customizations with aggregation data
98
134
  const options = customizations
99
135
  .filter((customization) => customization.name &&
100
136
  customization._id &&
@@ -119,11 +155,11 @@ export const CatalogOptionsService = implementService.withConfig()(CatalogOption
119
155
  };
120
156
  })
121
157
  .filter((option) => option.choices.length > 0);
122
- // Step 4: Add inventory filter if there are multiple inventory statuses
158
+ // Add inventory filter if there are multiple inventory statuses
123
159
  if (inventoryStatuses.length > 1) {
124
160
  const inventoryChoices = inventoryStatuses.map((status) => ({
125
- id: status.toUpperCase(), // Use uppercase to match actual availabilityStatus values
126
- name: status.toUpperCase(), // Use raw status value - UI components will handle display conversion
161
+ id: status.toUpperCase(),
162
+ name: status.toUpperCase(),
127
163
  }));
128
164
  options.push({
129
165
  id: "inventory-filter",
@@ -135,9 +171,10 @@ export const CatalogOptionsService = implementService.withConfig()(CatalogOption
135
171
  catalogOptions.set(options);
136
172
  }
137
173
  catch (err) {
138
- console.error("Failed to load catalog options:", err);
139
- error.set(err instanceof Error ? err.message : "Failed to load catalog options");
174
+ console.error("Failed to load catalog data:", err);
175
+ error.set(err instanceof Error ? err.message : "Failed to load catalog data");
140
176
  catalogOptions.set([]);
177
+ catalogPriceRange.set(null);
141
178
  }
142
179
  finally {
143
180
  isLoading.set(false);
@@ -145,11 +182,12 @@ export const CatalogOptionsService = implementService.withConfig()(CatalogOption
145
182
  };
146
183
  return {
147
184
  catalogOptions,
185
+ catalogPriceRange,
148
186
  isLoading,
149
187
  error,
150
- loadCatalogOptions,
188
+ loadCatalogData,
151
189
  };
152
190
  });
153
- export async function loadCatalogOptionsServiceConfig() {
191
+ export async function loadCatalogServiceConfig() {
154
192
  return {};
155
193
  }
@@ -1,6 +1,6 @@
1
1
  import { type ServiceFactoryConfig } from "@wix/services-definitions";
2
- import { type Signal } from "@wix/services-definitions/core-services/signals";
3
- import * as productsV3 from "@wix/auto_sdk_stores_products-v-3";
2
+ import type { Signal } from "@wix/services-definitions/core-services/signals";
3
+ import { productsV3 } from "@wix/stores";
4
4
  import { type Filter } from "./filter-service.js";
5
5
  import { type SortBy } from "./sort-service.js";
6
6
  export interface CollectionServiceAPI {
@@ -1,10 +1,10 @@
1
1
  import { defineService, implementService, } from "@wix/services-definitions";
2
- import { SignalsServiceDefinition, } from "@wix/services-definitions/core-services/signals";
3
- import * as productsV3 from "@wix/auto_sdk_stores_products-v-3";
4
- import * as readOnlyVariantsV3 from "@wix/auto_sdk_stores_read-only-variants-v-3";
2
+ import { SignalsServiceDefinition } from "@wix/services-definitions/core-services/signals";
3
+ import { productsV3, readOnlyVariantsV3 } from "@wix/stores";
5
4
  import { FilterServiceDefinition } from "./filter-service.js";
6
5
  import { CategoryServiceDefinition } from "./category-service.js";
7
6
  import { SortServiceDefinition } from "./sort-service.js";
7
+ import { CatalogServiceDefinition } from "./catalog-service.js";
8
8
  import { URLParamsUtils } from "../utils/url-params.js";
9
9
  import { SortType } from "../enums/sort-enums.js";
10
10
  const { SortDirection } = productsV3;
@@ -159,6 +159,7 @@ export const CollectionService = implementService.withConfig()(CollectionService
159
159
  const collectionFilters = getService(FilterServiceDefinition);
160
160
  const categoryService = getService(CategoryServiceDefinition);
161
161
  const sortService = getService(SortServiceDefinition);
162
+ const catalogService = getService(CatalogServiceDefinition);
162
163
  const hasMoreProducts = signalsService.signal((config.initialHasMore ?? true));
163
164
  let nextCursor = config.initialCursor;
164
165
  const initialProducts = config.initialProducts || [];
@@ -287,13 +288,11 @@ export const CollectionService = implementService.withConfig()(CollectionService
287
288
  const initializeCatalogData = async () => {
288
289
  isInitializingCatalogData = true; // Set flag BEFORE loading
289
290
  const selectedCategory = categoryService.selectedCategory.get();
290
- await collectionFilters.loadCatalogPriceRange(selectedCategory || undefined);
291
- await collectionFilters.loadCatalogOptions(selectedCategory || undefined);
291
+ // Load catalog data from the combined catalog service
292
+ await catalogService.loadCatalogData(selectedCategory || undefined);
292
293
  // Reset flag to allow filter changes to trigger refreshes
293
294
  isInitializingCatalogData = false;
294
295
  };
295
- // Load catalog data on initialization
296
- void initializeCatalogData();
297
296
  signalsService.effect(() => {
298
297
  sortService.currentSort.get();
299
298
  debouncedRefresh(false);
@@ -1,14 +1,5 @@
1
- import { type Signal, type ReadOnlySignal } from "@wix/services-definitions/core-services/signals";
2
- export interface ProductOption {
3
- id: string;
4
- name: string;
5
- choices: {
6
- id: string;
7
- name: string;
8
- colorCode?: string;
9
- }[];
10
- optionRenderType?: string;
11
- }
1
+ import type { Signal, ReadOnlySignal } from "@wix/services-definitions/core-services/signals";
2
+ import { type ProductOption } from "./catalog-service.js";
12
3
  export interface PriceRange {
13
4
  min: number;
14
5
  max: number;
@@ -37,8 +28,6 @@ export interface FilterServiceAPI {
37
28
  max: number;
38
29
  };
39
30
  }>;
40
- loadCatalogPriceRange: (categoryId?: string) => Promise<void>;
41
- loadCatalogOptions: (categoryId?: string) => Promise<void>;
42
31
  isFullyLoaded: ReadOnlySignal<boolean>;
43
32
  }
44
33
  export declare const FilterServiceDefinition: string & {
@@ -1,8 +1,7 @@
1
1
  import { defineService, implementService } from "@wix/services-definitions";
2
- import { SignalsServiceDefinition, } from "@wix/services-definitions/core-services/signals";
2
+ import { SignalsServiceDefinition } from "@wix/services-definitions/core-services/signals";
3
3
  import { URLParamsUtils } from "../utils/url-params.js";
4
- import { CatalogPriceRangeServiceDefinition } from "./catalog-price-range-service.js";
5
- import { CatalogOptionsServiceDefinition } from "./catalog-options-service.js";
4
+ import { CatalogServiceDefinition, } from "./catalog-service.js";
6
5
  export const FilterServiceDefinition = defineService("filtered-collection");
7
6
  export const defaultFilter = {
8
7
  priceRange: { min: 0, max: 0 },
@@ -10,13 +9,12 @@ export const defaultFilter = {
10
9
  };
11
10
  export const FilterService = implementService.withConfig()(FilterServiceDefinition, ({ getService, config }) => {
12
11
  const signalsService = getService(SignalsServiceDefinition);
13
- const catalogPriceRangeService = getService(CatalogPriceRangeServiceDefinition);
14
- const catalogOptionsService = getService(CatalogOptionsServiceDefinition);
12
+ const catalogService = getService(CatalogServiceDefinition);
15
13
  const currentFilters = signalsService.signal((config?.initialFilters || defaultFilter));
16
14
  // Use computed signal for availableOptions to automatically track dependencies
17
15
  const availableOptions = signalsService.computed(() => {
18
- const catalogPriceRange = catalogPriceRangeService.catalogPriceRange.get();
19
- const catalogOptions = catalogOptionsService.catalogOptions.get();
16
+ const catalogPriceRange = catalogService.catalogPriceRange.get();
17
+ const catalogOptions = catalogService.catalogOptions.get();
20
18
  const priceRange = catalogPriceRange &&
21
19
  catalogPriceRange.minPrice < catalogPriceRange.maxPrice
22
20
  ? { min: catalogPriceRange.minPrice, max: catalogPriceRange.maxPrice }
@@ -29,8 +27,8 @@ export const FilterService = implementService.withConfig()(FilterServiceDefiniti
29
27
  });
30
28
  // Use computed signal for isFullyLoaded to automatically track dependencies
31
29
  const isFullyLoaded = signalsService.computed(() => {
32
- const catalogPriceRange = catalogPriceRangeService.catalogPriceRange.get();
33
- const catalogOptions = catalogOptionsService.catalogOptions.get();
30
+ const catalogPriceRange = catalogService.catalogPriceRange.get();
31
+ const catalogOptions = catalogService.catalogOptions.get();
34
32
  // Price range data is considered loaded whether it's null (no prices) or has valid data
35
33
  const hasPriceRangeData = catalogPriceRange !== undefined; // includes null case
36
34
  const hasOptionsData = !!(catalogOptions && catalogOptions.length >= 0); // Even 0 options is valid
@@ -38,7 +36,7 @@ export const FilterService = implementService.withConfig()(FilterServiceDefiniti
38
36
  });
39
37
  // Effect to update currentFilters when catalog data loads (only if filters are at defaults)
40
38
  signalsService.effect(() => {
41
- const catalogPriceRange = catalogPriceRangeService.catalogPriceRange.get();
39
+ const catalogPriceRange = catalogService.catalogPriceRange.get();
42
40
  if (catalogPriceRange &&
43
41
  catalogPriceRange.minPrice < catalogPriceRange.maxPrice) {
44
42
  const priceRange = {
@@ -111,23 +109,11 @@ export const FilterService = implementService.withConfig()(FilterServiceDefiniti
111
109
  }
112
110
  URLParamsUtils.updateURL(urlParams);
113
111
  };
114
- // Note: calculateAvailableOptions was removed as it's no longer needed
115
- // Options are now loaded from the catalog-wide service via loadCatalogOptions
116
- const loadCatalogPriceRange = async (categoryId) => {
117
- // Just call the catalog service - subscriptions will handle signal updates
118
- await catalogPriceRangeService.loadCatalogPriceRange(categoryId);
119
- };
120
- const loadCatalogOptions = async (categoryId) => {
121
- // Just call the catalog service - subscriptions will handle signal updates
122
- await catalogOptionsService.loadCatalogOptions(categoryId);
123
- };
124
112
  return {
125
113
  currentFilters,
126
114
  applyFilters,
127
115
  clearFilters,
128
116
  availableOptions,
129
- loadCatalogPriceRange,
130
- loadCatalogOptions,
131
117
  isFullyLoaded,
132
118
  };
133
119
  });
@@ -1,9 +1,8 @@
1
1
  export { buyNowServiceBinding, loadBuyNowServiceInitialData, } from "./buy-now-service.js";
2
2
  export { payNowServiceBinding, loadPayNowServiceInitialData, } from "./pay-now-service.js";
3
- export { CatalogOptionsService, CatalogOptionsServiceDefinition, loadCatalogOptionsServiceConfig, } from "./catalog-options-service.js";
3
+ export { CatalogService, CatalogServiceDefinition, loadCatalogServiceConfig, } from "./catalog-service.js";
4
4
  export { CategoryService, CategoryServiceDefinition, loadCategoriesConfig, } from "./category-service.js";
5
5
  export { CollectionService, CollectionServiceDefinition, loadCollectionServiceConfig, } from "./collection-service.js";
6
- export { CatalogPriceRangeService, CatalogPriceRangeServiceDefinition, loadCatalogPriceRangeServiceConfig, } from "./catalog-price-range-service.js";
7
6
  export { FilterService, FilterServiceDefinition, Filter, AvailableOptions, } from "./filter-service.js";
8
7
  export { ProductModifiersService, ProductModifiersServiceDefinition, } from "./product-modifiers-service.js";
9
8
  export { ProductService, ProductServiceDefinition, loadProductServiceConfig, } from "./product-service.js";
@@ -1,9 +1,8 @@
1
1
  export { buyNowServiceBinding, loadBuyNowServiceInitialData, } from "./buy-now-service.js";
2
2
  export { payNowServiceBinding, loadPayNowServiceInitialData, } from "./pay-now-service.js";
3
- export { CatalogOptionsService, CatalogOptionsServiceDefinition, loadCatalogOptionsServiceConfig, } from "./catalog-options-service.js";
3
+ export { CatalogService, CatalogServiceDefinition, loadCatalogServiceConfig, } from "./catalog-service.js";
4
4
  export { CategoryService, CategoryServiceDefinition, loadCategoriesConfig, } from "./category-service.js";
5
5
  export { CollectionService, CollectionServiceDefinition, loadCollectionServiceConfig, } from "./collection-service.js";
6
- export { CatalogPriceRangeService, CatalogPriceRangeServiceDefinition, loadCatalogPriceRangeServiceConfig, } from "./catalog-price-range-service.js";
7
6
  export { FilterService, FilterServiceDefinition, } from "./filter-service.js";
8
7
  export { ProductModifiersService, ProductModifiersServiceDefinition, } from "./product-modifiers-service.js";
9
8
  export { ProductService, ProductServiceDefinition, loadProductServiceConfig, } from "./product-service.js";
@@ -0,0 +1,35 @@
1
+ import { type ServiceFactoryConfig } from "@wix/services-definitions";
2
+ import { type Signal } from "@wix/services-definitions/core-services/signals";
3
+ export interface ProductOption {
4
+ id: string;
5
+ name: string;
6
+ choices: ProductChoice[];
7
+ optionRenderType?: string;
8
+ }
9
+ export interface ProductChoice {
10
+ id: string;
11
+ name: string;
12
+ colorCode?: string;
13
+ }
14
+ export interface CatalogPriceRange {
15
+ minPrice: number;
16
+ maxPrice: number;
17
+ }
18
+ export interface CatalogServiceAPI {
19
+ catalogOptions: Signal<ProductOption[] | null>;
20
+ catalogPriceRange: Signal<CatalogPriceRange | null>;
21
+ isLoading: Signal<boolean>;
22
+ error: Signal<string | null>;
23
+ loadCatalogData: (categoryId?: string) => Promise<void>;
24
+ }
25
+ export declare const CatalogServiceDefinition: string & {
26
+ __api: CatalogServiceAPI;
27
+ __config: {};
28
+ isServiceDefinition?: boolean;
29
+ } & CatalogServiceAPI;
30
+ export declare const CatalogService: import("@wix/services-definitions").ServiceFactory<string & {
31
+ __api: CatalogServiceAPI;
32
+ __config: {};
33
+ isServiceDefinition?: boolean;
34
+ } & CatalogServiceAPI, {}>;
35
+ export declare function loadCatalogServiceConfig(): Promise<ServiceFactoryConfig<typeof CatalogService>>;
@@ -9,6 +9,12 @@ const extractAggregationValues = (aggregationResponse, name) => {
9
9
  aggregationResponse.aggregationData?.results?.find((r) => r.name === name);
10
10
  return aggregation?.values?.results?.map((item) => item.value) || [];
11
11
  };
12
+ const extractScalarAggregationValue = (aggregationResponse, name) => {
13
+ const aggregation = aggregationResponse.aggregations?.[name] ||
14
+ aggregationResponse.aggregationData?.results?.find((r) => r.name === name);
15
+ const value = aggregation?.scalar?.value;
16
+ return value !== undefined && value !== null ? parseFloat(value) : null;
17
+ };
12
18
  const matchesAggregationName = (name, aggregationNames) => {
13
19
  return aggregationNames.some((aggName) => aggName.toLowerCase() === name.toLowerCase());
14
20
  };
@@ -37,19 +43,34 @@ const buildCategoryFilter = (categoryId) => {
37
43
  },
38
44
  };
39
45
  };
40
- export const CatalogOptionsServiceDefinition = defineService("catalogOptions");
41
- export const CatalogOptionsService = implementService.withConfig()(CatalogOptionsServiceDefinition, ({ getService }) => {
46
+ export const CatalogServiceDefinition = defineService("catalog");
47
+ export const CatalogService = implementService.withConfig()(CatalogServiceDefinition, ({ getService }) => {
42
48
  const signalsService = getService(SignalsServiceDefinition);
43
49
  const catalogOptions = signalsService.signal(null);
50
+ const catalogPriceRange = signalsService.signal(null);
44
51
  const isLoading = signalsService.signal(false);
45
52
  const error = signalsService.signal(null);
46
- const loadCatalogOptions = async (categoryId) => {
53
+ const loadCatalogData = async (categoryId) => {
47
54
  isLoading.set(true);
48
55
  error.set(null);
49
56
  try {
50
- // Step 1: Get unique option and choice names from catalog via aggregation (no products returned)
57
+ // Single aggregation request to get ALL catalog data at once
51
58
  const aggregationRequest = {
52
59
  aggregations: [
60
+ // Price range aggregations
61
+ {
62
+ name: "minPrice",
63
+ fieldPath: "actualPriceRange.minValue.amount",
64
+ type: "SCALAR",
65
+ scalar: { type: "MIN" },
66
+ },
67
+ {
68
+ name: "maxPrice",
69
+ fieldPath: "actualPriceRange.maxValue.amount",
70
+ type: "SCALAR",
71
+ scalar: { type: "MAX" },
72
+ },
73
+ // Options aggregations
53
74
  {
54
75
  name: "optionNames",
55
76
  fieldPath: "options.name",
@@ -85,16 +106,31 @@ export const CatalogOptionsService = implementService.withConfig()(CatalogOption
85
106
  includeProducts: false,
86
107
  cursorPaging: { limit: 0 },
87
108
  };
88
- const aggregationResponse = await productsV3.searchProducts(aggregationRequest);
109
+ // Make the single aggregation request
110
+ const [aggregationResponse, customizationsResponse] = await Promise.all([
111
+ productsV3.searchProducts(aggregationRequest),
112
+ customizationsV3.queryCustomizations().find(),
113
+ ]);
114
+ // Process price range data
115
+ const minPrice = extractScalarAggregationValue(aggregationResponse, "minPrice");
116
+ const maxPrice = extractScalarAggregationValue(aggregationResponse, "maxPrice");
117
+ if (minPrice !== null &&
118
+ maxPrice !== null &&
119
+ (minPrice > 0 || maxPrice > 0)) {
120
+ catalogPriceRange.set({
121
+ minPrice,
122
+ maxPrice,
123
+ });
124
+ }
125
+ else {
126
+ catalogPriceRange.set(null);
127
+ }
128
+ // Process options data
89
129
  const optionNames = extractAggregationValues(aggregationResponse, "optionNames");
90
130
  const choiceNames = extractAggregationValues(aggregationResponse, "choiceNames");
91
131
  const inventoryStatuses = extractAggregationValues(aggregationResponse, "inventoryStatus");
92
- // Step 2: Get option structure from customizations API
93
- const customizationsResponse = await customizationsV3
94
- .queryCustomizations()
95
- .find();
96
132
  const customizations = customizationsResponse.items || [];
97
- // Step 3: Build options by matching customizations with aggregation data
133
+ // Build options by matching customizations with aggregation data
98
134
  const options = customizations
99
135
  .filter((customization) => customization.name &&
100
136
  customization._id &&
@@ -119,11 +155,11 @@ export const CatalogOptionsService = implementService.withConfig()(CatalogOption
119
155
  };
120
156
  })
121
157
  .filter((option) => option.choices.length > 0);
122
- // Step 4: Add inventory filter if there are multiple inventory statuses
158
+ // Add inventory filter if there are multiple inventory statuses
123
159
  if (inventoryStatuses.length > 1) {
124
160
  const inventoryChoices = inventoryStatuses.map((status) => ({
125
- id: status.toUpperCase(), // Use uppercase to match actual availabilityStatus values
126
- name: status.toUpperCase(), // Use raw status value - UI components will handle display conversion
161
+ id: status.toUpperCase(),
162
+ name: status.toUpperCase(),
127
163
  }));
128
164
  options.push({
129
165
  id: "inventory-filter",
@@ -135,9 +171,10 @@ export const CatalogOptionsService = implementService.withConfig()(CatalogOption
135
171
  catalogOptions.set(options);
136
172
  }
137
173
  catch (err) {
138
- console.error("Failed to load catalog options:", err);
139
- error.set(err instanceof Error ? err.message : "Failed to load catalog options");
174
+ console.error("Failed to load catalog data:", err);
175
+ error.set(err instanceof Error ? err.message : "Failed to load catalog data");
140
176
  catalogOptions.set([]);
177
+ catalogPriceRange.set(null);
141
178
  }
142
179
  finally {
143
180
  isLoading.set(false);
@@ -145,11 +182,12 @@ export const CatalogOptionsService = implementService.withConfig()(CatalogOption
145
182
  };
146
183
  return {
147
184
  catalogOptions,
185
+ catalogPriceRange,
148
186
  isLoading,
149
187
  error,
150
- loadCatalogOptions,
188
+ loadCatalogData,
151
189
  };
152
190
  });
153
- export async function loadCatalogOptionsServiceConfig() {
191
+ export async function loadCatalogServiceConfig() {
154
192
  return {};
155
193
  }
@@ -1,6 +1,6 @@
1
1
  import { type ServiceFactoryConfig } from "@wix/services-definitions";
2
- import { type Signal } from "@wix/services-definitions/core-services/signals";
3
- import * as productsV3 from "@wix/auto_sdk_stores_products-v-3";
2
+ import type { Signal } from "@wix/services-definitions/core-services/signals";
3
+ import { productsV3 } from "@wix/stores";
4
4
  import { type Filter } from "./filter-service.js";
5
5
  import { type SortBy } from "./sort-service.js";
6
6
  export interface CollectionServiceAPI {
@@ -1,10 +1,10 @@
1
1
  import { defineService, implementService, } from "@wix/services-definitions";
2
- import { SignalsServiceDefinition, } from "@wix/services-definitions/core-services/signals";
3
- import * as productsV3 from "@wix/auto_sdk_stores_products-v-3";
4
- import * as readOnlyVariantsV3 from "@wix/auto_sdk_stores_read-only-variants-v-3";
2
+ import { SignalsServiceDefinition } from "@wix/services-definitions/core-services/signals";
3
+ import { productsV3, readOnlyVariantsV3 } from "@wix/stores";
5
4
  import { FilterServiceDefinition } from "./filter-service.js";
6
5
  import { CategoryServiceDefinition } from "./category-service.js";
7
6
  import { SortServiceDefinition } from "./sort-service.js";
7
+ import { CatalogServiceDefinition } from "./catalog-service.js";
8
8
  import { URLParamsUtils } from "../utils/url-params.js";
9
9
  import { SortType } from "../enums/sort-enums.js";
10
10
  const { SortDirection } = productsV3;
@@ -159,6 +159,7 @@ export const CollectionService = implementService.withConfig()(CollectionService
159
159
  const collectionFilters = getService(FilterServiceDefinition);
160
160
  const categoryService = getService(CategoryServiceDefinition);
161
161
  const sortService = getService(SortServiceDefinition);
162
+ const catalogService = getService(CatalogServiceDefinition);
162
163
  const hasMoreProducts = signalsService.signal((config.initialHasMore ?? true));
163
164
  let nextCursor = config.initialCursor;
164
165
  const initialProducts = config.initialProducts || [];
@@ -287,13 +288,11 @@ export const CollectionService = implementService.withConfig()(CollectionService
287
288
  const initializeCatalogData = async () => {
288
289
  isInitializingCatalogData = true; // Set flag BEFORE loading
289
290
  const selectedCategory = categoryService.selectedCategory.get();
290
- await collectionFilters.loadCatalogPriceRange(selectedCategory || undefined);
291
- await collectionFilters.loadCatalogOptions(selectedCategory || undefined);
291
+ // Load catalog data from the combined catalog service
292
+ await catalogService.loadCatalogData(selectedCategory || undefined);
292
293
  // Reset flag to allow filter changes to trigger refreshes
293
294
  isInitializingCatalogData = false;
294
295
  };
295
- // Load catalog data on initialization
296
- void initializeCatalogData();
297
296
  signalsService.effect(() => {
298
297
  sortService.currentSort.get();
299
298
  debouncedRefresh(false);
@@ -1,14 +1,5 @@
1
- import { type Signal, type ReadOnlySignal } from "@wix/services-definitions/core-services/signals";
2
- export interface ProductOption {
3
- id: string;
4
- name: string;
5
- choices: {
6
- id: string;
7
- name: string;
8
- colorCode?: string;
9
- }[];
10
- optionRenderType?: string;
11
- }
1
+ import type { Signal, ReadOnlySignal } from "@wix/services-definitions/core-services/signals";
2
+ import { type ProductOption } from "./catalog-service.js";
12
3
  export interface PriceRange {
13
4
  min: number;
14
5
  max: number;
@@ -37,8 +28,6 @@ export interface FilterServiceAPI {
37
28
  max: number;
38
29
  };
39
30
  }>;
40
- loadCatalogPriceRange: (categoryId?: string) => Promise<void>;
41
- loadCatalogOptions: (categoryId?: string) => Promise<void>;
42
31
  isFullyLoaded: ReadOnlySignal<boolean>;
43
32
  }
44
33
  export declare const FilterServiceDefinition: string & {
@@ -1,8 +1,7 @@
1
1
  import { defineService, implementService } from "@wix/services-definitions";
2
- import { SignalsServiceDefinition, } from "@wix/services-definitions/core-services/signals";
2
+ import { SignalsServiceDefinition } from "@wix/services-definitions/core-services/signals";
3
3
  import { URLParamsUtils } from "../utils/url-params.js";
4
- import { CatalogPriceRangeServiceDefinition } from "./catalog-price-range-service.js";
5
- import { CatalogOptionsServiceDefinition } from "./catalog-options-service.js";
4
+ import { CatalogServiceDefinition, } from "./catalog-service.js";
6
5
  export const FilterServiceDefinition = defineService("filtered-collection");
7
6
  export const defaultFilter = {
8
7
  priceRange: { min: 0, max: 0 },
@@ -10,13 +9,12 @@ export const defaultFilter = {
10
9
  };
11
10
  export const FilterService = implementService.withConfig()(FilterServiceDefinition, ({ getService, config }) => {
12
11
  const signalsService = getService(SignalsServiceDefinition);
13
- const catalogPriceRangeService = getService(CatalogPriceRangeServiceDefinition);
14
- const catalogOptionsService = getService(CatalogOptionsServiceDefinition);
12
+ const catalogService = getService(CatalogServiceDefinition);
15
13
  const currentFilters = signalsService.signal((config?.initialFilters || defaultFilter));
16
14
  // Use computed signal for availableOptions to automatically track dependencies
17
15
  const availableOptions = signalsService.computed(() => {
18
- const catalogPriceRange = catalogPriceRangeService.catalogPriceRange.get();
19
- const catalogOptions = catalogOptionsService.catalogOptions.get();
16
+ const catalogPriceRange = catalogService.catalogPriceRange.get();
17
+ const catalogOptions = catalogService.catalogOptions.get();
20
18
  const priceRange = catalogPriceRange &&
21
19
  catalogPriceRange.minPrice < catalogPriceRange.maxPrice
22
20
  ? { min: catalogPriceRange.minPrice, max: catalogPriceRange.maxPrice }
@@ -29,8 +27,8 @@ export const FilterService = implementService.withConfig()(FilterServiceDefiniti
29
27
  });
30
28
  // Use computed signal for isFullyLoaded to automatically track dependencies
31
29
  const isFullyLoaded = signalsService.computed(() => {
32
- const catalogPriceRange = catalogPriceRangeService.catalogPriceRange.get();
33
- const catalogOptions = catalogOptionsService.catalogOptions.get();
30
+ const catalogPriceRange = catalogService.catalogPriceRange.get();
31
+ const catalogOptions = catalogService.catalogOptions.get();
34
32
  // Price range data is considered loaded whether it's null (no prices) or has valid data
35
33
  const hasPriceRangeData = catalogPriceRange !== undefined; // includes null case
36
34
  const hasOptionsData = !!(catalogOptions && catalogOptions.length >= 0); // Even 0 options is valid
@@ -38,7 +36,7 @@ export const FilterService = implementService.withConfig()(FilterServiceDefiniti
38
36
  });
39
37
  // Effect to update currentFilters when catalog data loads (only if filters are at defaults)
40
38
  signalsService.effect(() => {
41
- const catalogPriceRange = catalogPriceRangeService.catalogPriceRange.get();
39
+ const catalogPriceRange = catalogService.catalogPriceRange.get();
42
40
  if (catalogPriceRange &&
43
41
  catalogPriceRange.minPrice < catalogPriceRange.maxPrice) {
44
42
  const priceRange = {
@@ -111,23 +109,11 @@ export const FilterService = implementService.withConfig()(FilterServiceDefiniti
111
109
  }
112
110
  URLParamsUtils.updateURL(urlParams);
113
111
  };
114
- // Note: calculateAvailableOptions was removed as it's no longer needed
115
- // Options are now loaded from the catalog-wide service via loadCatalogOptions
116
- const loadCatalogPriceRange = async (categoryId) => {
117
- // Just call the catalog service - subscriptions will handle signal updates
118
- await catalogPriceRangeService.loadCatalogPriceRange(categoryId);
119
- };
120
- const loadCatalogOptions = async (categoryId) => {
121
- // Just call the catalog service - subscriptions will handle signal updates
122
- await catalogOptionsService.loadCatalogOptions(categoryId);
123
- };
124
112
  return {
125
113
  currentFilters,
126
114
  applyFilters,
127
115
  clearFilters,
128
116
  availableOptions,
129
- loadCatalogPriceRange,
130
- loadCatalogOptions,
131
117
  isFullyLoaded,
132
118
  };
133
119
  });
@@ -1,9 +1,8 @@
1
1
  export { buyNowServiceBinding, loadBuyNowServiceInitialData, } from "./buy-now-service.js";
2
2
  export { payNowServiceBinding, loadPayNowServiceInitialData, } from "./pay-now-service.js";
3
- export { CatalogOptionsService, CatalogOptionsServiceDefinition, loadCatalogOptionsServiceConfig, } from "./catalog-options-service.js";
3
+ export { CatalogService, CatalogServiceDefinition, loadCatalogServiceConfig, } from "./catalog-service.js";
4
4
  export { CategoryService, CategoryServiceDefinition, loadCategoriesConfig, } from "./category-service.js";
5
5
  export { CollectionService, CollectionServiceDefinition, loadCollectionServiceConfig, } from "./collection-service.js";
6
- export { CatalogPriceRangeService, CatalogPriceRangeServiceDefinition, loadCatalogPriceRangeServiceConfig, } from "./catalog-price-range-service.js";
7
6
  export { FilterService, FilterServiceDefinition, Filter, AvailableOptions, } from "./filter-service.js";
8
7
  export { ProductModifiersService, ProductModifiersServiceDefinition, } from "./product-modifiers-service.js";
9
8
  export { ProductService, ProductServiceDefinition, loadProductServiceConfig, } from "./product-service.js";
@@ -1,9 +1,8 @@
1
1
  export { buyNowServiceBinding, loadBuyNowServiceInitialData, } from "./buy-now-service.js";
2
2
  export { payNowServiceBinding, loadPayNowServiceInitialData, } from "./pay-now-service.js";
3
- export { CatalogOptionsService, CatalogOptionsServiceDefinition, loadCatalogOptionsServiceConfig, } from "./catalog-options-service.js";
3
+ export { CatalogService, CatalogServiceDefinition, loadCatalogServiceConfig, } from "./catalog-service.js";
4
4
  export { CategoryService, CategoryServiceDefinition, loadCategoriesConfig, } from "./category-service.js";
5
5
  export { CollectionService, CollectionServiceDefinition, loadCollectionServiceConfig, } from "./collection-service.js";
6
- export { CatalogPriceRangeService, CatalogPriceRangeServiceDefinition, loadCatalogPriceRangeServiceConfig, } from "./catalog-price-range-service.js";
7
6
  export { FilterService, FilterServiceDefinition, } from "./filter-service.js";
8
7
  export { ProductModifiersService, ProductModifiersServiceDefinition, } from "./product-modifiers-service.js";
9
8
  export { ProductService, ProductServiceDefinition, loadProductServiceConfig, } from "./product-service.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/headless-stores",
3
- "version": "0.0.18",
3
+ "version": "0.0.20",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "prebuild": "cd ../media && yarn build && cd ../ecom && yarn build",
@@ -58,8 +58,8 @@
58
58
  "@wix/auto_sdk_stores_read-only-variants-v-3": "^1.0.23",
59
59
  "@wix/ecom": "^1.0.1238",
60
60
  "@wix/essentials": "^0.1.22",
61
- "@wix/headless-ecom": "^0.0.5",
62
- "@wix/headless-media": "^0.0.2",
61
+ "@wix/headless-ecom": "^0.0.7",
62
+ "@wix/headless-media": "^0.0.4",
63
63
  "@wix/redirects": "^1.0.79",
64
64
  "@wix/services-definitions": "^0.1.4",
65
65
  "@wix/services-manager-react": "^0.1.26"
@@ -1,4 +0,0 @@
1
- {
2
- "main": "../../cjs/dist/astro/actions/index.js",
3
- "types": "../../dist/astro/actions/index.d.ts"
4
- }
@@ -1,30 +0,0 @@
1
- import { type ServiceFactoryConfig } from "@wix/services-definitions";
2
- import { type Signal } from "@wix/services-definitions/core-services/signals";
3
- export interface ProductOption {
4
- id: string;
5
- name: string;
6
- choices: ProductChoice[];
7
- optionRenderType?: string;
8
- }
9
- export interface ProductChoice {
10
- id: string;
11
- name: string;
12
- colorCode?: string;
13
- }
14
- export interface CatalogOptionsServiceAPI {
15
- catalogOptions: Signal<ProductOption[] | null>;
16
- isLoading: Signal<boolean>;
17
- error: Signal<string | null>;
18
- loadCatalogOptions: (categoryId?: string) => Promise<void>;
19
- }
20
- export declare const CatalogOptionsServiceDefinition: string & {
21
- __api: CatalogOptionsServiceAPI;
22
- __config: {};
23
- isServiceDefinition?: boolean;
24
- } & CatalogOptionsServiceAPI;
25
- export declare const CatalogOptionsService: import("@wix/services-definitions").ServiceFactory<string & {
26
- __api: CatalogOptionsServiceAPI;
27
- __config: {};
28
- isServiceDefinition?: boolean;
29
- } & CatalogOptionsServiceAPI, {}>;
30
- export declare function loadCatalogOptionsServiceConfig(): Promise<ServiceFactoryConfig<typeof CatalogOptionsService>>;
@@ -1,23 +0,0 @@
1
- import { type ServiceFactoryConfig } from "@wix/services-definitions";
2
- import { type Signal } from "@wix/services-definitions/core-services/signals";
3
- export interface CatalogPriceRange {
4
- minPrice: number;
5
- maxPrice: number;
6
- }
7
- export interface CatalogPriceRangeServiceAPI {
8
- catalogPriceRange: Signal<CatalogPriceRange | null>;
9
- isLoading: Signal<boolean>;
10
- error: Signal<string | null>;
11
- loadCatalogPriceRange: (categoryId?: string) => Promise<void>;
12
- }
13
- export declare const CatalogPriceRangeServiceDefinition: string & {
14
- __api: CatalogPriceRangeServiceAPI;
15
- __config: {};
16
- isServiceDefinition?: boolean;
17
- } & CatalogPriceRangeServiceAPI;
18
- export declare const CatalogPriceRangeService: import("@wix/services-definitions").ServiceFactory<string & {
19
- __api: CatalogPriceRangeServiceAPI;
20
- __config: {};
21
- isServiceDefinition?: boolean;
22
- } & CatalogPriceRangeServiceAPI, {}>;
23
- export declare function loadCatalogPriceRangeServiceConfig(): Promise<ServiceFactoryConfig<typeof CatalogPriceRangeService>>;
@@ -1,93 +0,0 @@
1
- import { defineService, implementService, } from "@wix/services-definitions";
2
- import { SignalsServiceDefinition, } from "@wix/services-definitions/core-services/signals";
3
- import * as productsV3 from "@wix/auto_sdk_stores_products-v-3";
4
- // Helper function to extract scalar aggregation values
5
- const extractScalarAggregationValue = (aggregationResponse, name) => {
6
- const aggregation = aggregationResponse.aggregations?.[name] ||
7
- aggregationResponse.aggregationData?.results?.find((r) => r.name === name);
8
- const value = aggregation?.scalar?.value;
9
- return value !== undefined && value !== null ? parseFloat(value) : null;
10
- };
11
- const buildCategoryFilter = (categoryId) => {
12
- if (!categoryId) {
13
- return { visible: true };
14
- }
15
- return {
16
- visible: true,
17
- "allCategoriesInfo.categories": {
18
- $matchItems: [{ _id: { $in: [categoryId] } }],
19
- },
20
- };
21
- };
22
- export const CatalogPriceRangeServiceDefinition = defineService("catalogPriceRange");
23
- export const CatalogPriceRangeService = implementService.withConfig()(CatalogPriceRangeServiceDefinition, ({ getService }) => {
24
- const signalsService = getService(SignalsServiceDefinition);
25
- // Signal declarations
26
- const catalogPriceRange = signalsService.signal(null);
27
- const isLoading = signalsService.signal(false);
28
- const error = signalsService.signal(null);
29
- /**
30
- * Load the catalog-wide price range using a single aggregation query
31
- * This fetches min/max prices from ALL products in the catalog using SCALAR aggregations
32
- */
33
- const loadCatalogPriceRange = async (categoryId) => {
34
- isLoading.set(true);
35
- error.set(null);
36
- try {
37
- // Single aggregation request to get both min and max prices (no products returned)
38
- const aggregationRequest = {
39
- aggregations: [
40
- {
41
- name: "minPrice",
42
- fieldPath: "actualPriceRange.minValue.amount",
43
- type: "SCALAR",
44
- scalar: { type: "MIN" },
45
- },
46
- {
47
- name: "maxPrice",
48
- fieldPath: "actualPriceRange.maxValue.amount",
49
- type: "SCALAR",
50
- scalar: { type: "MAX" },
51
- },
52
- ],
53
- filter: buildCategoryFilter(categoryId),
54
- includeProducts: false,
55
- cursorPaging: { limit: 0 },
56
- };
57
- const aggregationResponse = await productsV3.searchProducts(aggregationRequest);
58
- const minPrice = extractScalarAggregationValue(aggregationResponse, "minPrice");
59
- const maxPrice = extractScalarAggregationValue(aggregationResponse, "maxPrice");
60
- // Only set price range if we found valid prices
61
- if (minPrice !== null &&
62
- maxPrice !== null &&
63
- (minPrice > 0 || maxPrice > 0)) {
64
- catalogPriceRange.set({
65
- minPrice,
66
- maxPrice,
67
- });
68
- }
69
- else {
70
- // No products found or no valid prices - don't show the filter
71
- catalogPriceRange.set(null);
72
- }
73
- }
74
- catch (err) {
75
- console.error("Failed to load catalog price range:", err);
76
- error.set(err instanceof Error ? err.message : "Failed to load price range");
77
- // Don't set fallback values - let the component handle the error state
78
- catalogPriceRange.set(null);
79
- }
80
- finally {
81
- isLoading.set(false);
82
- }
83
- };
84
- return {
85
- catalogPriceRange,
86
- isLoading,
87
- error,
88
- loadCatalogPriceRange,
89
- };
90
- });
91
- export async function loadCatalogPriceRangeServiceConfig() {
92
- return {};
93
- }
@@ -1,30 +0,0 @@
1
- import { type ServiceFactoryConfig } from "@wix/services-definitions";
2
- import { type Signal } from "@wix/services-definitions/core-services/signals";
3
- export interface ProductOption {
4
- id: string;
5
- name: string;
6
- choices: ProductChoice[];
7
- optionRenderType?: string;
8
- }
9
- export interface ProductChoice {
10
- id: string;
11
- name: string;
12
- colorCode?: string;
13
- }
14
- export interface CatalogOptionsServiceAPI {
15
- catalogOptions: Signal<ProductOption[] | null>;
16
- isLoading: Signal<boolean>;
17
- error: Signal<string | null>;
18
- loadCatalogOptions: (categoryId?: string) => Promise<void>;
19
- }
20
- export declare const CatalogOptionsServiceDefinition: string & {
21
- __api: CatalogOptionsServiceAPI;
22
- __config: {};
23
- isServiceDefinition?: boolean;
24
- } & CatalogOptionsServiceAPI;
25
- export declare const CatalogOptionsService: import("@wix/services-definitions").ServiceFactory<string & {
26
- __api: CatalogOptionsServiceAPI;
27
- __config: {};
28
- isServiceDefinition?: boolean;
29
- } & CatalogOptionsServiceAPI, {}>;
30
- export declare function loadCatalogOptionsServiceConfig(): Promise<ServiceFactoryConfig<typeof CatalogOptionsService>>;
@@ -1,23 +0,0 @@
1
- import { type ServiceFactoryConfig } from "@wix/services-definitions";
2
- import { type Signal } from "@wix/services-definitions/core-services/signals";
3
- export interface CatalogPriceRange {
4
- minPrice: number;
5
- maxPrice: number;
6
- }
7
- export interface CatalogPriceRangeServiceAPI {
8
- catalogPriceRange: Signal<CatalogPriceRange | null>;
9
- isLoading: Signal<boolean>;
10
- error: Signal<string | null>;
11
- loadCatalogPriceRange: (categoryId?: string) => Promise<void>;
12
- }
13
- export declare const CatalogPriceRangeServiceDefinition: string & {
14
- __api: CatalogPriceRangeServiceAPI;
15
- __config: {};
16
- isServiceDefinition?: boolean;
17
- } & CatalogPriceRangeServiceAPI;
18
- export declare const CatalogPriceRangeService: import("@wix/services-definitions").ServiceFactory<string & {
19
- __api: CatalogPriceRangeServiceAPI;
20
- __config: {};
21
- isServiceDefinition?: boolean;
22
- } & CatalogPriceRangeServiceAPI, {}>;
23
- export declare function loadCatalogPriceRangeServiceConfig(): Promise<ServiceFactoryConfig<typeof CatalogPriceRangeService>>;
@@ -1,93 +0,0 @@
1
- import { defineService, implementService, } from "@wix/services-definitions";
2
- import { SignalsServiceDefinition, } from "@wix/services-definitions/core-services/signals";
3
- import * as productsV3 from "@wix/auto_sdk_stores_products-v-3";
4
- // Helper function to extract scalar aggregation values
5
- const extractScalarAggregationValue = (aggregationResponse, name) => {
6
- const aggregation = aggregationResponse.aggregations?.[name] ||
7
- aggregationResponse.aggregationData?.results?.find((r) => r.name === name);
8
- const value = aggregation?.scalar?.value;
9
- return value !== undefined && value !== null ? parseFloat(value) : null;
10
- };
11
- const buildCategoryFilter = (categoryId) => {
12
- if (!categoryId) {
13
- return { visible: true };
14
- }
15
- return {
16
- visible: true,
17
- "allCategoriesInfo.categories": {
18
- $matchItems: [{ _id: { $in: [categoryId] } }],
19
- },
20
- };
21
- };
22
- export const CatalogPriceRangeServiceDefinition = defineService("catalogPriceRange");
23
- export const CatalogPriceRangeService = implementService.withConfig()(CatalogPriceRangeServiceDefinition, ({ getService }) => {
24
- const signalsService = getService(SignalsServiceDefinition);
25
- // Signal declarations
26
- const catalogPriceRange = signalsService.signal(null);
27
- const isLoading = signalsService.signal(false);
28
- const error = signalsService.signal(null);
29
- /**
30
- * Load the catalog-wide price range using a single aggregation query
31
- * This fetches min/max prices from ALL products in the catalog using SCALAR aggregations
32
- */
33
- const loadCatalogPriceRange = async (categoryId) => {
34
- isLoading.set(true);
35
- error.set(null);
36
- try {
37
- // Single aggregation request to get both min and max prices (no products returned)
38
- const aggregationRequest = {
39
- aggregations: [
40
- {
41
- name: "minPrice",
42
- fieldPath: "actualPriceRange.minValue.amount",
43
- type: "SCALAR",
44
- scalar: { type: "MIN" },
45
- },
46
- {
47
- name: "maxPrice",
48
- fieldPath: "actualPriceRange.maxValue.amount",
49
- type: "SCALAR",
50
- scalar: { type: "MAX" },
51
- },
52
- ],
53
- filter: buildCategoryFilter(categoryId),
54
- includeProducts: false,
55
- cursorPaging: { limit: 0 },
56
- };
57
- const aggregationResponse = await productsV3.searchProducts(aggregationRequest);
58
- const minPrice = extractScalarAggregationValue(aggregationResponse, "minPrice");
59
- const maxPrice = extractScalarAggregationValue(aggregationResponse, "maxPrice");
60
- // Only set price range if we found valid prices
61
- if (minPrice !== null &&
62
- maxPrice !== null &&
63
- (minPrice > 0 || maxPrice > 0)) {
64
- catalogPriceRange.set({
65
- minPrice,
66
- maxPrice,
67
- });
68
- }
69
- else {
70
- // No products found or no valid prices - don't show the filter
71
- catalogPriceRange.set(null);
72
- }
73
- }
74
- catch (err) {
75
- console.error("Failed to load catalog price range:", err);
76
- error.set(err instanceof Error ? err.message : "Failed to load price range");
77
- // Don't set fallback values - let the component handle the error state
78
- catalogPriceRange.set(null);
79
- }
80
- finally {
81
- isLoading.set(false);
82
- }
83
- };
84
- return {
85
- catalogPriceRange,
86
- isLoading,
87
- error,
88
- loadCatalogPriceRange,
89
- };
90
- });
91
- export async function loadCatalogPriceRangeServiceConfig() {
92
- return {};
93
- }
@@ -1,4 +0,0 @@
1
- {
2
- "main": "../cjs/dist/react/index.js",
3
- "types": "../dist/react/index.d.ts"
4
- }
@@ -1,4 +0,0 @@
1
- {
2
- "main": "../cjs/dist/server-actions/index.js",
3
- "types": "../dist/server-actions/index.d.ts"
4
- }
@@ -1,4 +0,0 @@
1
- {
2
- "main": "../cjs/dist/services/index.js",
3
- "types": "../dist/services/index.d.ts"
4
- }