@storepecker/storefront-core 1.0.2 → 1.1.0

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.
@@ -13,10 +13,10 @@ var useCartStore = create((set) => ({
13
13
  if (!state.cart) return state;
14
14
  return { cart: { ...state.cart, total_quantity: 0 } };
15
15
  }),
16
- syncCart: async () => {
16
+ syncCart: async (addressId) => {
17
17
  set({ status: "loading" /* LOADING */ });
18
18
  try {
19
- const response = await getCart("/customer/cart/");
19
+ const response = await getCart("/customer/cart/", addressId);
20
20
  if (response) {
21
21
  set({ cart: response, status: "idle" /* IDLE */ });
22
22
  }
@@ -15,10 +15,10 @@ var useCartStore = zustand.create((set) => ({
15
15
  if (!state.cart) return state;
16
16
  return { cart: { ...state.cart, total_quantity: 0 } };
17
17
  }),
18
- syncCart: async () => {
18
+ syncCart: async (addressId) => {
19
19
  set({ status: "loading" /* LOADING */ });
20
20
  try {
21
- const response = await chunk34KV265H_cjs.getCart("/customer/cart/");
21
+ const response = await chunk34KV265H_cjs.getCart("/customer/cart/", addressId);
22
22
  if (response) {
23
23
  set({ cart: response, status: "idle" /* IDLE */ });
24
24
  }
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkKWKHHRRJ_cjs = require('../chunk-KWKHHRRJ.cjs');
4
- var chunkNBZIUA6W_cjs = require('../chunk-NBZIUA6W.cjs');
4
+ var chunkS3WIGYDS_cjs = require('../chunk-S3WIGYDS.cjs');
5
5
  var chunk34KV265H_cjs = require('../chunk-34KV265H.cjs');
6
6
  require('../chunk-CFF632IQ.cjs');
7
7
  var chunkLC5ZAUKL_cjs = require('../chunk-LC5ZAUKL.cjs');
@@ -94,29 +94,91 @@ var useOrders = (filters) => {
94
94
  }, [fetchData]);
95
95
  return { data, error, isLoading, mutate: fetchData };
96
96
  };
97
- var useGetProducts = (filter) => {
98
- const [data, setData] = react.useState(
99
- void 0
100
- );
97
+ function useGetProducts(initialFilter, options) {
98
+ const paginationMode = options?.paginationMode || "replace";
99
+ const endpoint = options?.endpoint || "/inventory/products/";
100
+ const [filters, setFiltersState] = react.useState(initialFilter);
101
+ const [data, setData] = react.useState(void 0);
102
+ const [products, setProducts] = react.useState([]);
101
103
  const [isLoading, setIsLoading] = react.useState(true);
102
- const hasFetched = react.useRef(false);
103
- react.useEffect(() => {
104
- if (hasFetched.current) return;
105
- hasFetched.current = true;
106
- const fetchData = async () => {
104
+ const isLoadMoreRef = react.useRef(false);
105
+ const modeRef = react.useRef(paginationMode);
106
+ modeRef.current = paginationMode;
107
+ const fetchProducts = react.useCallback(
108
+ async (currentFilters) => {
109
+ setIsLoading(true);
107
110
  try {
108
- setIsLoading(true);
109
- const result = await chunk34KV265H_cjs.getProductList("/inventory/products/", filter);
111
+ const apiParams = {};
112
+ for (const [key, value] of Object.entries(currentFilters)) {
113
+ if (value === void 0 || value === "" || value === null) continue;
114
+ if (Array.isArray(value)) {
115
+ if (value.length > 0) apiParams[key] = value.join(",");
116
+ } else {
117
+ apiParams[key] = value;
118
+ }
119
+ }
120
+ const result = await chunk34KV265H_cjs.getProductList(endpoint, apiParams);
110
121
  setData(result);
111
- } catch (err) {
122
+ if (modeRef.current === "append" && isLoadMoreRef.current) {
123
+ setProducts((prev) => [...prev, ...result.results || []]);
124
+ } else {
125
+ setProducts(result.results || []);
126
+ }
127
+ isLoadMoreRef.current = false;
128
+ } catch {
129
+ isLoadMoreRef.current = false;
112
130
  } finally {
113
131
  setIsLoading(false);
114
132
  }
115
- };
116
- fetchData();
133
+ },
134
+ [endpoint]
135
+ );
136
+ react.useEffect(() => {
137
+ fetchProducts(filters);
138
+ }, [filters, fetchProducts]);
139
+ const setFilters = react.useCallback((newFilters) => {
140
+ isLoadMoreRef.current = false;
141
+ setProducts([]);
142
+ setFiltersState({ ...newFilters, page: newFilters.page || 1 });
117
143
  }, []);
118
- return { data, loading: isLoading && !data, isLoading };
119
- };
144
+ const updateFilter = react.useCallback((key, value) => {
145
+ isLoadMoreRef.current = false;
146
+ setProducts([]);
147
+ setFiltersState((prev) => {
148
+ const next = { ...prev, page: 1 };
149
+ if (value === void 0 || value === "" || Array.isArray(value) && value.length === 0) {
150
+ delete next[key];
151
+ } else {
152
+ next[key] = value;
153
+ }
154
+ return next;
155
+ });
156
+ }, []);
157
+ const loadMore = react.useCallback(() => {
158
+ if (!data?.next) return;
159
+ isLoadMoreRef.current = true;
160
+ setFiltersState((prev) => ({ ...prev, page: (prev.page || 1) + 1 }));
161
+ }, [data?.next]);
162
+ const goToPage = react.useCallback((page) => {
163
+ isLoadMoreRef.current = false;
164
+ setProducts([]);
165
+ setFiltersState((prev) => ({ ...prev, page }));
166
+ }, []);
167
+ return {
168
+ data,
169
+ products,
170
+ loading: isLoading && products.length === 0,
171
+ isLoading,
172
+ hasMore: !!data?.next,
173
+ totalCount: data?.count || 0,
174
+ currentPage: filters.page || 1,
175
+ filters,
176
+ setFilters,
177
+ updateFilter,
178
+ loadMore,
179
+ goToPage
180
+ };
181
+ }
120
182
  var useGetProductDetails = (slug) => {
121
183
  const [data, setData] = react.useState(void 0);
122
184
  const [isLoading, setIsLoading] = react.useState(true);
@@ -249,7 +311,7 @@ function useCheckoutPayment(options) {
249
311
  const [isConfirmingPayment, setIsConfirmingPayment] = react.useState(false);
250
312
  const [showSuccessModal, setShowSuccessModal] = react.useState(false);
251
313
  const [showFailureModal, setShowFailureModal] = react.useState(false);
252
- const resetCartCount = chunkNBZIUA6W_cjs.useCartStore((s) => s.resetCartCount);
314
+ const resetCartCount = chunkS3WIGYDS_cjs.useCartStore((s) => s.resetCartCount);
253
315
  const callbacks = {
254
316
  onSuccess: (order) => {
255
317
  setOrderSummary(order);
@@ -1,5 +1,5 @@
1
1
  import { A as Address } from '../address-DQEZzyUG.cjs';
2
- import { C as Cart, d as Category, j as Product, e as Coupon } from '../cart-D8UXTY1g.cjs';
2
+ import { C as Cart, j as Product, d as Category, e as Coupon } from '../cart-D8UXTY1g.cjs';
3
3
  import { O as Order } from '../orders-DHJhJ3xz.cjs';
4
4
  import { U as User, W as Wishlist } from '../wishlist-C3SK9SS0.cjs';
5
5
  export { U as UseAddressFormOptions, a as UseAddressFormReturn, u as useAddressForm } from '../useAddressForm-DgkCP1nG.cjs';
@@ -29,11 +29,26 @@ declare const useOrders: (filters: any) => {
29
29
  mutate: () => Promise<void>;
30
30
  };
31
31
 
32
- declare const useGetProducts: (filter: CustomObject) => {
32
+ type PaginationMode = "append" | "replace";
33
+ interface UseGetProductsOptions {
34
+ paginationMode?: PaginationMode;
35
+ endpoint?: string;
36
+ }
37
+ interface UseGetProductsReturn {
33
38
  data: Product.ProductResponse | undefined;
39
+ products: Product.ProductList[];
34
40
  loading: boolean;
35
41
  isLoading: boolean;
36
- };
42
+ hasMore: boolean;
43
+ totalCount: number;
44
+ currentPage: number;
45
+ filters: Record<string, any>;
46
+ setFilters: (filters: Record<string, any>) => void;
47
+ updateFilter: (key: string, value: any) => void;
48
+ loadMore: () => void;
49
+ goToPage: (page: number) => void;
50
+ }
51
+ declare function useGetProducts(initialFilter: CustomObject, options?: UseGetProductsOptions): UseGetProductsReturn;
37
52
  declare const useGetProductDetails: (slug: string) => {
38
53
  data: any;
39
54
  loading: boolean;
@@ -119,4 +134,4 @@ interface UseCheckoutCouponReturn {
119
134
  */
120
135
  declare function useCheckoutCoupon(options: UseCheckoutCouponOptions): UseCheckoutCouponReturn;
121
136
 
122
- export { type UseCheckoutCouponOptions, type UseCheckoutCouponReturn, type UseCheckoutPaymentOptions, type UseCheckoutPaymentReturn, useAddress, useCart, useCheckoutCoupon, useCheckoutPayment, useGetProductCategories, useGetProductDetails, useGetProducts, useOrders, useProductDetailModal, useServiceFormModal, useUserDetails, useWishlist };
137
+ export { type PaginationMode, type UseCheckoutCouponOptions, type UseCheckoutCouponReturn, type UseCheckoutPaymentOptions, type UseCheckoutPaymentReturn, type UseGetProductsOptions, type UseGetProductsReturn, useAddress, useCart, useCheckoutCoupon, useCheckoutPayment, useGetProductCategories, useGetProductDetails, useGetProducts, useOrders, useProductDetailModal, useServiceFormModal, useUserDetails, useWishlist };
@@ -1,5 +1,5 @@
1
1
  import { A as Address } from '../address-DQEZzyUG.js';
2
- import { C as Cart, d as Category, j as Product, e as Coupon } from '../cart-D8UXTY1g.js';
2
+ import { C as Cart, j as Product, d as Category, e as Coupon } from '../cart-D8UXTY1g.js';
3
3
  import { O as Order } from '../orders-DHJhJ3xz.js';
4
4
  import { U as User, W as Wishlist } from '../wishlist-BMWfB0Xe.js';
5
5
  export { U as UseAddressFormOptions, a as UseAddressFormReturn, u as useAddressForm } from '../useAddressForm-C-Uzug4d.js';
@@ -29,11 +29,26 @@ declare const useOrders: (filters: any) => {
29
29
  mutate: () => Promise<void>;
30
30
  };
31
31
 
32
- declare const useGetProducts: (filter: CustomObject) => {
32
+ type PaginationMode = "append" | "replace";
33
+ interface UseGetProductsOptions {
34
+ paginationMode?: PaginationMode;
35
+ endpoint?: string;
36
+ }
37
+ interface UseGetProductsReturn {
33
38
  data: Product.ProductResponse | undefined;
39
+ products: Product.ProductList[];
34
40
  loading: boolean;
35
41
  isLoading: boolean;
36
- };
42
+ hasMore: boolean;
43
+ totalCount: number;
44
+ currentPage: number;
45
+ filters: Record<string, any>;
46
+ setFilters: (filters: Record<string, any>) => void;
47
+ updateFilter: (key: string, value: any) => void;
48
+ loadMore: () => void;
49
+ goToPage: (page: number) => void;
50
+ }
51
+ declare function useGetProducts(initialFilter: CustomObject, options?: UseGetProductsOptions): UseGetProductsReturn;
37
52
  declare const useGetProductDetails: (slug: string) => {
38
53
  data: any;
39
54
  loading: boolean;
@@ -119,4 +134,4 @@ interface UseCheckoutCouponReturn {
119
134
  */
120
135
  declare function useCheckoutCoupon(options: UseCheckoutCouponOptions): UseCheckoutCouponReturn;
121
136
 
122
- export { type UseCheckoutCouponOptions, type UseCheckoutCouponReturn, type UseCheckoutPaymentOptions, type UseCheckoutPaymentReturn, useAddress, useCart, useCheckoutCoupon, useCheckoutPayment, useGetProductCategories, useGetProductDetails, useGetProducts, useOrders, useProductDetailModal, useServiceFormModal, useUserDetails, useWishlist };
137
+ export { type PaginationMode, type UseCheckoutCouponOptions, type UseCheckoutCouponReturn, type UseCheckoutPaymentOptions, type UseCheckoutPaymentReturn, type UseGetProductsOptions, type UseGetProductsReturn, useAddress, useCart, useCheckoutCoupon, useCheckoutPayment, useGetProductCategories, useGetProductDetails, useGetProducts, useOrders, useProductDetailModal, useServiceFormModal, useUserDetails, useWishlist };
@@ -1,6 +1,6 @@
1
1
  import { getUserDetails } from '../chunk-7UH6REGV.js';
2
- import { useCartStore } from '../chunk-AZIFK7HK.js';
3
- import { getCart, getProductDetail, getWishlist, getProductCategories, getProductList } from '../chunk-Z7LHRD3V.js';
2
+ import { useCartStore } from '../chunk-7DQIKSGY.js';
3
+ import { getCart, getProductList, getProductDetail, getWishlist, getProductCategories } from '../chunk-Z7LHRD3V.js';
4
4
  import '../chunk-E7WPE3PV.js';
5
5
  import { processPayment, fetchAvailableCoupons, applyCheckoutCoupon, removeCheckoutCoupon } from '../chunk-QCQCFYYR.js';
6
6
  import { getOrders, placeOrder } from '../chunk-4MJMNIAB.js';
@@ -92,29 +92,91 @@ var useOrders = (filters) => {
92
92
  }, [fetchData]);
93
93
  return { data, error, isLoading, mutate: fetchData };
94
94
  };
95
- var useGetProducts = (filter) => {
96
- const [data, setData] = useState(
97
- void 0
98
- );
95
+ function useGetProducts(initialFilter, options) {
96
+ const paginationMode = options?.paginationMode || "replace";
97
+ const endpoint = options?.endpoint || "/inventory/products/";
98
+ const [filters, setFiltersState] = useState(initialFilter);
99
+ const [data, setData] = useState(void 0);
100
+ const [products, setProducts] = useState([]);
99
101
  const [isLoading, setIsLoading] = useState(true);
100
- const hasFetched = useRef(false);
101
- useEffect(() => {
102
- if (hasFetched.current) return;
103
- hasFetched.current = true;
104
- const fetchData = async () => {
102
+ const isLoadMoreRef = useRef(false);
103
+ const modeRef = useRef(paginationMode);
104
+ modeRef.current = paginationMode;
105
+ const fetchProducts = useCallback(
106
+ async (currentFilters) => {
107
+ setIsLoading(true);
105
108
  try {
106
- setIsLoading(true);
107
- const result = await getProductList("/inventory/products/", filter);
109
+ const apiParams = {};
110
+ for (const [key, value] of Object.entries(currentFilters)) {
111
+ if (value === void 0 || value === "" || value === null) continue;
112
+ if (Array.isArray(value)) {
113
+ if (value.length > 0) apiParams[key] = value.join(",");
114
+ } else {
115
+ apiParams[key] = value;
116
+ }
117
+ }
118
+ const result = await getProductList(endpoint, apiParams);
108
119
  setData(result);
109
- } catch (err) {
120
+ if (modeRef.current === "append" && isLoadMoreRef.current) {
121
+ setProducts((prev) => [...prev, ...result.results || []]);
122
+ } else {
123
+ setProducts(result.results || []);
124
+ }
125
+ isLoadMoreRef.current = false;
126
+ } catch {
127
+ isLoadMoreRef.current = false;
110
128
  } finally {
111
129
  setIsLoading(false);
112
130
  }
113
- };
114
- fetchData();
131
+ },
132
+ [endpoint]
133
+ );
134
+ useEffect(() => {
135
+ fetchProducts(filters);
136
+ }, [filters, fetchProducts]);
137
+ const setFilters = useCallback((newFilters) => {
138
+ isLoadMoreRef.current = false;
139
+ setProducts([]);
140
+ setFiltersState({ ...newFilters, page: newFilters.page || 1 });
115
141
  }, []);
116
- return { data, loading: isLoading && !data, isLoading };
117
- };
142
+ const updateFilter = useCallback((key, value) => {
143
+ isLoadMoreRef.current = false;
144
+ setProducts([]);
145
+ setFiltersState((prev) => {
146
+ const next = { ...prev, page: 1 };
147
+ if (value === void 0 || value === "" || Array.isArray(value) && value.length === 0) {
148
+ delete next[key];
149
+ } else {
150
+ next[key] = value;
151
+ }
152
+ return next;
153
+ });
154
+ }, []);
155
+ const loadMore = useCallback(() => {
156
+ if (!data?.next) return;
157
+ isLoadMoreRef.current = true;
158
+ setFiltersState((prev) => ({ ...prev, page: (prev.page || 1) + 1 }));
159
+ }, [data?.next]);
160
+ const goToPage = useCallback((page) => {
161
+ isLoadMoreRef.current = false;
162
+ setProducts([]);
163
+ setFiltersState((prev) => ({ ...prev, page }));
164
+ }, []);
165
+ return {
166
+ data,
167
+ products,
168
+ loading: isLoading && products.length === 0,
169
+ isLoading,
170
+ hasMore: !!data?.next,
171
+ totalCount: data?.count || 0,
172
+ currentPage: filters.page || 1,
173
+ filters,
174
+ setFilters,
175
+ updateFilter,
176
+ loadMore,
177
+ goToPage
178
+ };
179
+ }
118
180
  var useGetProductDetails = (slug) => {
119
181
  const [data, setData] = useState(void 0);
120
182
  const [isLoading, setIsLoading] = useState(true);
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkNBZIUA6W_cjs = require('../chunk-NBZIUA6W.cjs');
3
+ var chunkS3WIGYDS_cjs = require('../chunk-S3WIGYDS.cjs');
4
4
  var chunk34KV265H_cjs = require('../chunk-34KV265H.cjs');
5
5
  var chunkCFF632IQ_cjs = require('../chunk-CFF632IQ.cjs');
6
6
  require('../chunk-UXLUE3HW.cjs');
@@ -46,7 +46,7 @@ var useAuthStore = zustand.create((set, get) => ({
46
46
  );
47
47
  if (response) {
48
48
  chunkCFF632IQ_cjs.cart_default.clear();
49
- chunkNBZIUA6W_cjs.useCartStore.getState().fetchCart();
49
+ chunkS3WIGYDS_cjs.useCartStore.getState().fetchCart();
50
50
  setTimeout(() => {
51
51
  if (!preventRefreshAfterLogin) {
52
52
  if (redirect) {
@@ -141,7 +141,7 @@ var useProductStore = zustand.create((set, get) => ({
141
141
 
142
142
  Object.defineProperty(exports, "useCartStore", {
143
143
  enumerable: true,
144
- get: function () { return chunkNBZIUA6W_cjs.useCartStore; }
144
+ get: function () { return chunkS3WIGYDS_cjs.useCartStore; }
145
145
  });
146
146
  exports.loginUser = loginUser;
147
147
  exports.useAuthStore = useAuthStore;
@@ -7,7 +7,7 @@ interface CartStore extends CartState {
7
7
  setCart: (cart: Cart.Root) => void;
8
8
  resetCart: () => void;
9
9
  resetCartCount: () => void;
10
- syncCart: () => Promise<void>;
10
+ syncCart: (addressId?: number) => Promise<void>;
11
11
  fetchCart: (addressId?: number) => Promise<void>;
12
12
  }
13
13
  declare const useCartStore: zustand.UseBoundStore<zustand.StoreApi<CartStore>>;
@@ -7,7 +7,7 @@ interface CartStore extends CartState {
7
7
  setCart: (cart: Cart.Root) => void;
8
8
  resetCart: () => void;
9
9
  resetCartCount: () => void;
10
- syncCart: () => Promise<void>;
10
+ syncCart: (addressId?: number) => Promise<void>;
11
11
  fetchCart: (addressId?: number) => Promise<void>;
12
12
  }
13
13
  declare const useCartStore: zustand.UseBoundStore<zustand.StoreApi<CartStore>>;
@@ -1,5 +1,5 @@
1
- import { useCartStore } from '../chunk-AZIFK7HK.js';
2
- export { useCartStore } from '../chunk-AZIFK7HK.js';
1
+ import { useCartStore } from '../chunk-7DQIKSGY.js';
2
+ export { useCartStore } from '../chunk-7DQIKSGY.js';
3
3
  import { addToCart, getWishlist, getProducts } from '../chunk-Z7LHRD3V.js';
4
4
  import { cart_default } from '../chunk-E7WPE3PV.js';
5
5
  import '../chunk-GB3HW6DM.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storepecker/storefront-core",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Shared logic and utilities for Snapstore themes",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -76,6 +76,8 @@
76
76
  "scripts": {
77
77
  "build": "tsup",
78
78
  "dev": "tsup --watch",
79
+ "test": "vitest run",
80
+ "test:watch": "vitest",
79
81
  "lint": "tsc --noEmit",
80
82
  "prepublishOnly": "npm run build",
81
83
  "release": "npm run build && npm publish --access public",
@@ -109,12 +111,14 @@
109
111
  }
110
112
  },
111
113
  "devDependencies": {
114
+ "@testing-library/react": "^16.3.2",
112
115
  "@types/node": "^25.3.3",
113
116
  "@types/react": "^18.0.0",
114
117
  "axios": "^1.7.0",
115
118
  "clsx": "^2.1.1",
116
119
  "esbuild-css-modules-plugin": "^3.1.5",
117
120
  "formik": "^2.4.6",
121
+ "happy-dom": "^20.8.3",
118
122
  "libphonenumber-js": "^1.11.0",
119
123
  "react": "^18.3.1",
120
124
  "react-dom": "^18.3.1",
@@ -124,6 +128,7 @@
124
128
  "tsup": "^8.0.0",
125
129
  "typescript": "^5.5.0",
126
130
  "typescript-cookie": "^1.0.0",
131
+ "vitest": "^4.0.18",
127
132
  "yup": "^1.4.0",
128
133
  "zustand": "^5.0.11"
129
134
  },