@mohasinac/appkit 4.55.0 → 4.56.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 (46) hide show
  1. package/dist/.build-stamp.json +4 -4
  2. package/dist/_internal/server/features/checkout/actions.d.ts +22 -0
  3. package/dist/_internal/server/features/checkout/actions.js +39 -3
  4. package/dist/_internal/server/features/shipping/reconcile.d.ts +28 -0
  5. package/dist/_internal/server/features/shipping/reconcile.js +93 -0
  6. package/dist/_internal/shared/config/schema.d.ts +6 -0
  7. package/dist/_internal/shared/listing-types/route-gating.d.ts +56 -0
  8. package/dist/_internal/shared/listing-types/route-gating.js +114 -0
  9. package/dist/_internal/shared/shipping/quote.d.ts +153 -0
  10. package/dist/_internal/shared/shipping/quote.js +124 -0
  11. package/dist/client.d.ts +1 -0
  12. package/dist/client.js +8 -0
  13. package/dist/features/auctions/components/AuctionDetailPageView.js +2 -1
  14. package/dist/features/auth/schemas/firestore.d.ts +15 -0
  15. package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +2 -1
  16. package/dist/features/homepage/components/HeroCarousel.js +4 -2
  17. package/dist/features/homepage/components/MarketplaceHomepageView.js +16 -1
  18. package/dist/features/homepage/components/WelcomeSection.d.ts +16 -3
  19. package/dist/features/homepage/components/WelcomeSection.js +3 -3
  20. package/dist/features/homepage/lib/section-renderer.d.ts +9 -0
  21. package/dist/features/homepage/lib/section-renderer.js +22 -5
  22. package/dist/features/homepage/lib/slide-gating.d.ts +52 -0
  23. package/dist/features/homepage/lib/slide-gating.js +98 -0
  24. package/dist/features/live/components/LiveItemDetailPageView.js +2 -1
  25. package/dist/features/media/MediaAvatar.js +1 -1
  26. package/dist/features/media/MediaImage.d.ts +18 -1
  27. package/dist/features/media/MediaImage.js +1 -1
  28. package/dist/features/orders/schemas/firestore.d.ts +38 -0
  29. package/dist/features/points/repository/points.repository.d.ts +81 -0
  30. package/dist/features/points/repository/points.repository.js +155 -0
  31. package/dist/features/points/schemas/firestore.d.ts +102 -0
  32. package/dist/features/points/schemas/firestore.js +99 -0
  33. package/dist/features/pre-orders/components/PreOrderDetailPageView.js +2 -1
  34. package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -1
  35. package/dist/features/products/components/ProductDetailPageView.js +3 -1
  36. package/dist/features/seller/actions/seller-actions.d.ts +17 -0
  37. package/dist/features/seller/actions/seller-actions.js +33 -0
  38. package/dist/index.d.ts +1 -0
  39. package/dist/index.js +8 -0
  40. package/dist/seed/carousel-slides-seed-data.js +48 -7
  41. package/dist/styles.css +38 -6
  42. package/dist/tailwind-utilities.css +1 -1
  43. package/dist/ui/components/ImageMosaic.d.ts +17 -1
  44. package/dist/ui/components/ImageMosaic.js +28 -5
  45. package/dist/ui/components/ImageMosaic.style.css +37 -5
  46. package/package.json +1 -1
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import { serverLogger } from "../../../monitoring/index.js";
12
12
  import { normalizeError } from "../../../errors/normalize.js";
13
+ import { reconcileOrderShipping } from "../../../_internal/server/features/shipping/reconcile.js";
13
14
  import { enqueueJob } from "../../jobs/actions/enqueue-job.js";
14
15
  import { isAdminUser, isStrictSellerUser, isTesterUser } from "../../auth/role-predicates.js";
15
16
  import { ApiError, AuthorizationError, NotFoundError, ValidationError, } from "../../../errors/index.js";
@@ -712,6 +713,38 @@ export async function customShipOrder(userId, userRole, orderId, input) {
712
713
  shippingDate: new Date(),
713
714
  payoutStatus: "eligible",
714
715
  });
