react-shopwave-connect 0.1.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.
@@ -0,0 +1,783 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+
23
+ //#endregion
24
+ let react = require("react");
25
+ react = __toESM(react);
26
+
27
+ //#region src/core/request.ts
28
+ function resolveFetch(options) {
29
+ const f = options.fetch ?? (typeof fetch !== "undefined" ? fetch : void 0);
30
+ if (!f) throw new Error("No fetch implementation available. Pass `options.fetch` (e.g. node-fetch / undici) when running outside the browser.");
31
+ return f;
32
+ }
33
+ function buildExtras(extras, options) {
34
+ const merged = {
35
+ "Content-Type": "application/json",
36
+ ...extras
37
+ };
38
+ if (options.token != null && merged.token == null) merged.token = options.token;
39
+ return merged;
40
+ }
41
+ /**
42
+ * Returns a JSON string of API errors if the response carries any, otherwise
43
+ * `null`. Mirrors the original `!responseJson.api.message.errors` checks.
44
+ */
45
+ function getApiErrors(api) {
46
+ const errors = api?.message?.errors;
47
+ if (errors && Object.keys(errors).length > 0) return JSON.stringify(errors);
48
+ return null;
49
+ }
50
+ /**
51
+ * GET helper. Sends request metadata via the `extras` header exactly like the
52
+ * original hooks did, parses JSON, and returns the typed payload.
53
+ */
54
+ async function apiGet(path, extras, options = {}) {
55
+ const doFetch = resolveFetch(options);
56
+ const url = (options.baseUrl ?? "") + path;
57
+ const headers = { extras: JSON.stringify(buildExtras(extras, options)) };
58
+ console.log("[API GET]", {
59
+ url,
60
+ headers
61
+ });
62
+ const response = await doFetch(url, {
63
+ method: "GET",
64
+ headers,
65
+ signal: options.signal
66
+ });
67
+ if (!response.ok) throw new Error(`Network response was not ok (${response.status})`);
68
+ return await response.json();
69
+ }
70
+ /**
71
+ * JSON body helper for POST/PUT mutations.
72
+ */
73
+ async function apiSend(path, method, payload, options = {}) {
74
+ const doFetch = resolveFetch(options);
75
+ const url = (options.baseUrl ?? "") + path;
76
+ const headers = { "Content-Type": "application/json" };
77
+ if (options.token != null) headers.token = options.token;
78
+ console.log(`[API ${method}]`, {
79
+ url,
80
+ headers,
81
+ payload
82
+ });
83
+ const response = await doFetch(url, {
84
+ method,
85
+ headers,
86
+ body: JSON.stringify(payload),
87
+ signal: options.signal
88
+ });
89
+ if (!response.ok) throw new Error(`Failed to ${method.toLowerCase()}: ${response.statusText}`);
90
+ return await response.json();
91
+ }
92
+ /**
93
+ * DELETE helper.
94
+ */
95
+ async function apiDelete(path, options = {}) {
96
+ const doFetch = resolveFetch(options);
97
+ const url = (options.baseUrl ?? "") + path;
98
+ const headers = {};
99
+ if (options.token != null) headers.token = options.token;
100
+ console.log("[API DELETE]", {
101
+ url,
102
+ headers
103
+ });
104
+ const response = await doFetch(url, {
105
+ method: "DELETE",
106
+ headers,
107
+ signal: options.signal
108
+ });
109
+ if (!response.ok) throw new Error(`Failed to delete: ${response.statusText}`);
110
+ }
111
+
112
+ //#endregion
113
+ //#region src/core/category.ts
114
+ /**
115
+ * Fetches categories. Framework-agnostic: returns the flattened `Category[]`
116
+ * or throws on API errors.
117
+ */
118
+ async function fetchCategories(params = {}, options = {}) {
119
+ const extras = { deleted: params.deleted ?? false };
120
+ if (params.categoryIds) extras.categoryIds = params.categoryIds;
121
+ if (params.token) extras.token = params.token;
122
+ const json = await apiGet("/api/categories", extras, options);
123
+ const error = getApiErrors(json.api);
124
+ if (error) throw new Error(error);
125
+ return Object.values(json.categories ?? {});
126
+ }
127
+
128
+ //#endregion
129
+ //#region src/core/consumer.ts
130
+ /**
131
+ * Fetches consumers by id. Returns an empty array when no ids are supplied
132
+ * (the original hook short-circuited in that case).
133
+ */
134
+ async function fetchConsumers(consumerIds, options = {}) {
135
+ if (!consumerIds || consumerIds.length === 0) return [];
136
+ const json = await apiGet("/api/consumer", { ids: consumerIds.join(",") }, options);
137
+ const error = getApiErrors(json.api);
138
+ if (error) throw new Error(error);
139
+ return Object.values(json.consumers ?? {});
140
+ }
141
+
142
+ //#endregion
143
+ //#region src/core/employee.ts
144
+ /**
145
+ * Fetches employees. Returns the flattened `Employee[]`.
146
+ */
147
+ async function fetchEmployees(params = {}, options = {}) {
148
+ const extras = { deleted: params.deleted ?? false };
149
+ if (params.employeeIds) extras.employeeIds = params.employeeIds;
150
+ const json = await apiGet("/api/employees", extras, options);
151
+ const error = getApiErrors(json.api);
152
+ if (error) throw new Error(error);
153
+ return Object.values(json.employees ?? {});
154
+ }
155
+
156
+ //#endregion
157
+ //#region src/core/product.ts
158
+ /**
159
+ * Fetches products. Returns the flattened `Product[]`.
160
+ */
161
+ async function fetchProducts(params = {}, options = {}) {
162
+ const extras = { deleted: params.deleted ?? false };
163
+ if (params.productIds) extras.productIds = params.productIds;
164
+ if (params.storeId) extras.storeId = params.storeId;
165
+ if (params.token) extras.token = params.token;
166
+ const json = await apiGet("/api/products", extras, options);
167
+ const error = getApiErrors(json.api);
168
+ if (error) throw new Error(error);
169
+ return Object.values(json.products ?? {});
170
+ }
171
+ /**
172
+ * Fetches products by id and returns them keyed by id, transparently batching
173
+ * the request (default 200 ids per call) to stay within API limits. Extracted
174
+ * from the original `useBasketReport` inline batching loop so it can be reused
175
+ * outside React.
176
+ */
177
+ async function fetchProductsMap(productIds, options = {}, batchSize = 200) {
178
+ const result = {};
179
+ if (!productIds || productIds.length === 0) return result;
180
+ for (let i = 0; i < productIds.length; i += batchSize) {
181
+ const products = await fetchProducts({ productIds: productIds.slice(i, i + batchSize) }, options);
182
+ for (const product of products) result[product.id] = product;
183
+ }
184
+ return result;
185
+ }
186
+
187
+ //#endregion
188
+ //#region src/core/promotion.ts
189
+ /**
190
+ * Fetches promotions. Returns the flattened `Promotion[]`.
191
+ */
192
+ async function fetchPromotions(params = {}, options = {}) {
193
+ const extras = { deleted: params.deleted ?? false };
194
+ if (params.promotionIds) extras.promotionIds = params.promotionIds;
195
+ if (params.token) extras.token = params.token;
196
+ const json = await apiGet("/api/promotions", extras, options);
197
+ const error = getApiErrors(json.api);
198
+ if (error) throw new Error(error);
199
+ return Object.values(json.promotions ?? {});
200
+ }
201
+
202
+ //#endregion
203
+ //#region src/core/store.ts
204
+ /**
205
+ * Fetches stores. Returns the flattened `Store[]`.
206
+ */
207
+ async function fetchStores(params = {}, options = {}) {
208
+ const json = await apiGet("/api/stores", { deleted: params.deleted ?? false }, options);
209
+ const error = getApiErrors(json.api);
210
+ if (error) throw new Error(error);
211
+ return Object.values(json.stores ?? {});
212
+ }
213
+
214
+ //#endregion
215
+ //#region src/core/report.ts
216
+ /**
217
+ * Runs one or more report queries and returns the `reports` map keyed by query
218
+ * name. The query is JSON-encoded into the `extras.query` header, matching the
219
+ * original endpoint contract.
220
+ */
221
+ async function fetchReport(query, options = {}) {
222
+ const json = await apiGet("/api/report", { query: JSON.stringify(query) }, options);
223
+ const error = getApiErrors(json.api);
224
+ if (error) throw new Error(error);
225
+ return json.reports ?? {};
226
+ }
227
+
228
+ //#endregion
229
+ //#region src/core/session.ts
230
+ /**
231
+ * Ends the current server session.
232
+ *
233
+ * This is the framework-agnostic half of the original `useLogout` hook. The
234
+ * Next.js-specific redirect logic (router.push to the auth server logout URL,
235
+ * built from server actions) is intentionally NOT included here because it is
236
+ * tied to `next/navigation` and `'use server'` actions — see the README.
237
+ */
238
+ async function logout(options = {}) {
239
+ await apiDelete("/api/session?action=logout", options);
240
+ }
241
+
242
+ //#endregion
243
+ //#region src/core/entity.ts
244
+ /**
245
+ * Generic CRUD-style mutations extracted from `useHandleDelete` /
246
+ * `useHandleSubmit`. The original hooks also surfaced notistack snackbars; that
247
+ * UI concern is deliberately left to the hooks/host layer so core stays
248
+ * dependency-free.
249
+ */
250
+ /**
251
+ * Deletes `/api/{resourcePath}/{entityId}`.
252
+ */
253
+ async function deleteEntity(resourcePath, entityId, options = {}) {
254
+ if (entityId == null || entityId === "") throw new Error("entityId is missing");
255
+ await apiDelete(`/api/${resourcePath}/${entityId}`, options);
256
+ }
257
+ /**
258
+ * Sends a create/update mutation and returns the parsed `result` field
259
+ * (matching the original `result.result` access).
260
+ */
261
+ async function submitEntity({ endpoint, method = "POST", payload }, options = {}) {
262
+ return (await apiSend(endpoint, method, payload, options)).result;
263
+ }
264
+
265
+ //#endregion
266
+ //#region src/core/basket.ts
267
+ const DEFAULT_BASKET_FILTERS = {
268
+ search: "",
269
+ dateRange: {
270
+ start: null,
271
+ end: null
272
+ },
273
+ consumerId: null,
274
+ status: "all"
275
+ };
276
+ const DEFAULT_PAGINATION = {
277
+ page: 0,
278
+ pageSize: 25,
279
+ total: 0
280
+ };
281
+ /** Formats a date as `YYYY-MM-DD HH:mm:ss` for the report query. */
282
+ function formatDateForQuery(date, isEndOfDay = false) {
283
+ return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")} ${isEndOfDay ? "23:59:59" : "00:00:00"}`;
284
+ }
285
+ /** Builds the `Basket` report query from a set of filters. */
286
+ function buildBasketReportQuery(filters) {
287
+ const whereConditions = [];
288
+ const today = /* @__PURE__ */ new Date();
289
+ const startDate = filters.dateRange.start ?? today;
290
+ const endDate = filters.dateRange.end ?? today;
291
+ whereConditions.push(`Basket.completeDate > '${formatDateForQuery(startDate, false)}'`);
292
+ whereConditions.push(`Basket.completeDate < '${formatDateForQuery(endDate, true)}'`);
293
+ if (filters.consumerId) whereConditions.push(`Basket.consumerId = ${filters.consumerId}`);
294
+ if (filters.status !== "all") whereConditions.push(`Basket.status = '${filters.status}'`);
295
+ return { baskets: {
296
+ FROM: "Basket",
297
+ WHERE: whereConditions.length > 0 ? { AND: whereConditions } : void 0
298
+ } };
299
+ }
300
+ /** Flattens the raw report rows/keys into structured baskets. */
301
+ function parseBasketReportData(reports) {
302
+ if (!reports?.baskets?.data || !reports?.baskets?.keys) return [];
303
+ const { keys, data } = reports.baskets;
304
+ const keyIndices = {};
305
+ keys.forEach((key, index) => {
306
+ keyIndices[key.name] = index;
307
+ });
308
+ const productKey = keys.find((k) => k.name === "Basket.Product");
309
+ const productSubKeys = {};
310
+ if (productKey?.item) productKey.item.forEach((subKey, index) => {
311
+ productSubKeys[subKey.name] = index;
312
+ });
313
+ return data.map((row) => {
314
+ const products = (row[keyIndices["Basket.Product"]] || []).map((productRow) => ({
315
+ productId: productRow[productSubKeys["Basket.Product.productId"]],
316
+ productName: `Product #${productRow[productSubKeys["Basket.Product.productId"]]}`,
317
+ quantity: productRow[productSubKeys["Basket.Product.quantity"]] || 1,
318
+ price: productRow[productSubKeys["Basket.Product.productPrice"]] || 0,
319
+ tax: productRow[productSubKeys["Basket.Product.productTaxPercentage"]] || 0
320
+ }));
321
+ const total = products.reduce((sum, p) => sum + p.price * p.quantity, 0);
322
+ const taxTotal = products.reduce((sum, p) => sum + p.price * p.quantity * p.tax, 0);
323
+ const completeDateTimestamp = row[keyIndices["Basket.completeDate"]];
324
+ const completeDate = completeDateTimestamp ? new Date(completeDateTimestamp).toISOString() : "";
325
+ return {
326
+ id: row[keyIndices["Basket.id"]],
327
+ name: row[keyIndices["Basket.name"]],
328
+ consumerId: row[keyIndices["Basket.consumerId"]],
329
+ products,
330
+ total,
331
+ taxTotal,
332
+ status: "completed",
333
+ completeDate
334
+ };
335
+ });
336
+ }
337
+ /** Unique non-null consumer ids referenced by a set of baskets. */
338
+ function collectConsumerIds(baskets) {
339
+ if (!baskets.length) return null;
340
+ const ids = baskets.map((b) => b.consumerId).filter((id) => id != null);
341
+ return ids.length > 0 ? [...new Set(ids)] : null;
342
+ }
343
+ /** Unique product ids referenced by a set of baskets. */
344
+ function collectProductIds(baskets) {
345
+ if (!baskets.length) return null;
346
+ const ids = /* @__PURE__ */ new Set();
347
+ baskets.forEach((basket) => {
348
+ basket.products.forEach((product) => {
349
+ if (product.productId) ids.add(product.productId);
350
+ });
351
+ });
352
+ return ids.size > 0 ? [...ids] : null;
353
+ }
354
+ /** Joins baskets with consumer + product lookups, applies search, sorts. */
355
+ function combineBasketRows({ baskets, consumerMap, productMap, search }) {
356
+ if (!baskets.length) return [];
357
+ let combinedRows = baskets.map((basket) => {
358
+ const consumer = basket.consumerId ? consumerMap[basket.consumerId] : null;
359
+ const productsWithNames = basket.products.map((p) => {
360
+ const product = productMap[p.productId];
361
+ return {
362
+ ...p,
363
+ productName: product?.name || `Product #${p.productId}`
364
+ };
365
+ });
366
+ return {
367
+ id: basket.id,
368
+ basketId: basket.id,
369
+ basketName: basket.name || `Basket #${basket.id}`,
370
+ consumerId: basket.consumerId,
371
+ consumerName: consumer ? `${consumer.firstName} ${consumer.lastName}` : "Unknown",
372
+ consumerEmail: consumer?.email || "-",
373
+ products: productsWithNames,
374
+ basketTotal: (basket.total || 0) / 100,
375
+ tax: basket.taxTotal || 0,
376
+ dateTime: basket.completeDate,
377
+ status: basket.status || "pending"
378
+ };
379
+ });
380
+ if (search) {
381
+ const searchLower = search.toLowerCase();
382
+ combinedRows = combinedRows.filter((row) => row.basketId.toString().includes(searchLower) || row.basketName.toLowerCase().includes(searchLower) || row.consumerName.toLowerCase().includes(searchLower) || row.consumerEmail.toLowerCase().includes(searchLower));
383
+ }
384
+ combinedRows.sort((a, b) => {
385
+ const dateA = new Date(a.dateTime).getTime();
386
+ return new Date(b.dateTime).getTime() - dateA;
387
+ });
388
+ return combinedRows;
389
+ }
390
+ /** Aggregate totals for a set of report rows. */
391
+ function computeBasketSummary(rows) {
392
+ const uniqueConsumerIds = new Set(rows.map((row) => row.consumerId));
393
+ return {
394
+ totalBaskets: rows.length,
395
+ totalRevenue: rows.reduce((sum, row) => sum + row.basketTotal, 0),
396
+ totalTax: rows.reduce((sum, row) => sum + row.tax, 0),
397
+ uniqueConsumers: uniqueConsumerIds.size
398
+ };
399
+ }
400
+ /** Builds a consumer id → consumer lookup. */
401
+ function buildConsumerMap(consumers) {
402
+ const map = {};
403
+ if (consumers) consumers.forEach((consumer) => {
404
+ map[consumer.id] = consumer;
405
+ });
406
+ return map;
407
+ }
408
+
409
+ //#endregion
410
+ //#region src/hooks/shared.ts
411
+ function toMessage(e) {
412
+ if (e instanceof Error) return e.message;
413
+ return String(e);
414
+ }
415
+ /**
416
+ * Internal primitive powering the auto-fetch hooks. Runs `fetcher` on mount and
417
+ * whenever `deps` change, tracks loading/error/data, cancels in-flight requests
418
+ * on unmount or dep change, and exposes `refetch`.
419
+ *
420
+ * When `enabled` is false the fetch is skipped (used for hooks that need
421
+ * arguments before they can run, e.g. consumers-by-id).
422
+ */
423
+ function useQuery(fetcher, deps, enabled = true) {
424
+ const [data, setData] = (0, react.useState)(null);
425
+ const [loading, setLoading] = (0, react.useState)(enabled);
426
+ const [error, setError] = (0, react.useState)(null);
427
+ const [tick, setTick] = (0, react.useState)(0);
428
+ const refetch = (0, react.useCallback)(() => setTick((t) => t + 1), []);
429
+ (0, react.useEffect)(() => {
430
+ if (!enabled) {
431
+ setLoading(false);
432
+ return;
433
+ }
434
+ const controller = new AbortController();
435
+ let active = true;
436
+ setData(null);
437
+ setError(null);
438
+ setLoading(true);
439
+ fetcher(controller.signal).then((result) => {
440
+ if (active) setData(result);
441
+ }).catch((e) => {
442
+ if (active && e?.name !== "AbortError") setError(toMessage(e));
443
+ }).finally(() => {
444
+ if (active) setLoading(false);
445
+ });
446
+ return () => {
447
+ active = false;
448
+ controller.abort();
449
+ };
450
+ }, [
451
+ ...deps,
452
+ tick,
453
+ enabled
454
+ ]);
455
+ return {
456
+ data,
457
+ loading,
458
+ error,
459
+ refetch
460
+ };
461
+ }
462
+ /**
463
+ * Internal primitive powering the manually-triggered hooks.
464
+ */
465
+ function useMutation(action, deps) {
466
+ const [data, setData] = (0, react.useState)(null);
467
+ const [loading, setLoading] = (0, react.useState)(false);
468
+ const [error, setError] = (0, react.useState)(null);
469
+ return {
470
+ mutate: (0, react.useCallback)(async (...args) => {
471
+ setLoading(true);
472
+ setError(null);
473
+ try {
474
+ const result = await action(...args);
475
+ setData(result);
476
+ return result;
477
+ } catch (e) {
478
+ setError(toMessage(e));
479
+ return null;
480
+ } finally {
481
+ setLoading(false);
482
+ }
483
+ }, deps),
484
+ data,
485
+ loading,
486
+ error
487
+ };
488
+ }
489
+
490
+ //#endregion
491
+ //#region src/hooks/useCategory.ts
492
+ /**
493
+ * Auto-fetches categories on mount and whenever the params change.
494
+ * Manual reloads are available via `refetch` (replaces the old `reloadFlag`).
495
+ */
496
+ function useCategory(params = {}, options) {
497
+ return useQuery((signal) => fetchCategories(params, {
498
+ ...options,
499
+ signal
500
+ }), [
501
+ JSON.stringify(params),
502
+ options?.baseUrl,
503
+ options?.token
504
+ ]);
505
+ }
506
+
507
+ //#endregion
508
+ //#region src/hooks/useConsumer.ts
509
+ /**
510
+ * Auto-fetches consumers by id. Disabled (no request) until at least one id is
511
+ * supplied, mirroring the original early-return behaviour.
512
+ */
513
+ function useConsumer(consumerIds, options) {
514
+ const enabled = !!consumerIds && consumerIds.length > 0;
515
+ return useQuery((signal) => fetchConsumers(consumerIds, {
516
+ ...options,
517
+ signal
518
+ }), [
519
+ JSON.stringify(consumerIds),
520
+ options?.baseUrl,
521
+ options?.token
522
+ ], enabled);
523
+ }
524
+ var useConsumer_default = useConsumer;
525
+
526
+ //#endregion
527
+ //#region src/hooks/useEmployee.ts
528
+ /**
529
+ * Auto-fetches employees on mount and whenever the params change.
530
+ */
531
+ function useEmployee(params = {}, options) {
532
+ return useQuery((signal) => fetchEmployees(params, {
533
+ ...options,
534
+ signal
535
+ }), [
536
+ JSON.stringify(params),
537
+ options?.baseUrl,
538
+ options?.token
539
+ ]);
540
+ }
541
+
542
+ //#endregion
543
+ //#region src/hooks/useProduct.ts
544
+ /**
545
+ * Auto-fetches products on mount and whenever the params change.
546
+ */
547
+ function useProduct(params = {}, options) {
548
+ return useQuery((signal) => fetchProducts(params, {
549
+ ...options,
550
+ signal
551
+ }), [
552
+ JSON.stringify(params),
553
+ options?.baseUrl,
554
+ options?.token
555
+ ]);
556
+ }
557
+
558
+ //#endregion
559
+ //#region src/hooks/usePromotion.ts
560
+ /**
561
+ * Auto-fetches promotions on mount and whenever the params change.
562
+ * Manual reloads are available via `refetch`.
563
+ */
564
+ function usePromotion(params = {}, options) {
565
+ return useQuery((signal) => fetchPromotions(params, {
566
+ ...options,
567
+ signal
568
+ }), [
569
+ JSON.stringify(params),
570
+ options?.baseUrl,
571
+ options?.token
572
+ ]);
573
+ }
574
+
575
+ //#endregion
576
+ //#region src/hooks/useStore.ts
577
+ /**
578
+ * Auto-fetches stores on mount. Manual reloads via `refetch`
579
+ * (replaces the old `reloadFlag` argument).
580
+ */
581
+ function useStore(params = {}, options) {
582
+ return useQuery((signal) => fetchStores(params, {
583
+ ...options,
584
+ signal
585
+ }), [
586
+ JSON.stringify(params),
587
+ options?.baseUrl,
588
+ options?.token
589
+ ]);
590
+ }
591
+
592
+ //#endregion
593
+ //#region src/hooks/useReport.ts
594
+ /**
595
+ * Auto-fetches one or more reports. Disabled (no request) until a non-null
596
+ * query is supplied, mirroring the original early-return behaviour.
597
+ */
598
+ function useReport(query, options) {
599
+ const enabled = !!query;
600
+ return useQuery((signal) => fetchReport(query, {
601
+ ...options,
602
+ signal
603
+ }), [
604
+ JSON.stringify(query),
605
+ options?.baseUrl,
606
+ options?.token
607
+ ], enabled);
608
+ }
609
+ var useReport_default = useReport;
610
+
611
+ //#endregion
612
+ //#region src/hooks/useDelete.ts
613
+ /**
614
+ * Manually-triggered delete. Returns `{ mutate, loading, error, data }`.
615
+ *
616
+ * Note: the original `useHandleDelete` surfaced notistack toasts. That UI
617
+ * concern is left to the host app — handle success/error from the returned
618
+ * state or the promise resolved by `mutate`.
619
+ *
620
+ * @example
621
+ * const { mutate, loading } = useDelete();
622
+ * await mutate("products", 42);
623
+ */
624
+ function useDelete(options) {
625
+ return useMutation((resourcePath, entityId) => deleteEntity(resourcePath, entityId, options), [options?.baseUrl, options?.token]);
626
+ }
627
+
628
+ //#endregion
629
+ //#region src/hooks/useSubmit.ts
630
+ /**
631
+ * Manually-triggered create/update. Returns `{ mutate, loading, error, data }`,
632
+ * where `data` is the API `result` payload.
633
+ *
634
+ * The original `useHandleSubmit` bundled validation, payload transformation and
635
+ * notistack toasts. Those are app concerns: resolve the endpoint/method/payload
636
+ * in your component and pass them in, then react to the returned state.
637
+ *
638
+ * @example
639
+ * const { mutate, loading } = useSubmit<Product>();
640
+ * const saved = await mutate({
641
+ * endpoint: id ? `/api/products/${id}` : "/api/products",
642
+ * method: id ? "PUT" : "POST",
643
+ * payload,
644
+ * });
645
+ */
646
+ function useSubmit(options) {
647
+ return useMutation((args) => submitEntity(args, options), [options?.baseUrl, options?.token]);
648
+ }
649
+
650
+ //#endregion
651
+ //#region src/hooks/useLogout.ts
652
+ /**
653
+ * Manually-triggered logout (ends the server session).
654
+ *
655
+ * This is framework-agnostic and does NOT redirect. The original `useLogout`
656
+ * also pushed the browser to the auth-server logout URL via `next/navigation`
657
+ * and `'use server'` actions; that part is Next-specific and stays in your app.
658
+ *
659
+ * @example
660
+ * const { mutate: doLogout } = useLogout();
661
+ * await doLogout();
662
+ * router.push(logoutUrl); // app-specific redirect
663
+ */
664
+ function useLogout(options) {
665
+ return useMutation(() => logout(options), [options?.baseUrl, options?.token]);
666
+ }
667
+
668
+ //#endregion
669
+ //#region src/hooks/useBasketReport.ts
670
+ /**
671
+ * Composite hook that orchestrates the report → consumers → products pipeline
672
+ * for the basket report screen. All the data-shaping logic now lives in the
673
+ * framework-agnostic `core/basket` helpers; this hook is the thin React glue
674
+ * (state, memoisation, effects) on top of them.
675
+ */
676
+ function useBasketReport(options) {
677
+ const [filters, setFiltersState] = (0, react.useState)(DEFAULT_BASKET_FILTERS);
678
+ const [pagination, setPaginationState] = (0, react.useState)(DEFAULT_PAGINATION);
679
+ const [refreshFlag, setRefreshFlag] = (0, react.useState)(0);
680
+ const { data: reports, loading: basketLoading, error: basketError } = useReport_default((0, react.useMemo)(() => buildBasketReportQuery(filters), [filters, refreshFlag]), options);
681
+ const basketData = (0, react.useMemo)(() => parseBasketReportData(reports), [reports]);
682
+ const consumerIds = (0, react.useMemo)(() => collectConsumerIds(basketData), [basketData]);
683
+ const productIds = (0, react.useMemo)(() => collectProductIds(basketData), [basketData]);
684
+ const { data: consumers, loading: consumerLoading, error: consumerError } = useConsumer_default(consumerIds, options);
685
+ const [productMap, setProductMap] = (0, react.useState)({});
686
+ const [productLoading, setProductLoading] = (0, react.useState)(false);
687
+ const [productError, setProductError] = (0, react.useState)(null);
688
+ (0, react.useEffect)(() => {
689
+ if (!productIds || productIds.length === 0) {
690
+ setProductMap({});
691
+ return;
692
+ }
693
+ let active = true;
694
+ setProductLoading(true);
695
+ setProductError(null);
696
+ fetchProductsMap(productIds, options).then((map) => {
697
+ if (active) setProductMap(map);
698
+ }).catch((e) => {
699
+ if (active) setProductError(e instanceof Error ? e.message : "Failed to fetch products");
700
+ }).finally(() => {
701
+ if (active) setProductLoading(false);
702
+ });
703
+ return () => {
704
+ active = false;
705
+ };
706
+ }, [
707
+ JSON.stringify(productIds),
708
+ options?.baseUrl,
709
+ options?.token
710
+ ]);
711
+ const consumerMap = (0, react.useMemo)(() => buildConsumerMap(consumers), [consumers]);
712
+ const rows = (0, react.useMemo)(() => combineBasketRows({
713
+ baskets: basketData,
714
+ consumerMap,
715
+ productMap,
716
+ search: filters.search
717
+ }), [
718
+ basketData,
719
+ consumerMap,
720
+ productMap,
721
+ filters.search
722
+ ]);
723
+ const summary = (0, react.useMemo)(() => computeBasketSummary(rows), [rows]);
724
+ (0, react.useEffect)(() => {
725
+ if (rows.length > 0 && rows.length === pagination.pageSize) setPaginationState((prev) => ({
726
+ ...prev,
727
+ total: (pagination.page + 2) * pagination.pageSize
728
+ }));
729
+ else if (rows.length > 0) setPaginationState((prev) => ({
730
+ ...prev,
731
+ total: pagination.page * pagination.pageSize + rows.length
732
+ }));
733
+ }, [
734
+ rows.length,
735
+ pagination.page,
736
+ pagination.pageSize
737
+ ]);
738
+ const setFilters = (0, react.useCallback)((newFilters) => {
739
+ setFiltersState((prev) => ({
740
+ ...prev,
741
+ ...newFilters
742
+ }));
743
+ setPaginationState((prev) => ({
744
+ ...prev,
745
+ page: 0
746
+ }));
747
+ }, []);
748
+ const setPagination = (0, react.useCallback)((page, pageSize) => {
749
+ setPaginationState((prev) => ({
750
+ ...prev,
751
+ page,
752
+ pageSize
753
+ }));
754
+ }, []);
755
+ const refresh = (0, react.useCallback)(() => {
756
+ setRefreshFlag((prev) => prev + 1);
757
+ }, []);
758
+ return {
759
+ rows,
760
+ summary,
761
+ loading: basketLoading || consumerLoading || productLoading,
762
+ error: basketError || consumerError || productError,
763
+ filters,
764
+ setFilters,
765
+ pagination,
766
+ setPagination,
767
+ refresh
768
+ };
769
+ }
770
+
771
+ //#endregion
772
+ exports.useBasketReport = useBasketReport;
773
+ exports.useCategory = useCategory;
774
+ exports.useConsumer = useConsumer;
775
+ exports.useDelete = useDelete;
776
+ exports.useEmployee = useEmployee;
777
+ exports.useLogout = useLogout;
778
+ exports.useProduct = useProduct;
779
+ exports.usePromotion = usePromotion;
780
+ exports.useReport = useReport;
781
+ exports.useStore = useStore;
782
+ exports.useSubmit = useSubmit;
783
+ //# sourceMappingURL=index.cjs.map