@mohasinac/appkit 4.53.1 → 4.54.0-next.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/.build-stamp.json +5 -5
- package/dist/_internal/client/theme/ThemeProvider.js +15 -1
- package/dist/_internal/server/features/auth/capabilities.js +7 -5
- package/dist/_internal/server/features/categories/price-guidance-resolve.d.ts +66 -0
- package/dist/_internal/server/features/categories/price-guidance-resolve.js +138 -0
- package/dist/_internal/server/features/checkout/supplier-identity.js +28 -6
- package/dist/_internal/server/features/price-index/market.d.ts +57 -0
- package/dist/_internal/server/features/price-index/market.js +120 -0
- package/dist/_internal/server/features/products/list-public.d.ts +12 -0
- package/dist/_internal/server/features/products/list-public.js +10 -1
- package/dist/_internal/server/features/products/listing-tab-counts.d.ts +25 -1
- package/dist/_internal/server/features/products/listing-tab-counts.js +184 -10
- package/dist/_internal/server/features/search/index-build/sources.js +43 -2
- package/dist/_internal/server/features/stores/tabs.d.ts +7 -2
- package/dist/_internal/server/features/stores/tabs.js +5 -3
- package/dist/_internal/server/jobs/core/jobRunners.js +0 -10
- package/dist/_internal/server/jobs/core/onOrderCreate.js +10 -104
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +128 -0
- package/dist/_internal/shared/features/categories/bundle-copy.d.ts +1 -1
- package/dist/_internal/shared/features/site-settings/supplier-of-record.d.ts +56 -0
- package/dist/_internal/shared/features/site-settings/supplier-of-record.js +57 -0
- package/dist/_internal/shared/listing-types/tiers.d.ts +94 -0
- package/dist/_internal/shared/listing-types/tiers.js +150 -0
- package/dist/_internal/shared/search/match.js +28 -2
- package/dist/cli/index.js +0 -2
- package/dist/client.d.ts +21 -2
- package/dist/client.js +23 -4
- package/dist/constants/api-endpoints.d.ts +28 -0
- package/dist/constants/api-endpoints.js +14 -0
- package/dist/features/account/components/UserOrdersView.js +26 -7
- package/dist/features/addresses/utils/postal-autofill.d.ts +39 -0
- package/dist/features/addresses/utils/postal-autofill.js +54 -0
- package/dist/features/admin/components/AdminStoreEditorView.js +73 -34
- package/dist/features/admin/components/OwnerQuickActions.d.ts +29 -0
- package/dist/features/admin/components/OwnerQuickActions.js +142 -0
- package/dist/features/admin/schemas/admin-editor-forms.d.ts +1 -1
- package/dist/features/admin/schemas/admin-editor-forms.js +4 -1
- package/dist/features/auctions/components/AuctionDetailPageView.js +8 -7
- package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +11 -1
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +9 -2
- package/dist/features/auth/permissions/constants.d.ts +29 -2
- package/dist/features/auth/permissions/constants.js +68 -1
- package/dist/features/blog/components/BlogFeaturedCard.d.ts +11 -1
- package/dist/features/blog/components/BlogFeaturedCard.js +9 -2
- package/dist/features/cart/components/CartDrawer.js +3 -2
- package/dist/features/cart/components/CartGroupLineRow.js +2 -1
- package/dist/features/cart/hooks/useAddToCart.js +10 -5
- package/dist/features/cart/utils/pending-ops.d.ts +11 -1
- package/dist/features/cart/utils/pending-ops.js +14 -2
- package/dist/features/categories/api/[id]/price-guidance/route.js +1 -24
- package/dist/features/categories/components/BrandDetailPageView.js +15 -6
- package/dist/features/categories/components/BrandDetailTabs.d.ts +7 -1
- package/dist/features/categories/components/BrandDetailTabs.js +18 -3
- package/dist/features/categories/components/BundleCollage.js +3 -2
- package/dist/features/categories/components/BundleItemsPicker.js +3 -4
- package/dist/features/categories/components/CategoryDetailPageView.js +19 -8
- package/dist/features/categories/components/CategoryDetailTabs.d.ts +7 -1
- package/dist/features/categories/components/CategoryDetailTabs.js +29 -7
- package/dist/features/categories/components/CategoryGrid.d.ts +11 -1
- package/dist/features/categories/components/CategoryGrid.js +9 -2
- package/dist/features/checkout/actions/checkout-value-otp-actions.d.ts +10 -5
- package/dist/features/checkout/actions/checkout-value-otp-actions.js +2 -88
- package/dist/features/classified/components/ClassifiedDetailPageView.js +10 -1
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +10 -1
- package/dist/features/events/components/EventCard.d.ts +11 -1
- package/dist/features/events/components/EventCard.js +9 -2
- package/dist/features/layout/AppLayoutShell.js +2 -1
- package/dist/features/layout/BottomActions.d.ts +1 -0
- package/dist/features/layout/BottomActions.js +330 -0
- package/dist/features/layout/BottomChrome.d.ts +45 -0
- package/dist/features/layout/BottomChrome.js +5 -17
- package/dist/features/layout/SideActionDock.js +36 -13
- package/dist/features/layout/index.d.ts +2 -7
- package/dist/features/layout/index.js +7 -7
- package/dist/features/layout/useIsDesktop.d.ts +36 -0
- package/dist/features/layout/useIsDesktop.js +48 -0
- package/dist/features/live/components/LiveItemDetailPageView.js +10 -1
- package/dist/features/media/messages/en.json +67 -67
- package/dist/features/orders/components/YourPurchaseStrip.d.ts +24 -0
- package/dist/features/orders/components/YourPurchaseStrip.js +77 -0
- package/dist/features/orders/components/index.d.ts +2 -1
- package/dist/features/orders/components/index.js +1 -1
- package/dist/features/orders/utils/order-announcement-message.d.ts +18 -0
- package/dist/features/orders/utils/order-announcement-message.js +16 -0
- package/dist/features/orders/utils/payment-proof-message.d.ts +20 -0
- package/dist/features/orders/utils/payment-proof-message.js +17 -0
- package/dist/features/parts/schemas/firestore.d.ts +227 -0
- package/dist/features/parts/schemas/firestore.js +6 -6
- package/dist/features/pre-orders/components/MarketplacePreorderCard.d.ts +11 -1
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +10 -3
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +8 -7
- package/dist/features/price-index/components/PriceIndexToolView.d.ts +21 -0
- package/dist/features/price-index/components/PriceIndexToolView.js +230 -27
- package/dist/features/price-index/lookup-target.d.ts +48 -0
- package/dist/features/price-index/lookup-target.js +107 -0
- package/dist/features/products/api/[id]/route.js +8 -1
- package/dist/features/products/api/route.js +16 -4
- package/dist/features/products/components/AvailabilityScopeHint.d.ts +22 -0
- package/dist/features/products/components/AvailabilityScopeHint.js +27 -0
- package/dist/features/products/components/CompareOverlay.d.ts +2 -1
- package/dist/features/products/components/CompareOverlay.js +2 -1
- package/dist/features/products/components/GroupSettingsPanel.js +2 -1
- package/dist/features/products/components/InteractiveProductCard.d.ts +7 -1
- package/dist/features/products/components/InteractiveProductCard.js +16 -4
- package/dist/features/products/components/MarketplaceBundleCard.d.ts +11 -1
- package/dist/features/products/components/MarketplaceBundleCard.js +9 -2
- package/dist/features/products/components/MarketplacePrizeDrawCard.d.ts +11 -1
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +9 -2
- package/dist/features/products/components/ProductDetailPageView.js +34 -4
- package/dist/features/products/components/ProductGrid.d.ts +14 -1
- package/dist/features/products/components/ProductGrid.js +22 -5
- package/dist/features/products/components/ProductsIndexListing.js +2 -2
- package/dist/features/products/components/SublistingCarouselSection.js +2 -1
- package/dist/features/products/repository/products.repository.d.ts +1 -0
- package/dist/features/products/repository/products.repository.js +5 -1
- package/dist/features/products/utils/group-member-image.d.ts +9 -2
- package/dist/features/products/utils/group-member-image.js +2 -1
- package/dist/features/reviews/api/[id]/route.js +83 -23
- package/dist/features/reviews/components/PurchaseReviewAffordance.d.ts +6 -0
- package/dist/features/reviews/components/PurchaseReviewAffordance.js +42 -0
- package/dist/features/reviews/components/ReviewFormDrawer.d.ts +34 -0
- package/dist/features/reviews/components/ReviewFormDrawer.js +127 -0
- package/dist/features/reviews/components/index.d.ts +4 -0
- package/dist/features/reviews/components/index.js +2 -0
- package/dist/features/seller/actions/seller-actions.d.ts +1 -1
- package/dist/features/seller/actions/seller-actions.js +49 -3
- package/dist/features/seller/components/SellerPayoutSettingsView.js +30 -2
- package/dist/features/seller/components/SellerProductShell.d.ts +17 -1
- package/dist/features/seller/components/SellerProductsView.js +3 -2
- package/dist/features/seller/hooks/useStoreListingEntitlements.d.ts +8 -0
- package/dist/features/seller/hooks/useStoreListingEntitlements.js +70 -0
- package/dist/features/stores/components/InteractiveStoreCard.d.ts +11 -1
- package/dist/features/stores/components/InteractiveStoreCard.js +9 -2
- package/dist/features/stores/components/StoreAuctionsListing.js +2 -1
- package/dist/features/stores/components/index.d.ts +0 -2
- package/dist/features/stores/components/index.js +0 -1
- package/dist/features/stores/schemas/index.d.ts +1 -1
- package/dist/features/stores/schemas/index.js +10 -1
- package/dist/features/tester/seed-data/authored/content-discovery__authenticity-checker.js +114 -2
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +415 -0
- package/dist/index.d.ts +8 -27
- package/dist/index.js +14 -35
- package/dist/next/routing/route-map.d.ts +2 -0
- package/dist/next/routing/route-map.js +9 -0
- package/dist/providers/shipping-shiprocket/index.d.ts +98 -0
- package/dist/providers/shipping-shiprocket/index.js +353 -0
- package/dist/schemas/registry.d.ts +2 -2
- package/dist/security/csp.d.ts +16 -1
- package/dist/security/csp.js +5 -3
- package/dist/seed/_helpers/category-forest.js +27 -0
- package/dist/seed/_helpers/category-parts-glossary-map.js +1816 -86
- package/dist/seed/_helpers/category-parts.d.ts +1 -1
- package/dist/seed/_helpers/category-parts.js +13412 -3020
- package/dist/seed/categories-seed-data.js +48 -6
- package/dist/seed/homepage-sections-seed-data.js +41 -28
- package/dist/seed/model-composition-seed-data.js +6 -8
- package/dist/seed/part-glossary/chunk0.d.ts +9 -0
- package/dist/seed/part-glossary/chunk0.js +35044 -0
- package/dist/seed/part-glossary/chunk1.d.ts +9 -0
- package/dist/seed/part-glossary/chunk1.js +39955 -0
- package/dist/seed/part-glossary/chunk2.d.ts +9 -0
- package/dist/seed/part-glossary/chunk2.js +50335 -0
- package/dist/seed/part-glossary/chunk3.d.ts +9 -0
- package/dist/seed/part-glossary/chunk3.js +52386 -0
- package/dist/seed/part-glossary/chunk4.d.ts +9 -0
- package/dist/seed/part-glossary/chunk4.js +50549 -0
- package/dist/seed/part-glossary-seed-data.d.ts +1 -1
- package/dist/seed/part-glossary-seed-data.js +23 -47483
- package/dist/seed/products-beyblade-x-seed-data.js +36 -1
- package/dist/server.d.ts +6 -8
- package/dist/server.js +16 -20
- package/dist/styles.css +516 -358
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/index.d.ts +3 -1
- package/dist/tokens/themes/index.js +11 -1
- package/dist/tokens/themes/technical-dark.d.ts +30 -0
- package/dist/tokens/themes/technical-dark.js +103 -0
- package/dist/tokens/themes/technical-light.d.ts +22 -0
- package/dist/tokens/themes/technical-light.js +89 -0
- package/dist/tokens/tokens.css +155 -0
- package/dist/ui/components/GatedPrice.js +30 -3
- package/dist/ui/components/GatedPrice.style.css +22 -19
- package/dist/ui/components/QuickCreateModal.style.css +78 -78
- package/dist/ui/forms/FormShell.style.css +260 -260
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +133 -27
- package/dist/_internal/server/jobs/core/whatsappCatalogImport.d.ts +0 -27
- package/dist/_internal/server/jobs/core/whatsappCatalogImport.js +0 -172
- package/dist/_internal/server/jobs/core/whatsappNotify.d.ts +0 -35
- package/dist/_internal/server/jobs/core/whatsappNotify.js +0 -140
- package/dist/features/admin/components/AdminWhatsAppGuideView.d.ts +0 -2
- package/dist/features/admin/components/AdminWhatsAppGuideView.js +0 -35
- package/dist/features/orders/components/MarketplaceOrderCard.d.ts +0 -36
- package/dist/features/orders/components/MarketplaceOrderCard.js +0 -57
- package/dist/features/stores/components/StoreWhatsAppGuideView.d.ts +0 -3
- package/dist/features/stores/components/StoreWhatsAppGuideView.js +0 -12
- package/dist/features/whatsapp-bot/api/send-status/route.d.ts +0 -12
- package/dist/features/whatsapp-bot/api/send-status/route.js +0 -38
- package/dist/features/whatsapp-bot/api/webhook/route.d.ts +0 -12
- package/dist/features/whatsapp-bot/api/webhook/route.js +0 -43
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.d.ts +0 -6
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +0 -270
- package/dist/features/whatsapp-bot/components/WhatsAppChatButton.d.ts +0 -9
- package/dist/features/whatsapp-bot/components/WhatsAppChatButton.js +0 -7
- package/dist/features/whatsapp-bot/components/index.d.ts +0 -3
- package/dist/features/whatsapp-bot/components/index.js +0 -2
- package/dist/features/whatsapp-bot/helpers/payment-proof-message.d.ts +0 -14
- package/dist/features/whatsapp-bot/helpers/payment-proof-message.js +0 -17
- package/dist/features/whatsapp-bot/helpers/whatsapp.d.ts +0 -52
- package/dist/features/whatsapp-bot/helpers/whatsapp.js +0 -283
- package/dist/features/whatsapp-bot/index.d.ts +0 -3
- package/dist/features/whatsapp-bot/index.js +0 -3
- package/dist/features/whatsapp-bot/manifest.d.ts +0 -2
- package/dist/features/whatsapp-bot/manifest.js +0 -13
- package/dist/features/whatsapp-bot/messages/en.json +0 -7
- package/dist/features/whatsapp-bot/server.d.ts +0 -7
- package/dist/features/whatsapp-bot/server.js +0 -7
- package/dist/features/whatsapp-bot/types/index.d.ts +0 -126
- package/dist/features/whatsapp-bot/types/index.js +0 -1
|
@@ -210,6 +210,67 @@ export interface PartColourway {
|
|
|
210
210
|
/** The bey this colourway was released in. */
|
|
211
211
|
inBey: string;
|
|
212
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* The infobox stat grid, as NUMBERS.
|
|
215
|
+
*
|
|
216
|
+
* 🛑 This is a deliberate second representation of the `Stats` row in
|
|
217
|
+
* `specs[]`, not a mirror to keep in sync. The row is a RENDERING — one line,
|
|
218
|
+
* because six rows of single digits crowd out every real specification on a
|
|
219
|
+
* part page — and this is the VALUE, which is what a sort, a filter, a
|
|
220
|
+
* side-by-side comparison and the simulation all need. Neither can do the
|
|
221
|
+
* other's job, and both are produced by one parse in one pass, so there is no
|
|
222
|
+
* second write path to forget (unlike Root Cause #42's shape).
|
|
223
|
+
*
|
|
224
|
+
* Every key is OPTIONAL because the source states them per part: a page that
|
|
225
|
+
* gives Attack and Burst and nothing else yields exactly those two. A missing
|
|
226
|
+
* key is "not stated", never zero — `weight: 0` is a real rating on a
|
|
227
|
+
* featherweight disc and must stay distinguishable from silence.
|
|
228
|
+
*
|
|
229
|
+
* The scale is 0–6 and is the wiki's own editorial judgement, not a
|
|
230
|
+
* measurement. It is useful for comparison between parts of the same slot and
|
|
231
|
+
* should not be presented as a physical quantity.
|
|
232
|
+
*/
|
|
233
|
+
export interface PartRatings {
|
|
234
|
+
attack?: number;
|
|
235
|
+
defense?: number;
|
|
236
|
+
stamina?: number;
|
|
237
|
+
weight?: number;
|
|
238
|
+
speed?: number;
|
|
239
|
+
burst?: number;
|
|
240
|
+
dash?: number;
|
|
241
|
+
burstResistance?: number;
|
|
242
|
+
power?: number;
|
|
243
|
+
height?: number;
|
|
244
|
+
runTime?: number;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* WBO tournament-format legality.
|
|
248
|
+
*
|
|
249
|
+
* 🛑 A format the source does not rule on is ABSENT, never `false`. Three
|
|
250
|
+
* states exist — legal, banned, and not stated — and collapsing the third into
|
|
251
|
+
* the second would publish "banned in Burst Classic" as a fact about every
|
|
252
|
+
* part whose page simply never mentions Burst Classic.
|
|
253
|
+
*/
|
|
254
|
+
export interface PartWboLegality {
|
|
255
|
+
burst?: boolean;
|
|
256
|
+
burstGt?: boolean;
|
|
257
|
+
burstLimited?: boolean;
|
|
258
|
+
burstClassic?: boolean;
|
|
259
|
+
xStandard?: boolean;
|
|
260
|
+
xMasters?: boolean;
|
|
261
|
+
xOpen?: boolean;
|
|
262
|
+
mfbStandard?: boolean;
|
|
263
|
+
mfbLimited?: boolean;
|
|
264
|
+
mfbZeroG?: boolean;
|
|
265
|
+
mfb4d?: boolean;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* OUR role vocabulary — 10 values covering all 59 slots.
|
|
269
|
+
*
|
|
270
|
+
* Mirrors `PART_ROLE` in `scripts/lib/part-slot-vocab.mjs`, which is the
|
|
271
|
+
* source. A slot missing from that map yields no `role` rather than a guess.
|
|
272
|
+
*/
|
|
273
|
+
export type PartRole = "chip" | "ring" | "wheel" | "weight" | "base" | "blade" | "track" | "tip" | "gear" | "support";
|
|
213
274
|
export interface PartGlossaryDocument extends BaseDocument {
|
|
214
275
|
/** `${slot}--${codeOrSlug}` — both halves, because a code is not unique. */
|
|
215
276
|
id: string;
|
|
@@ -294,6 +355,172 @@ export interface PartGlossaryDocument extends BaseDocument {
|
|
|
294
355
|
* it is a DIFFERENT axis from colour and must not be folded into it.
|
|
295
356
|
*/
|
|
296
357
|
hasMoldVariations?: boolean;
|
|
358
|
+
/**
|
|
359
|
+
* The 0–6 stat grid as numbers. Present on 1,149 of 2,490 rows — the source
|
|
360
|
+
* states it per part, so absence is "not stated", not zero.
|
|
361
|
+
*/
|
|
362
|
+
ratings?: PartRatings;
|
|
363
|
+
/**
|
|
364
|
+
* The ceiling `ratings` are out of — 6, 8 or 100.
|
|
365
|
+
*
|
|
366
|
+
* 🛑 LOAD-BEARING, because each generation rates on its own scale and they
|
|
367
|
+
* share these fields. Measured: Beyblade X rates 0-100 (237 rows) while
|
|
368
|
+
* Burst rates 0-6 or 0-8 (945 rows), so mean `attack` by slot reads
|
|
369
|
+
* `blade 31.8` beside `driver 1.78`. Anything that sorts, compares or
|
|
370
|
+
* simulates on a rating MUST divide by this first; without it a Beyblade X
|
|
371
|
+
* blade outranks every Burst layer five times over.
|
|
372
|
+
*
|
|
373
|
+
* Derived per system from the highest rating observed for it, rounded up to
|
|
374
|
+
* a standard step — not from a hand-written table, which was wrong within
|
|
375
|
+
* one run (`warrior-wheel--dragoon` carries attack 7 against a declared 6).
|
|
376
|
+
*/
|
|
377
|
+
ratingScale?: number;
|
|
378
|
+
/**
|
|
379
|
+
* The same axes rescaled to 0-100, so a part can be compared ACROSS
|
|
380
|
+
* generations — which the game needs, since it has cross-gen battles.
|
|
381
|
+
*
|
|
382
|
+
* 🛑 This equalises the EDITORIAL JUDGEMENT, not the capability. A Burst
|
|
383
|
+
* layer at 6/6 and a Beyblade X blade at 100/100 both land on 100 and are
|
|
384
|
+
* not equally strong — that blade is 30 g against the layer's 13 g. So it
|
|
385
|
+
* answers "how good is this part for its generation", which is the right
|
|
386
|
+
* basis for a balanced roster and the wrong basis for claiming physical
|
|
387
|
+
* equivalence. `weightG` carries the latter, in grams, unnormalised.
|
|
388
|
+
*
|
|
389
|
+
* `ratings` stays raw beside it: a normalised value can be recomputed from
|
|
390
|
+
* the raw pair, a discarded raw value cannot.
|
|
391
|
+
*
|
|
392
|
+
* 🛑 FOR COMPARING OPPONENTS, NEVER FOR SUGGESTING A BUILD. A bey can only
|
|
393
|
+
* be ASSEMBLED within one generation — a Burst layer does not fit a Beyblade
|
|
394
|
+
* X ratchet — while battles are cross-generation. So this field belongs in a
|
|
395
|
+
* matchup, a leaderboard or a side-by-side, and a "build your own" picker
|
|
396
|
+
* must constrain to a single system's stack regardless of what it says.
|
|
397
|
+
*/
|
|
398
|
+
ratingsNormalised?: PartRatings;
|
|
399
|
+
/** WBO format legality across 11 formats; an unstated format is absent. */
|
|
400
|
+
wboLegality?: PartWboLegality;
|
|
401
|
+
/**
|
|
402
|
+
* Other names for this exact part — the community abbreviation (`Armor Tip
|
|
403
|
+
* - Adventure` is `Ad`) and the other vendor's name. On 1,072 of 2,490 rows.
|
|
404
|
+
* Feeds search, so a buyer can find a part by the code printed on it.
|
|
405
|
+
*/
|
|
406
|
+
aka?: string[];
|
|
407
|
+
/**
|
|
408
|
+
* Hasbro's own product code, a SEPARATE field from `productCodes`. On 309
|
|
409
|
+
* rows, and it is the Hasbro <-> Takara-Tomy equivalence stated outright.
|
|
410
|
+
*/
|
|
411
|
+
hasbroCode?: string;
|
|
412
|
+
/** Attack / Defense / Stamina / Balance, per part. 793 rows. */
|
|
413
|
+
partType?: string;
|
|
414
|
+
/** Right-Spin / Left-Spin / Dual. 756 rows. */
|
|
415
|
+
spinDirection?: string;
|
|
416
|
+
/** Beyblade X's attribute label — "Sky" and siblings. 29 rows. */
|
|
417
|
+
element?: string;
|
|
418
|
+
/**
|
|
419
|
+
* The character this part belongs to in the story — `Bit Chip - Black
|
|
420
|
+
* Dranzer` is Kai Hiwatari's. Only 27 rows, but it is the sole place a PART
|
|
421
|
+
* is tied to a character.
|
|
422
|
+
*/
|
|
423
|
+
owner?: string;
|
|
424
|
+
/** The episode this part appeared in. 22 rows. */
|
|
425
|
+
appearAnime?: string;
|
|
426
|
+
/**
|
|
427
|
+
* OUR OWN classification — what the part DOES, across generations.
|
|
428
|
+
*
|
|
429
|
+
* 🛑 `slot` cannot carry this. It mirrors the source's `Classification=`
|
|
430
|
+
* spelling and it is half of every document id (`${slot}--${code}`), and
|
|
431
|
+
* those ids are LIVE URLS (`/parts/{classification}/{code}`). Renaming a
|
|
432
|
+
* slot re-keys 2,490 rows and breaks every inbound link — a slug is the one
|
|
433
|
+
* field that cannot be quietly corrected later. So this is additive.
|
|
434
|
+
*
|
|
435
|
+
* It collapses the source's line-specific names onto one role:
|
|
436
|
+
* `bu-blade` / `qs-blade` / `blade` are all `blade`; the six Burst chip
|
|
437
|
+
* variants are all `chip`. Derived in `scripts/lib/part-slot-vocab.mjs`,
|
|
438
|
+
* which covers all 59 slots with 10 roles.
|
|
439
|
+
*/
|
|
440
|
+
role?: PartRole;
|
|
441
|
+
/**
|
|
442
|
+
* Every stack role one moulding fills, listed TOP-TO-BOTTOM. A part that
|
|
443
|
+
* covers a single position does not carry this field at all.
|
|
444
|
+
*
|
|
445
|
+
* Integration is real and spans generations: HMS merges spin gear and blade
|
|
446
|
+
* base into a Running Core, a 4D Bottom (`D:D`, `X:D`) is a spin track and
|
|
447
|
+
* a performance tip in one piece, a Burst `disc-integrated-driver` is a disc
|
|
448
|
+
* and a driver, and `fusion-wheel--variares` is an energy ring and a fusion
|
|
449
|
+
* wheel. A Plastic-era base can integrate its Sub Attack Ring.
|
|
450
|
+
*
|
|
451
|
+
* 🛑 Load-bearing for any stack validation. `role` files an integrated part
|
|
452
|
+
* under its PRIMARY function, so a validator reading only that believes the
|
|
453
|
+
* other position is still empty and offers a part that cannot physically go
|
|
454
|
+
* there — it will refuse a legal build, and an exploded 3D view will show a
|
|
455
|
+
* gap where a solid piece is.
|
|
456
|
+
*
|
|
457
|
+
* 🛑 PER PART, NOT PER SLOT — that is the whole reason this is a field and
|
|
458
|
+
* not a lookup. `fusion-wheel--variares` integrates the ring and
|
|
459
|
+
* `fusion-wheel--jade` does not; same slot, different moulding. Jade is
|
|
460
|
+
* multi-PIECE, which is a different fact (see `pieceCount` when it lands).
|
|
461
|
+
*
|
|
462
|
+
* 🛑 Do NOT re-spell this as "fused". "Fusion Wheel" is a literal MFB slot
|
|
463
|
+
* name, so `fusedRoles` reads as *the roles of the fusion wheel* — the
|
|
464
|
+
* opposite of the meaning. The source's own slot names already use the
|
|
465
|
+
* right word: `disc-integrated-driver`, `ratchet-integrated-bit`.
|
|
466
|
+
*
|
|
467
|
+
* Populated by `scripts/derive-part-integration.mjs`, which reads it off the
|
|
468
|
+
* compositions each part actually appears in — the source states a 4D
|
|
469
|
+
* Bottom's `Classification` as plain "Performance Tip", so this cannot be
|
|
470
|
+
* copied from an infobox and is not guessed either.
|
|
471
|
+
*/
|
|
472
|
+
integratedRoles?: PartRole[];
|
|
473
|
+
/**
|
|
474
|
+
* An owner-uploaded 3D model. Empty by default and deliberately so — the
|
|
475
|
+
* models are supplied by the owner, not ingested from anywhere.
|
|
476
|
+
*
|
|
477
|
+
* 🛑 NEVER served through `/api/media/[...slug]`. That route applies NO
|
|
478
|
+
* authentication, raw-pipes non-images with a 30-day immutable cache, and
|
|
479
|
+
* sits over a bucket whose storage rule is `allow read: if true` — so a GLB
|
|
480
|
+
* there is a public download. A mesh is the asset someone could build a
|
|
481
|
+
* competing game from, so it needs a signed, short-lived URL and an
|
|
482
|
+
* authenticated route of its own.
|
|
483
|
+
*
|
|
484
|
+
* 🛑 And a browser viewer cannot make a mesh unextractable — WebGL buffers
|
|
485
|
+
* can be dumped from any page that renders them. The honest mitigation is to
|
|
486
|
+
* publish a DECIMATED preview and keep the full-resolution mesh server-side;
|
|
487
|
+
* "protected" here means inconvenient, never impossible.
|
|
488
|
+
*/
|
|
489
|
+
model3d?: {
|
|
490
|
+
url: string;
|
|
491
|
+
format: "glb";
|
|
492
|
+
sizeBytes: number;
|
|
493
|
+
/** A low-poly public preview, when the full mesh is held back. */
|
|
494
|
+
previewUrl?: string;
|
|
495
|
+
credit?: string;
|
|
496
|
+
creditUrl?: string;
|
|
497
|
+
};
|
|
498
|
+
/**
|
|
499
|
+
* An owner-uploaded sound, with its waveform for display.
|
|
500
|
+
*
|
|
501
|
+
* Renders through the existing `<MediaAudio>` primitive, so this needs no
|
|
502
|
+
* new UI primitive. `waveform` is a coarse amplitude array for drawing the
|
|
503
|
+
* trace without decoding the audio client-side.
|
|
504
|
+
*/
|
|
505
|
+
sound?: {
|
|
506
|
+
url: string;
|
|
507
|
+
durationMs?: number;
|
|
508
|
+
waveform?: number[];
|
|
509
|
+
label?: string;
|
|
510
|
+
};
|
|
511
|
+
/**
|
|
512
|
+
* The Japanese name, the romanisation, and the kanji where the source gives
|
|
513
|
+
* one.
|
|
514
|
+
*
|
|
515
|
+
* 🛑 Top-level fields AS WELL AS `specs[]` rows, which is not duplication for
|
|
516
|
+
* its own sake: a `<title>`, an `og:title`, an hreflang alternate and a
|
|
517
|
+
* JSON-LD `alternateName` all need a FIELD. None of them can scan an array
|
|
518
|
+
* of label/value pairs for one whose label happens to read "Japanese name",
|
|
519
|
+
* and that is where these 1,068 values previously lived and died.
|
|
520
|
+
*/
|
|
521
|
+
jpName?: string;
|
|
522
|
+
romaji?: string;
|
|
523
|
+
kanjiName?: string;
|
|
297
524
|
/** `/media/<slug>`. 🛑 Never a raw Storage host URL. */
|
|
298
525
|
image?: string;
|
|
299
526
|
/** The wiki page the photo came from — the CC-BY-SA attribution target. */
|
|
@@ -269,11 +269,11 @@ export const PART_SLOT_CATEGORY = {
|
|
|
269
269
|
// Burst — a driver with the disc moulded in is still a driver.
|
|
270
270
|
"disc-integrated-driver": "category-burst-drivers",
|
|
271
271
|
// Beyblade X — the CX Custom line, which has its own node.
|
|
272
|
-
"lock-chip": "category-x-
|
|
273
|
-
"main-blade": "category-x-
|
|
274
|
-
"assist-blade": "category-x-
|
|
275
|
-
"over-blade": "category-x-
|
|
276
|
-
"metal-blade": "category-x-
|
|
272
|
+
"lock-chip": "category-x-lock-chips",
|
|
273
|
+
"main-blade": "category-x-main-blades",
|
|
274
|
+
"assist-blade": "category-x-assist-blades",
|
|
275
|
+
"over-blade": "category-x-over-blades",
|
|
276
|
+
"metal-blade": "category-x-metal-blades",
|
|
277
277
|
"gear-chip": "category-x-custom-cx",
|
|
278
278
|
// Beyblade X — integrated parts file under the half a buyer shops for.
|
|
279
279
|
"ratchet-integrated-blade": "category-x-blades",
|
|
@@ -290,7 +290,7 @@ export const PART_SLOT_CATEGORY = {
|
|
|
290
290
|
"evolution-gear": "category-original-engine-gears",
|
|
291
291
|
"bit-protector": "category-original-bit-chips",
|
|
292
292
|
cew: "category-original-weight-disks",
|
|
293
|
-
"support-parts": "category-original-parts",
|
|
293
|
+
"support-parts": "category-original-support-parts",
|
|
294
294
|
};
|
|
295
295
|
/** Every slot key, for an index page that must not hand-write the list. */
|
|
296
296
|
export const ALL_PART_SLOTS = Object.values(PART_SLOT_VALUES);
|
|
@@ -23,5 +23,15 @@ export interface MarketplacePreorderCardProps {
|
|
|
23
23
|
onAddToCart?: (product: MarketplacePreorderCardData) => void;
|
|
24
24
|
wishlistActions?: WishlistToggleActions;
|
|
25
25
|
labels?: MarketplacePreorderCardLabels;
|
|
26
|
+
/**
|
|
27
|
+
* Owner-only pencil / clone / bin. Pass `<OwnerQuickActions>`; it renders
|
|
28
|
+
* nothing for a visitor who does not own the record.
|
|
29
|
+
*
|
|
30
|
+
* 🛑 Rendered OUTSIDE this card's own anchor, as a sibling. A `<button>`
|
|
31
|
+
* inside an `<a>` is invalid HTML that browsers resolve by closing the
|
|
32
|
+
* anchor early, which breaks the CARD's navigation rather than just the
|
|
33
|
+
* button's — `stopPropagation` stops a click but cannot fix a parse.
|
|
34
|
+
*/
|
|
35
|
+
renderOwnerActions?: () => React.ReactNode;
|
|
26
36
|
}
|
|
27
|
-
export declare function MarketplacePreorderCard({ product, className, variant, selectable, isSelected, onSelect, inWishlist: initialInWishlist, href, hrefBuilder, onNavigate, onAddToCart, wishlistActions, labels, }: MarketplacePreorderCardProps): import("react").JSX.Element;
|
|
37
|
+
export declare function MarketplacePreorderCard({ product, className, variant, selectable, isSelected, onSelect, inWishlist: initialInWishlist, href, hrefBuilder, onNavigate, onAddToCart, wishlistActions, labels, renderOwnerActions, }: MarketplacePreorderCardProps): import("react").JSX.Element;
|
|
@@ -14,7 +14,7 @@ const CLS_PREORDER_BADGE = "inline-flex items-center rounded-full bg-warning-sol
|
|
|
14
14
|
const CLS_SALE_BADGE = "rounded-full bg-success-solid px-[var(--appkit-space-2)] py-[var(--appkit-space-0-5)] text-[length:var(--appkit-text-xs)] font-bold text-success-on-solid";
|
|
15
15
|
const CLS_TRENDING_BADGE = "rounded-full bg-indigo-600 px-[var(--appkit-space-2)] py-[var(--appkit-space-0-5)] text-[length:var(--appkit-text-xs)] font-bold text-white";
|
|
16
16
|
const CLS_LIMITED_BADGE = "rounded-full bg-info-solid px-[var(--appkit-space-2)] py-[var(--appkit-space-0-5)] text-[length:var(--appkit-text-xs)] font-bold text-info-on-solid";
|
|
17
|
-
import { BaseListingCard, Button, GatedPrice, RichText, Row, Span, Stack, Text, TextLink } from "../../../ui/index.js";
|
|
17
|
+
import { Div, BaseListingCard, Button, GatedPrice, RichText, Row, Span, Stack, Text, TextLink } from "../../../ui/index.js";
|
|
18
18
|
import { normalizeRichTextHtml } from "../../../utils/string.formatter.js";
|
|
19
19
|
import { PreorderBadge } from "./PreorderCard.js";
|
|
20
20
|
import { WishlistHeartButton } from "../../wishlist/components/WishlistHeartButton.js";
|
|
@@ -34,7 +34,7 @@ function resolveHref(product, href, hrefBuilder) {
|
|
|
34
34
|
return hrefBuilder(product);
|
|
35
35
|
return ROUTES.PUBLIC.PRE_ORDER_DETAIL(product.slug ?? product.id);
|
|
36
36
|
}
|
|
37
|
-
export function MarketplacePreorderCard({ product, className = "", variant = "grid", selectable = false, isSelected = false, onSelect, inWishlist: initialInWishlist = false, href, hrefBuilder, onNavigate, onAddToCart, wishlistActions, labels, }) {
|
|
37
|
+
export function MarketplacePreorderCard({ product, className = "", variant = "grid", selectable = false, isSelected = false, onSelect, inWishlist: initialInWishlist = false, href, hrefBuilder, onNavigate, onAddToCart, wishlistActions, labels, renderOwnerActions, }) {
|
|
38
38
|
const router = useRouter();
|
|
39
39
|
const mergedLabels = { ...DEFAULT_LABELS, ...labels };
|
|
40
40
|
const detailHref = resolveHref(product, href, hrefBuilder);
|
|
@@ -61,7 +61,7 @@ export function MarketplacePreorderCard({ product, className = "", variant = "gr
|
|
|
61
61
|
void normalizeError(_err);
|
|
62
62
|
}
|
|
63
63
|
}, [toggle]);
|
|
64
|
-
|
|
64
|
+
const card = (_jsxs(BaseListingCard, { isSelected: isSelected, variant: variant, className: className, onMouseDown: !isSelected ? longPress.onMouseDown : undefined, onMouseUp: !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: !isSelected ? longPress.onTouchEnd : undefined, onTouchCancel: !isSelected ? longPress.onTouchCancel : undefined, children: [_jsxs(BaseListingCard.Hero, { aspect: "square", variant: variant, children: [_jsx(TextLink, { href: detailHref, className: "absolute inset-0 block", children: _jsx(MediaImage, { src: product.mainImage, alt: product.title, size: "card", fallback: "Product image" }) }), _jsxs(Stack, { className: "absolute right-2 top-2", align: "end", gap: "xs", children: [_jsx(Span, { layout: "inline-flex", color: "inverse", size: "xs", weight: "medium", className: "bg-cobalt", rounded: "full", padding: "pill-xs", children: mergedLabels.preOrderBadge }), product.featured && (_jsx(Span, { size: "xs", weight: "medium", className: CLS_PREORDER_BADGE, children: mergedLabels.featuredBadge }))] }), _jsxs(Stack, { className: "absolute left-2 top-2 pointer-events-none", gap: "xs", children: [product.partOfBundleIds && product.partOfBundleIds.length > 0 && (_jsx(Span, { className: CLS_SALE_BADGE, title: product.partOfBundleTitles?.[0] ? `In bundle: ${product.partOfBundleTitles[0]}` : "In a bundle", children: "Bundled" })), product.groupId && (_jsx(Span, { className: CLS_TRENDING_BADGE, title: product.groupTitle ? `Part of set: ${product.groupTitle}` : "Part of a set", children: product.isGroupParent ? "Set Parent" : "In Set" })), product.sublistingCategoryId && (_jsx(Span, { className: CLS_LIMITED_BADGE, title: "Has variants or sub-listings", children: "Has Variants" }))] }), onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (event) => {
|
|
65
65
|
event.stopPropagation();
|
|
66
66
|
onSelect(product.id, !isSelected);
|
|
67
67
|
}, className: selectable || isSelected ? "opacity-100" : "opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto transition-opacity" }))] }), _jsx(BaseListingCard.Info, { variant: variant, children: variant === "list" ? (
|
|
@@ -69,4 +69,11 @@ export function MarketplacePreorderCard({ product, className = "", variant = "gr
|
|
|
69
69
|
_jsxs(_Fragment, { children: [_jsxs(Row, { className: "min-w-0", align: "start", justify: "between", gap: "sm", children: [_jsx(TextLink, { href: detailHref, className: "min-w-0 flex-1", children: _jsx(Text, { size: "sm", weight: "medium", color: "primary", truncate: 2, children: product.title }) }), wishlistActions && (_jsx(WishlistHeartButton, { size: "inline", inWishlist: inWishlist, addLabel: mergedLabels.addToWishlist, removeLabel: mergedLabels.removeFromWishlist, onToggle: handleWishlist }))] }), _jsx(CardStoreLine, { storeName: product.storeName, storeId: product.storeId }), _jsxs(Row, { align: "center", gap: "sm", wrap: true, children: [_jsx(Text, { className: "text-primary", size: "sm", weight: "bold", children: _jsx(GatedPrice, { children: formatCurrency(product.price, getDefaultCurrency()) }) }), shipDate && _jsx(PreorderBadge, { shipDate: shipDate })] }), _jsx(Button, { type: "button", variant: "primary", size: "sm", textSize: "xs", className: "self-start mt-0.5", onClick: handleNavigate, children: mergedLabels.reserveNow })] })) : (
|
|
70
70
|
/* ── Full grid layout ── */
|
|
71
71
|
_jsxs(_Fragment, { children: [_jsx(TextLink, { href: detailHref, children: _jsx(Text, { size: "sm", weight: "medium", color: "primary", truncate: 2, children: product.title }) }), _jsx(CardStoreLine, { storeName: product.storeName, storeId: product.storeId }), product.description ? (_jsx(RichText, { html: normalizeRichTextHtml(product.description), proseClass: "prose prose-sm max-w-none dark:prose-invert prose-p:my-0", className: "line-clamp-2 text-[length:var(--appkit-text-xs)] text-[var(--appkit-color-text-muted)]" })) : null, _jsxs(Row, { justify: "between", className: "mt-1", gap: "sm", children: [_jsx(Text, { size: "sm", weight: "semibold", color: "primary", children: _jsx(GatedPrice, { children: formatCurrency(product.price, getDefaultCurrency()) }) }), shipDate && _jsx(PreorderBadge, { shipDate: shipDate })] }), _jsxs(Row, { justify: "between", className: "mt-auto pt-[var(--appkit-space-2)]", gap: "sm", children: [_jsx(Button, { type: "button", variant: "primary", size: "sm", textSize: "xs", className: "flex-1", onClick: handleNavigate, children: mergedLabels.reserveNow }), onAddToCart ? (_jsx(Button, { type: "button", variant: "outline", size: "sm", textSize: "xs", className: "flex-1", onClick: (e) => { e.stopPropagation(); onAddToCart(product); }, children: mergedLabels.addToCart })) : null, wishlistActions ? (_jsx(WishlistHeartButton, { size: "inline", inWishlist: inWishlist, addLabel: mergedLabels.addToWishlist, removeLabel: mergedLabels.removeFromWishlist, onToggle: handleWishlist })) : null] })] })) })] }));
|
|
72
|
+
if (!renderOwnerActions)
|
|
73
|
+
return card;
|
|
74
|
+
/*
|
|
75
|
+
* Sibling of the card, never a child — see the prop's docstring. `z-20`
|
|
76
|
+
* clears this card's own overlays, which all sit at `z-10`.
|
|
77
|
+
*/
|
|
78
|
+
return (_jsxs(Div, { className: "relative h-full", children: [card, _jsx(Div, { className: "absolute right-2 bottom-2 z-20", children: renderOwnerActions() })] }));
|
|
72
79
|
}
|
|
@@ -27,6 +27,7 @@ import { ProductGalleryClient } from "../../products/components/ProductGalleryCl
|
|
|
27
27
|
import { ProductFeatureBadges } from "../../products/components/ProductFeatureBadges.js";
|
|
28
28
|
import { isFinalSale } from "../../products/constants/final-sale.js";
|
|
29
29
|
import { FeatureBadgeList } from "../../products/components/FeatureBadge.js";
|
|
30
|
+
import { productImageUrls } from "../../products/schemas/firestore.js";
|
|
30
31
|
import { ShareButton } from "../../products/components/ShareButton.js";
|
|
31
32
|
import { SublistingCarouselSection } from "../../products/components/SublistingCarouselSection.js";
|
|
32
33
|
import { HistoryTracker } from "../../history/components/HistoryTracker.js";
|
|
@@ -86,8 +87,12 @@ export async function PreOrderDetailPageView({ id, initialPreOrder, onReserveNow
|
|
|
86
87
|
const currency = p.currency || getDefaultCurrency();
|
|
87
88
|
const title = String(p.title ?? p.name ?? "Pre-Order Item");
|
|
88
89
|
const price = typeof p.price === "number" ? p.price : null;
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
// `images` is `ProductImage[]` since A6 — `productImageUrls` is the one
|
|
91
|
+
// normaliser (it also accepts the legacy `string[]`). Casting it to
|
|
92
|
+
// `string[]` handed the gallery objects and rendered a placeholder.
|
|
93
|
+
const galleryUrls = productImageUrls(p.images);
|
|
94
|
+
const images = galleryUrls.length > 0
|
|
95
|
+
? galleryUrls
|
|
91
96
|
: typeof p.mainImage === "string"
|
|
92
97
|
? [p.mainImage]
|
|
93
98
|
: [];
|
|
@@ -153,11 +158,7 @@ export async function PreOrderDetailPageView({ id, initialPreOrder, onReserveNow
|
|
|
153
158
|
]);
|
|
154
159
|
return (_jsxs(Main, { children: [_jsx(HistoryTracker, { productId: String(p.id ?? p.slug ?? ""), productType: "preorder", snapshot: {
|
|
155
160
|
title: typeof p.title === "string" ? p.title : undefined,
|
|
156
|
-
thumb: typeof p.mainImage === "string"
|
|
157
|
-
? p.mainImage
|
|
158
|
-
: Array.isArray(p.images) && typeof p.images[0] === "string"
|
|
159
|
-
? p.images[0]
|
|
160
|
-
: undefined,
|
|
161
|
+
thumb: typeof p.mainImage === "string" ? p.mainImage : images[0],
|
|
161
162
|
price: typeof p.price === "number" ? p.price : undefined,
|
|
162
163
|
storeId: typeof p.storeId === "string" ? p.storeId : undefined,
|
|
163
164
|
storeName: typeof p.storeName === "string" ? p.storeName : undefined,
|
|
@@ -79,6 +79,27 @@ export interface PriceIndexToolResponse {
|
|
|
79
79
|
truncated: boolean;
|
|
80
80
|
minConfidentSamples: number;
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* The competitor-crawl band for one leaf. Mirrors `PriceIndexMarketRow`.
|
|
84
|
+
*
|
|
85
|
+
* Every field but `median` and `categoryId` is optional because the crawl
|
|
86
|
+
* genuinely did not measure them everywhere, and an absent figure must render
|
|
87
|
+
* as absent. See `price-bands.md`: "a band with `n=2` is not evidence".
|
|
88
|
+
*/
|
|
89
|
+
export interface PriceIndexMarketRowView {
|
|
90
|
+
categoryId: string;
|
|
91
|
+
median: number;
|
|
92
|
+
p25?: number;
|
|
93
|
+
p75?: number;
|
|
94
|
+
sampleSize?: number;
|
|
95
|
+
asOf?: string;
|
|
96
|
+
soldOutShare?: number;
|
|
97
|
+
source?: string;
|
|
98
|
+
inherited: boolean;
|
|
99
|
+
viaCategoryId?: string;
|
|
100
|
+
viaName?: string;
|
|
101
|
+
taxBasis: string;
|
|
102
|
+
}
|
|
82
103
|
export interface PriceIndexToolViewProps {
|
|
83
104
|
/** Seeded from `?q=` so a shared link reproduces the lookup. */
|
|
84
105
|
initialQuery?: string;
|