@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.
- package/package.json +7 -3
- package/src/api-manager/README.md +52 -27
- package/src/api-manager/datasources/d365/d365-cart.datasource.ts +166 -17
- package/src/api-manager/datasources/d365/d365-invoice.datasource.ts +100 -0
- package/src/api-manager/datasources/d365/d365-order.datasource.ts +75 -18
- package/src/api-manager/datasources/d365/d365-product.datasource.ts +19 -1
- package/src/api-manager/datasources/d365/d365-user.datasource.ts +96 -22
- package/src/api-manager/datasources/d365/d365.datasource.ts +3 -0
- package/src/api-manager/datasources/d365/utils/get-context-cookie.ts +10 -3
- package/src/api-manager/datasources/e4/e4.datasource.ts +9 -0
- package/src/api-manager/datasources/e4/order/e4-order.datasource.ts +7 -4
- package/src/api-manager/datasources/e4/user/e4-user.datasource.ts +4 -0
- package/src/api-manager/index.ts +54 -3
- package/src/api-manager/lib/get-graphql-client.ts +11 -5
- package/src/api-manager/schemas/cart.schema.ts +10 -1
- package/src/api-manager/schemas/invoice.schema.ts +55 -0
- package/src/api-manager/schemas/order.schema.ts +13 -1
- package/src/api-manager/schemas/user.schema.ts +8 -0
- package/src/api-manager/services/create-query.ts +4 -10
- package/src/api-manager/services/invoice/queries/get-invoice-details.ts +18 -0
- package/src/api-manager/services/invoice/queries/get-invoices.ts +18 -0
- package/src/api-manager/services/order/queries/get-order-details.ts +1 -1
- package/src/api-manager/services/order/queries/get-orders.ts +6 -3
- package/src/api-manager/services/user/queries/get-customer-balance.ts +20 -0
- package/src/api-manager/types/Datasource.ts +21 -2
- package/src/auth/better-auth.ts +1 -1
- package/src/cms/blocks/block-manager.tsx +18 -3
- package/src/cms/blocks/components/cta-banner.tsx +65 -0
- package/src/cms/blocks/components/hero-image.tsx +83 -0
- package/src/cms/blocks/index.tsx +2 -0
- package/src/cms/blocks/interfaces.ts +20 -0
- package/src/cms/draft-mode-badge.tsx +26 -0
- package/src/cms/queries.ts +81 -0
- package/src/components/flex-box/flex-box.tsx +2 -0
- package/src/components/header/components/user.tsx +37 -12
- package/src/components/mini-cart/mini-cart.tsx +3 -3
- package/src/components/wishlist-dialogs/add-to-wishlist/compound/wishlist-dialog-header.tsx +1 -0
- package/src/pages/account/account-navigation.tsx +88 -60
- package/src/pages/account/account-routes.ts +52 -0
- package/src/pages/account/orders/order-history-filter-types.tsx +26 -0
- package/src/pages/account/orders/order-history-filters-panel.tsx +375 -0
- package/src/pages/account/orders/order-history-header.tsx +54 -0
- package/src/pages/account/orders/order-history-pagination.tsx +55 -0
- package/src/pages/account/orders/order-history-root.tsx +125 -0
- package/src/pages/account/orders/order-history-row.tsx +110 -0
- package/src/pages/account/orders/order-history-search-and-filter.tsx +449 -0
- package/src/pages/account/orders/order-history-search-bar.tsx +84 -0
- package/src/pages/account/orders/order-history-search-dropdown.tsx +53 -0
- package/src/pages/account/orders/order-history-sort.tsx +90 -0
- package/src/pages/account/orders/order-history-table.tsx +54 -0
- package/src/pages/account/orders/order-row.tsx +2 -2
- package/src/pages/account/orders/orders-drop-down-handler.tsx +19 -0
- package/src/pages/account/table-header.tsx +20 -0
- package/src/pages/account/wishlist/wishlist-item.tsx +1 -1
- package/src/pages/blog/blog-card.tsx +9 -3
- package/src/pages/blog/blog-detail-view.tsx +150 -0
- package/src/pages/blog/blog-list-view.tsx +59 -0
- package/src/pages/cart/estimate-shipping.tsx +6 -5
- package/src/pages/checkout/checkout-alt-form/checkout-form.tsx +43 -21
- package/src/pages/checkout/checkout-alt-form/steps/payment/payment-details.tsx +144 -19
- package/src/pages/cms-page-view.tsx +53 -0
- package/src/pages/quickorder/order-upload.tsx +12 -6
- package/src/pages/quickorder/quick-order.tsx +25 -20
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import getStoresList from "@evenicanpm/storefront-core/src/api-manager/services/organization/queries/get-stores";
|
|
2
|
+
import type {
|
|
3
|
+
OrderSearchCriteria,
|
|
4
|
+
SalesStatus,
|
|
5
|
+
} from "@msdyn365-commerce/retail-proxy";
|
|
6
|
+
import { Box } from "@mui/material";
|
|
7
|
+
import { createContext, useContext, useState } from "react";
|
|
8
|
+
import {
|
|
9
|
+
type CreatedDateFilter,
|
|
10
|
+
FilterSourceType,
|
|
11
|
+
type OrderStatusFilter,
|
|
12
|
+
type OriginFilter,
|
|
13
|
+
} from "./order-history-filter-types";
|
|
14
|
+
import OrdersFilterPanel from "./order-history-filters-panel";
|
|
15
|
+
import { useOrderHistory } from "./order-history-root";
|
|
16
|
+
import OrdersSearchBar from "./order-history-search-bar";
|
|
17
|
+
|
|
18
|
+
// interface OrdersSearchAndFilterProps {
|
|
19
|
+
// handleSearch: (criteria: OrderSearchCriteria) => void;
|
|
20
|
+
// }
|
|
21
|
+
|
|
22
|
+
// export default function OrdersSearchAndFilter({
|
|
23
|
+
// handleSearch,
|
|
24
|
+
// }: OrdersSearchAndFilterProps) {
|
|
25
|
+
// const { data: stores } = getStoresList.useData({
|
|
26
|
+
// queryResultSettings: {
|
|
27
|
+
// Paging: {
|
|
28
|
+
// Skip: 0,
|
|
29
|
+
// Top: 1000,
|
|
30
|
+
// },
|
|
31
|
+
// },
|
|
32
|
+
// searchArea: {
|
|
33
|
+
// DistanceUnitValue: 0,
|
|
34
|
+
// Latitude: 0,
|
|
35
|
+
// Longitude: 0,
|
|
36
|
+
// Radius: 0,
|
|
37
|
+
// },
|
|
38
|
+
// });
|
|
39
|
+
|
|
40
|
+
// const originOptions: OriginFilter[] =
|
|
41
|
+
// stores?.map((store) => ({
|
|
42
|
+
// sourceType: FilterSourceType.Origin,
|
|
43
|
+
// label: store.location.OrgUnitName || "",
|
|
44
|
+
// value: store.location.ChannelId || 0,
|
|
45
|
+
// })) || [];
|
|
46
|
+
|
|
47
|
+
// // Search bar
|
|
48
|
+
// const [searchText, setSearchText] = useState<string>("");
|
|
49
|
+
// const [selectedSearchOption, setSelectedSearchOption] = useState<number>(0);
|
|
50
|
+
|
|
51
|
+
// // Filters
|
|
52
|
+
// const [selectedOrigins, setSelectedOrigins] = useState<OriginFilter[]>([]);
|
|
53
|
+
// const [createdDate, setCreatedDate] = useState<CreatedDateFilter>();
|
|
54
|
+
// const [orderStatus, setOrderStatus] = useState<OrderStatusFilter>();
|
|
55
|
+
|
|
56
|
+
// const handleSelectCreatedDate = (
|
|
57
|
+
// date: CreatedDateFilter | undefined,
|
|
58
|
+
// toggle = true,
|
|
59
|
+
// ) => {
|
|
60
|
+
// toggle
|
|
61
|
+
// ? setCreatedDate((prev) =>
|
|
62
|
+
// prev?.label === date?.label ? undefined : date,
|
|
63
|
+
// )
|
|
64
|
+
// : setCreatedDate(date);
|
|
65
|
+
// };
|
|
66
|
+
|
|
67
|
+
// const handleSelectOrderStatus = (status: OrderStatusFilter | undefined) => {
|
|
68
|
+
// setOrderStatus((prev) =>
|
|
69
|
+
// prev?.label === status?.label ? undefined : status,
|
|
70
|
+
// );
|
|
71
|
+
// };
|
|
72
|
+
|
|
73
|
+
// const handleSelectOrigin = (origin: OriginFilter) => {
|
|
74
|
+
// setSelectedOrigins((prev) => {
|
|
75
|
+
// const exists = prev.some((o) => o.label === origin.label);
|
|
76
|
+
// return exists
|
|
77
|
+
// ? prev.filter((o) => o.label !== origin.label)
|
|
78
|
+
// : [...prev, origin];
|
|
79
|
+
// });
|
|
80
|
+
// };
|
|
81
|
+
|
|
82
|
+
// const executeSearch = () => {
|
|
83
|
+
// const searchCriteria = createSearchCriteria();
|
|
84
|
+
// handleSearch(searchCriteria);
|
|
85
|
+
// };
|
|
86
|
+
|
|
87
|
+
// const createSearchCriteria = (): OrderSearchCriteria => {
|
|
88
|
+
// const orderSearchCriteria: OrderSearchCriteria = {};
|
|
89
|
+
|
|
90
|
+
// const channelIds: number[] = selectedOrigins.map((origin) => origin.value);
|
|
91
|
+
// if (channelIds.length > 0) {
|
|
92
|
+
// orderSearchCriteria.ChannelIds = channelIds;
|
|
93
|
+
// }
|
|
94
|
+
// const salesStatus = orderStatus?.value ?? undefined;
|
|
95
|
+
// const salesStatuses: SalesStatus[] =
|
|
96
|
+
// salesStatus !== undefined ? [salesStatus] : [];
|
|
97
|
+
// if (salesStatuses.length > 0) {
|
|
98
|
+
// orderSearchCriteria.OrderStatusValues = salesStatuses;
|
|
99
|
+
// }
|
|
100
|
+
|
|
101
|
+
// if (createdDate) {
|
|
102
|
+
// const { startDate, endDate } = getDateRange(createdDate);
|
|
103
|
+
// orderSearchCriteria.StartDateTime = startDate;
|
|
104
|
+
// orderSearchCriteria.EndDateTime = endDate;
|
|
105
|
+
// }
|
|
106
|
+
|
|
107
|
+
// if (searchText) {
|
|
108
|
+
// if (selectedSearchOption === 0) {
|
|
109
|
+
// orderSearchCriteria.ChannelReferenceId = searchText;
|
|
110
|
+
// } else if (selectedSearchOption === 1) {
|
|
111
|
+
// orderSearchCriteria.SalesId = searchText;
|
|
112
|
+
// } else if (selectedSearchOption === 2) {
|
|
113
|
+
// const channelIds = stores
|
|
114
|
+
// ?.filter((store) =>
|
|
115
|
+
// store.location.OrgUnitName?.toLocaleLowerCase().includes(
|
|
116
|
+
// searchText.toLowerCase(),
|
|
117
|
+
// ),
|
|
118
|
+
// )
|
|
119
|
+
// .map((store) => store.location.ChannelId)
|
|
120
|
+
// .filter((id): id is number => id !== undefined);
|
|
121
|
+
|
|
122
|
+
// if (channelIds?.length) {
|
|
123
|
+
// if (orderSearchCriteria.ChannelIds?.length) {
|
|
124
|
+
// // Find interesting channels between searched name and selected filters
|
|
125
|
+
// const intersectingChannelIds = channelIds.filter((id) =>
|
|
126
|
+
// orderSearchCriteria.ChannelIds?.includes(id),
|
|
127
|
+
// );
|
|
128
|
+
|
|
129
|
+
// orderSearchCriteria.ChannelIds = intersectingChannelIds;
|
|
130
|
+
// } else {
|
|
131
|
+
// orderSearchCriteria.ChannelIds = channelIds;
|
|
132
|
+
// }
|
|
133
|
+
// }
|
|
134
|
+
// }
|
|
135
|
+
// }
|
|
136
|
+
|
|
137
|
+
// return orderSearchCriteria;
|
|
138
|
+
// };
|
|
139
|
+
|
|
140
|
+
// const getDateRange = (
|
|
141
|
+
// option: CreatedDateFilter | undefined,
|
|
142
|
+
// ): {
|
|
143
|
+
// startDate: Date;
|
|
144
|
+
// endDate: Date;
|
|
145
|
+
// } => {
|
|
146
|
+
// const endDate = new Date(); // today
|
|
147
|
+
// const startDate = new Date();
|
|
148
|
+
|
|
149
|
+
// switch (option?.label) {
|
|
150
|
+
// case "Last 7 days":
|
|
151
|
+
// startDate.setDate(endDate.getDate() - 7);
|
|
152
|
+
// break;
|
|
153
|
+
// case "Last 30 days":
|
|
154
|
+
// startDate.setDate(endDate.getDate() - 30);
|
|
155
|
+
// break;
|
|
156
|
+
// case "Last 3 months":
|
|
157
|
+
// startDate.setMonth(endDate.getMonth() - 3);
|
|
158
|
+
// break;
|
|
159
|
+
// case "Last 6 months":
|
|
160
|
+
// startDate.setMonth(endDate.getMonth() - 6);
|
|
161
|
+
// break;
|
|
162
|
+
// case "Custom Dates":
|
|
163
|
+
// if (option.startValue && option.endValue) {
|
|
164
|
+
// const customStart = parseLocalDate(option.startValue);
|
|
165
|
+
// const customEnd = parseLocalDate(option.endValue, true);
|
|
166
|
+
// if (isNaN(customStart.getTime()) || isNaN(customEnd.getTime())) {
|
|
167
|
+
// throw new Error("Invalid date format in customized date range.");
|
|
168
|
+
// }
|
|
169
|
+
|
|
170
|
+
// startDate.setTime(customStart.getTime());
|
|
171
|
+
// endDate.setTime(customEnd.getTime());
|
|
172
|
+
// } else {
|
|
173
|
+
// throw new Error(
|
|
174
|
+
// "Customized date range must have start and end values.",
|
|
175
|
+
// );
|
|
176
|
+
// }
|
|
177
|
+
// break;
|
|
178
|
+
// default:
|
|
179
|
+
// throw new Error(`Unsupported date filter option: ${option}`);
|
|
180
|
+
// }
|
|
181
|
+
|
|
182
|
+
// return { startDate, endDate };
|
|
183
|
+
// };
|
|
184
|
+
|
|
185
|
+
// const parseLocalDate = (dateStr: string, endOfDay = false): Date => {
|
|
186
|
+
// const [year, month, day] = dateStr.split("-").map(Number);
|
|
187
|
+
// // month is 0-based in JS Date
|
|
188
|
+
// return endOfDay
|
|
189
|
+
// ? new Date(year, month - 1, day, 23, 59, 59, 999)
|
|
190
|
+
// : new Date(year, month - 1, day, 0, 0, 0, 0);
|
|
191
|
+
// };
|
|
192
|
+
|
|
193
|
+
// return (
|
|
194
|
+
// <Box>
|
|
195
|
+
// {/* SEARCH BAR */}
|
|
196
|
+
// <OrdersSearchBar
|
|
197
|
+
// setSearchText={setSearchText}
|
|
198
|
+
// selectedSearchOption={selectedSearchOption}
|
|
199
|
+
// setSelectedSearchOption={setSelectedSearchOption}
|
|
200
|
+
// executeSearch={executeSearch}
|
|
201
|
+
// />
|
|
202
|
+
|
|
203
|
+
// {/* FITLERS BAR */}
|
|
204
|
+
// <OrdersFilterPanel
|
|
205
|
+
// originOptions={originOptions}
|
|
206
|
+
// selectedOrigins={selectedOrigins}
|
|
207
|
+
// handleSelectOrigin={handleSelectOrigin}
|
|
208
|
+
// createdDate={createdDate}
|
|
209
|
+
// setCreatedDate={handleSelectCreatedDate}
|
|
210
|
+
// orderStatus={orderStatus}
|
|
211
|
+
// setOrderStatus={handleSelectOrderStatus}
|
|
212
|
+
// />
|
|
213
|
+
// </Box>
|
|
214
|
+
// );
|
|
215
|
+
// }
|
|
216
|
+
|
|
217
|
+
type OrdersSearchAndFilterContextType = {
|
|
218
|
+
// Search states
|
|
219
|
+
searchText: string;
|
|
220
|
+
setSearchText: (text: string) => void;
|
|
221
|
+
selectedSearchOption: number;
|
|
222
|
+
setSelectedSearchOption: (option: number) => void;
|
|
223
|
+
|
|
224
|
+
// Filter states
|
|
225
|
+
originOptions: OriginFilter[];
|
|
226
|
+
selectedOrigins: OriginFilter[];
|
|
227
|
+
handleSelectOrigin: (origin: OriginFilter) => void;
|
|
228
|
+
createdDate?: CreatedDateFilter;
|
|
229
|
+
handleSelectCreatedDate: (
|
|
230
|
+
date: CreatedDateFilter | undefined,
|
|
231
|
+
toggle?: boolean,
|
|
232
|
+
) => void;
|
|
233
|
+
orderStatus?: OrderStatusFilter;
|
|
234
|
+
handleSelectOrderStatus: (status: OrderStatusFilter | undefined) => void;
|
|
235
|
+
|
|
236
|
+
// Actions
|
|
237
|
+
executeSearch: () => void;
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const OrdersSearchAndFilterContext = createContext<
|
|
241
|
+
OrdersSearchAndFilterContextType | undefined
|
|
242
|
+
>(undefined);
|
|
243
|
+
|
|
244
|
+
export const useOrdersSearchAndFilter = () => {
|
|
245
|
+
const context = useContext(OrdersSearchAndFilterContext);
|
|
246
|
+
if (!context) {
|
|
247
|
+
throw new Error("Must be used within OrdersSearchAndFilterProvider");
|
|
248
|
+
}
|
|
249
|
+
return context;
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const OrdersSearchAndFilter = ({ children }: { children: React.ReactNode }) => {
|
|
253
|
+
const { setOrderSearchCriteria } = useOrderHistory();
|
|
254
|
+
const { data: stores } = getStoresList.useData({
|
|
255
|
+
queryResultSettings: {
|
|
256
|
+
Paging: {
|
|
257
|
+
Skip: 0,
|
|
258
|
+
Top: 1000,
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
searchArea: {
|
|
262
|
+
DistanceUnitValue: 0,
|
|
263
|
+
Latitude: 0,
|
|
264
|
+
Longitude: 0,
|
|
265
|
+
Radius: 0,
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Search states
|
|
270
|
+
const [searchText, setSearchText] = useState<string>("");
|
|
271
|
+
const [selectedSearchOption, setSelectedSearchOption] = useState<number>(0);
|
|
272
|
+
|
|
273
|
+
// Filter states
|
|
274
|
+
const [selectedOrigins, setSelectedOrigins] = useState<OriginFilter[]>([]);
|
|
275
|
+
const [createdDate, setCreatedDate] = useState<CreatedDateFilter>();
|
|
276
|
+
const [orderStatus, setOrderStatus] = useState<OrderStatusFilter>();
|
|
277
|
+
|
|
278
|
+
const originOptions: OriginFilter[] =
|
|
279
|
+
stores?.map((store) => ({
|
|
280
|
+
sourceType: FilterSourceType.Origin,
|
|
281
|
+
label: store.location.OrgUnitName || "",
|
|
282
|
+
value: store.location.ChannelId || 0,
|
|
283
|
+
})) || [];
|
|
284
|
+
|
|
285
|
+
const handleSelectOrigin = (origin: OriginFilter) => {
|
|
286
|
+
setSelectedOrigins((prev) => {
|
|
287
|
+
const exists = prev.some((o) => o.label === origin.label);
|
|
288
|
+
return exists
|
|
289
|
+
? prev.filter((o) => o.label !== origin.label)
|
|
290
|
+
: [...prev, origin];
|
|
291
|
+
});
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const handleSelectCreatedDate = (
|
|
295
|
+
date: CreatedDateFilter | undefined,
|
|
296
|
+
toggle = true,
|
|
297
|
+
) => {
|
|
298
|
+
toggle
|
|
299
|
+
? setCreatedDate((prev) =>
|
|
300
|
+
prev?.label === date?.label ? undefined : date,
|
|
301
|
+
)
|
|
302
|
+
: setCreatedDate(date);
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const handleSelectOrderStatus = (status: OrderStatusFilter | undefined) => {
|
|
306
|
+
setOrderStatus((prev) =>
|
|
307
|
+
prev?.label === status?.label ? undefined : status,
|
|
308
|
+
);
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const executeSearch = () => {
|
|
312
|
+
const searchCriteria = createSearchCriteria();
|
|
313
|
+
setOrderSearchCriteria(searchCriteria);
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const createSearchCriteria = (): OrderSearchCriteria => {
|
|
317
|
+
const orderSearchCriteria: OrderSearchCriteria = {};
|
|
318
|
+
|
|
319
|
+
const channelIds: number[] = selectedOrigins.map((origin) => origin.value);
|
|
320
|
+
if (channelIds.length > 0) {
|
|
321
|
+
orderSearchCriteria.ChannelIds = channelIds;
|
|
322
|
+
}
|
|
323
|
+
const salesStatus = orderStatus?.value ?? undefined;
|
|
324
|
+
const salesStatuses: SalesStatus[] =
|
|
325
|
+
salesStatus !== undefined ? [salesStatus] : [];
|
|
326
|
+
if (salesStatuses.length > 0) {
|
|
327
|
+
orderSearchCriteria.OrderStatusValues = salesStatuses;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (createdDate) {
|
|
331
|
+
const { startDate, endDate } = getDateRange(createdDate);
|
|
332
|
+
orderSearchCriteria.StartDateTime = startDate;
|
|
333
|
+
orderSearchCriteria.EndDateTime = endDate;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (searchText) {
|
|
337
|
+
if (selectedSearchOption === 0) {
|
|
338
|
+
orderSearchCriteria.ChannelReferenceId = searchText;
|
|
339
|
+
} else if (selectedSearchOption === 1) {
|
|
340
|
+
orderSearchCriteria.SalesId = searchText;
|
|
341
|
+
} else if (selectedSearchOption === 2) {
|
|
342
|
+
const channelIds = stores
|
|
343
|
+
?.filter((store) =>
|
|
344
|
+
store.location.OrgUnitName?.toLocaleLowerCase().includes(
|
|
345
|
+
searchText.toLowerCase(),
|
|
346
|
+
),
|
|
347
|
+
)
|
|
348
|
+
.map((store) => store.location.ChannelId)
|
|
349
|
+
.filter((id): id is number => id !== undefined);
|
|
350
|
+
|
|
351
|
+
if (channelIds?.length) {
|
|
352
|
+
if (orderSearchCriteria.ChannelIds?.length) {
|
|
353
|
+
const intersectingChannelIds = channelIds.filter((id) =>
|
|
354
|
+
orderSearchCriteria.ChannelIds?.includes(id),
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
orderSearchCriteria.ChannelIds = intersectingChannelIds;
|
|
358
|
+
} else {
|
|
359
|
+
orderSearchCriteria.ChannelIds = channelIds;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return orderSearchCriteria;
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
const getDateRange = (
|
|
369
|
+
option: CreatedDateFilter | undefined,
|
|
370
|
+
): {
|
|
371
|
+
startDate: Date;
|
|
372
|
+
endDate: Date;
|
|
373
|
+
} => {
|
|
374
|
+
const endDate = new Date();
|
|
375
|
+
const startDate = new Date();
|
|
376
|
+
|
|
377
|
+
switch (option?.label) {
|
|
378
|
+
case "Last 7 days":
|
|
379
|
+
startDate.setDate(endDate.getDate() - 7);
|
|
380
|
+
break;
|
|
381
|
+
case "Last 30 days":
|
|
382
|
+
startDate.setDate(endDate.getDate() - 30);
|
|
383
|
+
break;
|
|
384
|
+
case "Last 3 months":
|
|
385
|
+
startDate.setMonth(endDate.getMonth() - 3);
|
|
386
|
+
break;
|
|
387
|
+
case "Last 6 months":
|
|
388
|
+
startDate.setMonth(endDate.getMonth() - 6);
|
|
389
|
+
break;
|
|
390
|
+
case "Custom Dates":
|
|
391
|
+
if (option.startValue && option.endValue) {
|
|
392
|
+
const customStart = parseLocalDate(option.startValue);
|
|
393
|
+
const customEnd = parseLocalDate(option.endValue, true);
|
|
394
|
+
if (
|
|
395
|
+
Number.isNaN(customStart.getTime()) ||
|
|
396
|
+
Number.isNaN(customEnd.getTime())
|
|
397
|
+
) {
|
|
398
|
+
throw new Error("Invalid date format in customized date range.");
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
startDate.setTime(customStart.getTime());
|
|
402
|
+
endDate.setTime(customEnd.getTime());
|
|
403
|
+
} else {
|
|
404
|
+
throw new Error(
|
|
405
|
+
"Customized date range must have start and end values.",
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
break;
|
|
409
|
+
default:
|
|
410
|
+
throw new Error(`Unsupported date filter option: ${option}`);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return { startDate, endDate };
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
const parseLocalDate = (dateStr: string, endOfDay = false): Date => {
|
|
417
|
+
const [year, month, day] = dateStr.split("-").map(Number);
|
|
418
|
+
// month is 0-based in JS Date
|
|
419
|
+
return endOfDay
|
|
420
|
+
? new Date(year, month - 1, day, 23, 59, 59, 999)
|
|
421
|
+
: new Date(year, month - 1, day, 0, 0, 0, 0);
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const value = {
|
|
425
|
+
searchText,
|
|
426
|
+
setSearchText,
|
|
427
|
+
selectedSearchOption,
|
|
428
|
+
setSelectedSearchOption,
|
|
429
|
+
originOptions,
|
|
430
|
+
selectedOrigins,
|
|
431
|
+
handleSelectOrigin,
|
|
432
|
+
createdDate,
|
|
433
|
+
handleSelectCreatedDate,
|
|
434
|
+
orderStatus,
|
|
435
|
+
handleSelectOrderStatus,
|
|
436
|
+
executeSearch,
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
return (
|
|
440
|
+
<OrdersSearchAndFilterContext.Provider value={value}>
|
|
441
|
+
<Box>{children}</Box>
|
|
442
|
+
</OrdersSearchAndFilterContext.Provider>
|
|
443
|
+
);
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
OrdersSearchAndFilter.Search = OrdersSearchBar;
|
|
447
|
+
OrdersSearchAndFilter.FiltersPanel = OrdersFilterPanel;
|
|
448
|
+
|
|
449
|
+
export default OrdersSearchAndFilter;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Box, Button, TextField } from "@mui/material";
|
|
2
|
+
import { useTranslations } from "next-intl";
|
|
3
|
+
import { useOrdersSearchAndFilter } from "./order-history-search-and-filter";
|
|
4
|
+
import OrdersSearchDropdown from "./order-history-search-dropdown";
|
|
5
|
+
|
|
6
|
+
export type DisplaySearchOption = {
|
|
7
|
+
name: string;
|
|
8
|
+
id: number;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const searchOptions: DisplaySearchOption[] = [
|
|
12
|
+
{ id: 0, name: "Order Number" },
|
|
13
|
+
{ id: 1, name: "Sales ID" },
|
|
14
|
+
{ id: 2, name: "Channel Name" },
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const OrdersSearchBar = () => {
|
|
18
|
+
const {
|
|
19
|
+
setSearchText,
|
|
20
|
+
selectedSearchOption,
|
|
21
|
+
setSelectedSearchOption,
|
|
22
|
+
executeSearch,
|
|
23
|
+
} = useOrdersSearchAndFilter();
|
|
24
|
+
const t = useTranslations("Account.Orders");
|
|
25
|
+
|
|
26
|
+
const findOptionNameById = () => {
|
|
27
|
+
return (
|
|
28
|
+
searchOptions.find((opt) => opt.id === selectedSearchOption)?.name || ""
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const INPUT_PROPS = {
|
|
33
|
+
sx: {
|
|
34
|
+
height: 44,
|
|
35
|
+
padding: 0,
|
|
36
|
+
overflow: "hidden",
|
|
37
|
+
backgroundColor: "grey.200",
|
|
38
|
+
"& .MuiOutlinedInput-notchedOutline": { border: 0 },
|
|
39
|
+
"& .MuiOutlinedInput-input": {
|
|
40
|
+
paddingLeft: 2,
|
|
41
|
+
paddingRight: 2,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
startAdornment: (
|
|
45
|
+
<OrdersSearchDropdown
|
|
46
|
+
title={findOptionNameById()}
|
|
47
|
+
handleChange={setSelectedSearchOption}
|
|
48
|
+
searchOptions={searchOptions}
|
|
49
|
+
/>
|
|
50
|
+
),
|
|
51
|
+
endAdornment: (
|
|
52
|
+
<Button
|
|
53
|
+
onClick={executeSearch}
|
|
54
|
+
color="primary"
|
|
55
|
+
disableElevation
|
|
56
|
+
variant="contained"
|
|
57
|
+
sx={{ px: "3rem", height: "100%", borderRadius: "0 4px 4px 0" }}
|
|
58
|
+
>
|
|
59
|
+
{t("searchBtn")}
|
|
60
|
+
</Button>
|
|
61
|
+
),
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<Box
|
|
66
|
+
position="relative"
|
|
67
|
+
flex="1 1 0"
|
|
68
|
+
maxWidth="670px"
|
|
69
|
+
mx="auto"
|
|
70
|
+
mr="auto"
|
|
71
|
+
ml="0px"
|
|
72
|
+
>
|
|
73
|
+
<TextField
|
|
74
|
+
fullWidth
|
|
75
|
+
variant="outlined"
|
|
76
|
+
placeholder={t("searchOrderHistoryPlaceholder")}
|
|
77
|
+
onChange={(e) => setSearchText(e.target.value)}
|
|
78
|
+
InputProps={INPUT_PROPS}
|
|
79
|
+
/>
|
|
80
|
+
</Box>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default OrdersSearchBar;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Menu from "@evenicanpm/storefront-core/src/components/BazaarMenu";
|
|
4
|
+
import TouchRipple from "@mui/material/ButtonBase";
|
|
5
|
+
import MenuItem from "@mui/material/MenuItem";
|
|
6
|
+
import useTheme from "@mui/material/styles/useTheme";
|
|
7
|
+
import { MdKeyboardArrowDown as KeyboardArrowDownOutlined } from "react-icons/md";
|
|
8
|
+
import type { DisplaySearchOption } from "./order-history-search-bar";
|
|
9
|
+
import { OrdersDropDownHandler } from "./orders-drop-down-handler";
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
title: string;
|
|
13
|
+
handleChange: (optionId: number) => void;
|
|
14
|
+
searchOptions: DisplaySearchOption[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default function OrdersSearchDropdown({
|
|
18
|
+
title,
|
|
19
|
+
handleChange,
|
|
20
|
+
searchOptions,
|
|
21
|
+
}: Props) {
|
|
22
|
+
const { breakpoints } = useTheme();
|
|
23
|
+
const _searchOptions = Array.isArray(searchOptions) ? searchOptions : [];
|
|
24
|
+
const options: DisplaySearchOption[] = [
|
|
25
|
+
..._searchOptions.map((opt) => ({ name: opt?.name ?? "", id: opt.id })),
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<Menu
|
|
30
|
+
direction="right"
|
|
31
|
+
sx={{ zIndex: breakpoints.down("md") ? 99999 : 1502 }}
|
|
32
|
+
handler={(e) => (
|
|
33
|
+
<OrdersDropDownHandler as={TouchRipple} onClick={e}>
|
|
34
|
+
{title}
|
|
35
|
+
<KeyboardArrowDownOutlined fontSize="small" color="inherit" />
|
|
36
|
+
</OrdersDropDownHandler>
|
|
37
|
+
)}
|
|
38
|
+
options={(onClose) => {
|
|
39
|
+
return options.map((item: DisplaySearchOption) => (
|
|
40
|
+
<MenuItem
|
|
41
|
+
key={item.id}
|
|
42
|
+
onClick={() => {
|
|
43
|
+
handleChange(item.id);
|
|
44
|
+
onClose();
|
|
45
|
+
}}
|
|
46
|
+
>
|
|
47
|
+
{item.name}
|
|
48
|
+
</MenuItem>
|
|
49
|
+
));
|
|
50
|
+
}}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { SortColumn } from "@msdyn365-commerce/retail-proxy";
|
|
2
|
+
import { FormControl, InputLabel, MenuItem, Select } from "@mui/material";
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
|
|
6
|
+
interface SortOption {
|
|
7
|
+
id: number;
|
|
8
|
+
label: string;
|
|
9
|
+
columnName: string;
|
|
10
|
+
isDescending: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const sortOptions: SortOption[] = [
|
|
14
|
+
{
|
|
15
|
+
id: 0,
|
|
16
|
+
label: "Newest order placed",
|
|
17
|
+
columnName: "CREATEDDATETIME",
|
|
18
|
+
isDescending: true,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 1,
|
|
22
|
+
label: "Oldest order placed",
|
|
23
|
+
columnName: "CREATEDDATETIME",
|
|
24
|
+
isDescending: false,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 2,
|
|
28
|
+
label: "Newest confirmed ship date",
|
|
29
|
+
columnName: "CONFIRMEDSHIPDATE",
|
|
30
|
+
isDescending: true,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 3,
|
|
34
|
+
label: "Oldest confirmed ship date",
|
|
35
|
+
columnName: "CONFIRMEDSHIPDATE",
|
|
36
|
+
isDescending: false,
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
interface OrderSortProps {
|
|
41
|
+
setSortColumn: (selectedSortColumn: SortColumn) => void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default function OrdersSort({ setSortColumn }: OrderSortProps) {
|
|
45
|
+
const [selectedSort, setSelectedSort] = useState<number>(0);
|
|
46
|
+
const t = useTranslations("Account.Orders");
|
|
47
|
+
|
|
48
|
+
const handleSelectSort = (sortId: number) => {
|
|
49
|
+
const sortColumn = getSortCriteria(sortId);
|
|
50
|
+
setSortColumn(sortColumn);
|
|
51
|
+
setSelectedSort(sortId);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const getSortCriteria = (sortId: number): SortColumn => {
|
|
55
|
+
const sortOption = sortOptions.find((e) => e.id === sortId);
|
|
56
|
+
if (sortOption) {
|
|
57
|
+
return {
|
|
58
|
+
ColumnName: sortOption.columnName,
|
|
59
|
+
IsDescending: sortOption.isDescending,
|
|
60
|
+
};
|
|
61
|
+
} else {
|
|
62
|
+
return {
|
|
63
|
+
ColumnName: "CREATEDDATETIME",
|
|
64
|
+
IsDescending: false,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<FormControl
|
|
71
|
+
fullWidth
|
|
72
|
+
size="small"
|
|
73
|
+
sx={{ maxWidth: 240, backgroundColor: "#fff" }}
|
|
74
|
+
>
|
|
75
|
+
<InputLabel id="orders-sort-label">{t("sortByLabel")}</InputLabel>
|
|
76
|
+
<Select
|
|
77
|
+
labelId="orders-sort-label"
|
|
78
|
+
value={selectedSort}
|
|
79
|
+
label={t("sortByLabel")}
|
|
80
|
+
onChange={(e) => handleSelectSort(Number(e.target.value))}
|
|
81
|
+
>
|
|
82
|
+
{sortOptions.map((option) => (
|
|
83
|
+
<MenuItem key={option.id} value={option.id}>
|
|
84
|
+
{option.label}
|
|
85
|
+
</MenuItem>
|
|
86
|
+
))}
|
|
87
|
+
</Select>
|
|
88
|
+
</FormControl>
|
|
89
|
+
);
|
|
90
|
+
}
|