@mohasinac/appkit 4.23.0 → 4.25.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/dist/_internal/server/features/bundles/actions.js +7 -0
- package/dist/_internal/server/features/checkout/actions.js +30 -21
- package/dist/_internal/server/features/checkout/bundle-expansion.d.ts +4 -6
- package/dist/_internal/server/features/checkout/bundle-expansion.js +19 -17
- package/dist/_internal/server/features/checkout/stock-restore.js +6 -5
- package/dist/_internal/server/features/products/group-members.d.ts +55 -0
- package/dist/_internal/server/features/products/group-members.js +41 -0
- package/dist/_internal/shared/actions/action-registry.js +41 -0
- package/dist/_internal/shared/checkout/line-members.d.ts +60 -0
- package/dist/_internal/shared/checkout/line-members.js +68 -0
- package/dist/_internal/shared/checkout/order-math.d.ts +23 -5
- package/dist/_internal/shared/checkout/order-math.js +29 -6
- package/dist/_internal/shared/checkout/rules/_limits.d.ts +12 -2
- package/dist/_internal/shared/checkout/rules/_limits.js +12 -2
- package/dist/_internal/shared/checkout/rules/_registry.js +8 -2
- package/dist/_internal/shared/features/cart/config.d.ts +2 -1
- package/dist/_internal/shared/features/cart/config.js +2 -1
- package/dist/_internal/shared/features/cart/schema.d.ts +6 -6
- package/dist/_internal/shared/features/notifications/action-url.d.ts +47 -0
- package/dist/_internal/shared/features/notifications/action-url.js +111 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +1 -0
- package/dist/constants/api-endpoints.d.ts +27 -0
- package/dist/constants/api-endpoints.js +9 -0
- package/dist/constants/notification-types.d.ts +38 -10
- package/dist/constants/notification-types.js +39 -28
- package/dist/features/account/types/index.d.ts +1 -10
- package/dist/features/admin/actions/notification-actions.d.ts +12 -0
- package/dist/features/admin/actions/notification-actions.js +149 -4
- package/dist/features/admin/schemas/firestore.d.ts +24 -29
- package/dist/features/admin/schemas/firestore.js +89 -26
- package/dist/features/cart/actions/cart-actions.d.ts +47 -1
- package/dist/features/cart/actions/cart-actions.js +189 -12
- package/dist/features/cart/components/CartDrawer.js +2 -1
- package/dist/features/cart/repository/cart.repository.d.ts +33 -1
- package/dist/features/cart/repository/cart.repository.js +146 -2
- package/dist/features/cart/schemas/firestore.d.ts +132 -8
- package/dist/features/cart/schemas/firestore.js +22 -0
- package/dist/features/email/index.d.ts +1 -1
- package/dist/features/email/index.js +5 -1
- package/dist/features/email/notification-templates.d.ts +35 -0
- package/dist/features/email/notification-templates.js +96 -0
- package/dist/features/orders/schemas/index.d.ts +2 -2
- package/dist/features/products/components/GroupMemberPicker.d.ts +46 -0
- package/dist/features/products/components/GroupMemberPicker.js +108 -0
- package/dist/features/products/components/ShowGroupSection.js +7 -9
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +30 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +7 -1
- package/dist/schemas/registry.d.ts +2 -2
- package/dist/seed/factories/notification.factory.d.ts +2 -1
- package/dist/seed/notifications-seed-data.js +225 -0
- package/dist/seed/payouts-seed-data.js +47 -0
- package/dist/seed/scammers-seed-data.js +34 -0
- package/dist/seed/stores-seed-data.js +34 -0
- package/dist/seed/support-tickets-seed-data.js +34 -0
- package/dist/server-entry.d.ts +1 -0
- package/dist/server-entry.js +5 -0
- package/dist/server.d.ts +5 -1
- package/dist/server.js +10 -1
- package/dist/styles.css +58 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/QuantityStepper.d.ts +38 -0
- package/dist/ui/components/QuantityStepper.js +20 -0
- package/dist/ui/components/QuantityStepper.style.css +56 -0
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +1 -0
- package/package.json +1 -1
|
@@ -355,4 +355,229 @@ export const notificationsSeedData = [
|
|
|
355
355
|
createdAt: daysAgo(60),
|
|
356
356
|
updatedAt: daysAgo(60),
|
|
357
357
|
},
|
|
358
|
+
// -- Coverage fixtures (16) - one per type that had none --------------------
|
|
359
|
+
//
|
|
360
|
+
// W19 gave every notification type a real email template and a resolved
|
|
361
|
+
// actionUrl. 12 of the 28 types were seeded; the other 16 had NO fixture at
|
|
362
|
+
// all, so a tester could not see 16 of those templates or verify where they
|
|
363
|
+
// land. Each of these exists to make one template and one resolver branch
|
|
364
|
+
// reachable.
|
|
365
|
+
//
|
|
366
|
+
// `actionUrl` is deliberately OMITTED on all but one: it is now resolved
|
|
367
|
+
// from relatedType + relatedId inside sendNotification, and hardcoding one
|
|
368
|
+
// here would seed data that no longer matches what the code produces -
|
|
369
|
+
// which is exactly how a fixture stops being a test.
|
|
370
|
+
{
|
|
371
|
+
id: "notif-yugi-order-confirmed-001",
|
|
372
|
+
userId: "user-yugi-muto",
|
|
373
|
+
type: NOTIFICATION_FIELDS.TYPE_VALUES.ORDER_CONFIRMED,
|
|
374
|
+
priority: "normal",
|
|
375
|
+
title: "Order confirmed",
|
|
376
|
+
message: "Your order is confirmed and the seller is preparing it for dispatch.",
|
|
377
|
+
isRead: false,
|
|
378
|
+
relatedId: "order-2-20260819-a1b2c3",
|
|
379
|
+
relatedType: "order",
|
|
380
|
+
createdAt: daysAgo(3),
|
|
381
|
+
updatedAt: daysAgo(3),
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
id: "notif-yugi-bid-lost-001",
|
|
385
|
+
userId: "user-yugi-muto",
|
|
386
|
+
type: NOTIFICATION_FIELDS.TYPE_VALUES.BID_LOST,
|
|
387
|
+
priority: "normal",
|
|
388
|
+
title: "This auction closed without you",
|
|
389
|
+
message: "Beyblade Original Dragoon Storm sold to another bidder at Rs 5,200.",
|
|
390
|
+
isRead: false,
|
|
391
|
+
relatedId: "auction-beyblade-original-dragoon-storm",
|
|
392
|
+
relatedType: "bid",
|
|
393
|
+
createdAt: daysAgo(2),
|
|
394
|
+
updatedAt: daysAgo(2),
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
// Seller-facing - the ONE fixture proving TYPE_AUDIENCE routes auction_ended to /store rather than /user.
|
|
398
|
+
id: "notif-tyson-auction-ended-001",
|
|
399
|
+
userId: "user-tyson-blader",
|
|
400
|
+
type: NOTIFICATION_FIELDS.TYPE_VALUES.AUCTION_ENDED,
|
|
401
|
+
priority: "normal",
|
|
402
|
+
title: "Your auction ended with no winner",
|
|
403
|
+
message: "The highest bid did not meet your reserve price. The listing has been archived.",
|
|
404
|
+
isRead: false,
|
|
405
|
+
relatedId: "auction-beyblade-lord-spryzen-ended-unsold",
|
|
406
|
+
relatedType: "product",
|
|
407
|
+
createdAt: daysAgo(1),
|
|
408
|
+
updatedAt: daysAgo(1),
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
id: "notif-yugi-review-replied-001",
|
|
412
|
+
userId: "user-yugi-muto",
|
|
413
|
+
type: NOTIFICATION_FIELDS.TYPE_VALUES.REVIEW_REPLIED,
|
|
414
|
+
priority: "low",
|
|
415
|
+
title: "The seller replied to your review",
|
|
416
|
+
message: "Beyblade Arena responded to your review of Beyblade X Dran Sword.",
|
|
417
|
+
isRead: false,
|
|
418
|
+
relatedId: "review-dran-sword-yugi-20260819",
|
|
419
|
+
relatedType: "review",
|
|
420
|
+
createdAt: daysAgo(5),
|
|
421
|
+
updatedAt: daysAgo(5),
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
// No relatedId on purpose - this fixture proves a promotion renders WITHOUT a CTA button rather than linking to the dashboard the reader is already on.
|
|
425
|
+
id: "notif-yugi-promotion-001",
|
|
426
|
+
userId: "user-yugi-muto",
|
|
427
|
+
type: NOTIFICATION_FIELDS.TYPE_VALUES.PROMOTION,
|
|
428
|
+
priority: "low",
|
|
429
|
+
title: "25% off at Beyblade Arena",
|
|
430
|
+
message: "Use code ARENA25 on your next order. Ends Sunday.",
|
|
431
|
+
isRead: false,
|
|
432
|
+
createdAt: hoursAgo(20),
|
|
433
|
+
updatedAt: hoursAgo(20),
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
id: "notif-yugi-account-action-001",
|
|
437
|
+
userId: "user-yugi-muto",
|
|
438
|
+
type: NOTIFICATION_FIELDS.TYPE_VALUES.ACCOUNT_ACTION,
|
|
439
|
+
priority: "high",
|
|
440
|
+
title: "Your account details were updated",
|
|
441
|
+
message: "Your delivery address was changed. If this wasn't you, contact support immediately.",
|
|
442
|
+
isRead: false,
|
|
443
|
+
relatedId: "user-yugi-muto",
|
|
444
|
+
relatedType: "user",
|
|
445
|
+
createdAt: daysAgo(6),
|
|
446
|
+
updatedAt: daysAgo(6),
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
id: "notif-yugi-offer-responded-001",
|
|
450
|
+
userId: "user-yugi-muto",
|
|
451
|
+
type: NOTIFICATION_FIELDS.TYPE_VALUES.OFFER_RESPONDED,
|
|
452
|
+
priority: "high",
|
|
453
|
+
title: "The seller countered your offer",
|
|
454
|
+
message: "Beyblade Arena countered at Rs 3,800 on Metal Storm Pegasus.",
|
|
455
|
+
isRead: false,
|
|
456
|
+
relatedId: "offer-metal-storm-pegasus-yugi-20260819-countered",
|
|
457
|
+
relatedType: "offer",
|
|
458
|
+
createdAt: daysAgo(2),
|
|
459
|
+
updatedAt: daysAgo(2),
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
id: "notif-yugi-offer-expired-001",
|
|
463
|
+
userId: "user-yugi-muto",
|
|
464
|
+
type: NOTIFICATION_FIELDS.TYPE_VALUES.OFFER_EXPIRED,
|
|
465
|
+
priority: "normal",
|
|
466
|
+
title: "Your offer expired",
|
|
467
|
+
message: "The seller did not respond within 48 hours. You can make a new offer.",
|
|
468
|
+
isRead: false,
|
|
469
|
+
relatedId: "offer-kaiba-x-wizard-arrow-expired",
|
|
470
|
+
relatedType: "offer",
|
|
471
|
+
createdAt: daysAgo(8),
|
|
472
|
+
updatedAt: daysAgo(8),
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
id: "notif-yugi-offer-counter-accepted-001",
|
|
476
|
+
userId: "user-yugi-muto",
|
|
477
|
+
type: NOTIFICATION_FIELDS.TYPE_VALUES.OFFER_COUNTER_ACCEPTED,
|
|
478
|
+
priority: "high",
|
|
479
|
+
title: "Your counter-offer was accepted",
|
|
480
|
+
message: "Complete checkout within 24 hours to secure it at Rs 3,500.",
|
|
481
|
+
isRead: false,
|
|
482
|
+
relatedId: "offer-metal-storm-pegasus-yugi-20260819-countered",
|
|
483
|
+
relatedType: "offer",
|
|
484
|
+
createdAt: hoursAgo(6),
|
|
485
|
+
updatedAt: hoursAgo(6),
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
id: "notif-yugi-refund-initiated-001",
|
|
489
|
+
userId: "user-yugi-muto",
|
|
490
|
+
type: NOTIFICATION_FIELDS.TYPE_VALUES.REFUND_INITIATED,
|
|
491
|
+
priority: "high",
|
|
492
|
+
title: "Refund on its way",
|
|
493
|
+
message: "Rs 1,499 is being returned to your original payment method. Allow 5-7 working days.",
|
|
494
|
+
isRead: false,
|
|
495
|
+
relatedId: "order-1-20260812-r3f4nd",
|
|
496
|
+
relatedType: "order",
|
|
497
|
+
createdAt: daysAgo(4),
|
|
498
|
+
updatedAt: daysAgo(4),
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
id: "notif-yugi-prize-won-001",
|
|
502
|
+
userId: "user-yugi-muto",
|
|
503
|
+
type: NOTIFICATION_FIELDS.TYPE_VALUES.PRIZE_WON,
|
|
504
|
+
priority: "high",
|
|
505
|
+
title: "You won a prize",
|
|
506
|
+
message: "Your entry in the Beyblade Vault draw won slot #14. Reveal it within 7 days.",
|
|
507
|
+
isRead: false,
|
|
508
|
+
relatedId: "prizedraw-beyblade-vault-closed-pending-reveal",
|
|
509
|
+
relatedType: "product",
|
|
510
|
+
createdAt: daysAgo(1),
|
|
511
|
+
updatedAt: daysAgo(1),
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
id: "notif-kaiba-prize-reveal-expired-001",
|
|
515
|
+
userId: "user-seto-kaiba",
|
|
516
|
+
type: NOTIFICATION_FIELDS.TYPE_VALUES.PRIZE_REVEAL_EXPIRED,
|
|
517
|
+
priority: "normal",
|
|
518
|
+
title: "Your prize reveal window closed",
|
|
519
|
+
message: "The 7-day reveal window passed and your entry was refunded.",
|
|
520
|
+
isRead: false,
|
|
521
|
+
relatedId: "prizedraw-beyblade-vault-closed-pending-reveal",
|
|
522
|
+
relatedType: "product",
|
|
523
|
+
createdAt: daysAgo(10),
|
|
524
|
+
updatedAt: daysAgo(10),
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
id: "notif-yugi-emi-due-soon-001",
|
|
528
|
+
userId: "user-yugi-muto",
|
|
529
|
+
type: NOTIFICATION_FIELDS.TYPE_VALUES.EMI_INSTALLMENT_DUE_SOON,
|
|
530
|
+
priority: "normal",
|
|
531
|
+
title: "EMI installment due in 3 days",
|
|
532
|
+
message: "Installment 2 of 6 (Rs 1,250) is due on the 29th.",
|
|
533
|
+
isRead: false,
|
|
534
|
+
relatedId: "order-3-20260801-em1pay",
|
|
535
|
+
relatedType: "order",
|
|
536
|
+
createdAt: hoursAgo(12),
|
|
537
|
+
updatedAt: hoursAgo(12),
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
id: "notif-kaiba-emi-overdue-001",
|
|
541
|
+
userId: "user-seto-kaiba",
|
|
542
|
+
type: NOTIFICATION_FIELDS.TYPE_VALUES.EMI_INSTALLMENT_OVERDUE,
|
|
543
|
+
priority: "high",
|
|
544
|
+
title: "EMI installment overdue",
|
|
545
|
+
message: "Installment 3 of 6 (Rs 1,250) was due on the 20th. Pay now to avoid a late fee.",
|
|
546
|
+
isRead: false,
|
|
547
|
+
relatedId: "order-3-20260801-em1pay",
|
|
548
|
+
relatedType: "order",
|
|
549
|
+
createdAt: daysAgo(5),
|
|
550
|
+
updatedAt: daysAgo(5),
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
id: "notif-tester-catalogue-stale-001",
|
|
554
|
+
userId: "user-tester-qa",
|
|
555
|
+
type: NOTIFICATION_FIELDS.TYPE_VALUES.CATALOGUE_IMAGES_STALE,
|
|
556
|
+
priority: "normal",
|
|
557
|
+
title: "Refresh your catalogue photos",
|
|
558
|
+
message: "Photos for your vintage Dragoon are getting old - refresh them within 14 days or you won't be able to list it.",
|
|
559
|
+
isRead: false,
|
|
560
|
+
relatedId: "mycatalog-user-tester-qa-vintage-dragoon",
|
|
561
|
+
relatedType: "catalogueItem",
|
|
562
|
+
createdAt: daysAgo(2),
|
|
563
|
+
updatedAt: daysAgo(2),
|
|
564
|
+
},
|
|
565
|
+
{
|
|
566
|
+
// The ONE fixture keeping an explicit actionUrl, and it earns it: the buyer
|
|
567
|
+
// must land on the proof-UPLOAD page, which relatedType:"order" cannot
|
|
568
|
+
// express. This is the case the caller-wins rule exists for.
|
|
569
|
+
id: "notif-yugi-payment-review-001",
|
|
570
|
+
userId: "user-yugi-muto",
|
|
571
|
+
type: NOTIFICATION_FIELDS.TYPE_VALUES.PAYMENT_REVIEW,
|
|
572
|
+
priority: "high",
|
|
573
|
+
title: "Please re-upload your payment proof",
|
|
574
|
+
message: "The screenshot you uploaded was unreadable. Upload a clearer one within 2 hours.",
|
|
575
|
+
isRead: false,
|
|
576
|
+
actionUrl: "/user/orders/order-4-20260825-upiman/payment",
|
|
577
|
+
actionLabel: "Re-upload proof",
|
|
578
|
+
relatedId: "order-4-20260825-upiman",
|
|
579
|
+
relatedType: "order",
|
|
580
|
+
createdAt: hoursAgo(1),
|
|
581
|
+
updatedAt: hoursAgo(1),
|
|
582
|
+
},
|
|
358
583
|
];
|
|
@@ -17,6 +17,17 @@ const _CURRENCY = getDefaultCurrency();
|
|
|
17
17
|
import { PAYOUT_FIELDS, DEFAULT_PLATFORM_FEE_RATE, } from "../features/payments/schemas";
|
|
18
18
|
const NOW = new Date();
|
|
19
19
|
const daysAgo = (n) => new Date(NOW.getTime() - n * 86400000);
|
|
20
|
+
/**
|
|
21
|
+
* A payout timeline entry, for the W18 `statusHistory` fixtures below.
|
|
22
|
+
*
|
|
23
|
+
* Dates come from the file's own `daysAgo()` helper, never `Date.now()` at
|
|
24
|
+
* import time in an ID position — Root Cause #25. `actorUid` only, never a
|
|
25
|
+
* name: `withHistory` scrubs PII from real writes and a fixture that carried
|
|
26
|
+
* a name would be the one place the rule is broken.
|
|
27
|
+
*/
|
|
28
|
+
function entry(at, actorRole, trigger, changes, extra) {
|
|
29
|
+
return { at, actorRole, trigger, changes, ...extra };
|
|
30
|
+
}
|
|
20
31
|
function payoutAmounts(grossINR) {
|
|
21
32
|
const grossAmount = grossINR;
|
|
22
33
|
const platformFee = Math.round(grossAmount * DEFAULT_PLATFORM_FEE_RATE * 100) / 100;
|
|
@@ -32,6 +43,20 @@ export const payoutsSeedData = [
|
|
|
32
43
|
// 1. Beyblade Arena — Jan 2026
|
|
33
44
|
{
|
|
34
45
|
id: "payout-beyblade-arena-jan-2026-completed",
|
|
46
|
+
/*
|
|
47
|
+
* Carries the transactionId AND the timeline entry recording it. Until
|
|
48
|
+
* 2026-08-26 `transactionId` was absent from
|
|
49
|
+
* PAYOUT_ADMIN_UPDATEABLE_FIELDS, so every real payout marked paid was
|
|
50
|
+
* stored without the reference the modal required. A fixture that
|
|
51
|
+
* omitted it would have kept agreeing with the bug.
|
|
52
|
+
*/
|
|
53
|
+
transactionId: "UTR2026011500042",
|
|
54
|
+
statusHistory: [
|
|
55
|
+
entry(daysAgo(150), "admin", "adminUpdatePayout", {
|
|
56
|
+
status: { from: "processing", to: "paid" },
|
|
57
|
+
transactionId: { from: null, to: "UTR2026011500042" },
|
|
58
|
+
}, { actorUid: "user-admin-letitrip" }),
|
|
59
|
+
],
|
|
35
60
|
storeId: "store-beyblade-arena",
|
|
36
61
|
sellerName: "Beyblade Arena",
|
|
37
62
|
sellerEmail: "tyson@beybladearena.in",
|
|
@@ -231,6 +256,22 @@ export const payoutsSeedData = [
|
|
|
231
256
|
processedAt: daysAgo(153),
|
|
232
257
|
createdAt: daysAgo(155),
|
|
233
258
|
updatedAt: daysAgo(153),
|
|
259
|
+
/*
|
|
260
|
+
* TWO failed attempts, and that is the point. `lastFailureReason` holds
|
|
261
|
+
* only the second one; the timeline is the only place the first survives.
|
|
262
|
+
* This fixture is what the "a payout that failed twice shows BOTH
|
|
263
|
+
* reasons" tester case reads.
|
|
264
|
+
*/
|
|
265
|
+
statusHistory: [
|
|
266
|
+
entry(daysAgo(154), "system", "payoutBatch:dispatch", {
|
|
267
|
+
status: { from: "pending", to: "processing" },
|
|
268
|
+
}),
|
|
269
|
+
entry(daysAgo(154), "system", "payoutBatch:failedRetrying", { status: { from: "processing", to: "pending" }, lastFailureReason: { from: null, to: "Razorpay 400: beneficiary bank returned NEFT rejection" } }, { reason: "Razorpay 400: beneficiary bank returned NEFT rejection" }),
|
|
270
|
+
entry(daysAgo(153), "system", "payoutBatch:dispatch", {
|
|
271
|
+
status: { from: "pending", to: "processing" },
|
|
272
|
+
}),
|
|
273
|
+
entry(daysAgo(153), "system", "payoutBatch:failedFinal", { status: { from: "processing", to: "failed" }, lastFailureReason: { from: "Razorpay 400: beneficiary bank returned NEFT rejection", to: "Razorpay 400: IFSC code mismatch" } }, { reason: "Razorpay 400: IFSC code mismatch" }),
|
|
274
|
+
],
|
|
234
275
|
},
|
|
235
276
|
// 10. Beyblade Arena — Jan 2026 — UPI limit exceeded
|
|
236
277
|
{
|
|
@@ -251,5 +292,11 @@ export const payoutsSeedData = [
|
|
|
251
292
|
processedAt: daysAgo(108),
|
|
252
293
|
createdAt: daysAgo(110),
|
|
253
294
|
updatedAt: daysAgo(108),
|
|
295
|
+
statusHistory: [
|
|
296
|
+
entry(daysAgo(109), "system", "payoutBatch:dispatch", {
|
|
297
|
+
status: { from: "pending", to: "processing" },
|
|
298
|
+
}),
|
|
299
|
+
entry(daysAgo(108), "system", "payoutBatch:failedFinal", { status: { from: "processing", to: "failed" }, lastFailureReason: { from: null, to: "Razorpay 400: UPI daily limit exceeded" } }, { reason: "Razorpay 400: UPI daily limit exceeded" }),
|
|
300
|
+
],
|
|
254
301
|
},
|
|
255
302
|
];
|
|
@@ -21,6 +21,17 @@ import { SCAMMER_FIELDS } from "../constants/field-names";
|
|
|
21
21
|
import { seedExtMedia } from "./_helpers/media";
|
|
22
22
|
const NOW = new Date("2026-05-10T00:00:00.000Z");
|
|
23
23
|
const daysAgo = (n) => new Date(NOW.getTime() - n * 86400000);
|
|
24
|
+
/**
|
|
25
|
+
* A timeline entry for the W18 `statusHistory` fixtures.
|
|
26
|
+
*
|
|
27
|
+
* 🛑 `actorUid` only. A scammer profile is built ENTIRELY out of identifying
|
|
28
|
+
* details, which is why `SCAMMER_HISTORY_PII_FIELDS` is the longest scrub
|
|
29
|
+
* list of any adopter — a fixture carrying a name here would be modelling the
|
|
30
|
+
* exact leak the primitive exists to prevent.
|
|
31
|
+
*/
|
|
32
|
+
function entry(at, actorRole, trigger, changes, extra) {
|
|
33
|
+
return { at, actorRole, trigger, changes, ...extra };
|
|
34
|
+
}
|
|
24
35
|
export const scammersSeedData = [
|
|
25
36
|
// ── 1. Verified — advance payment ghost ──────────────────────────────────────
|
|
26
37
|
{
|
|
@@ -120,6 +131,29 @@ export const scammersSeedData = [
|
|
|
120
131
|
isContested: false,
|
|
121
132
|
createdAt: daysAgo(15),
|
|
122
133
|
updatedAt: daysAgo(8),
|
|
134
|
+
/*
|
|
135
|
+
* pending_review -> rejected, with the note that decided it.
|
|
136
|
+
*
|
|
137
|
+
* `verifiedBy`/`verifiedAt` hold only the LAST decision, so a profile
|
|
138
|
+
* that was verified, removed, then re-verified kept no trace of the
|
|
139
|
+
* middle. This is the fixture the "decision history" panel reads.
|
|
140
|
+
*/
|
|
141
|
+
statusHistory: [
|
|
142
|
+
entry(daysAgo(15), "system", "publicScamReport", {
|
|
143
|
+
status: { from: null, to: "pending_review" },
|
|
144
|
+
}),
|
|
145
|
+
entry(daysAgo(8), "admin", "adminScammerPatch", {
|
|
146
|
+
status: { from: "pending_review", to: "rejected" },
|
|
147
|
+
verifiedBy: { from: null, to: "user-admin-letitrip" },
|
|
148
|
+
verificationNote: {
|
|
149
|
+
from: null,
|
|
150
|
+
to: "Seller provided courier proof. Likely delivery dispute, not fraud. Rejected.",
|
|
151
|
+
},
|
|
152
|
+
}, {
|
|
153
|
+
actorUid: "user-admin-letitrip",
|
|
154
|
+
reason: "Seller provided courier proof. Likely delivery dispute, not fraud. Rejected.",
|
|
155
|
+
}),
|
|
156
|
+
],
|
|
123
157
|
},
|
|
124
158
|
// ── 4. Verified — advance payment ghost (same scamType as #1, different person) ──
|
|
125
159
|
{
|
|
@@ -19,6 +19,17 @@ import { STORE_FIELDS } from "../constants/field-names";
|
|
|
19
19
|
import { seedExtMedia } from "./_helpers/media";
|
|
20
20
|
const NOW = new Date();
|
|
21
21
|
const daysAgo = (n) => new Date(NOW.getTime() - n * 86400000);
|
|
22
|
+
/**
|
|
23
|
+
* A timeline entry for the W18 `statusHistory` fixtures.
|
|
24
|
+
*
|
|
25
|
+
* Dates come from this file's own `daysAgo()`, never `Date.now()` in an ID
|
|
26
|
+
* position (Root Cause #25). `actorUid` only, never a name — `withHistory`
|
|
27
|
+
* scrubs PII from real writes, and a fixture carrying a name would be the one
|
|
28
|
+
* place that rule is broken.
|
|
29
|
+
*/
|
|
30
|
+
function entry(at, actorRole, trigger, changes, extra) {
|
|
31
|
+
return { at, actorRole, trigger, changes, ...extra };
|
|
32
|
+
}
|
|
22
33
|
export const storesSeedData = [
|
|
23
34
|
// ── Store 1: LetItRip Official (admin-owned, platform storefront) ──────────
|
|
24
35
|
{
|
|
@@ -226,5 +237,28 @@ export const storesSeedData = [
|
|
|
226
237
|
capabilities: ["suggest_brands"],
|
|
227
238
|
createdAt: daysAgo(220),
|
|
228
239
|
updatedAt: daysAgo(9),
|
|
240
|
+
/*
|
|
241
|
+
* active -> suspended, with the admin who did it and why. This is the
|
|
242
|
+
* fixture behind "a suspended store's page shows who suspended it" — the
|
|
243
|
+
* answer was previously only in adminAuditLog, which the store owner can
|
|
244
|
+
* never see.
|
|
245
|
+
*
|
|
246
|
+
* `isPublic` moves WITH `status`, which is the invariant the admin PATCH
|
|
247
|
+
* route was breaking until 2026-08-26 (it wrote status alone, leaving an
|
|
248
|
+
* approved store active and invisible).
|
|
249
|
+
*/
|
|
250
|
+
statusHistory: [
|
|
251
|
+
entry(daysAgo(9), "admin", "adminStorePatch", {
|
|
252
|
+
status: { from: "active", to: "suspended" },
|
|
253
|
+
isPublic: { from: true, to: false },
|
|
254
|
+
suspensionReason: {
|
|
255
|
+
from: null,
|
|
256
|
+
to: "Three listings flagged as possible reproductions; suspended while authenticity documentation is reviewed.",
|
|
257
|
+
},
|
|
258
|
+
}, {
|
|
259
|
+
actorUid: "user-admin-letitrip",
|
|
260
|
+
reason: "Three listings flagged as possible reproductions; suspended while authenticity documentation is reviewed.",
|
|
261
|
+
}),
|
|
262
|
+
],
|
|
229
263
|
},
|
|
230
264
|
];
|
|
@@ -21,6 +21,16 @@ const BASE = Date.now();
|
|
|
21
21
|
function daysBack(n) {
|
|
22
22
|
return new Date(BASE - n * 24 * 60 * 60 * 1000);
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* A timeline entry for the W18 `statusHistory` fixtures.
|
|
26
|
+
*
|
|
27
|
+
* `actorUid` only, never a name — `withHistory` scrubs
|
|
28
|
+
* SUPPORT_TICKET_PII_FIELDS (which includes `assignedToName`) from real
|
|
29
|
+
* writes, so a fixture carrying one would be the single place that is untrue.
|
|
30
|
+
*/
|
|
31
|
+
function entry(at, actorRole, trigger, changes, extra) {
|
|
32
|
+
return { at, actorRole, trigger, changes, ...extra };
|
|
33
|
+
}
|
|
24
34
|
export const supportTicketsSeedData = [
|
|
25
35
|
// ── 1. Order issue — in progress ─────────────────────────────────────────
|
|
26
36
|
{
|
|
@@ -100,6 +110,30 @@ export const supportTicketsSeedData = [
|
|
|
100
110
|
msg("msg-yugi-003-s2", "user-admin-letitrip", "support", "Refund of ₹2,499 has been processed back to your original payment method. This ticket is now resolved.", 8),
|
|
101
111
|
],
|
|
102
112
|
resolvedAt: daysBack(8),
|
|
113
|
+
/*
|
|
114
|
+
* open -> in_progress -> resolved, with the assignment in between.
|
|
115
|
+
*
|
|
116
|
+
* `resolvedAt` above was ALREADY in this fixture, and no code path had
|
|
117
|
+
* ever written it (verified 2026-08-26) — the seed described a shape the
|
|
118
|
+
* application could not produce. The repository stamps it on the
|
|
119
|
+
* transition now, so this fixture and reality finally agree.
|
|
120
|
+
*/
|
|
121
|
+
statusHistory: [
|
|
122
|
+
entry(daysBack(12), "buyer", "createTicket", {
|
|
123
|
+
status: { from: null, to: "open" },
|
|
124
|
+
}, { actorUid: "user-yugi-muto" }),
|
|
125
|
+
entry(daysBack(11), "admin", "assignTicket", {
|
|
126
|
+
assignedTo: { from: null, to: "user-admin-letitrip" },
|
|
127
|
+
status: { from: "open", to: "in_progress" },
|
|
128
|
+
}, { actorUid: "user-admin-letitrip" }),
|
|
129
|
+
entry(daysBack(8), "admin", "adminUpdateTicket", {
|
|
130
|
+
status: { from: "in_progress", to: "resolved" },
|
|
131
|
+
resolvedAt: { from: null, to: "2026-08-18T00:00:00.000Z" },
|
|
132
|
+
}, {
|
|
133
|
+
actorUid: "user-admin-letitrip",
|
|
134
|
+
reason: "Return accepted and refund processed.",
|
|
135
|
+
}),
|
|
136
|
+
],
|
|
103
137
|
createdAt: daysBack(12),
|
|
104
138
|
updatedAt: daysBack(8),
|
|
105
139
|
},
|
package/dist/server-entry.d.ts
CHANGED
|
@@ -67,6 +67,7 @@ export { getSearchResults, searchAction, type SearchQuery, } from "./_internal/s
|
|
|
67
67
|
export { getCategoryForDetail, listRootCategories, listFeaturedCategories, listMenuCategories, getCategoryTree, listSitemapCategories, CATEGORIES_PAGE_SIZE, CATEGORIES_ROOT_TIER, CATEGORIES_MAX_DEPTH, CATEGORIES_SITEMAP_LIMIT, CATEGORIES_FEATURED_LIMIT, CATEGORIES_MENU_LIMIT, } from "./_internal/server/features/categories/index";
|
|
68
68
|
export { getGroupedListingForDetail, getGroupedListingWithItems, listGroupedListings, listFeaturedGroupedListings, listSitemapGroupedListings, getGroupsForProduct, getGroupsWithItemsForProduct, GROUPED_LISTINGS_PAGE_SIZE, GROUPED_LISTINGS_FEATURED_LIMIT, GROUPED_LISTINGS_SITEMAP_LIMIT, type GroupedListingWithItems, type ListGroupedListingsParams, type SitemapGroupedListing, } from "./_internal/server/features/grouped/index";
|
|
69
69
|
export { createCheckoutOrderAction, attachPaymentAction, verifyAndPlaceRazorpayOrderAction, previewCheckoutPricing, resolveShippingCost, formatShippingAddress, CHECKOUT_DEFAULT_COMMISSIONS, CHECKOUT_PAYMENT_METHODS, type CreateCheckoutOrderInput, type VerifyAndPlaceRazorpayOrderInput, type CheckoutOrderResult, type CheckoutPaymentMethod, type CheckoutPricingPreviewInput, type CheckoutPricingPreview, } from "./_internal/server/features/checkout/index";
|
|
70
|
+
export { unitPriceFor, lineTotalFor, computePreOrderDepositAmount, } from "./_internal/shared/checkout/order-math";
|
|
70
71
|
export { processRefundAction, type ProcessRefundInput, } from "./_internal/server/features/refunds/actions";
|
|
71
72
|
export { createPaymentIntentAction, verifyPaymentSignatureAction, resolvePaymentFee, PAYMENTS_DEFAULT_PLATFORM_FEE_PERCENT, PAYMENTS_DEFAULT_GST_PERCENT, PAYMENTS_RECEIPT_PREFIX, type CreatePaymentIntentInput, type CreatePaymentIntentResult, type VerifyPaymentSignatureInput, type ResolvedPaymentFee, } from "./_internal/server/features/payments/index";
|
|
72
73
|
export { getSublistingCategoryForDetail } from "./_internal/server/features/sublisting-categories/index";
|
package/dist/server-entry.js
CHANGED
|
@@ -97,6 +97,11 @@ export { getGroupedListingForDetail, getGroupedListingWithItems, listGroupedList
|
|
|
97
97
|
// `.listByType("bundle", opts)` / `.findById(id)` directly.
|
|
98
98
|
// S4: checkout actions
|
|
99
99
|
export { createCheckoutOrderAction, attachPaymentAction, verifyAndPlaceRazorpayOrderAction, previewCheckoutPricing, resolveShippingCost, formatShippingAddress, CHECKOUT_DEFAULT_COMMISSIONS, CHECKOUT_PAYMENT_METHODS, } from "./_internal/server/features/checkout/index";
|
|
100
|
+
// The one definition of "what does this cart line cost". Exported because the
|
|
101
|
+
// consumer's Razorpay create-order route needs it: that route used to hand-roll
|
|
102
|
+
// the rule and reproduced only the bundle branch, so it captured the LIST price
|
|
103
|
+
// for an accepted offer. Pure math — no Firestore, no server-only imports.
|
|
104
|
+
export { unitPriceFor, lineTotalFor, computePreOrderDepositAmount, } from "./_internal/shared/checkout/order-math";
|
|
100
105
|
// S-SBUNI-RULES: refund action
|
|
101
106
|
export { processRefundAction, } from "./_internal/server/features/refunds/actions";
|
|
102
107
|
// S4: payments actions
|
package/dist/server.d.ts
CHANGED
|
@@ -231,7 +231,7 @@ export { getLatestBlogPosts } from "./features/blog/server";
|
|
|
231
231
|
export { listBlogPosts } from "./features/blog/server";
|
|
232
232
|
export { updateBlogPost } from "./features/blog/server";
|
|
233
233
|
export { updateBlogPostSchema } from "./features/blog/server";
|
|
234
|
-
export { addItemToCart } from "./features/cart/server";
|
|
234
|
+
export { addItemToCart, assertCanAddNewItems, addGroupLineToCart, updateCartGroupMembers, GROUP_LINE_MAX_MEMBERS, type AddGroupLineToCartInput, } from "./features/cart/server";
|
|
235
235
|
export { clearCart } from "./features/cart/server";
|
|
236
236
|
export { getCart } from "./features/cart/server";
|
|
237
237
|
export { mergeGuestCart } from "./features/cart/server";
|
|
@@ -573,3 +573,7 @@ export { toClientLotterySlot, toClientLotteryConfig } from "./_internal/server/f
|
|
|
573
573
|
export { buildLotteryMetadata } from "./_internal/server/features/lottery/metadata";
|
|
574
574
|
export { submitLotteryPullAction, flagLotteryEntryAction, reopenLotterySlotAction, createLotteryEventAction, updateLotteryEventAction, setProductLotteryModeAction, } from "./_internal/server/features/lottery/actions";
|
|
575
575
|
export { lotteryEntryRepository } from "./features/lottery/repository/lottery-entry.repository";
|
|
576
|
+
export { renderNotificationEmail, NOTIFICATION_EMAIL_TEMPLATES, } from "./features/email/notification-templates";
|
|
577
|
+
export type { NotificationTemplateDef, NotificationEmailContext, } from "./features/email/notification-templates";
|
|
578
|
+
export { resolveNotificationActionUrl, } from "./_internal/shared/features/notifications/action-url";
|
|
579
|
+
export type { NotificationAudience } from "./_internal/shared/features/notifications/action-url";
|
package/dist/server.js
CHANGED
|
@@ -658,7 +658,8 @@ export { updateBlogPost } from "./features/blog/server";
|
|
|
658
658
|
export { updateBlogPostSchema } from "./features/blog/server";
|
|
659
659
|
// [SERVER-ONLY]-Server-only — uses Node.js, Next.js server internals, or third-party server SDKs (auth, email, payment, shipping).
|
|
660
660
|
// addItemToCart - Helper for add item to cart.
|
|
661
|
-
|
|
661
|
+
// assertCanAddNewItems - the lane gate every add-to-cart entry point must run.
|
|
662
|
+
export { addItemToCart, assertCanAddNewItems, addGroupLineToCart, updateCartGroupMembers, GROUP_LINE_MAX_MEMBERS, } from "./features/cart/server";
|
|
662
663
|
// [SERVER-ONLY]-Server-only — uses Node.js, Next.js server internals, or third-party server SDKs (auth, email, payment, shipping).
|
|
663
664
|
// clearCart - Shared export for clear cart.
|
|
664
665
|
export { clearCart } from "./features/cart/server";
|
|
@@ -1444,3 +1445,11 @@ export { toClientLotterySlot, toClientLotteryConfig } from "./_internal/server/f
|
|
|
1444
1445
|
export { buildLotteryMetadata } from "./_internal/server/features/lottery/metadata";
|
|
1445
1446
|
export { submitLotteryPullAction, flagLotteryEntryAction, reopenLotterySlotAction, createLotteryEventAction, updateLotteryEventAction, setProductLotteryModeAction, } from "./_internal/server/features/lottery/actions";
|
|
1446
1447
|
export { lotteryEntryRepository } from "./features/lottery/repository/lottery-entry.repository";
|
|
1448
|
+
/*
|
|
1449
|
+
* Notification email templates + the shared actionUrl resolver.
|
|
1450
|
+
*
|
|
1451
|
+
* Server-only: the templates call `renderToStaticMarkup` through a lazy
|
|
1452
|
+
* `require`, which must never end up reachable from a client chunk.
|
|
1453
|
+
*/
|
|
1454
|
+
export { renderNotificationEmail, NOTIFICATION_EMAIL_TEMPLATES, } from "./features/email/notification-templates";
|
|
1455
|
+
export { resolveNotificationActionUrl, } from "./_internal/shared/features/notifications/action-url";
|