@evenicanpm/storefront-core 2.2.0 → 2.3.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.
Files changed (63) hide show
  1. package/package.json +7 -3
  2. package/src/api-manager/README.md +52 -27
  3. package/src/api-manager/datasources/d365/d365-cart.datasource.ts +166 -17
  4. package/src/api-manager/datasources/d365/d365-invoice.datasource.ts +100 -0
  5. package/src/api-manager/datasources/d365/d365-order.datasource.ts +75 -18
  6. package/src/api-manager/datasources/d365/d365-product.datasource.ts +19 -1
  7. package/src/api-manager/datasources/d365/d365-user.datasource.ts +96 -22
  8. package/src/api-manager/datasources/d365/d365.datasource.ts +3 -0
  9. package/src/api-manager/datasources/d365/utils/get-context-cookie.ts +10 -3
  10. package/src/api-manager/datasources/e4/e4.datasource.ts +9 -0
  11. package/src/api-manager/datasources/e4/order/e4-order.datasource.ts +7 -4
  12. package/src/api-manager/datasources/e4/user/e4-user.datasource.ts +4 -0
  13. package/src/api-manager/index.ts +54 -3
  14. package/src/api-manager/lib/get-graphql-client.ts +11 -5
  15. package/src/api-manager/schemas/cart.schema.ts +10 -1
  16. package/src/api-manager/schemas/invoice.schema.ts +55 -0
  17. package/src/api-manager/schemas/order.schema.ts +13 -1
  18. package/src/api-manager/schemas/user.schema.ts +8 -0
  19. package/src/api-manager/services/create-query.ts +4 -10
  20. package/src/api-manager/services/invoice/queries/get-invoice-details.ts +18 -0
  21. package/src/api-manager/services/invoice/queries/get-invoices.ts +18 -0
  22. package/src/api-manager/services/order/queries/get-order-details.ts +1 -1
  23. package/src/api-manager/services/order/queries/get-orders.ts +6 -3
  24. package/src/api-manager/services/user/queries/get-customer-balance.ts +20 -0
  25. package/src/api-manager/types/Datasource.ts +21 -2
  26. package/src/auth/better-auth.ts +1 -1
  27. package/src/cms/blocks/block-manager.tsx +18 -3
  28. package/src/cms/blocks/components/cta-banner.tsx +65 -0
  29. package/src/cms/blocks/components/hero-image.tsx +83 -0
  30. package/src/cms/blocks/index.tsx +2 -0
  31. package/src/cms/blocks/interfaces.ts +20 -0
  32. package/src/cms/draft-mode-badge.tsx +26 -0
  33. package/src/cms/queries.ts +81 -0
  34. package/src/components/flex-box/flex-box.tsx +2 -0
  35. package/src/components/header/components/user.tsx +37 -12
  36. package/src/components/mini-cart/mini-cart.tsx +3 -3
  37. package/src/components/wishlist-dialogs/add-to-wishlist/compound/wishlist-dialog-header.tsx +1 -0
  38. package/src/pages/account/account-navigation.tsx +88 -60
  39. package/src/pages/account/account-routes.ts +52 -0
  40. package/src/pages/account/orders/order-history-filter-types.tsx +26 -0
  41. package/src/pages/account/orders/order-history-filters-panel.tsx +375 -0
  42. package/src/pages/account/orders/order-history-header.tsx +54 -0
  43. package/src/pages/account/orders/order-history-pagination.tsx +55 -0
  44. package/src/pages/account/orders/order-history-root.tsx +125 -0
  45. package/src/pages/account/orders/order-history-row.tsx +110 -0
  46. package/src/pages/account/orders/order-history-search-and-filter.tsx +449 -0
  47. package/src/pages/account/orders/order-history-search-bar.tsx +84 -0
  48. package/src/pages/account/orders/order-history-search-dropdown.tsx +53 -0
  49. package/src/pages/account/orders/order-history-sort.tsx +90 -0
  50. package/src/pages/account/orders/order-history-table.tsx +54 -0
  51. package/src/pages/account/orders/order-row.tsx +2 -2
  52. package/src/pages/account/orders/orders-drop-down-handler.tsx +19 -0
  53. package/src/pages/account/table-header.tsx +20 -0
  54. package/src/pages/account/wishlist/wishlist-item.tsx +1 -1
  55. package/src/pages/blog/blog-card.tsx +9 -3
  56. package/src/pages/blog/blog-detail-view.tsx +150 -0
  57. package/src/pages/blog/blog-list-view.tsx +59 -0
  58. package/src/pages/cart/estimate-shipping.tsx +6 -5
  59. package/src/pages/checkout/checkout-alt-form/checkout-form.tsx +43 -21
  60. package/src/pages/checkout/checkout-alt-form/steps/payment/payment-details.tsx +144 -19
  61. package/src/pages/cms-page-view.tsx +53 -0
  62. package/src/pages/quickorder/order-upload.tsx +12 -6
  63. package/src/pages/quickorder/quick-order.tsx +25 -20
