@mohasinac/appkit 4.47.0 → 4.48.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 +4 -4
- package/dist/_internal/server/features/checkout/expand-order-items.js +3 -0
- package/dist/_internal/server/features/messages/actions.d.ts +68 -0
- package/dist/_internal/server/features/messages/actions.js +249 -0
- package/dist/_internal/server/features/parts/buildable.d.ts +47 -0
- package/dist/_internal/server/features/parts/buildable.js +159 -0
- package/dist/_internal/server/features/products/list-public.d.ts +17 -0
- package/dist/_internal/server/features/products/list-public.js +92 -5
- package/dist/_internal/server/features/products/og.d.ts +7 -0
- package/dist/_internal/server/features/products/og.js +38 -0
- package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
- package/dist/_internal/server/features/site-settings/adapters.js +6 -0
- package/dist/_internal/server/jobs/core/cleanupRtdbEvents.js +28 -0
- package/dist/_internal/server/jobs/core/notificationPrune.js +87 -3
- package/dist/_internal/server/jobs/core/priceIndexRollup.js +36 -1
- package/dist/_internal/shared/actions/action-registry.js +8 -0
- package/dist/_internal/shared/features/notifications/action-url.js +8 -0
- package/dist/_internal/shared/features/notifications/email-eligibility.js +3 -0
- package/dist/_internal/shared/features/site-settings/types.d.ts +4 -0
- package/dist/client.d.ts +10 -0
- package/dist/client.js +8 -0
- package/dist/constants/api-endpoints.d.ts +48 -0
- package/dist/constants/api-endpoints.js +30 -0
- package/dist/constants/notification-types.d.ts +1 -1
- package/dist/features/about/components/GstExplainerView.d.ts +28 -0
- package/dist/features/about/components/GstExplainerView.js +59 -0
- package/dist/features/about/components/GuidesHubView.d.ts +28 -0
- package/dist/features/about/components/GuidesHubView.js +58 -0
- package/dist/features/about/components/PublicProfileView.js +5 -0
- package/dist/features/about/components/SpotAFakeView.d.ts +29 -0
- package/dist/features/about/components/SpotAFakeView.js +49 -0
- package/dist/features/admin/actions/notification-actions.js +4 -0
- package/dist/features/admin/schemas/firestore.d.ts +13 -3
- package/dist/features/admin/schemas/firestore.js +8 -0
- package/dist/features/blog/repository/blog.repository.js +21 -7
- package/dist/features/categories/api/route.js +41 -1
- package/dist/features/categories/components/BreadcrumbTrail.d.ts +39 -17
- package/dist/features/categories/components/BreadcrumbTrail.js +46 -50
- package/dist/features/categories/components/CategoryDetailPageView.js +92 -38
- package/dist/features/categories/components/ContextualTrail.d.ts +62 -0
- package/dist/features/categories/components/ContextualTrail.js +81 -0
- package/dist/features/categories/components/LastCategoryBeacon.d.ts +18 -0
- package/dist/features/categories/components/LastCategoryBeacon.js +33 -0
- package/dist/features/categories/repository/categories.repository.d.ts +21 -0
- package/dist/features/categories/repository/categories.repository.js +36 -3
- package/dist/features/categories/repository/price-index.repository.d.ts +25 -0
- package/dist/features/email/notification-templates.js +4 -0
- package/dist/features/events/repository/events.repository.js +10 -3
- package/dist/features/faq/constants/read-caps.d.ts +29 -0
- package/dist/features/faq/constants/read-caps.js +29 -0
- package/dist/features/faq/repository/faqs.repository.d.ts +0 -2
- package/dist/features/faq/repository/faqs.repository.js +19 -16
- package/dist/features/homepage/components/AdSlot.js +10 -4
- package/dist/features/homepage/hooks/useActiveAd.d.ts +18 -1
- package/dist/features/homepage/hooks/useActiveAd.js +42 -2
- package/dist/features/homepage/lib/section-renderer.js +12 -2
- package/dist/features/layout/AppLayoutShell.d.ts +4 -1
- package/dist/features/layout/AppLayoutShell.js +2 -2
- package/dist/features/layout/TitleBarLayout.d.ts +4 -1
- package/dist/features/layout/TitleBarLayout.js +8 -4
- package/dist/features/media/AvatarUpload.js +8 -1
- package/dist/features/media/MediaAvatar.js +1 -1
- package/dist/features/media/MediaImage.js +3 -1
- package/dist/features/media/MediaLightbox.js +4 -1
- package/dist/features/media/MediaPickerModal.js +6 -1
- package/dist/features/media/MediaVideo.js +3 -1
- package/dist/features/messages/components/ConversationThreadView.d.ts +3 -0
- package/dist/features/messages/components/ConversationThreadView.js +75 -0
- package/dist/features/messages/components/ConversationsInboxView.d.ts +1 -0
- package/dist/features/messages/components/ConversationsInboxView.js +39 -0
- package/dist/features/messages/components/MessageSellerButton.d.ts +17 -0
- package/dist/features/messages/components/MessageSellerButton.js +33 -0
- package/dist/features/messages/hooks/useConversationChannel.d.ts +24 -0
- package/dist/features/messages/hooks/useConversationChannel.js +89 -0
- package/dist/features/messages/hooks/useMessages.d.ts +347 -0
- package/dist/features/messages/hooks/useMessages.js +113 -0
- package/dist/features/messages/repository/conversations.repository.d.ts +44 -0
- package/dist/features/messages/repository/conversations.repository.js +173 -0
- package/dist/features/messages/repository/messages.repository.d.ts +42 -0
- package/dist/features/messages/repository/messages.repository.js +142 -0
- package/dist/features/messages/schemas/firestore.d.ts +99 -0
- package/dist/features/messages/schemas/firestore.js +46 -0
- package/dist/features/orders/repository/orders.repository.js +21 -7
- package/dist/features/orders/schemas/firestore.d.ts +11 -0
- package/dist/features/products/components/CityCollectiblesView.d.ts +31 -0
- package/dist/features/products/components/CityCollectiblesView.js +82 -0
- package/dist/features/products/components/MarketPriceAnchor.d.ts +45 -0
- package/dist/features/products/components/MarketPriceAnchor.js +62 -0
- package/dist/features/products/components/ProductDetailPageView.js +71 -5
- package/dist/features/products/constants/meetup-cities.d.ts +47 -0
- package/dist/features/products/constants/meetup-cities.js +49 -0
- package/dist/features/products/repository/category-assignment.d.ts +82 -11
- package/dist/features/products/repository/category-assignment.js +93 -12
- package/dist/features/products/repository/products.repository.d.ts +12 -1
- package/dist/features/products/repository/products.repository.js +66 -34
- package/dist/features/products/schemas/firestore.d.ts +24 -2
- package/dist/features/products/schemas/firestore.js +13 -1
- package/dist/features/products/schemas/index.d.ts +2 -2
- package/dist/features/promotions/repository/coupons.repository.js +11 -4
- package/dist/features/reviews/repository/reviews.repository.js +11 -4
- package/dist/features/scams/repository/scammer.repository.js +21 -7
- package/dist/features/seller/components/SellerProductShell.d.ts +1 -1
- package/dist/features/seller/components/SellerProductShell.js +2 -2
- package/dist/features/seller/repository/offer.repository.js +21 -6
- package/dist/features/seller/schemas/product-form.d.ts +3 -3
- package/dist/features/seller/schemas/product-form.js +1 -1
- package/dist/features/server-errors/repository/server-errors.repository.js +11 -0
- package/dist/features/server-errors/schemas/firestore.d.ts +17 -0
- package/dist/features/server-errors/schemas/firestore.js +2 -0
- package/dist/features/store-extensions/repository/store-extensions.repositories.d.ts +17 -1
- package/dist/features/store-extensions/repository/store-extensions.repositories.js +31 -0
- package/dist/features/store-extensions/schemas/firestore.d.ts +1 -1
- package/dist/features/store-extensions/schemas/firestore.js +4 -0
- package/dist/features/stores/repository/store.repository.js +11 -3
- package/dist/features/tester/seed-data/authored/community-support__messages.d.ts +2 -0
- package/dist/features/tester/seed-data/authored/community-support__messages.js +200 -0
- package/dist/features/tester/seed-data/authored/index.js +2 -0
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +64 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.js +1 -1
- package/dist/next/routing/route-map.d.ts +14 -0
- package/dist/next/routing/route-map.js +26 -0
- package/dist/providers/db-firebase/rtdb-paths.d.ts +8 -0
- package/dist/providers/db-firebase/rtdb-paths.js +8 -0
- package/dist/repositories/index.d.ts +1 -0
- package/dist/repositories/index.js +4 -0
- package/dist/schemas/registry.d.ts +3 -3
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/pii-schemas.d.ts +17 -0
- package/dist/security/pii-schemas.js +15 -7
- package/dist/seed/_helpers/category-content-generated.d.ts +3 -3
- package/dist/seed/_helpers/category-content-generated.js +23 -7
- package/dist/seed/_helpers/category-models.d.ts +1 -1
- package/dist/seed/_helpers/category-models.js +25 -1
- package/dist/seed/_helpers/category-names.d.ts +54 -0
- package/dist/seed/_helpers/category-names.js +118 -0
- package/dist/seed/_helpers/character-features.d.ts +1 -1
- package/dist/seed/_helpers/character-features.js +5 -4
- package/dist/seed/_helpers/final-sale-fixtures.js +1 -1
- package/dist/seed/categories-seed-data.js +55 -1
- package/dist/seed/grouped-listings-seed-data.js +2 -2
- package/dist/seed/products-art-seed-data.js +2 -1
- package/dist/seed/products-auctions-seed-data.js +42 -22
- package/dist/seed/products-classifieds-seed-data.js +21 -3
- package/dist/seed/products-digital-codes-seed-data.js +2 -1
- package/dist/seed/products-live-items-seed-data.js +2 -1
- package/dist/seed/products-preorders-seed-data.js +26 -11
- package/dist/seed/products-prize-draws-seed-data.js +8 -1
- package/dist/seed/products-standard-seed-data.js +26 -25
- package/dist/seed/products-stickers-seed-data.js +12 -11
- package/dist/server.d.ts +8 -0
- package/dist/server.js +15 -0
- package/dist/styles.css +113 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/sunset.js +17 -0
- package/dist/tokens/tokens.css +5 -0
- package/dist/ui/components/Breadcrumb.d.ts +8 -1
- package/dist/ui/components/Breadcrumb.js +2 -2
- package/dist/ui/components/Breadcrumb.style.css +37 -0
- package/dist/ui/components/Details.d.ts +11 -1
- package/dist/ui/components/Details.js +2 -2
- package/dist/ui/components/ImageMosaic.d.ts +43 -0
- package/dist/ui/components/ImageMosaic.js +46 -0
- package/dist/ui/components/ImageMosaic.style.css +69 -0
- package/dist/ui/components/SplitTile.d.ts +34 -0
- package/dist/ui/components/SplitTile.js +28 -0
- package/dist/ui/components/index.style.css +1 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.js +4 -0
- package/dist/utils/search-txt-query.d.ts +77 -0
- package/dist/utils/search-txt-query.js +50 -28
- package/package.json +1 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { CATEGORY_FOREST } from "./category-forest.js";
|
|
2
|
+
let nameById = null;
|
|
3
|
+
/** id -> its ancestors, NEAREST FIRST (parent, grandparent, …, root). */
|
|
4
|
+
let ancestorsById = null;
|
|
5
|
+
function build() {
|
|
6
|
+
const n = new Map();
|
|
7
|
+
const a = new Map();
|
|
8
|
+
const walk = (nodes, trail) => {
|
|
9
|
+
for (const node of nodes ?? []) {
|
|
10
|
+
if (typeof node.id === "string") {
|
|
11
|
+
n.set(node.id, node.name);
|
|
12
|
+
a.set(node.id, [...trail]);
|
|
13
|
+
walk(node.children, [node.id, ...trail]);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
// A node with no id contributes no ancestry, but its children still do.
|
|
17
|
+
walk(node.children, trail);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
walk(CATEGORY_FOREST, []);
|
|
22
|
+
nameById = n;
|
|
23
|
+
ancestorsById = a;
|
|
24
|
+
}
|
|
25
|
+
function names() {
|
|
26
|
+
if (!nameById)
|
|
27
|
+
build();
|
|
28
|
+
return nameById;
|
|
29
|
+
}
|
|
30
|
+
function ancestors() {
|
|
31
|
+
if (!ancestorsById)
|
|
32
|
+
build();
|
|
33
|
+
return ancestorsById;
|
|
34
|
+
}
|
|
35
|
+
export function deriveCategoryNames(slugs) {
|
|
36
|
+
if (!Array.isArray(slugs) || slugs.length === 0)
|
|
37
|
+
return undefined;
|
|
38
|
+
const byId = names();
|
|
39
|
+
return slugs.map((id) => byId.get(id) ?? id);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Derive the full ancestor closure from the head of an existing chain.
|
|
43
|
+
*
|
|
44
|
+
* ## 🛑 The drift this ends — measured, 6 of 1,039 live products
|
|
45
|
+
*
|
|
46
|
+
* `categorySlugs` is the ancestor closure and it was hand-written per record
|
|
47
|
+
* too, so a chain could SKIP A TIER and nothing noticed:
|
|
48
|
+
*
|
|
49
|
+
* product-beyblade-original-driger-v
|
|
50
|
+
* got driger-v > original-tops > beyblade-original > spinning-tops
|
|
51
|
+
* expected driger-v > original-magnacore > original-tops > …
|
|
52
|
+
*
|
|
53
|
+
* The category page matches `array-contains-any [its own id]` against this
|
|
54
|
+
* array (CLAUDE.md: "a category page matches on its own id alone"), so a
|
|
55
|
+
* missing intermediate means the product is **absent from that node's page**
|
|
56
|
+
* while appearing correctly on every other node in its chain. Six products were
|
|
57
|
+
* invisible on their own system sub-line.
|
|
58
|
+
*
|
|
59
|
+
* `countersReconcile`'s rollup invariant cannot see this: it computes the counts
|
|
60
|
+
* FROM these closures, so a skipped tier is simply a tier with a lower count,
|
|
61
|
+
* and `own + Σ(children.total) === total` still holds exactly. The only way to
|
|
62
|
+
* find it is to re-derive the chain and compare.
|
|
63
|
+
*
|
|
64
|
+
* ## 🛑 A SECOND ASSIGNMENT IS LEFT ALONE, NOT COLLAPSED
|
|
65
|
+
*
|
|
66
|
+
* This derives from `slugs[0]` — the head, which is what `readAssignments`
|
|
67
|
+
* treats as the assignment. A product filed under two roots would carry two
|
|
68
|
+
* concatenated closures, and deriving from the head alone would silently DELETE
|
|
69
|
+
* the second one, turning a drift-fix into data loss.
|
|
70
|
+
*
|
|
71
|
+
* So the rule is: overwrite only when the existing array is a SUBSET of the
|
|
72
|
+
* derived closure. That is precisely the drift case (ids missing from the
|
|
73
|
+
* middle). An id outside the derived closure means either a second assignment
|
|
74
|
+
* or something this helper cannot safely interpret, and both must survive to be
|
|
75
|
+
* reported rather than be quietly rewritten.
|
|
76
|
+
*
|
|
77
|
+
* Measured when this was written: 0 of 71 seeded products carry `categoryIds`
|
|
78
|
+
* and 0 carry a multi-root closure — so the subset rule fires on all six real
|
|
79
|
+
* cases and guards a case that does not exist yet.
|
|
80
|
+
*/
|
|
81
|
+
export function deriveCategoryClosure(slugs) {
|
|
82
|
+
if (!Array.isArray(slugs) || slugs.length === 0)
|
|
83
|
+
return undefined;
|
|
84
|
+
const head = slugs[0];
|
|
85
|
+
const chain = ancestors().get(head);
|
|
86
|
+
// An unknown head keeps its hand-written chain: inventing one from a node the
|
|
87
|
+
// forest does not have would be worse than leaving a visibly wrong value for
|
|
88
|
+
// the dangling-id sweep to find.
|
|
89
|
+
if (!chain)
|
|
90
|
+
return undefined;
|
|
91
|
+
const derived = [head, ...chain];
|
|
92
|
+
const outside = slugs.filter((s) => !derived.includes(s));
|
|
93
|
+
if (outside.length > 0)
|
|
94
|
+
return undefined;
|
|
95
|
+
return derived;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Pipeline step: replace whatever `categorySlugs` and `categoryNames` a record
|
|
99
|
+
* carries with the derivation. Deliberately an OVERWRITE rather than a
|
|
100
|
+
* fill-if-absent — a hand-written value that disagrees IS the defect, so
|
|
101
|
+
* preferring it would preserve every one of them.
|
|
102
|
+
*
|
|
103
|
+
* Order matters: the closure is derived first, then the names from the derived
|
|
104
|
+
* closure. Deriving names from the stale chain and then fixing the chain would
|
|
105
|
+
* leave the two mirrors disagreeing again, one tier apart.
|
|
106
|
+
*/
|
|
107
|
+
export function withDerivedCategoryNames(p) {
|
|
108
|
+
const closure = deriveCategoryClosure(p.categorySlugs);
|
|
109
|
+
const slugs = closure ?? p.categorySlugs;
|
|
110
|
+
const names = deriveCategoryNames(slugs);
|
|
111
|
+
if (!closure && !names)
|
|
112
|
+
return p;
|
|
113
|
+
return {
|
|
114
|
+
...p,
|
|
115
|
+
...(closure ? { categorySlugs: closure } : {}),
|
|
116
|
+
...(names ? { categoryNames: names } : {}),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
*/
|
|
23
23
|
export const CHARACTER_SEEDS = [
|
|
24
24
|
{ slug: "blader-tyson-granger", name: "Tyson Granger", beyCount: 9 },
|
|
25
|
-
{ slug: "blader-kai-hiwatari", name: "Kai Hiwatari", beyCount:
|
|
25
|
+
{ slug: "blader-kai-hiwatari", name: "Kai Hiwatari", beyCount: 8 },
|
|
26
|
+
{ slug: "blader-shu-kurenai", name: "Shu Kurenai", beyCount: 7 },
|
|
26
27
|
{ slug: "blader-daichi-sumeragi", name: "Daichi Sumeragi", beyCount: 6 },
|
|
27
28
|
{ slug: "blader-max-tate", name: "Max Tate", beyCount: 6 },
|
|
28
29
|
{ slug: "blader-ray-kon", name: "Ray Kon", beyCount: 6 },
|
|
29
|
-
{ slug: "blader-shu-kurenai", name: "Shu Kurenai", beyCount: 6 },
|
|
30
30
|
{ slug: "blader-gingka-hagane", name: "Gingka Hagane", beyCount: 5 },
|
|
31
31
|
{ slug: "blader-lui-shirosagi", name: "Lui Shirosagi", beyCount: 5 },
|
|
32
32
|
{ slug: "blader-tala", name: "Tala", beyCount: 5 },
|
|
@@ -56,6 +56,7 @@ export const CHARACTER_SEEDS = [
|
|
|
56
56
|
{ slug: "blader-michael-summers", name: "Michael Summers", beyCount: 2 },
|
|
57
57
|
{ slug: "blader-miguel", name: "Miguel", beyCount: 2 },
|
|
58
58
|
{ slug: "blader-ozuma", name: "Ozuma", beyCount: 2 },
|
|
59
|
+
{ slug: "blader-rago", name: "Rago", beyCount: 2 },
|
|
59
60
|
{ slug: "blader-robin-kazami", name: "Robin Kazami", beyCount: 2 },
|
|
60
61
|
{ slug: "blader-tsubasa-otori", name: "Tsubasa Otori", beyCount: 2 },
|
|
61
62
|
{ slug: "blader-wakiya-murasaki", name: "Wakiya Murasaki", beyCount: 2 },
|
|
@@ -75,6 +76,7 @@ export const CHARACTER_SEEDS = [
|
|
|
75
76
|
{ slug: "blader-dan-sodo-and-reiki-sodo", name: "Dan Sodo and Reiki Sodo", beyCount: 1 },
|
|
76
77
|
{ slug: "blader-doji", name: "Doji", beyCount: 1 },
|
|
77
78
|
{ slug: "blader-faust", name: "Faust", beyCount: 1 },
|
|
79
|
+
{ slug: "blader-fubuki-sumiye", name: "Fubuki Sumiye", beyCount: 1 },
|
|
78
80
|
{ slug: "blader-garland-siebald", name: "Garland Siebald", beyCount: 1 },
|
|
79
81
|
{ slug: "blader-gary", name: "Gary", beyCount: 1 },
|
|
80
82
|
{ slug: "blader-ginro", name: "Ginro", beyCount: 1 },
|
|
@@ -98,7 +100,6 @@ export const CHARACTER_SEEDS = [
|
|
|
98
100
|
{ slug: "blader-nine-kurosu", name: "Nine Kurosu", beyCount: 1 },
|
|
99
101
|
{ slug: "blader-omega-shiroboshi", name: "Omega Shiroboshi", beyCount: 1 },
|
|
100
102
|
{ slug: "blader-phi", name: "Phi", beyCount: 1 },
|
|
101
|
-
{ slug: "blader-rago", name: "Rago", beyCount: 1 },
|
|
102
103
|
{ slug: "blader-reiji-mizuchi", name: "Reiji Mizuchi", beyCount: 1 },
|
|
103
104
|
{ slug: "blader-reiyu-kuwabara", name: "Reiyu Kuwabara", beyCount: 1 },
|
|
104
105
|
{ slug: "blader-rex-jura", name: "Rex Jura", beyCount: 1 },
|
|
@@ -115,4 +116,4 @@ export const CHARACTER_SEEDS = [
|
|
|
115
116
|
{ slug: "blader-yuni-naniwa", name: "Yuni Naniwa", beyCount: 1 },
|
|
116
117
|
{ slug: "blader-zyro-kurogane", name: "Zyro Kurogane", beyCount: 1 },
|
|
117
118
|
];
|
|
118
|
-
export const CHARACTER_SEED_COUNT =
|
|
119
|
+
export const CHARACTER_SEED_COUNT = 94;
|
|
@@ -36,7 +36,7 @@ export const RETURNABLE_FIXTURE_IDS = new Set([
|
|
|
36
36
|
// art
|
|
37
37
|
"art-valkyrie-holographic-print",
|
|
38
38
|
// stickers
|
|
39
|
-
"
|
|
39
|
+
"sticker-beyblade-x-holographic-pack",
|
|
40
40
|
]);
|
|
41
41
|
/**
|
|
42
42
|
* Set `finalSale: false` on the opted-out fixtures and leave it ABSENT on
|
|
@@ -59,6 +59,7 @@ import { BRAND_ROW_COUNT, buildBrandRows } from "./_helpers/brand-rows.js";
|
|
|
59
59
|
import { MODEL_LEAF_COUNT } from "./_helpers/category-models.js";
|
|
60
60
|
import { PART_LEAF_COUNT } from "./_helpers/category-parts.js";
|
|
61
61
|
import { MARKET_BAND_LEAF_IDS } from "./_helpers/category-content.js";
|
|
62
|
+
import { GENERATED_CATEGORY_CONTENT, GENERATED_CATEGORY_CONTENT_COUNT, } from "./_helpers/category-content-generated.js";
|
|
62
63
|
import { productsStandardSeedData } from "./products-standard-seed-data.js";
|
|
63
64
|
const NOW = new Date();
|
|
64
65
|
const daysAgo = (n) => new Date(NOW.getTime() - n * 86400000);
|
|
@@ -442,12 +443,42 @@ const bundleRows = [
|
|
|
442
443
|
* name/slug/description only and silently drop the lineage search — typing
|
|
443
444
|
* "burst" would stop reaching a tier-4 model under Beyblade Burst.
|
|
444
445
|
*/
|
|
446
|
+
/**
|
|
447
|
+
* I7 — attach the GENERATED body to any row that has one and does not already
|
|
448
|
+
* carry a hand-authored one.
|
|
449
|
+
*
|
|
450
|
+
* 🛑 The generator ran and its 745 bodies were imported by NOBODY. The corpus
|
|
451
|
+
* sat in the repo as 128 KB of dead weight: not seeded, so not in Firestore,
|
|
452
|
+
* so unreadable by the page that renders it. The generator being written is
|
|
453
|
+
* not the same as its output being wired, and the second half is invisible —
|
|
454
|
+
* a category page with no body renders perfectly well, it just ranks on
|
|
455
|
+
* nothing.
|
|
456
|
+
*
|
|
457
|
+
* 🛑 A HAND-AUTHORED body always wins. The four generation roots and several
|
|
458
|
+
* tier-1 nodes carry prose written against the crawl research; the generator
|
|
459
|
+
* composes from facts and is deliberately the weaker writer at those tiers
|
|
460
|
+
* (its own header says the part leaves are its strongest tier). Overwriting
|
|
461
|
+
* authored copy with composed copy would be a silent downgrade of the exact
|
|
462
|
+
* pages this is for.
|
|
463
|
+
*
|
|
464
|
+
* 🛑 And nothing is INVENTED here: a row absent from the map keeps no body at
|
|
465
|
+
* all, because `GENERATED_CATEGORY_CONTENT` omits a node it has nothing
|
|
466
|
+
* factual to say about rather than emitting an empty one.
|
|
467
|
+
*/
|
|
468
|
+
function withGeneratedContent(row) {
|
|
469
|
+
if (typeof row.contentBody === "string" && row.contentBody.trim().length > 0)
|
|
470
|
+
return row;
|
|
471
|
+
const generated = row.id ? GENERATED_CATEGORY_CONTENT[row.id] : undefined;
|
|
472
|
+
return generated ? { ...row, contentBody: generated } : row;
|
|
473
|
+
}
|
|
445
474
|
const allCategoryRows = [
|
|
446
475
|
...rawCategories.map((c) => ({ ancestors: [], ...c, createdByType: "admin" })),
|
|
447
476
|
...sublistingRows.map((s) => ({ ancestors: [], ...s, createdByType: "admin" })),
|
|
448
477
|
...brandRows.map((b) => ({ ancestors: [], ...b, createdByType: "admin" })),
|
|
449
478
|
...bundleRows.map((b) => ({ ancestors: [], ...b })),
|
|
450
|
-
]
|
|
479
|
+
]
|
|
480
|
+
.map(withGeneratedContent)
|
|
481
|
+
.map(withCategorySearchTxt);
|
|
451
482
|
/*
|
|
452
483
|
* ── Assertions, at MODULE LOAD, so a silent drop is loud ─────────────────────
|
|
453
484
|
*
|
|
@@ -487,6 +518,29 @@ const allCategoryRows = [
|
|
|
487
518
|
* Counting rows is not counting documents, and this is the one check that can
|
|
488
519
|
* tell the difference BEFORE anything is written.
|
|
489
520
|
*/
|
|
521
|
+
/*
|
|
522
|
+
* 🛑 THE GENERATED BODIES ACTUALLY LANDED ON ROWS.
|
|
523
|
+
*
|
|
524
|
+
* I7's corpus is a `Record<id, html>` keyed on category ids, and a key that no
|
|
525
|
+
* longer matches a row is invisible everywhere: the map still exports 745
|
|
526
|
+
* entries, the seed still loads 1,173 rows, `appkit-seed status` still reports
|
|
527
|
+
* in sync, and every one of those category pages renders without a body. That
|
|
528
|
+
* is the same failure shape as the 20 ids that overwrote each other below —
|
|
529
|
+
* counting the SOURCE is not counting what reached a row.
|
|
530
|
+
*
|
|
531
|
+
* Tolerance rather than exact equality, for one real reason: a hand-authored
|
|
532
|
+
* body legitimately wins over a generated one, so the number of rows carrying
|
|
533
|
+
* a GENERATED body is `COUNT` minus however many nodes have been written by
|
|
534
|
+
* hand since. A floor of 90% catches a key-space break (which drops it to ~0)
|
|
535
|
+
* without failing on authored prose being added.
|
|
536
|
+
*/
|
|
537
|
+
const generatedApplied = allCategoryRows.filter((c) => typeof c.id === "string" && GENERATED_CATEGORY_CONTENT[c.id] === c.contentBody).length;
|
|
538
|
+
if (generatedApplied < Math.floor(GENERATED_CATEGORY_CONTENT_COUNT * 0.9)) {
|
|
539
|
+
throw new Error(`category seed: only ${generatedApplied} of ${GENERATED_CATEGORY_CONTENT_COUNT} generated ` +
|
|
540
|
+
`content bodies matched a row id. The corpus is keyed on category ids, so this means ` +
|
|
541
|
+
`the key space has drifted — re-run scripts/gen-category-content.mjs against the ` +
|
|
542
|
+
`current forest. Nothing would error at load time: the pages would simply have no body.`);
|
|
543
|
+
}
|
|
490
544
|
const allIds = allCategoryRows.map((c) => c.id).filter((x) => typeof x === "string");
|
|
491
545
|
const duplicateIds = [...new Set(allIds.filter((id, i) => allIds.indexOf(id) !== i))];
|
|
492
546
|
if (duplicateIds.length > 0) {
|
|
@@ -226,8 +226,8 @@ export const groupedListingsSeedData = [
|
|
|
226
226
|
productIds: [
|
|
227
227
|
"art-dranzer-phoenix-poster",
|
|
228
228
|
"art-valkyrie-holographic-print",
|
|
229
|
-
"
|
|
230
|
-
"
|
|
229
|
+
"sticker-beyblade-original-classic-sheet",
|
|
230
|
+
"sticker-beyblade-x-holographic-pack",
|
|
231
231
|
],
|
|
232
232
|
coverImage: seedPhoto("group-beyblade-fan-art-and-stickers-20260819", 1200, 900),
|
|
233
233
|
groupTheme: "related",
|
|
@@ -21,6 +21,7 @@ import { PRODUCT_FIELDS } from "../constants/field-names.js";
|
|
|
21
21
|
import { buildSearchTxt } from "../utils/search-txt.js";
|
|
22
22
|
import { seedPhoto } from "./_helpers/media.js";
|
|
23
23
|
import { withFinalSale } from "./_helpers/final-sale-fixtures.js";
|
|
24
|
+
import { withDerivedCategoryNames } from "./_helpers/category-names.js";
|
|
24
25
|
const _rawProductsArtSeedData = [
|
|
25
26
|
{
|
|
26
27
|
id: "art-dranzer-phoenix-poster",
|
|
@@ -296,4 +297,4 @@ const _rawProductsArtSeedData = [
|
|
|
296
297
|
* so the final-sale opt-outs are applied the same way everywhere rather than
|
|
297
298
|
* being hand-written into two records that reviewers would have to find.
|
|
298
299
|
*/
|
|
299
|
-
export const productsArtSeedData = _rawProductsArtSeedData.map(withFinalSale);
|
|
300
|
+
export const productsArtSeedData = _rawProductsArtSeedData.map(withFinalSale).map(withDerivedCategoryNames);
|
|
@@ -26,6 +26,7 @@ import { PRODUCT_FIELDS } from "../constants/field-names.js";
|
|
|
26
26
|
import { buildSearchTxt } from "../utils/search-txt.js";
|
|
27
27
|
import { seedPhoto } from "./_helpers/media.js";
|
|
28
28
|
import { withFinalSale } from "./_helpers/final-sale-fixtures.js";
|
|
29
|
+
import { withDerivedCategoryNames } from "./_helpers/category-names.js";
|
|
29
30
|
function withTokens(p) {
|
|
30
31
|
return {
|
|
31
32
|
tags: [],
|
|
@@ -53,7 +54,7 @@ const _rawProductsAuctionsSeedData = [
|
|
|
53
54
|
slug: "auction-beyblade-original-dragoon-storm",
|
|
54
55
|
title: "Beyblade Original — Dragoon Storm (Rare Sealed)",
|
|
55
56
|
description: "Sealed, unopened Dragoon Storm from the original 1999-2003 series. A grail piece for original-generation collectors.",
|
|
56
|
-
categorySlugs: ["category-
|
|
57
|
+
categorySlugs: ["category-dragoon-storm", "category-original-tops", "category-beyblade-original", "category-spinning-tops"],
|
|
57
58
|
categoryNames: ["Plastic Generation", "Original Tops", "Beyblade Original", "Spinning Tops"],
|
|
58
59
|
brandSlug: "brand-beyblade",
|
|
59
60
|
brand: "Beyblade",
|
|
@@ -97,7 +98,7 @@ const _rawProductsAuctionsSeedData = [
|
|
|
97
98
|
slug: "auction-beyblade-metal-lightning-l-drago",
|
|
98
99
|
title: "Metal Fight Beyblade BB-99 Lightning L-Drago",
|
|
99
100
|
description: "Lightning L-Drago 100HF/S, a highly sought-after Metal Fury era top in excellent condition.",
|
|
100
|
-
categorySlugs: ["category-metal-
|
|
101
|
+
categorySlugs: ["category-lightning-l-drago-100hf", "category-metal-fusion", "category-metal-tops", "category-beyblade-metal", "category-spinning-tops"],
|
|
101
102
|
categoryNames: ["Metal Masters", "Metal Fight Tops", "Beyblade Metal Fight", "Spinning Tops"],
|
|
102
103
|
brandSlug: "brand-takara-tomy",
|
|
103
104
|
brand: "Takara-Tomy",
|
|
@@ -138,7 +139,7 @@ const _rawProductsAuctionsSeedData = [
|
|
|
138
139
|
slug: "auction-beyblade-x-shark-edge",
|
|
139
140
|
title: "Beyblade X BX-04 Shark Edge (Buy It Now Available)",
|
|
140
141
|
description: "Shark Edge 3-60GF straight from a fresh case pull. Grab it at the Buy It Now price before the clock runs, or start the bidding.",
|
|
141
|
-
categorySlugs: ["category-
|
|
142
|
+
categorySlugs: ["category-sharkedge-3-60lf", "category-x-tops", "category-beyblade-x", "category-spinning-tops"],
|
|
142
143
|
categoryNames: ["Basic Line", "Beyblade X Tops", "Beyblade X", "Spinning Tops"],
|
|
143
144
|
brandSlug: "brand-takara-tomy",
|
|
144
145
|
brand: "Takara-Tomy",
|
|
@@ -179,7 +180,7 @@ const _rawProductsAuctionsSeedData = [
|
|
|
179
180
|
slug: "auction-beyblade-burst-xcalius-passed-bin",
|
|
180
181
|
title: "Beyblade Burst Xcalius X2 (bidding passed Buy It Now)",
|
|
181
182
|
description: "Xcalius X2 attack type. The Buy It Now price has already been overtaken by live bidding, so only bidding remains.",
|
|
182
|
-
categorySlugs: ["category-
|
|
183
|
+
categorySlugs: ["category-xcalius-x2-1iron", "category-burst-classic", "category-burst-tops", "category-beyblade-burst", "category-spinning-tops"],
|
|
183
184
|
categoryNames: ["Cho-Z", "Burst Tops", "Beyblade Burst", "Spinning Tops"],
|
|
184
185
|
brandSlug: "brand-takara-tomy",
|
|
185
186
|
brand: "Takara-Tomy",
|
|
@@ -215,7 +216,13 @@ const _rawProductsAuctionsSeedData = [
|
|
|
215
216
|
slug: "auction-beyblade-original-seaborg",
|
|
216
217
|
title: "Beyblade Original — Seaborg 2000 (Reserve Auction)",
|
|
217
218
|
description: "Rare Seaborg 2000 defense-type top from the original series. Reserve price set by the seller — current bids haven't met it yet.",
|
|
218
|
-
|
|
219
|
+
/*
|
|
220
|
+
* Was filed at `category-original-hms` — the Heavy Metal System line, a
|
|
221
|
+
* DIFFERENT generation from the one its own copy names ("from the original
|
|
222
|
+
* series"). Seaborg 2000 is the Spin Gear System release the canonical
|
|
223
|
+
* corpus carries as "Seaborg 2", which is its leaf.
|
|
224
|
+
*/
|
|
225
|
+
categorySlugs: ["category-seaborg-2", "category-original-spin-gear", "category-original-tops", "category-beyblade-original", "category-spinning-tops"],
|
|
219
226
|
categoryNames: ["Heavy Metal System", "Original Tops", "Beyblade Original", "Spinning Tops"],
|
|
220
227
|
brandSlug: "brand-beyblade",
|
|
221
228
|
brand: "Beyblade",
|
|
@@ -252,7 +259,7 @@ const _rawProductsAuctionsSeedData = [
|
|
|
252
259
|
slug: "auction-beyblade-metal-diablo-nemesis",
|
|
253
260
|
title: "Metal Fight Beyblade BB-122 Diablo Nemesis (Ended — Sold)",
|
|
254
261
|
description: "Diablo Nemesis XF, the final boss top of Metal Fight Beyblade Zero-G. Auction closed with a winning bidder.",
|
|
255
|
-
categorySlugs: ["category-metal-fury-4d", "category-metal-tops", "category-beyblade-metal", "category-spinning-tops"],
|
|
262
|
+
categorySlugs: ["category-diablo-nemesis-x-d", "category-metal-fury-4d", "category-metal-tops", "category-beyblade-metal", "category-spinning-tops"],
|
|
256
263
|
categoryNames: ["Metal Fury (4D)", "Metal Fight Tops", "Beyblade Metal Fight", "Spinning Tops"],
|
|
257
264
|
brandSlug: "brand-takara-tomy",
|
|
258
265
|
brand: "Takara-Tomy",
|
|
@@ -294,7 +301,7 @@ const _rawProductsAuctionsSeedData = [
|
|
|
294
301
|
slug: "auction-beyblade-burst-spriggan-requiem-bought-out",
|
|
295
302
|
title: "Beyblade Burst B-128 Spriggan Requiem (Ended — Bought Out)",
|
|
296
303
|
description: "Spriggan Requiem, the dual-spin legend of Burst Super Z. Bidding had reached ₹3,100 when a buyer took the ₹4,999 Buy Now price and ended the auction early.",
|
|
297
|
-
categorySlugs: ["category-burst-tops", "category-beyblade-burst", "category-spinning-tops"],
|
|
304
|
+
categorySlugs: ["category-spriggan-requiem-0-zeta", "category-burst-cho-z", "category-burst-tops", "category-beyblade-burst", "category-spinning-tops"],
|
|
298
305
|
categoryNames: ["Burst Tops", "Beyblade Burst", "Spinning Tops"],
|
|
299
306
|
brandSlug: "brand-takara-tomy",
|
|
300
307
|
brand: "Takara-Tomy",
|
|
@@ -331,7 +338,7 @@ const _rawProductsAuctionsSeedData = [
|
|
|
331
338
|
slug: "auction-beyblade-burst-cho-z-achilles",
|
|
332
339
|
title: "Beyblade Burst B-100 Cho-Z Achilles",
|
|
333
340
|
description: "Cho-Z Achilles.Ow.Zn', a Super Zeta stamina-type top from the Cho-Z generation, in excellent spinning condition.",
|
|
334
|
-
categorySlugs: ["category-burst-
|
|
341
|
+
categorySlugs: ["category-cho-z-achilles-00-dimension", "category-burst-cho-z", "category-burst-tops", "category-beyblade-burst", "category-spinning-tops"],
|
|
335
342
|
categoryNames: ["Superking", "Burst Tops", "Beyblade Burst", "Spinning Tops"],
|
|
336
343
|
brandSlug: "brand-beyblade",
|
|
337
344
|
brand: "Beyblade",
|
|
@@ -365,9 +372,9 @@ const _rawProductsAuctionsSeedData = [
|
|
|
365
372
|
{
|
|
366
373
|
id: "auction-beyblade-x-wizard-fafnir",
|
|
367
374
|
slug: "auction-beyblade-x-wizard-fafnir",
|
|
368
|
-
title: "Beyblade X
|
|
369
|
-
description: "
|
|
370
|
-
categorySlugs: ["category-x-
|
|
375
|
+
title: "Beyblade X UX-03 WizardRod 5-70DB (Long-Running Auction)",
|
|
376
|
+
description: "WizardRod 5-70DB from the Unique Line, one week left on the clock — a rare Beyblade X chase top with active bidding.",
|
|
377
|
+
categorySlugs: ["category-wizardrod-5-70db", "category-x-unique-ux", "category-x-tops", "category-beyblade-x", "category-spinning-tops"],
|
|
371
378
|
categoryNames: ["Basic Line", "Beyblade X Tops", "Beyblade X", "Spinning Tops"],
|
|
372
379
|
brandSlug: "brand-takara-tomy",
|
|
373
380
|
brand: "Takara-Tomy",
|
|
@@ -398,15 +405,24 @@ const _rawProductsAuctionsSeedData = [
|
|
|
398
405
|
updatedAt: new Date("2026-08-19"),
|
|
399
406
|
},
|
|
400
407
|
];
|
|
401
|
-
//
|
|
402
|
-
// spread
|
|
403
|
-
//
|
|
404
|
-
//
|
|
408
|
+
// 🛑 EVERY wrapper is applied to the WHOLE array at the end, never to the
|
|
409
|
+
// spread. It used to wrap only the spread, so the ended-unsold fixture appended
|
|
410
|
+
// after it shipped with no searchTxt — the one row that proves the auction
|
|
411
|
+
// availability branch works, invisible to search.
|
|
412
|
+
//
|
|
413
|
+
// `listingType` was the SAME BUG, one field over, and survived that fix: it
|
|
414
|
+
// stayed inside the spread while `withTokens` was lifted out, so the appended
|
|
415
|
+
// fixture reached Firestore with no `listingType` at all. Measured 2026-10-14 —
|
|
416
|
+
// 9 of the 10 seeded auctions carried it and this one did not. A missing
|
|
417
|
+
// `listingType` is invisible to inspection (`normalizeListingType` defaults it
|
|
418
|
+
// to "standard" in memory) and fatal to every query, because a Firestore
|
|
419
|
+
// equality cannot match an absent field: `where("listingType","==","auction")`
|
|
420
|
+
// skipped it, so the fixture never appeared on /auctions and the "Ended" tab it
|
|
421
|
+
// exists to prove had nothing to show. Root Cause #101.
|
|
422
|
+
//
|
|
423
|
+
// Anything added to this pipeline goes AFTER the `]`, applied to all rows.
|
|
405
424
|
export const productsAuctionsSeedData = [
|
|
406
|
-
..._rawProductsAuctionsSeedData
|
|
407
|
-
...a,
|
|
408
|
-
listingType: PRODUCT_FIELDS.LISTING_TYPE_VALUES.AUCTION,
|
|
409
|
-
})),
|
|
425
|
+
..._rawProductsAuctionsSeedData,
|
|
410
426
|
{
|
|
411
427
|
// Ended with NO winner — reserve never met, zero bids.
|
|
412
428
|
//
|
|
@@ -417,9 +433,9 @@ export const productsAuctionsSeedData = [
|
|
|
417
433
|
// the base check and exercises nothing type-specific.
|
|
418
434
|
id: "auction-beyblade-burst-lord-spryzen-ended-unsold",
|
|
419
435
|
slug: "auction-beyblade-burst-lord-spryzen-ended-unsold",
|
|
420
|
-
title: "Beyblade Burst
|
|
436
|
+
title: "Beyblade Burst Storm Spriggan Knuckle Unite (Ended — Reserve Not Met)",
|
|
421
437
|
description: "Auction closed without meeting its reserve, so no winner was declared. Relisting soon.",
|
|
422
|
-
categorySlugs: ["category-burst-classic", "category-burst-tops", "category-beyblade-burst", "category-spinning-tops"],
|
|
438
|
+
categorySlugs: ["category-storm-spriggan-knuckle-unite", "category-burst-classic", "category-burst-tops", "category-beyblade-burst", "category-spinning-tops"],
|
|
423
439
|
categoryNames: ["Burst Classic", "Burst Tops", "Beyblade Burst", "Spinning Tops"],
|
|
424
440
|
brandSlug: "brand-takara-tomy",
|
|
425
441
|
brand: "Takara-Tomy",
|
|
@@ -443,4 +459,8 @@ export const productsAuctionsSeedData = [
|
|
|
443
459
|
createdAt: new Date("2026-08-10"),
|
|
444
460
|
updatedAt: new Date("2026-08-24"),
|
|
445
461
|
},
|
|
446
|
-
]
|
|
462
|
+
]
|
|
463
|
+
.map((a) => ({ ...a, listingType: PRODUCT_FIELDS.LISTING_TYPE_VALUES.AUCTION }))
|
|
464
|
+
.map(withTokens)
|
|
465
|
+
.map(withFinalSale)
|
|
466
|
+
.map(withDerivedCategoryNames);
|
|
@@ -29,11 +29,13 @@ import { PRODUCT_FIELDS } from "../constants/field-names.js";
|
|
|
29
29
|
import { buildSearchTxt } from "../utils/search-txt.js";
|
|
30
30
|
import { seedPhoto } from "./_helpers/media.js";
|
|
31
31
|
import { withFinalSale } from "./_helpers/final-sale-fixtures.js";
|
|
32
|
+
import { withDerivedCategoryNames } from "./_helpers/category-names.js";
|
|
32
33
|
const _rawproductsClassifiedsSeedData = [
|
|
33
34
|
{
|
|
34
35
|
id: "classified-beyblade-stadium-set",
|
|
35
36
|
slug: "classified-beyblade-stadium-set",
|
|
36
37
|
title: "Used Beyblade Burst Stadium Set — Local Pickup Only",
|
|
38
|
+
isLot: true,
|
|
37
39
|
description: "Well-loved Beyblade stadium with 6 tops and 2 launchers, sold as-is. Meet up locally — price is negotiable.",
|
|
38
40
|
/*
|
|
39
41
|
* 🛑 Re-filed in B5. A stadium is GENERATION-SPECIFIC — a BX-10 Xtreme
|
|
@@ -45,7 +47,16 @@ const _rawproductsClassifiedsSeedData = [
|
|
|
45
47
|
* a specific generation while the title stays silent makes the data
|
|
46
48
|
* disagree with itself, and the title is the half a buyer reads.
|
|
47
49
|
*/
|
|
48
|
-
|
|
50
|
+
/*
|
|
51
|
+
* 🛑 A LOT FILES AT A NON-LEAF, which is the other half of the leaf rule
|
|
52
|
+
* and the only offender of its kind in the catalogue. This set was filed
|
|
53
|
+
* at `category-burst-standard-stadiums` — a LEAF, i.e. one specific
|
|
54
|
+
* stadium model — while being `isLot: true`. Filing a mixed used set under
|
|
55
|
+
* a single model's leaf is a false claim about what is in the box, which
|
|
56
|
+
* is exactly what the lot half of the rule refuses. Its parent is the
|
|
57
|
+
* correct home.
|
|
58
|
+
*/
|
|
59
|
+
categorySlugs: ["category-burst-stadiums", "category-beyblade-burst", "category-spinning-tops"],
|
|
49
60
|
categoryNames: ["Standard BeyStadiums", "Burst Stadiums", "Beyblade Burst", "Spinning Tops"],
|
|
50
61
|
brandSlug: "brand-beyblade",
|
|
51
62
|
brand: "Beyblade",
|
|
@@ -96,6 +107,8 @@ const _rawproductsClassifiedsSeedData = [
|
|
|
96
107
|
id: "classified-beyblade-burst-collection-bengaluru",
|
|
97
108
|
slug: "classified-beyblade-burst-collection-bengaluru",
|
|
98
109
|
title: "Beyblade Burst Collection (12 Tops) — Bengaluru",
|
|
110
|
+
isLot: true,
|
|
111
|
+
lotSize: 12,
|
|
99
112
|
description: "Downsizing my Burst collection — 12 tops in good condition, fixed price, will also ship nationwide if you'd rather not meet up.",
|
|
100
113
|
categorySlugs: ["category-burst-cho-z", "category-burst-tops", "category-beyblade-burst", "category-spinning-tops"],
|
|
101
114
|
categoryNames: ["Cho-Z", "Burst Tops", "Beyblade Burst", "Spinning Tops"],
|
|
@@ -142,6 +155,7 @@ const _rawproductsClassifiedsSeedData = [
|
|
|
142
155
|
id: "classified-beyblade-x-starter-pune",
|
|
143
156
|
slug: "classified-beyblade-x-starter-pune",
|
|
144
157
|
title: "Beyblade X Starter Pack — Pune, Phone Contact Only",
|
|
158
|
+
isLot: true,
|
|
145
159
|
description: "Barely used Beyblade X starter pack, selling because I upgraded to a full set. Call or WhatsApp only, no chat.",
|
|
146
160
|
categorySlugs: ["category-x-basic", "category-x-tops", "category-beyblade-x", "category-spinning-tops"],
|
|
147
161
|
categoryNames: ["Basic Line", "Beyblade X Tops", "Beyblade X", "Spinning Tops"],
|
|
@@ -191,6 +205,8 @@ const _rawproductsClassifiedsSeedData = [
|
|
|
191
205
|
id: "classified-beyblade-metal-vintage-delhi",
|
|
192
206
|
slug: "classified-beyblade-metal-vintage-delhi",
|
|
193
207
|
title: "Vintage Metal Fight Lot (5 Tops) — Delhi NCR",
|
|
208
|
+
isLot: true,
|
|
209
|
+
lotSize: 5,
|
|
194
210
|
description: "A well-preserved 5-top Metal Fight lot from a long-time collector. Firm price, ships anywhere in India.",
|
|
195
211
|
categorySlugs: ["category-metal-fusion", "category-metal-tops", "category-beyblade-metal", "category-spinning-tops"],
|
|
196
212
|
categoryNames: ["Metal Fusion", "Metal Fight Tops", "Beyblade Metal Fight", "Spinning Tops"],
|
|
@@ -290,6 +306,7 @@ const _rawproductsClassifiedsSeedData = [
|
|
|
290
306
|
id: "classified-beyblade-x-tournament-kit-hyderabad",
|
|
291
307
|
slug: "classified-beyblade-x-tournament-kit-hyderabad",
|
|
292
308
|
title: "Tournament-Ready Beyblade X Kit — Hyderabad",
|
|
309
|
+
isLot: true,
|
|
293
310
|
description: "Complete tournament-legal kit including 3 tops, stadium, and a scorecard pad from our last local meetup event.",
|
|
294
311
|
categorySlugs: ["category-x-basic", "category-x-tops", "category-beyblade-x", "category-spinning-tops"],
|
|
295
312
|
categoryNames: ["Basic Line", "Beyblade X Tops", "Beyblade X", "Spinning Tops"],
|
|
@@ -342,6 +359,7 @@ const _rawproductsClassifiedsSeedData = [
|
|
|
342
359
|
id: "classified-beyblade-burst-parts-mumbai",
|
|
343
360
|
slug: "classified-beyblade-burst-parts-mumbai",
|
|
344
361
|
title: "Spare Burst Parts Bundle — Mumbai, Ships Too",
|
|
362
|
+
isLot: true,
|
|
345
363
|
description: "Assorted spare Burst layers, discs, and drivers cleared out from my parts box. Happy to meet locally or ship.",
|
|
346
364
|
categorySlugs: ["category-burst-superking", "category-burst-tops", "category-beyblade-burst", "category-spinning-tops"],
|
|
347
365
|
categoryNames: ["Superking", "Burst Tops", "Beyblade Burst", "Spinning Tops"],
|
|
@@ -394,7 +412,7 @@ const _rawproductsClassifiedsSeedData = [
|
|
|
394
412
|
slug: "classified-beyblade-metal-galaxy-pegasus-sold-mumbai",
|
|
395
413
|
title: "Galaxy Pegasus W105R2F — Sold (Mumbai)",
|
|
396
414
|
description: "Vintage Metal Fight Galaxy Pegasus, sold to a local collector. Listing kept up for reference.",
|
|
397
|
-
categorySlugs: ["category-metal-
|
|
415
|
+
categorySlugs: ["category-galaxy-pegasus-w105r2f", "category-metal-fusion", "category-metal-tops", "category-beyblade-metal", "category-spinning-tops"],
|
|
398
416
|
categoryNames: ["Metal Masters", "Metal Fight Tops", "Beyblade Metal Fight", "Spinning Tops"],
|
|
399
417
|
brandSlug: "brand-beyblade",
|
|
400
418
|
brand: "Beyblade",
|
|
@@ -432,4 +450,4 @@ const _rawproductsClassifiedsSeedData = [
|
|
|
432
450
|
* the bottom of the array cannot ship without it — which is exactly how the
|
|
433
451
|
* sold/depleted fixture in this file ended up unsearchable.
|
|
434
452
|
*/
|
|
435
|
-
export const productsClassifiedsSeedData = _rawproductsClassifiedsSeedData.map(withProductSearchTxt).map(withFinalSale);
|
|
453
|
+
export const productsClassifiedsSeedData = _rawproductsClassifiedsSeedData.map(withProductSearchTxt).map(withFinalSale).map(withDerivedCategoryNames);
|
|
@@ -22,6 +22,7 @@ import { PRODUCT_FIELDS } from "../constants/field-names.js";
|
|
|
22
22
|
import { buildSearchTxt } from "../utils/search-txt.js";
|
|
23
23
|
import { seedPhoto } from "./_helpers/media.js";
|
|
24
24
|
import { withFinalSale } from "./_helpers/final-sale-fixtures.js";
|
|
25
|
+
import { withDerivedCategoryNames } from "./_helpers/category-names.js";
|
|
25
26
|
const _rawproductsDigitalCodesSeedData = [
|
|
26
27
|
{
|
|
27
28
|
id: "digitalcode-beyblade-x-app-starter-pack",
|
|
@@ -400,4 +401,4 @@ const _rawproductsDigitalCodesSeedData = [
|
|
|
400
401
|
* the bottom of the array cannot ship without it — which is exactly how the
|
|
401
402
|
* sold/depleted fixture in this file ended up unsearchable.
|
|
402
403
|
*/
|
|
403
|
-
export const productsDigitalCodesSeedData = _rawproductsDigitalCodesSeedData.map(withProductSearchTxt).map(withFinalSale);
|
|
404
|
+
export const productsDigitalCodesSeedData = _rawproductsDigitalCodesSeedData.map(withProductSearchTxt).map(withFinalSale).map(withDerivedCategoryNames);
|
|
@@ -35,6 +35,7 @@ import { PRODUCT_FIELDS } from "../constants/field-names.js";
|
|
|
35
35
|
import { buildSearchTxt } from "../utils/search-txt.js";
|
|
36
36
|
import { seedPhoto } from "./_helpers/media.js";
|
|
37
37
|
import { withFinalSale } from "./_helpers/final-sale-fixtures.js";
|
|
38
|
+
import { withDerivedCategoryNames } from "./_helpers/category-names.js";
|
|
38
39
|
const _rawproductsLiveItemsSeedData = [
|
|
39
40
|
{
|
|
40
41
|
id: "live-golden-retriever-puppy",
|
|
@@ -273,4 +274,4 @@ const _rawproductsLiveItemsSeedData = [
|
|
|
273
274
|
* the bottom of the array cannot ship without it — which is exactly how the
|
|
274
275
|
* sold/depleted fixture in this file ended up unsearchable.
|
|
275
276
|
*/
|
|
276
|
-
export const productsLiveItemsSeedData = _rawproductsLiveItemsSeedData.map(withProductSearchTxt).map(withFinalSale);
|
|
277
|
+
export const productsLiveItemsSeedData = _rawproductsLiveItemsSeedData.map(withProductSearchTxt).map(withFinalSale).map(withDerivedCategoryNames);
|