716
+ /*
717
+ * 🛑 AFTER the order is marked shipped, and never allowed to fail the
718
+ * dispatch.
719
+ *
720
+ * The parcel has physically gone. If reconciliation throws — the points
721
+ * ledger is unreachable, the order read fails — the correct outcome is a
722
+ * shipped order that has not been reconciled yet, which is recoverable by
723
+ * re-running it. Letting it propagate would leave the seller unable to mark
724
+ * a parcel shipped because of an accounting step they have no control over,
725
+ * and they would retry the dispatch, which is the one thing that must not
726
+ * happen twice.
727
+ *
728
+ * `reconcileOrderShipping` is itself idempotent on
729
+ * `order.shippingReconciliation`, so a retry is safe.
730
+ */
731
+ if (typeof input.actualShippingCost === "number") {
732
+ try {
733
+ const outcome = await reconcileOrderShipping(orderId, {
734
+ courierId: input.actualCourierName ?? input.shippingCarrier,
735
+ courierName: input.actualCourierName ?? input.shippingCarrier,
736
+ actualRate: input.actualShippingCost,
737
+ });
738
+ serverLogger.info("shipping reconciled", { orderId, ...outcome });
739
+ }
740
+ catch (err) {
741
+ const normalized = normalizeError(err);
742
+ serverLogger.error("shipping reconciliation failed after dispatch", {
743
+ orderId,
744
+ message: normalized.message,
745
+ });
746
+ }
747
+ }
715
748
  serverLogger.info("customShipOrder", {
716
749
  orderId,
717
750
  userId,
package/dist/index.d.ts CHANGED
@@ -3123,6 +3123,7 @@ export { withRetry } from "./http/retry";
3123
3123
  export { normalizeError, getErrorMessage, isApiNormalized, isAppNormalized, isFirebaseAuthNormalized, isFirebaseFirestoreNormalized, isFirebaseStorageNormalized, isNativeNormalized, isNetworkNormalized, isUnknownNormalized, isZodNormalized, } from "./errors/normalize";
3124
3124
  export type { NormalizedError, NormalizedApiError, NormalizedAppError, NormalizedFirebaseAuthError, NormalizedFirebaseFirestoreError, NormalizedFirebaseStorageError, NormalizedNativeError, NormalizedNetworkError, NormalizedUnknownThrownValue, NormalizedZodError, } from "./errors/normalize";
3125
3125
  export { CART_LANE, CART_LANE_PRIORITY, CART_LANE_LABELS, laneOf, activeLane, laneItems, laneCounts, isLaneCheckoutable, laneBlockReason, isLockedLane, canAddNewItems, type CartLane, type LaneAssignable, } from "./_internal/shared/checkout/lanes";
3126
+ export { roundShippingUp, toQuoteOptions, reconcileShipping, SHIPPING_ROUND_UP_TO, type ShippingQuoteOption, type SelectedShippingQuote, type ActualShipmentCost, type ShippingReconciliation, } from "./_internal/shared/shipping/quote";
3126
3127
  export { newsletterSubscribeSchema, quickCreateTaxonomySchema, payoutMarkPaidSchema, } from "./features/admin/schemas/small-forms";
3127
3128
  export { raffleEntrySchema } from "./features/contact/schemas/inquiry-forms";
3128
3129
  export { orderCancelSchema, paymentProofSchema, } from "./features/orders/schemas/buyer-forms";
package/dist/index.js CHANGED
@@ -5559,6 +5559,14 @@ export { normalizeError, getErrorMessage, isApiNormalized, isAppNormalized, isFi
5559
5559
  // Checkout lanes — the derived auction > offer > standard partition of the
5560
5560
  // cart, and the priority rule that decides which one may be checked out.
5561
5561
  export { CART_LANE, CART_LANE_PRIORITY, CART_LANE_LABELS, laneOf, activeLane, laneItems, laneCounts, isLaneCheckoutable, laneBlockReason, isLockedLane, canAddNewItems, } from "./_internal/shared/checkout/lanes.js";
5562
+ /*
5563
+ * Shipping quote model. Public because the CONSUMER owns the route that asks
5564
+ * the carrier for rates (`/api/shipping/quote`) and the checkout UI that
5565
+ * renders them — the rounding rule has to be reachable from both, and it must
5566
+ * be the same rule the server-side reconciliation uses, or the buyer is
5567
+ * credited against a figure they were never charged.
5568
+ */
5569
+ export { roundShippingUp, toQuoteOptions, reconcileShipping, SHIPPING_ROUND_UP_TO, } from "./_internal/shared/shipping/quote.js";
5562
5570
  export { newsletterSubscribeSchema, quickCreateTaxonomySchema, payoutMarkPaidSchema, } from "./features/admin/schemas/small-forms.js";
5563
5571
  export { raffleEntrySchema } from "./features/contact/schemas/inquiry-forms.js";
5564
5572
  export { orderCancelSchema, paymentProofSchema, } from "./features/orders/schemas/buyer-forms.js";
@@ -1,8 +1,15 @@
1
1
  /*
2
2
  * WHY: Seeds 6 carousel slides for the marketplace homepage promo carousel.
3
- * WHAT: 5 active + 1 inactive. Deterministic Picsum backgrounds with a dim
3
+ * WHAT: 4 active + 2 inactive. Deterministic Picsum backgrounds with a dim
4
4
  * overlay, plus a headline / supporting line / CTA on each.
5
5
  *
6
+ * 🛑 FOUR ACTIVE, NOT FIVE, SINCE 2026-10-20. `MAX_ACTIVE_SLIDES` is a
7
+ * ceiling, not a quota — `slide-beyblade-auction` went inactive with
8
+ * `listings.listingTypes.auction: false` and nothing was promoted to backfill
9
+ * it. Do NOT activate `slide-upcoming-promo` to get back to five: that one is
10
+ * the fixture `getActiveSlides()`' own filter is tested against, and it says
11
+ * "A draft slide kept unpublished so admins have a starting point to copy."
12
+ *
6
13
  * 🛑 EVERY ACTIVE SLIDE MUST CARRY AN `overlay` OR `cards`, OR IT RENDERS BLANK.
7
14
  * `HeroCarousel` gates all slide copy on `slide.overlay` (line ~412) and the
8
15
  * card grid on `slide.cards.length` (line ~451). Before this pass every seeded
@@ -107,16 +114,34 @@ const _rawSlides = [
107
114
  hover: { effect: "scale" },
108
115
  },
109
116
  {
110
- id: "card-hero-bid",
117
+ /*
118
+ * 🛑 THIS WAS THE AUCTIONS CARD ("Bid / Live auctions / Place a bid
119
+ * before the timer runs out" → `/auctions`), and it was still on the
120
+ * first screen of the site weeks after `listings.listingTypes.auction`
121
+ * went `false`. The section-level gate in `MarketplaceHomepageView` is
122
+ * keyed on `SectionType` and a carousel is type `carousel` whatever it
123
+ * contains, so nothing could see it. `gateCarouselSlides` now can — but
124
+ * a filter that leaves a hole in a `repeat(3, 1fr)` row is a fallback,
125
+ * not a fix, so the slot gets real content instead.
126
+ *
127
+ * 🛑 NOT `/spares` and NOT `/art`, which were the obvious swaps: both
128
+ * types are ENABLED, both have a working browse page, and both held
129
+ * ZERO published products when this was written (measured against
130
+ * production, 2026-10-20). Replacing a dead-end card with a different
131
+ * dead-end card is not a fix. `/categories` is populated — six roots,
132
+ * 28 brands, 8 franchises — and browsing the tree is genuinely the
133
+ * third way to shop here.
134
+ */
135
+ id: "card-hero-browse",
111
136
  zone: 2,
112
137
  mobileZone: 2,
113
138
  background: { type: "image", url: MEDIA.lDrago, dimOverlay: { enabled: true, opacity: 0.45 } },
114
139
  content: {
115
- eyebrow: "Bid",
116
- title: "Live auctions",
117
- description: "Place a bid before the timer runs out.",
140
+ eyebrow: "Explore",
141
+ title: "Shop by category",
142
+ description: "Walk the tree from generation down to a single part.",
118
143
  },
119
- buttons: [{ id: "card-hero-bid-cta", text: "Browse auctions", href: String(ROUTES.PUBLIC.AUCTIONS), variant: "primary" }],
144
+ buttons: [{ id: "card-hero-browse-cta", text: "Browse categories", href: String(ROUTES.PUBLIC.CATEGORIES), variant: "primary" }],
120
145
  hover: { effect: "scale" },
121
146
  },
122
147
  {
@@ -139,10 +164,26 @@ const _rawSlides = [
139
164
  updatedAt: daysAgo(3),
140
165
  },
141
166
  {
167
+ /*
168
+ * 🛑 DISABLED, NOT DELETED — the same arrangement the auctions RAIL uses in
169
+ * `homepage-sections-seed-data.ts`, and for the same reason. Every word of
170
+ * the copy, the video background and the CTA stay exactly as they were, so
171
+ * flipping `listings.listingTypes.auction` back to `true` and this flag
172
+ * with it restores the slide intact.
173
+ *
174
+ * Two locks, deliberately. `gateCarouselSlides` already drops this at
175
+ * render because `overlay.button.link` and `link.url` both resolve to
176
+ * `/auctions` — but that one is invisible, and a slide listed as active in
177
+ * the carousel admin while never appearing on the page reads as a bug in
178
+ * the admin. `active: false` is the lock a human sees.
179
+ *
180
+ * 🛑 The copy names no listing type — "Win it at your price" — which is
181
+ * exactly why the gate matches on the LINK and not on the words.
182
+ */
142
183
  id: "slide-beyblade-auction",
143
184
  title: "Live auctions",
144
185
  order: 2,
145
- active: true,
186
+ active: false,
146
187
  background: {
147
188
  type: "video",
148
189
  // Direct MP4 URL, deliberately NOT wrapped in seedExtMedia() — that