@@ -0,0 +1,53 @@
1
+ "use client";
2
+ import { Box, Container, Paper } from "@mui/material";
3
+ import { useSuspenseQuery } from "@tanstack/react-query";
4
+ import BlockManager from "../cms/blocks/block-manager";
5
+ import type { BlockManagerType } from "../cms/blocks/interfaces";
6
+ import { DraftModeBadge } from "../cms/draft-mode-badge";
7
+ import { cmsQueryOptions } from "../cms/queries";
8
+ import { H1, H4 } from "../components/Typography";
9
+
10
+ interface Props {
11
+ slug: string;
12
+ cmsLocale?: string;
13
+ isDraftMode: boolean;
14
+ }
15
+
16
+ export const CmsPageView = ({ slug, cmsLocale, isDraftMode }: Props) => {
17
+ const { data } = useSuspenseQuery(
18
+ cmsQueryOptions.page(slug, isDraftMode ? "draft" : "published", cmsLocale),
19
+ );
20
+
21
+ const pageData = data?.data[0];
22
+
23
+ return (
24
+ <>
25
+ {isDraftMode && <DraftModeBadge />}
26
+ {pageData ? (
27
+ <Box
28
+ sx={{
29
+ width: "100%",
30
+ maxWidth: "100vw",
31
+ overflowX: "hidden",
32
+ wordWrap: "break-word",
33
+ marginTop: "25px",
34
+ }}
35
+ >
36
+ <BlockManager
37
+ blocks={pageData?.block as unknown as BlockManagerType}
38
+ />
39
+ </Box>
40
+ ) : (
41
+ <Container>
42
+ <Paper sx={{ marginTop: "25px", padding: "50px" }} elevation={1}>
43
+ <H1>E4 Storefront</H1>
44
+ <H4>
45
+ Add a page to the &quot;Page&quot; content-type in CMS to add
46
+ content to this page.
47
+ </H4>
48
+ </Paper>
49
+ </Container>
50
+ )}
51
+ </>
52
+ );
53
+ };
@@ -3,7 +3,7 @@
3
3
  import DropZone from "@evenicanpm/storefront-core/src/components/DropZone";
4
4
  import { Alert } from "@mui/material";
5
5
  import { useTranslations } from "next-intl";
6
- import { useEffect, useState } from "react";
6
+ import { useEffect, useRef, useState } from "react";
7
7
  import type { Accept, FileRejection } from "react-dropzone";
8
8
 
9
9
  export interface OrderUploadItem {
@@ -42,10 +42,16 @@ export default function OrderUpload({
42
42
  const t = useTranslations("QuickOrder");
43
43
 
44
44
  const [items, setItems] = useState<OrderUploadItem[]>([]);
45
- const [errCount, setErrCount] = useState<number>(0);
45
+ const [_errCount, setErrCount] = useState<number>(0);
46
46
  const [files, setFiles] = useState<File[]>();
47
47
  const [fileRejections, setFileRejections] = useState<string[]>([]);
48
48
 
49
+ const itemsRef = useRef(items);
50
+ itemsRef.current = items;
51
+
52
+ const onUploadItemsRef = useRef(onUploadItems);
53
+ onUploadItemsRef.current = onUploadItems;
54
+
49
55
  const accept: Accept = { "text/*": [".xlsx", ".csv", ".xls"] };
50
56
 
51
57
  useEffect(loadXlsxScript, []);
@@ -60,16 +66,16 @@ export default function OrderUpload({
60
66
  const sheet = readFirstSheet(buffer);
61
67
  const parsed = extractItemsFromSheet(sheet);
62
68
 
63
- const { validItems, errors } = validateItems(parsed, items);
69
+ const { validItems, errors } = validateItems(parsed, itemsRef.current);
64
70
 
65
71
  setItems(validItems);
66
- setErrCount(errCount + errors);
67
- onUploadItems(validItems);
72
+ setErrCount((prev) => prev + errors);
73
+ onUploadItemsRef.current(validItems);
68
74
  }
69
75
  };
70
76
 
71
77
  processFiles();
72
- }, [files, items, onUploadItems, errCount]);
78
+ }, [files]);
73
79
 
