orionplace-react 0.7.7 → 0.7.8
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orionplace-react",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.8",
|
|
4
4
|
"description": "Next.js 16 storefront for your Orionplace marketplace: setup CLI, server API gateway, typed store and ready-made catalog, cart, checkout and account components in English, Russian and Spanish, plus an Android app build.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -404,8 +404,8 @@ export function OrderDetails({
|
|
|
404
404
|
{step.current ? (
|
|
405
405
|
<span className={styles.stepState}>{labels.stepStates.current}</span>
|
|
406
406
|
) : null}
|
|
407
|
-
{step.
|
|
408
|
-
<span className={styles.stepNote}>{
|
|
407
|
+
{step.date ? (
|
|
408
|
+
<span className={styles.stepNote}>{step.date}</span>
|
|
409
409
|
) : step.total > 1 ? (
|
|
410
410
|
<span className={styles.stepNote}>
|
|
411
411
|
{step.count} {labels.of} {step.total}
|
|
@@ -29,6 +29,17 @@ import { apiErrorMessage, OrionplaceApiError } from '@/lib/orionplace/api';
|
|
|
29
29
|
import { useOrionplaceApi } from '@/hooks/useOrionplaceApi';
|
|
30
30
|
import { productAvailability } from '@/lib/orionplace/productAvailability';
|
|
31
31
|
import { productActionLabels } from '@/lib/orionplace/productActionLabels';
|
|
32
|
+
|
|
33
|
+
// What a finished product action did: a key of productActionLabels.
|
|
34
|
+
type ProductActionDone =
|
|
35
|
+
| 'cartAdded'
|
|
36
|
+
| 'cartUpdated'
|
|
37
|
+
| 'cartRemoved'
|
|
38
|
+
| 'favoriteAdded'
|
|
39
|
+
| 'favoriteRemoved'
|
|
40
|
+
| 'subscribed'
|
|
41
|
+
| 'unsubscribed'
|
|
42
|
+
| 'preorderSaved';
|
|
32
43
|
import styles from '@/app/storefront.module.css';
|
|
33
44
|
import { signInPageLocation } from '@/lib/orionplace/signInReturn';
|
|
34
45
|
|
|
@@ -236,7 +247,11 @@ export function OrionplaceStorefrontProvider({
|
|
|
236
247
|
}
|
|
237
248
|
}
|
|
238
249
|
|
|
239
|
-
|
|
250
|
+
// done names what the action did, for the notice («Added to the cart.»).
|
|
251
|
+
async function action(
|
|
252
|
+
work: () => Promise<void>,
|
|
253
|
+
done: ProductActionDone,
|
|
254
|
+
) {
|
|
240
255
|
if (actionPending.current) {
|
|
241
256
|
return;
|
|
242
257
|
}
|
|
@@ -248,7 +263,7 @@ export function OrionplaceStorefrontProvider({
|
|
|
248
263
|
setMessageSignIn(false);
|
|
249
264
|
try {
|
|
250
265
|
await work();
|
|
251
|
-
setMessage(copy
|
|
266
|
+
setMessage(copy[done]);
|
|
252
267
|
} catch (reason) {
|
|
253
268
|
if (
|
|
254
269
|
reason instanceof OrionplaceApiError &&
|
|
@@ -399,9 +414,12 @@ export function OrionplaceStorefrontProvider({
|
|
|
399
414
|
} finally {
|
|
400
415
|
products.getState().setPendingFavorite(selection.product);
|
|
401
416
|
}
|
|
402
|
-
});
|
|
417
|
+
}, selection.selected ? 'favoriteAdded' : 'favoriteRemoved');
|
|
403
418
|
},
|
|
404
419
|
onAdd(selection) {
|
|
420
|
+
// Adding a product already in the cart sets its new quantity.
|
|
421
|
+
const inCart = Boolean(product(selection.product)?.cart_counts?.cart_id);
|
|
422
|
+
|
|
405
423
|
void action(async () => {
|
|
406
424
|
const data = await api.request<{
|
|
407
425
|
cart_item_id?: number;
|
|
@@ -431,7 +449,7 @@ export function OrionplaceStorefrontProvider({
|
|
|
431
449
|
c_cart_positions: data.c_cart_positions,
|
|
432
450
|
});
|
|
433
451
|
}
|
|
434
|
-
});
|
|
452
|
+
}, inCart ? 'cartUpdated' : 'cartAdded');
|
|
435
453
|
},
|
|
436
454
|
onRemove(selection) {
|
|
437
455
|
void action(async () => {
|
|
@@ -446,7 +464,7 @@ export function OrionplaceStorefrontProvider({
|
|
|
446
464
|
c_cart_positions: data.c_cart_positions,
|
|
447
465
|
});
|
|
448
466
|
}
|
|
449
|
-
});
|
|
467
|
+
}, 'cartRemoved');
|
|
450
468
|
},
|
|
451
469
|
onSubscribe(selection) {
|
|
452
470
|
if (!init?.is_authenticated) {
|
|
@@ -465,7 +483,7 @@ export function OrionplaceStorefrontProvider({
|
|
|
465
483
|
});
|
|
466
484
|
await refreshNotificationState();
|
|
467
485
|
await refreshProduct(selection.product);
|
|
468
|
-
});
|
|
486
|
+
}, 'subscribed');
|
|
469
487
|
},
|
|
470
488
|
onUnsubscribe(selection) {
|
|
471
489
|
if (!init?.is_authenticated) {
|
|
@@ -477,7 +495,7 @@ export function OrionplaceStorefrontProvider({
|
|
|
477
495
|
product_id: selection.product,
|
|
478
496
|
});
|
|
479
497
|
await refreshProduct(selection.product);
|
|
480
|
-
});
|
|
498
|
+
}, 'unsubscribed');
|
|
481
499
|
},
|
|
482
500
|
onPreorder(selection) {
|
|
483
501
|
const current = product(selection.product);
|
|
@@ -511,7 +529,7 @@ export function OrionplaceStorefrontProvider({
|
|
|
511
529
|
}
|
|
512
530
|
await api.request('preorders_create', body);
|
|
513
531
|
setPreorder(undefined);
|
|
514
|
-
});
|
|
532
|
+
}, 'preorderSaved');
|
|
515
533
|
},
|
|
516
534
|
closePreorder() {
|
|
517
535
|
setPreorder(undefined);
|
|
@@ -59,6 +59,8 @@ export interface OrderStep {
|
|
|
59
59
|
current: boolean;
|
|
60
60
|
count: number;
|
|
61
61
|
total: number;
|
|
62
|
+
/** The date the step was reached ('' until then); see orderStepDate. */
|
|
63
|
+
date?: string;
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
export type OrderShipmentStateKey =
|
|
@@ -228,7 +230,39 @@ export function orderCanCancel(
|
|
|
228
230
|
}
|
|
229
231
|
|
|
230
232
|
// Canceled and returned orders have no delivery progress to show.
|
|
231
|
-
|
|
233
|
+
// The date a step was reached: the order's own date for Placed; for Shipped
|
|
234
|
+
// and Delivered, once every shipment got there, the latest of their sent
|
|
235
|
+
// (or delivery) dates; '' while a shipment has not reached the step.
|
|
236
|
+
export function orderStepDate(
|
|
237
|
+
order: OrderShipmentCounters & Partial<Pick<IOrder, 'time_submited'>>,
|
|
238
|
+
key: OrderStepKey,
|
|
239
|
+
): string {
|
|
240
|
+
if (key === 'placed') {
|
|
241
|
+
return order.time_submited?.date ?? '';
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const shipments = order.delivery_froms ?? [];
|
|
245
|
+
const reached = shipments.map((shipment) =>
|
|
246
|
+
key === 'shipped'
|
|
247
|
+
? shipment.dates?.sent || shipment.dates?.delivery_date_real
|
|
248
|
+
: shipment.dates?.delivery_date_real,
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
if (!reached.length || reached.some((date) => !date?.date)) {
|
|
252
|
+
return '';
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// The latest is the one the fewest minutes ago (any date format).
|
|
256
|
+
const latest = reached.reduce((best, date) =>
|
|
257
|
+
Number(date?.minutes_ago ?? 0) < Number(best?.minutes_ago ?? 0) ? date : best,
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
return latest?.date ?? '';
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export function orderSteps(
|
|
264
|
+
order: OrderShipmentCounters & Partial<Pick<IOrder, 'time_submited'>>,
|
|
265
|
+
): OrderStep[] {
|
|
232
266
|
const { total, sent, delivered } = orderShipmentProgress(order);
|
|
233
267
|
|
|
234
268
|
if (
|
|
@@ -239,13 +273,21 @@ export function orderSteps(order: OrderShipmentCounters): OrderStep[] {
|
|
|
239
273
|
}
|
|
240
274
|
|
|
241
275
|
const steps = [
|
|
242
|
-
{
|
|
276
|
+
{
|
|
277
|
+
key: 'placed',
|
|
278
|
+
state: 'done',
|
|
279
|
+
current: false,
|
|
280
|
+
count: 1,
|
|
281
|
+
total: 1,
|
|
282
|
+
date: orderStepDate(order, 'placed'),
|
|
283
|
+
},
|
|
243
284
|
{
|
|
244
285
|
key: 'shipped',
|
|
245
286
|
state: stepState(sent, total),
|
|
246
287
|
current: false,
|
|
247
288
|
count: sent,
|
|
248
289
|
total,
|
|
290
|
+
date: orderStepDate(order, 'shipped'),
|
|
249
291
|
},
|
|
250
292
|
{
|
|
251
293
|
key: 'delivered',
|
|
@@ -253,6 +295,7 @@ export function orderSteps(order: OrderShipmentCounters): OrderStep[] {
|
|
|
253
295
|
current: false,
|
|
254
296
|
count: delivered,
|
|
255
297
|
total,
|
|
298
|
+
date: orderStepDate(order, 'delivered'),
|
|
256
299
|
},
|
|
257
300
|
] satisfies OrderStep[];
|
|
258
301
|
|
|
@@ -4,6 +4,13 @@ const labels = {
|
|
|
4
4
|
en: {
|
|
5
5
|
pending: 'Updating product…',
|
|
6
6
|
saved: 'Product updated.',
|
|
7
|
+
cartAdded: 'Added to the cart.',
|
|
8
|
+
cartUpdated: 'Cart quantity updated.',
|
|
9
|
+
cartRemoved: 'Removed from the cart.',
|
|
10
|
+
favoriteAdded: 'Saved to favorites.',
|
|
11
|
+
favoriteRemoved: 'Removed from favorites.',
|
|
12
|
+
subscribed: 'We will let you know when it is back in stock.',
|
|
13
|
+
unsubscribed: 'Availability notification turned off.',
|
|
7
14
|
failed: 'The request failed. Your confirmed product data is unchanged.',
|
|
8
15
|
unavailable: 'This action is unavailable.',
|
|
9
16
|
login: 'Sign in to save favorites or availability notifications.',
|
|
@@ -31,6 +38,13 @@ const labels = {
|
|
|
31
38
|
ru: {
|
|
32
39
|
pending: 'Обновляем товар…',
|
|
33
40
|
saved: 'Товар обновлён.',
|
|
41
|
+
cartAdded: 'Товар добавлен в корзину.',
|
|
42
|
+
cartUpdated: 'Количество в корзине изменено.',
|
|
43
|
+
cartRemoved: 'Товар удалён из корзины.',
|
|
44
|
+
favoriteAdded: 'Товар добавлен в избранное.',
|
|
45
|
+
favoriteRemoved: 'Товар удалён из избранного.',
|
|
46
|
+
subscribed: 'Сообщим, когда товар появится в наличии.',
|
|
47
|
+
unsubscribed: 'Уведомление о наличии отключено.',
|
|
34
48
|
failed: 'Запрос не выполнен. Подтверждённые данные товара сохранены.',
|
|
35
49
|
unavailable: 'Это действие недоступно.',
|
|
36
50
|
login: 'Войдите, чтобы сохранять избранное и уведомления о наличии.',
|
|
@@ -56,6 +70,13 @@ const labels = {
|
|
|
56
70
|
es: {
|
|
57
71
|
pending: 'Actualizando el producto…',
|
|
58
72
|
saved: 'Producto actualizado.',
|
|
73
|
+
cartAdded: 'Añadido al carrito.',
|
|
74
|
+
cartUpdated: 'Cantidad del carrito actualizada.',
|
|
75
|
+
cartRemoved: 'Eliminado del carrito.',
|
|
76
|
+
favoriteAdded: 'Guardado en favoritos.',
|
|
77
|
+
favoriteRemoved: 'Eliminado de favoritos.',
|
|
78
|
+
subscribed: 'Te avisaremos cuando vuelva a estar disponible.',
|
|
79
|
+
unsubscribed: 'Aviso de disponibilidad desactivado.',
|
|
59
80
|
failed: 'La solicitud ha fallado. Los datos confirmados del producto no han cambiado.',
|
|
60
81
|
unavailable: 'Esta acción no está disponible.',
|
|
61
82
|
login: 'Inicia sesión para guardar favoritos o avisos de disponibilidad.',
|