74
80
  const handleReject = (rejs: FileRejection[]) => {
75
81
  const msgs = rejs.map((r) => `${r.file.name} ${t("fileRejectionMsg")}`);
@@ -61,6 +61,7 @@ export interface QuickOrderRowData {
61
61
 
62
62
  type SearchableProduct = {
63
63
  ItemId?: string;
64
+ RecordId?: number;
64
65
  };
65
66
 
66
67
  type Ctx = {
@@ -93,17 +94,22 @@ const QuickOrder = ({ children }: { children?: ReactNode }) => {
93
94
 
94
95
  const tableInit = useCallback(() => {
95
96
  const next: QuickOrderRowData[] = [];
96
- for (let i = 0; i < 3; i++) next.push(generateEmptyRow(i + 1));
97
+ for (let i = 0; i < 2; i++) next.push(generateEmptyRow(i + 1));
97
98
  setRows(next);
98
99
  }, []);
99
100
 
100
101
  const deleteRow = (id: number) => {
102
+ if (rows.length <= 1) return;
101
103
  const current = [...rows];
102
104
  const idx = current.findIndex((r) => r.id === id);
103
105
  if (idx > -1) {
104
106
  current.splice(idx, 1);
105
- const newRowId = (current.at(-1)?.id ?? 0) + 1;
106
- if (current.length < 3) current.push(generateEmptyRow(newRowId));
107
+ // Ensure there's always a trailing empty row for input
108
+ const lastRow = current[current.length - 1];
109
+ if (lastRow.value) {
110
+ const newRowId = current.reduce((m, r) => Math.max(m, r.id), 0) + 1;
111
+ current.push(generateEmptyRow(newRowId));
112
+ }
107
113
  setRows(current);
108
114
  }
109
115
  };
@@ -176,6 +182,14 @@ const QuickOrder = ({ children }: { children?: ReactNode }) => {
176
182
  }
177
183
 
178
184
  current[idx] = generateRowData(product, id, value, found, quantity);
185
+
186
+ // Auto-add an empty row when the last row has data entered
187
+ const lastRow = current[current.length - 1];
188
+ if (lastRow.value) {
189
+ const newRowId = current.reduce((m, r) => Math.max(m, r.id), 0) + 1;
190
+ current.push(generateEmptyRow(newRowId));
191
+ }
192
+
179
193
  setRows(current);
180
194
  };
181
195
 
@@ -234,9 +248,11 @@ const QuickOrder = ({ children }: { children?: ReactNode }) => {
234
248
  let rowId: number = curr.reduce((m, r) => Math.max(m, r.id), 0) + 1;
235
249
 
236
250
  items.forEach((it: OrderUploadItem): void => {
251
+ const itemIdStr = it.itemId.toString().toLowerCase();
237
252
  const product: ProductSearchResult | undefined = response?.find(
238
253
  (r: SearchableProduct) =>
239
- it.itemId.toString().toLowerCase() === r.ItemId?.toLowerCase(),
254
+ itemIdStr === r.ItemId?.toLowerCase() ||
255
+ itemIdStr === String(r.RecordId),
240
256
  );
241
257
  const qty: number = it.qty || 1;
242
258
  curr.push(
@@ -250,7 +266,8 @@ const QuickOrder = ({ children }: { children?: ReactNode }) => {
250
266
  );
251
267
  });
252
268
 
253
- while (curr.length < 3) curr.push({ ...generateEmptyRow(rowId++) });
269
+ // Add one trailing empty row for the next entry
270
+ curr.push(generateEmptyRow(rowId++));
254
271
  setRows(curr);
255
272
  },
256
273
  [rows, searchProductsMultiple, generateRowData],
@@ -316,20 +333,14 @@ QuickOrder.Body = () => {
316
333
  const { rows } = ctx;
317
334
  return (
318
335
  <TableBody>
319
- {rows.map((r, i) => (
320
- <QuickOrder.Row key={r.id} row={r} isLast={i === rows.length - 1} />
336
+ {rows.map((r) => (
337
+ <QuickOrder.Row key={r.id} row={r} />
321
338
  ))}
322
339
  </TableBody>
323
340
  );
324
341
  };
325
342
 
326
- QuickOrder.Row = ({
327
- row,
328
- isLast,
329
- }: {
330
- row: QuickOrderRowData;
331
- isLast: boolean;
332
- }) => {
343
+ QuickOrder.Row = ({ row }: { row: QuickOrderRowData }) => {
333
344
  const ctx = useQuickOrder();
334
345
  const theme = useTheme();
335
346
  const t = useTranslations("QuickOrder");
@@ -378,10 +389,6 @@ QuickOrder.Row = ({
378
389
  ctx.updateRow(row.id, row.value, e.target.value);
379
390
  };
380
391
 
381
- const onFocus = () => {
382
- if (isLast) ctx.addRows();
383
- };
384
-
385
392
  return (
386
393
  <TableRow
387
394
  sx={{
@@ -394,7 +401,6 @@ QuickOrder.Row = ({
394
401
  <QuickOrder.SkuInput
395
402
  value={inputValue}
396
403
  onChange={onValueChange}
397
- onFocus={onFocus}
398
404
  placeholder={t("Table.itemNo")}
399
405
  />
400
406
  </TableCell>
@@ -403,7 +409,6 @@ QuickOrder.Row = ({
403
409
  <QuickOrder.QtyInput
404
410
  value={row.quantity}
405
411
  onChange={onQtyChange}
406
- onFocus={onFocus}
407
412
  placeholder={t("Table.quantity")}
408
413
  />
409
414
  </TableCell>