@mohasinac/appkit 2.6.1 → 2.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/layout/DashboardLayoutClient.d.ts +38 -0
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +75 -0
- package/dist/_internal/client/features/layout/RoleGuard.d.ts +15 -0
- package/dist/_internal/client/features/layout/RoleGuard.js +25 -0
- package/dist/_internal/client/features/layout/index.d.ts +5 -0
- package/dist/_internal/client/features/layout/index.js +4 -0
- package/dist/_internal/server/features/brands/actions.d.ts +3 -3
- package/dist/_internal/server/features/brands/actions.js +72 -5
- package/dist/_internal/server/features/brands/data.d.ts +8 -8
- package/dist/_internal/server/features/brands/data.js +10 -11
- package/dist/_internal/server/features/brands/service.d.ts +2 -2
- package/dist/_internal/server/features/brands/service.js +5 -5
- package/dist/_internal/server/features/categories/og.d.ts +33 -0
- package/dist/_internal/server/features/categories/og.js +75 -0
- package/dist/_internal/server/features/checkout/actions.d.ts +24 -0
- package/dist/_internal/server/features/checkout/actions.js +442 -13
- package/dist/_internal/server/features/checkout/index.d.ts +1 -1
- package/dist/_internal/server/features/checkout/index.js +1 -1
- package/dist/_internal/server/features/checkout/prize-bundle-gates.d.ts +59 -0
- package/dist/_internal/server/features/checkout/prize-bundle-gates.js +99 -0
- package/dist/_internal/server/features/grouped/data.js +12 -5
- package/dist/_internal/server/features/homepage/data.d.ts +1 -1
- package/dist/_internal/server/features/homepage/data.js +2 -2
- package/dist/_internal/server/features/media/contextGuards.d.ts +52 -0
- package/dist/_internal/server/features/media/contextGuards.js +198 -0
- package/dist/_internal/server/features/orders/adapters.js +12 -0
- package/dist/_internal/server/features/products/data.d.ts +1 -1
- package/dist/_internal/server/features/sublisting-categories/data.d.ts +1 -1
- package/dist/_internal/server/features/sublisting-categories/data.js +2 -2
- package/dist/_internal/server/jobs/handlers/assignSpinPrize.d.ts +24 -0
- package/dist/_internal/server/jobs/handlers/assignSpinPrize.js +86 -0
- package/dist/_internal/server/jobs/handlers/bundleStockSync.d.ts +18 -0
- package/dist/_internal/server/jobs/handlers/bundleStockSync.js +80 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +8 -0
- package/dist/_internal/server/jobs/handlers/index.js +13 -0
- package/dist/_internal/server/jobs/handlers/listingProcessor.js +13 -3
- package/dist/_internal/server/jobs/handlers/onProductStockChange.d.ts +17 -0
- package/dist/_internal/server/jobs/handlers/onProductStockChange.js +136 -0
- package/dist/_internal/server/jobs/handlers/onProductWrite.js +17 -1
- package/dist/_internal/server/jobs/handlers/prizeRevealClose.d.ts +9 -0
- package/dist/_internal/server/jobs/handlers/prizeRevealClose.js +29 -0
- package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.d.ts +10 -0
- package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.js +58 -0
- package/dist/_internal/server/jobs/handlers/prizeRevealOpen.d.ts +10 -0
- package/dist/_internal/server/jobs/handlers/prizeRevealOpen.js +65 -0
- package/dist/_internal/server/jobs/handlers/prizeRevealReminder.d.ts +9 -0
- package/dist/_internal/server/jobs/handlers/prizeRevealReminder.js +45 -0
- package/dist/_internal/server/jobs/handlers/triggerEventRaffle.d.ts +30 -0
- package/dist/_internal/server/jobs/handlers/triggerEventRaffle.js +94 -0
- package/dist/_internal/shared/features/brands/schema.d.ts +3 -3
- package/dist/_internal/shared/features/cart/schema.d.ts +8 -8
- package/dist/_internal/shared/features/cart/schema.js +1 -1
- package/dist/_internal/shared/features/categories/bundle-config.d.ts +17 -0
- package/dist/_internal/shared/features/categories/bundle-config.js +17 -0
- package/dist/_internal/shared/features/layout/config.d.ts +35 -0
- package/dist/_internal/shared/features/layout/config.js +58 -0
- package/dist/_internal/shared/features/layout/index.d.ts +3 -0
- package/dist/_internal/shared/features/layout/index.js +2 -0
- package/dist/_internal/shared/features/layout/types.d.ts +137 -0
- package/dist/_internal/shared/features/layout/types.js +13 -0
- package/dist/_internal/shared/features/products/types.d.ts +1 -1
- package/dist/_internal/shared/listing-types/_registry.d.ts +57 -0
- package/dist/_internal/shared/listing-types/_registry.js +28 -0
- package/dist/_internal/shared/listing-types/auction/config.d.ts +7 -0
- package/dist/_internal/shared/listing-types/auction/config.js +8 -0
- package/dist/_internal/shared/listing-types/auction/ctas.d.ts +1 -0
- package/dist/_internal/shared/listing-types/auction/ctas.js +2 -0
- package/dist/_internal/shared/listing-types/auction/og.d.ts +1 -0
- package/dist/_internal/shared/listing-types/auction/og.js +1 -0
- package/dist/_internal/shared/listing-types/auction/schema.d.ts +1 -0
- package/dist/_internal/shared/listing-types/auction/schema.js +1 -0
- package/dist/_internal/shared/listing-types/auction/seed-factory.d.ts +1 -0
- package/dist/_internal/shared/listing-types/auction/seed-factory.js +1 -0
- package/dist/_internal/shared/listing-types/capabilities.d.ts +41 -0
- package/dist/_internal/shared/listing-types/capabilities.js +75 -0
- package/dist/_internal/shared/listing-types/pre-order/config.d.ts +7 -0
- package/dist/_internal/shared/listing-types/pre-order/config.js +8 -0
- package/dist/_internal/shared/listing-types/pre-order/ctas.d.ts +1 -0
- package/dist/_internal/shared/listing-types/pre-order/ctas.js +2 -0
- package/dist/_internal/shared/listing-types/pre-order/og.d.ts +1 -0
- package/dist/_internal/shared/listing-types/pre-order/og.js +1 -0
- package/dist/_internal/shared/listing-types/pre-order/schema.d.ts +1 -0
- package/dist/_internal/shared/listing-types/pre-order/schema.js +1 -0
- package/dist/_internal/shared/listing-types/pre-order/seed-factory.d.ts +1 -0
- package/dist/_internal/shared/listing-types/pre-order/seed-factory.js +1 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +7 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.js +8 -0
- package/dist/_internal/shared/listing-types/prize-draw/ctas.d.ts +1 -0
- package/dist/_internal/shared/listing-types/prize-draw/ctas.js +2 -0
- package/dist/_internal/shared/listing-types/prize-draw/og.d.ts +1 -0
- package/dist/_internal/shared/listing-types/prize-draw/og.js +1 -0
- package/dist/_internal/shared/listing-types/prize-draw/schema.d.ts +1 -0
- package/dist/_internal/shared/listing-types/prize-draw/schema.js +1 -0
- package/dist/_internal/shared/listing-types/prize-draw/seed-factory.d.ts +1 -0
- package/dist/_internal/shared/listing-types/prize-draw/seed-factory.js +1 -0
- package/dist/_internal/shared/listing-types/standard/config.d.ts +7 -0
- package/dist/_internal/shared/listing-types/standard/config.js +8 -0
- package/dist/_internal/shared/listing-types/standard/ctas.d.ts +1 -0
- package/dist/_internal/shared/listing-types/standard/ctas.js +3 -0
- package/dist/_internal/shared/listing-types/standard/og.d.ts +1 -0
- package/dist/_internal/shared/listing-types/standard/og.js +1 -0
- package/dist/_internal/shared/listing-types/standard/schema.d.ts +1 -0
- package/dist/_internal/shared/listing-types/standard/schema.js +1 -0
- package/dist/_internal/shared/listing-types/standard/seed-factory.d.ts +1 -0
- package/dist/_internal/shared/listing-types/standard/seed-factory.js +1 -0
- package/dist/_internal/shared/media/limits.d.ts +33 -0
- package/dist/_internal/shared/media/limits.js +97 -0
- package/dist/_internal/shared/schema-versions.d.ts +76 -0
- package/dist/_internal/shared/schema-versions.js +82 -0
- package/dist/client.d.ts +9 -0
- package/dist/client.js +7 -0
- package/dist/constants/api-endpoints.d.ts +6 -3
- package/dist/constants/api-endpoints.js +2 -1
- package/dist/errors/messages.d.ts +1 -1
- package/dist/errors/messages.js +1 -1
- package/dist/features/account/migrations.d.ts +2 -0
- package/dist/features/account/migrations.js +10 -0
- package/dist/features/admin/components/AdminMediaView.js +1 -1
- package/dist/features/admin/components/AdminProductsView.js +7 -3
- package/dist/features/admin/migrations.d.ts +2 -0
- package/dist/features/admin/migrations.js +10 -0
- package/dist/features/admin/types/product.types.d.ts +1 -1
- package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +1 -1
- package/dist/features/auctions/migrations.d.ts +2 -0
- package/dist/features/auctions/migrations.js +10 -0
- package/dist/features/auctions/schemas/index.d.ts +3 -3
- package/dist/features/auctions/schemas/index.js +1 -1
- package/dist/features/auth/migrations.d.ts +2 -0
- package/dist/features/auth/migrations.js +10 -0
- package/dist/features/blog/migrations.d.ts +2 -0
- package/dist/features/blog/migrations.js +10 -0
- package/dist/features/brands/migrations.d.ts +2 -0
- package/dist/features/brands/migrations.js +10 -0
- package/dist/features/bundles/components/BundlesByCategoryListing.d.ts +6 -0
- package/dist/features/bundles/components/BundlesByCategoryListing.js +50 -0
- package/dist/features/bundles/components/index.d.ts +2 -0
- package/dist/features/bundles/components/index.js +1 -0
- package/dist/features/bundles/migrations.d.ts +2 -0
- package/dist/features/bundles/migrations.js +10 -0
- package/dist/features/bundles/schemas/index.d.ts +1 -0
- package/dist/features/bundles/schemas/index.js +1 -0
- package/dist/features/bundles/schemas/zod.d.ts +377 -0
- package/dist/features/bundles/schemas/zod.js +71 -0
- package/dist/features/cart/migrations.d.ts +2 -0
- package/dist/features/cart/migrations.js +10 -0
- package/dist/features/cart/schemas/firestore.d.ts +2 -2
- package/dist/features/categories/components/BrandDetailPageView.js +35 -4
- package/dist/features/categories/components/BrandDetailTabs.d.ts +5 -1
- package/dist/features/categories/components/BrandDetailTabs.js +22 -8
- package/dist/features/categories/components/CategoryBundlesListing.d.ts +6 -0
- package/dist/features/categories/components/CategoryBundlesListing.js +74 -0
- package/dist/features/categories/components/CategoryDetailPageView.js +29 -4
- package/dist/features/categories/components/CategoryDetailTabs.d.ts +5 -1
- package/dist/features/categories/components/CategoryDetailTabs.js +22 -8
- package/dist/features/categories/migrations.d.ts +2 -0
- package/dist/features/categories/migrations.js +10 -0
- package/dist/features/categories/repository/categories.repository.d.ts +29 -0
- package/dist/features/categories/repository/categories.repository.js +83 -0
- package/dist/features/categories/schemas/firestore.d.ts +59 -2
- package/dist/features/categories/schemas/firestore.js +6 -0
- package/dist/features/categories/types/index.d.ts +11 -3
- package/dist/features/events/migrations.d.ts +2 -0
- package/dist/features/events/migrations.js +10 -0
- package/dist/features/faq/migrations.d.ts +2 -0
- package/dist/features/faq/migrations.js +10 -0
- package/dist/features/grouped/migrations.d.ts +2 -0
- package/dist/features/grouped/migrations.js +10 -0
- package/dist/features/grouped/schemas/firestore.d.ts +29 -10
- package/dist/features/grouped/schemas/firestore.js +10 -5
- package/dist/features/history/migrations.d.ts +2 -0
- package/dist/features/history/migrations.js +10 -0
- package/dist/features/homepage/hooks/useFeaturedAuctions.js +2 -2
- package/dist/features/homepage/hooks/useFeaturedPreOrders.js +2 -2
- package/dist/features/homepage/lib/section-renderer.js +5 -3
- package/dist/features/media/AvatarUpload.js +6 -28
- package/dist/features/media/hooks/useMedia.d.ts +31 -15
- package/dist/features/media/hooks/useMedia.js +48 -13
- package/dist/features/media/upload/ImageUpload.js +1 -1
- package/dist/features/media/upload/MediaUploadField.js +1 -1
- package/dist/features/messages/migrations.d.ts +2 -0
- package/dist/features/messages/migrations.js +10 -0
- package/dist/features/orders/components/OrdersList.js +10 -1
- package/dist/features/orders/migrations.d.ts +2 -0
- package/dist/features/orders/migrations.js +10 -0
- package/dist/features/orders/repository/orders.repository.d.ts +16 -0
- package/dist/features/orders/repository/orders.repository.js +49 -0
- package/dist/features/orders/schemas/firestore.d.ts +8 -0
- package/dist/features/orders/types/index.d.ts +12 -0
- package/dist/features/orders/utils/order-splitter.d.ts +2 -2
- package/dist/features/orders/utils/order-splitter.js +5 -0
- package/dist/features/payments/migrations.d.ts +2 -0
- package/dist/features/payments/migrations.js +10 -0
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +4 -1
- package/dist/features/products/actions/product-actions.d.ts +1 -1
- package/dist/features/products/api/[id]/route.js +34 -0
- package/dist/features/products/api/route.js +1 -19
- package/dist/features/products/components/CompareOverlay.d.ts +1 -1
- package/dist/features/products/components/MarketplacePrizeDrawCard.d.ts +24 -0
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +102 -0
- package/dist/features/products/components/PrizeDrawCollage.d.ts +32 -0
- package/dist/features/products/components/PrizeDrawCollage.js +22 -0
- package/dist/features/products/components/PrizeDrawDetailPageView.d.ts +27 -0
- package/dist/features/products/components/PrizeDrawDetailPageView.js +118 -0
- package/dist/features/products/components/PrizeDrawEntryActions.d.ts +19 -0
- package/dist/features/products/components/PrizeDrawEntryActions.js +48 -0
- package/dist/features/products/components/PrizeDrawItemsEditor.d.ts +13 -0
- package/dist/features/products/components/PrizeDrawItemsEditor.js +97 -0
- package/dist/features/products/components/PrizeDrawsIndexListing.d.ts +8 -0
- package/dist/features/products/components/PrizeDrawsIndexListing.js +128 -0
- package/dist/features/products/components/PrizeDrawsListingView.d.ts +15 -0
- package/dist/features/products/components/PrizeDrawsListingView.js +49 -0
- package/dist/features/products/components/PrizeRevealModal.d.ts +34 -0
- package/dist/features/products/components/PrizeRevealModal.js +124 -0
- package/dist/features/products/components/ProductDetailPageView.js +13 -1
- package/dist/features/products/components/ProductForm.js +35 -2
- package/dist/features/products/components/ProductGrid.js +3 -1
- package/dist/features/products/components/index.d.ts +16 -0
- package/dist/features/products/components/index.js +8 -0
- package/dist/features/products/constants/listing-tabs.d.ts +113 -0
- package/dist/features/products/constants/listing-tabs.js +43 -0
- package/dist/features/products/index.d.ts +1 -0
- package/dist/features/products/index.js +1 -0
- package/dist/features/products/migrations.d.ts +2 -0
- package/dist/features/products/migrations.js +10 -0
- package/dist/features/products/repository/products.repository.d.ts +11 -7
- package/dist/features/products/repository/products.repository.js +49 -24
- package/dist/features/products/schemas/firestore.d.ts +3 -3
- package/dist/features/products/schemas/firestore.js +2 -2
- package/dist/features/products/schemas/index.d.ts +5 -5
- package/dist/features/products/schemas/index.js +3 -1
- package/dist/features/products/schemas/product-features.validators.d.ts +6 -6
- package/dist/features/products/types/index.d.ts +17 -1
- package/dist/features/products/utils/listing-type.d.ts +7 -4
- package/dist/features/products/utils/listing-type.js +8 -4
- package/dist/features/promotions/actions/coupon-actions.d.ts +1 -1
- package/dist/features/promotions/hooks/useCouponValidate.d.ts +1 -1
- package/dist/features/promotions/migrations.d.ts +2 -0
- package/dist/features/promotions/migrations.js +10 -0
- package/dist/features/promotions/repository/coupons.repository.d.ts +1 -1
- package/dist/features/promotions/schemas/index.d.ts +2 -2
- package/dist/features/reviews/migrations.d.ts +2 -0
- package/dist/features/reviews/migrations.js +10 -0
- package/dist/features/scams/migrations.d.ts +2 -0
- package/dist/features/scams/migrations.js +10 -0
- package/dist/features/search/api/route.d.ts +1 -1
- package/dist/features/search/api/route.js +3 -3
- package/dist/features/search/components/Search.d.ts +1 -1
- package/dist/features/search/schemas/index.d.ts +3 -3
- package/dist/features/search/schemas/index.js +3 -1
- package/dist/features/search/types/index.d.ts +2 -2
- package/dist/features/seller/components/SellerProductShell.d.ts +1 -1
- package/dist/features/seller/components/SellerProductsView.js +20 -6
- package/dist/features/seller/migrations.d.ts +2 -0
- package/dist/features/seller/migrations.js +10 -0
- package/dist/features/seller/schemas/index.d.ts +2 -2
- package/dist/features/stores/components/StoreBundlesPageView.d.ts +12 -0
- package/dist/features/stores/components/StoreBundlesPageView.js +24 -0
- package/dist/features/stores/components/StoreDetailLayoutView.js +15 -3
- package/dist/features/stores/components/StorePrizeDrawsPageView.d.ts +11 -0
- package/dist/features/stores/components/StorePrizeDrawsPageView.js +27 -0
- package/dist/features/stores/components/index.d.ts +2 -0
- package/dist/features/stores/migrations.d.ts +2 -0
- package/dist/features/stores/migrations.js +10 -0
- package/dist/features/stores/schemas/index.d.ts +2 -2
- package/dist/features/stores/types/index.d.ts +1 -1
- package/dist/features/sublisting/migrations.d.ts +2 -0
- package/dist/features/sublisting/migrations.js +10 -0
- package/dist/features/sublisting/schemas/firestore.d.ts +2 -0
- package/dist/features/support/migrations.d.ts +2 -0
- package/dist/features/support/migrations.js +10 -0
- package/dist/features/wishlist/migrations.d.ts +2 -0
- package/dist/features/wishlist/migrations.js +10 -0
- package/dist/features/wishlist/types/index.d.ts +1 -1
- package/dist/index.d.ts +26 -18
- package/dist/index.js +41 -24
- package/dist/jobs.d.ts +1 -1
- package/dist/jobs.js +4 -0
- package/dist/next/routing/route-map.d.ts +4 -0
- package/dist/next/routing/route-map.js +2 -0
- package/dist/providers/db-firebase/admin-app-lite.js +21 -0
- package/dist/providers/db-firebase/filter-aliases.d.ts +2 -2
- package/dist/repositories/index.d.ts +0 -5
- package/dist/repositories/index.js +5 -4
- package/dist/seed/actions/demo-seed-actions.d.ts +1 -1
- package/dist/seed/categories-seed-data.js +1105 -6
- package/dist/seed/faq-seed-data.js +160 -0
- package/dist/seed/grouped-listings-seed-data.js +32 -32
- package/dist/seed/homepage-sections-seed-data.js +52 -6
- package/dist/seed/index.d.ts +1 -3
- package/dist/seed/index.js +4 -3
- package/dist/seed/manifest.js +8 -13
- package/dist/seed/products-prize-draws-seed-data.d.ts +17 -0
- package/dist/seed/products-prize-draws-seed-data.js +313 -0
- package/dist/seo/json-ld.d.ts +1 -1
- package/dist/server-entry.d.ts +2 -2
- package/dist/server-entry.js +5 -3
- package/dist/server.d.ts +9 -2
- package/dist/server.js +11 -5
- package/dist/tailwind-utilities.css +1 -1
- package/dist/validation/schemas.d.ts +8 -8
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +2 -4
|
@@ -21,7 +21,7 @@ export interface CartItemDocument {
|
|
|
21
21
|
* carts) and cart-side UI badges. Replaces the legacy `isAuction`/`isPreOrder`
|
|
22
22
|
* pair on the cart item.
|
|
23
23
|
*/
|
|
24
|
-
listingType: "standard" | "auction" | "pre-order" | "prize-draw"
|
|
24
|
+
listingType: "standard" | "auction" | "pre-order" | "prize-draw";
|
|
25
25
|
/** True when item was added from an accepted Make-an-Offer */
|
|
26
26
|
isOffer?: boolean;
|
|
27
27
|
offerId?: string;
|
|
@@ -89,7 +89,7 @@ export type AddToCartInput = {
|
|
|
89
89
|
/** Store slug (= storeId = store.id) */
|
|
90
90
|
storeId: string;
|
|
91
91
|
storeName: string;
|
|
92
|
-
listingType: "standard" | "auction" | "pre-order" | "prize-draw"
|
|
92
|
+
listingType: "standard" | "auction" | "pre-order" | "prize-draw";
|
|
93
93
|
isOffer?: boolean;
|
|
94
94
|
offerId?: string;
|
|
95
95
|
lockedPrice?: number;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import Link from "next/link";
|
|
3
|
-
import { categoriesRepository, productRepository } from "../../../repositories";
|
|
3
|
+
import { categoriesRepository, productRepository, } from "../../../repositories";
|
|
4
4
|
import { ROUTES } from "../../../next";
|
|
5
5
|
import { Container, Main, Section } from "../../../ui";
|
|
6
6
|
import { BrandDetailTabs } from "./BrandDetailTabs";
|
|
@@ -9,7 +9,7 @@ export async function BrandDetailPageView({ slug, initialBrand }) {
|
|
|
9
9
|
.getCategoryBySlug(slug)
|
|
10
10
|
.catch(() => undefined)));
|
|
11
11
|
const brandName = brand?.name;
|
|
12
|
-
const [productsResult, auctionsResult, preOrdersResult] = await Promise.all([
|
|
12
|
+
const [productsResult, auctionsResult, preOrdersResult, prizeDrawsResult, allBundles] = await Promise.all([
|
|
13
13
|
brandName
|
|
14
14
|
? productRepository
|
|
15
15
|
.list({
|
|
@@ -40,7 +40,32 @@ export async function BrandDetailPageView({ slug, initialBrand }) {
|
|
|
40
40
|
})
|
|
41
41
|
.catch(() => null)
|
|
42
42
|
: Promise.resolve(null),
|
|
43
|
+
brandName
|
|
44
|
+
? productRepository
|
|
45
|
+
.list({
|
|
46
|
+
filters: `status==published,brand==${brandName},listingType==prize-draw`,
|
|
47
|
+
sorts: "-createdAt",
|
|
48
|
+
page: 1,
|
|
49
|
+
pageSize: 1,
|
|
50
|
+
})
|
|
51
|
+
.catch(() => null)
|
|
52
|
+
: Promise.resolve(null),
|
|
53
|
+
// SB-UNI-D — bundles are categoryType:"bundle" rows on the categories
|
|
54
|
+
// collection. We pull all bundle categories and filter client-side by
|
|
55
|
+
// brand affinity until the bundle storage carries an explicit brandSlug.
|
|
56
|
+
brandName
|
|
57
|
+
? categoriesRepository
|
|
58
|
+
.listByType("bundle", { activeOnly: true, limit: 50 })
|
|
59
|
+
.catch(() => [])
|
|
60
|
+
: Promise.resolve([]),
|
|
43
61
|
]);
|
|
62
|
+
const brandLower = brandName?.toLowerCase();
|
|
63
|
+
const brandBundles = brandLower
|
|
64
|
+
? allBundles.filter((b) => {
|
|
65
|
+
const seo = b.seo?.keywords?.map((k) => k.toLowerCase()) ?? [];
|
|
66
|
+
return seo.includes(brandLower);
|
|
67
|
+
})
|
|
68
|
+
: [];
|
|
44
69
|
const coverImage = brand?.display?.coverImage;
|
|
45
70
|
const hasCover = Boolean(coverImage);
|
|
46
71
|
const brandColor = brand?.display?.color || "#6366f1";
|
|
@@ -48,7 +73,13 @@ export async function BrandDetailPageView({ slug, initialBrand }) {
|
|
|
48
73
|
products: productsResult?.total ?? brand?.metrics?.productCount ?? 0,
|
|
49
74
|
auctions: auctionsResult?.total ?? brand?.metrics?.auctionCount ?? 0,
|
|
50
75
|
preOrders: preOrdersResult?.total ?? 0,
|
|
76
|
+
prizeDraws: prizeDrawsResult?.total ?? 0,
|
|
77
|
+
bundles: brandBundles.length,
|
|
51
78
|
};
|
|
52
|
-
const totalItems = counts.products +
|
|
53
|
-
|
|
79
|
+
const totalItems = counts.products +
|
|
80
|
+
counts.auctions +
|
|
81
|
+
counts.preOrders +
|
|
82
|
+
counts.prizeDraws +
|
|
83
|
+
counts.bundles;
|
|
84
|
+
return (_jsxs(Main, { children: [_jsxs("section", { className: `relative overflow-hidden ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-zinc-50 dark:bg-zinc-900"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute inset-0 bg-center bg-cover", style: { backgroundImage: `url(${coverImage})` } }), _jsx("div", { className: "absolute inset-0 bg-black/55" })] })), !hasCover && (_jsx("div", { className: "absolute inset-0 opacity-10", style: { backgroundColor: brandColor } })), _jsxs("div", { className: `relative z-10 max-w-7xl mx-auto px-4 ${hasCover ? "py-12" : "py-8"}`, children: [_jsxs("nav", { className: "flex items-center gap-1.5 text-sm mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 hover:text-primary-600 transition-colors", children: "Home" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.BRANDS ?? "/brands"), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 hover:text-primary-600 transition-colors", children: "Brands" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx("span", { className: hasCover ? "text-white font-medium" : "text-zinc-900 dark:text-zinc-100 font-medium", children: brand?.name ?? slug })] }), _jsxs("div", { className: "flex items-center gap-4 mb-3", children: [brand?.display?.icon && (_jsx("span", { className: `text-5xl leading-none ${!hasCover ? "" : ""}`, children: brand.display.icon })), _jsxs("div", { children: [_jsx("h1", { className: `text-3xl md:text-4xl font-bold ${hasCover ? "text-white" : "text-zinc-900 dark:text-zinc-50"}`, children: brand?.name ?? slug }), brand?.description && typeof brand.description === "string" && !brand.description.startsWith("{") && (_jsx("p", { className: `text-base max-w-2xl mt-1 ${hasCover ? "text-white/80" : "text-zinc-600 dark:text-zinc-400"}`, children: brand.description }))] })] }), _jsxs("div", { className: "flex flex-wrap gap-2 mt-3", children: [counts.products > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400"}`, children: [counts.products.toLocaleString(), " ", counts.products === 1 ? "product" : "products"] })), counts.auctions > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400"}`, children: [counts.auctions.toLocaleString(), " ", counts.auctions === 1 ? "auction" : "auctions"] })), counts.preOrders > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-400"}`, children: [counts.preOrders.toLocaleString(), " ", counts.preOrders === 1 ? "pre-order" : "pre-orders"] })), totalItems === 0 && (_jsx("span", { className: `text-sm ${hasCover ? "text-white/60" : "text-zinc-400"}`, children: "No items listed yet" }))] })] })] }), _jsx(Section, { className: "py-6", children: _jsx(Container, { size: "xl", children: brandName ? (_jsx(BrandDetailTabs, { brandName: brandName, initialProductsData: productsResult ?? undefined, initialBundles: brandBundles, counts: counts })) : (_jsx("p", { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "Brand not found." })) }) })] }));
|
|
54
85
|
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import type { CategoryDocument } from "../schemas";
|
|
1
2
|
export interface BrandDetailTabsProps {
|
|
2
3
|
brandName: string;
|
|
3
4
|
initialProductsData?: any;
|
|
5
|
+
initialBundles?: CategoryDocument[];
|
|
4
6
|
counts?: {
|
|
5
7
|
products?: number;
|
|
6
8
|
auctions?: number;
|
|
7
9
|
preOrders?: number;
|
|
10
|
+
prizeDraws?: number;
|
|
11
|
+
bundles?: number;
|
|
8
12
|
};
|
|
9
13
|
}
|
|
10
|
-
export declare function BrandDetailTabs({ brandName, initialProductsData, counts }: BrandDetailTabsProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export declare function BrandDetailTabs({ brandName, initialProductsData, initialBundles, counts, }: BrandDetailTabsProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -4,19 +4,33 @@ import { useState } from "react";
|
|
|
4
4
|
import { CategoryProductsListing } from "./CategoryProductsListing";
|
|
5
5
|
import { AuctionsIndexListing } from "../../products/components/AuctionsIndexListing";
|
|
6
6
|
import { PreOrdersIndexListing } from "../../pre-orders/components/PreOrdersIndexListing";
|
|
7
|
+
import { PrizeDrawsIndexListing } from "../../products/components/PrizeDrawsIndexListing";
|
|
8
|
+
import { CategoryBundlesListing } from "./CategoryBundlesListing";
|
|
9
|
+
import { CATEGORY_PAGE_TABS } from "../../products/constants/listing-tabs";
|
|
7
10
|
function tabLabel(label, count) {
|
|
8
11
|
if (!count)
|
|
9
12
|
return label;
|
|
10
13
|
return `${label} (${count.toLocaleString()})`;
|
|
11
14
|
}
|
|
12
|
-
export function BrandDetailTabs({ brandName, initialProductsData, counts }) {
|
|
15
|
+
export function BrandDetailTabs({ brandName, initialProductsData, initialBundles = [], counts, }) {
|
|
13
16
|
const [activeTab, setActiveTab] = useState("products");
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
const countFor = (id) => {
|
|
18
|
+
switch (id) {
|
|
19
|
+
case "products":
|
|
20
|
+
return counts?.products;
|
|
21
|
+
case "auctions":
|
|
22
|
+
return counts?.auctions;
|
|
23
|
+
case "pre-orders":
|
|
24
|
+
return counts?.preOrders;
|
|
25
|
+
case "prize-draws":
|
|
26
|
+
return counts?.prizeDraws;
|
|
27
|
+
case "bundles":
|
|
28
|
+
return counts?.bundles;
|
|
29
|
+
default:
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
return (_jsxs("div", { children: [_jsx("div", { className: "flex border-b border-zinc-200 dark:border-slate-700 mb-6 overflow-x-auto", children: CATEGORY_PAGE_TABS.map((t) => (_jsx("button", { type: "button", onClick: () => setActiveTab(t.id), className: `px-5 py-2.5 text-sm font-medium whitespace-nowrap transition-colors -mb-px border-b-2 ${activeTab === t.id
|
|
20
34
|
? "border-primary text-primary"
|
|
21
|
-
: "border-transparent text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-100"}`, children: t.label }, t.id))) }), activeTab === "products" && (_jsx(CategoryProductsListing, { categorySlug: "", brandName: brandName, initialData: initialProductsData })), activeTab === "auctions" && (_jsx(AuctionsIndexListing, { brandName: brandName })), activeTab === "pre-orders" && (_jsx(PreOrdersIndexListing, { brandName: brandName }))] }));
|
|
35
|
+
: "border-transparent text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-100"}`, children: tabLabel(t.label, countFor(t.id)) }, t.id))) }), activeTab === "products" && (_jsx(CategoryProductsListing, { categorySlug: "", brandName: brandName, initialData: initialProductsData })), activeTab === "auctions" && (_jsx(AuctionsIndexListing, { brandName: brandName })), activeTab === "pre-orders" && (_jsx(PreOrdersIndexListing, { brandName: brandName })), activeTab === "prize-draws" && (_jsx(PrizeDrawsIndexListing, { brandName: brandName })), activeTab === "bundles" && (_jsx(CategoryBundlesListing, { initialBundles: initialBundles, brandName: brandName }))] }));
|
|
22
36
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CategoryDocument } from "../schemas";
|
|
2
|
+
export interface CategoryBundlesListingProps {
|
|
3
|
+
initialBundles: CategoryDocument[];
|
|
4
|
+
brandName?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function CategoryBundlesListing({ initialBundles, brandName, }: CategoryBundlesListingProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* CategoryBundlesListing — SB-UNI-D + V replacement for the deleted
|
|
5
|
+
* BundlesByCategoryListing. Renders a sortable list of bundles that live
|
|
6
|
+
* as `categoryType:"bundle"` rows on the categories collection.
|
|
7
|
+
*
|
|
8
|
+
* Parent server-fetches the list; client owns only the sort state.
|
|
9
|
+
*/
|
|
10
|
+
import { useMemo, useState } from "react";
|
|
11
|
+
import Link from "next/link";
|
|
12
|
+
import { Badge, Div, Row, Select, Stack, Text } from "../../../ui";
|
|
13
|
+
import { ROUTES } from "../../../next/routing/route-map";
|
|
14
|
+
import { formatCurrency } from "../../../utils/number.formatter";
|
|
15
|
+
const SORT_OPTIONS = [
|
|
16
|
+
{ label: "Newest", value: "newest" },
|
|
17
|
+
{ label: "Price ↑", value: "price-asc" },
|
|
18
|
+
{ label: "Price ↓", value: "price-desc" },
|
|
19
|
+
];
|
|
20
|
+
const STOCK_BADGE_TEXT = {
|
|
21
|
+
in_stock: "",
|
|
22
|
+
partial: "Partial",
|
|
23
|
+
out_of_stock: "Out of stock",
|
|
24
|
+
};
|
|
25
|
+
const STOCK_BADGE_VARIANT = {
|
|
26
|
+
in_stock: "success",
|
|
27
|
+
partial: "warning",
|
|
28
|
+
out_of_stock: "danger",
|
|
29
|
+
};
|
|
30
|
+
const PLACEHOLDER_EMOJI = "📦";
|
|
31
|
+
const COPY = {
|
|
32
|
+
empty: "No bundles available",
|
|
33
|
+
forBrand: (name) => ` for ${name}`,
|
|
34
|
+
yetSuffix: " yet.",
|
|
35
|
+
sortLabel: "Sort:",
|
|
36
|
+
items: (n) => `${n} item${n !== 1 ? "s" : ""}`,
|
|
37
|
+
priceFallback: "—",
|
|
38
|
+
};
|
|
39
|
+
function comparator(sort) {
|
|
40
|
+
return (a, b) => {
|
|
41
|
+
if (sort === "price-asc") {
|
|
42
|
+
return (a.bundlePriceInPaise ?? 0) - (b.bundlePriceInPaise ?? 0);
|
|
43
|
+
}
|
|
44
|
+
if (sort === "price-desc") {
|
|
45
|
+
return (b.bundlePriceInPaise ?? 0) - (a.bundlePriceInPaise ?? 0);
|
|
46
|
+
}
|
|
47
|
+
const aT = a.createdAt instanceof Date ? a.createdAt.getTime() : 0;
|
|
48
|
+
const bT = b.createdAt instanceof Date ? b.createdAt.getTime() : 0;
|
|
49
|
+
return bT - aT;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export function CategoryBundlesListing({ initialBundles, brandName, }) {
|
|
53
|
+
const [sort, setSort] = useState("newest");
|
|
54
|
+
const filtered = useMemo(() => initialBundles
|
|
55
|
+
.filter((c) => c.categoryType === "bundle")
|
|
56
|
+
.slice()
|
|
57
|
+
.sort(comparator(sort)), [initialBundles, sort]);
|
|
58
|
+
if (filtered.length === 0) {
|
|
59
|
+
return (_jsx(Div, { className: "rounded-2xl border border-dashed border-zinc-200 py-16 text-center dark:border-zinc-700", children: _jsxs(Text, { color: "muted", children: [COPY.empty, brandName ? COPY.forBrand(brandName) : "", COPY.yetSuffix] }) }));
|
|
60
|
+
}
|
|
61
|
+
return (_jsxs(Stack, { gap: "md", children: [_jsxs(Row, { gap: "sm", align: "center", justify: "end", children: [_jsx(Text, { size: "sm", color: "muted", children: COPY.sortLabel }), _jsx(Select, { options: SORT_OPTIONS, value: sort, onValueChange: setSort, "aria-label": COPY.sortLabel })] }), _jsx(Div, { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3", children: filtered.map((bundle) => (_jsx(BundleCard, { bundle: bundle }, bundle.id))) })] }));
|
|
62
|
+
}
|
|
63
|
+
function BundleCard({ bundle }) {
|
|
64
|
+
const memberCount = bundle.bundleProductIds?.length ?? 0;
|
|
65
|
+
const stock = bundle.bundleStockStatus ?? "in_stock";
|
|
66
|
+
const badge = STOCK_BADGE_TEXT[stock];
|
|
67
|
+
const cover = bundle.display?.coverImage;
|
|
68
|
+
const href = String(ROUTES.PUBLIC.BUNDLE_DETAIL?.(bundle.slug) ?? "#");
|
|
69
|
+
return (_jsxs(Link, { href: href, className: "group rounded-xl border border-zinc-200 bg-white p-3 transition-colors hover:border-zinc-300 dark:border-zinc-800 dark:bg-zinc-900 dark:hover:border-zinc-700", children: [_jsx(Div, { className: "mb-2 aspect-video overflow-hidden rounded-lg bg-zinc-100 dark:bg-zinc-800", children: cover ? (
|
|
70
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
71
|
+
_jsx("img", { src: cover, alt: bundle.name, className: "h-full w-full object-cover transition-transform group-hover:scale-105", loading: "lazy" })) : (_jsx(Div, { className: "flex h-full w-full items-center justify-center text-3xl", children: PLACEHOLDER_EMOJI })) }), _jsx(Text, { className: "line-clamp-2 text-sm font-semibold", children: bundle.name }), _jsxs(Row, { gap: "sm", align: "center", className: "mt-1", children: [_jsx(Text, { size: "sm", weight: "bold", children: bundle.bundlePriceInPaise
|
|
72
|
+
? formatCurrency(bundle.bundlePriceInPaise / 100, "INR")
|
|
73
|
+
: COPY.priceFallback }), _jsx(Text, { size: "xs", color: "muted", children: COPY.items(memberCount) })] }), badge && (_jsx(Badge, { variant: STOCK_BADGE_VARIANT[stock], className: "mt-1", children: badge }))] }));
|
|
74
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import Link from "next/link";
|
|
3
|
-
import { categoriesRepository, productRepository } from "../../../repositories";
|
|
3
|
+
import { categoriesRepository, productRepository, } from "../../../repositories";
|
|
4
4
|
import { ROUTES } from "../../../next";
|
|
5
5
|
import { Container, Main, Section } from "../../../ui";
|
|
6
6
|
import { CategoryDetailTabs } from "./CategoryDetailTabs";
|
|
@@ -8,7 +8,7 @@ export async function CategoryDetailPageView({ slug }) {
|
|
|
8
8
|
const category = await categoriesRepository
|
|
9
9
|
.getCategoryBySlug(slug)
|
|
10
10
|
.catch(() => undefined);
|
|
11
|
-
const [productsResult, auctionsCountResult, preOrdersCountResult, childCategories] = await Promise.all([
|
|
11
|
+
const [productsResult, auctionsCountResult, preOrdersCountResult, prizeDrawsCountResult, bundlesResult, childCategories] = await Promise.all([
|
|
12
12
|
category?.id
|
|
13
13
|
? productRepository
|
|
14
14
|
.list({
|
|
@@ -39,6 +39,23 @@ export async function CategoryDetailPageView({ slug }) {
|
|
|
39
39
|
})
|
|
40
40
|
.catch(() => null)
|
|
41
41
|
: Promise.resolve(null),
|
|
42
|
+
category?.id
|
|
43
|
+
? productRepository
|
|
44
|
+
.list({
|
|
45
|
+
filters: `status==published,category==${category.id},listingType==prize-draw`,
|
|
46
|
+
sorts: "-createdAt",
|
|
47
|
+
page: 1,
|
|
48
|
+
pageSize: 1,
|
|
49
|
+
})
|
|
50
|
+
.catch(() => null)
|
|
51
|
+
: Promise.resolve(null),
|
|
52
|
+
// SB-UNI-D — bundles fetched from the categories collection. We pull
|
|
53
|
+
// all active bundle rows; the carousel filters by category affinity.
|
|
54
|
+
category?.id
|
|
55
|
+
? categoriesRepository
|
|
56
|
+
.listByType("bundle", { activeOnly: true, limit: 50 })
|
|
57
|
+
.catch(() => [])
|
|
58
|
+
: Promise.resolve([]),
|
|
42
59
|
category?.id
|
|
43
60
|
? categoriesRepository.getChildren(category.id).catch(() => [])
|
|
44
61
|
: Promise.resolve([]),
|
|
@@ -46,8 +63,16 @@ export async function CategoryDetailPageView({ slug }) {
|
|
|
46
63
|
const productCount = productsResult?.total ?? category?.metrics?.productCount ?? 0;
|
|
47
64
|
const auctionCount = auctionsCountResult?.total ?? category?.metrics?.auctionCount ?? 0;
|
|
48
65
|
const preOrderCount = preOrdersCountResult?.total ?? 0;
|
|
49
|
-
const
|
|
66
|
+
const prizeDrawCount = prizeDrawsCountResult?.total ?? 0;
|
|
67
|
+
const bundleCount = bundlesResult?.length ?? 0;
|
|
68
|
+
const totalCount = productCount + auctionCount + preOrderCount + prizeDrawCount + bundleCount;
|
|
50
69
|
const coverImage = category?.display?.coverImage;
|
|
51
70
|
const hasCover = Boolean(coverImage);
|
|
52
|
-
return (_jsxs(Main, { children: [_jsxs("section", { className: `relative overflow-hidden ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-zinc-50 dark:bg-zinc-900"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute inset-0 bg-center bg-cover", style: { backgroundImage: `url(${coverImage})` } }), _jsx("div", { className: "absolute inset-0 bg-black/55" })] })), _jsxs("div", { className: `relative z-10 max-w-7xl mx-auto px-4 ${hasCover ? "py-12" : "py-8"}`, children: [_jsxs("nav", { className: "flex items-center gap-1.5 text-sm mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 hover:text-primary-600 transition-colors", children: "Home" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORIES), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 hover:text-primary-600 transition-colors", children: "Categories" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx("span", { className: hasCover ? "text-white font-medium" : "text-zinc-900 dark:text-zinc-100 font-medium", children: category?.name ?? slug })] }), _jsx("h1", { className: `text-3xl md:text-4xl font-bold mb-2 ${hasCover ? "text-white" : "text-zinc-900 dark:text-zinc-50"}`, children: category?.name ?? slug }), category?.description && typeof category.description === "string" && !category.description.startsWith("{") && (_jsx("p", { className: `text-base max-w-2xl mb-4 ${hasCover ? "text-white/80" : "text-zinc-600 dark:text-zinc-400"}`, children: category.description })), _jsxs("div", { className: "flex flex-wrap gap-2", children: [productCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400"}`, children: [productCount.toLocaleString(), " ", productCount === 1 ? "product" : "products"] })), auctionCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400"}`, children: [auctionCount.toLocaleString(), " ", auctionCount === 1 ? "auction" : "auctions"] })), preOrderCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-400"}`, children: [preOrderCount.toLocaleString(), " ", preOrderCount === 1 ? "pre-order" : "pre-orders"] }))] })] })] }), childCategories.length > 0 && (_jsx("section", { className: "border-b border-zinc-100 dark:border-zinc-800 bg-white dark:bg-zinc-900", children: _jsx("div", { className: "max-w-7xl mx-auto px-4 py-3", children: _jsx("div", { className: "flex gap-2.5 overflow-x-auto pb-1", style: { scrollbarWidth: "none" }, children: childCategories.map((child) => (_jsxs(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(child.slug)), className: "flex-shrink-0 flex items-center gap-1.5 rounded-full border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800 px-4 py-1.5 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:border-primary hover:text-primary transition-colors whitespace-nowrap", children: [child.display?.icon && (_jsx("span", { className: "text-base leading-none", children: child.display.icon })), child.name, (child.metrics?.productCount ?? 0) > 0 && (_jsx("span", { className: "text-xs text-zinc-400 dark:text-zinc-500", children: (child.metrics?.productCount ?? 0).toLocaleString() }))] }, child.id))) }) }) })), _jsx(Section, { className: "py-6", children: _jsx(Container, { size: "xl", children: _jsx(CategoryDetailTabs, { categorySlug: slug, categoryId: category?.id, initialProductsData: productsResult ?? undefined,
|
|
71
|
+
return (_jsxs(Main, { children: [_jsxs("section", { className: `relative overflow-hidden ${hasCover ? "min-h-[220px] md:min-h-[280px]" : "bg-zinc-50 dark:bg-zinc-900"}`, children: [hasCover && (_jsxs(_Fragment, { children: [_jsx("div", { className: "absolute inset-0 bg-center bg-cover", style: { backgroundImage: `url(${coverImage})` } }), _jsx("div", { className: "absolute inset-0 bg-black/55" })] })), _jsxs("div", { className: `relative z-10 max-w-7xl mx-auto px-4 ${hasCover ? "py-12" : "py-8"}`, children: [_jsxs("nav", { className: "flex items-center gap-1.5 text-sm mb-4", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 hover:text-primary-600 transition-colors", children: "Home" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORIES), className: hasCover ? "text-white/70 hover:text-white transition-colors" : "text-zinc-500 hover:text-primary-600 transition-colors", children: "Categories" }), _jsx("span", { className: hasCover ? "text-white/40" : "text-zinc-400", children: "/" }), _jsx("span", { className: hasCover ? "text-white font-medium" : "text-zinc-900 dark:text-zinc-100 font-medium", children: category?.name ?? slug })] }), _jsx("h1", { className: `text-3xl md:text-4xl font-bold mb-2 ${hasCover ? "text-white" : "text-zinc-900 dark:text-zinc-50"}`, children: category?.name ?? slug }), category?.description && typeof category.description === "string" && !category.description.startsWith("{") && (_jsx("p", { className: `text-base max-w-2xl mb-4 ${hasCover ? "text-white/80" : "text-zinc-600 dark:text-zinc-400"}`, children: category.description })), _jsxs("div", { className: "flex flex-wrap gap-2", children: [productCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-primary/10 text-primary-700 dark:text-primary-400"}`, children: [productCount.toLocaleString(), " ", productCount === 1 ? "product" : "products"] })), auctionCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400"}`, children: [auctionCount.toLocaleString(), " ", auctionCount === 1 ? "auction" : "auctions"] })), preOrderCount > 0 && (_jsxs("span", { className: `inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hasCover ? "bg-white/20 text-white backdrop-blur-sm" : "bg-violet-100 text-violet-700 dark:bg-violet-900/30 dark:text-violet-400"}`, children: [preOrderCount.toLocaleString(), " ", preOrderCount === 1 ? "pre-order" : "pre-orders"] }))] })] })] }), childCategories.length > 0 && (_jsx("section", { className: "border-b border-zinc-100 dark:border-zinc-800 bg-white dark:bg-zinc-900", children: _jsx("div", { className: "max-w-7xl mx-auto px-4 py-3", children: _jsx("div", { className: "flex gap-2.5 overflow-x-auto pb-1", style: { scrollbarWidth: "none" }, children: childCategories.map((child) => (_jsxs(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(child.slug)), className: "flex-shrink-0 flex items-center gap-1.5 rounded-full border border-zinc-200 dark:border-zinc-700 bg-zinc-50 dark:bg-zinc-800 px-4 py-1.5 text-sm font-medium text-zinc-700 dark:text-zinc-200 hover:border-primary hover:text-primary transition-colors whitespace-nowrap", children: [child.display?.icon && (_jsx("span", { className: "text-base leading-none", children: child.display.icon })), child.name, (child.metrics?.productCount ?? 0) > 0 && (_jsx("span", { className: "text-xs text-zinc-400 dark:text-zinc-500", children: (child.metrics?.productCount ?? 0).toLocaleString() }))] }, child.id))) }) }) })), _jsx(Section, { className: "py-6", children: _jsx(Container, { size: "xl", children: _jsx(CategoryDetailTabs, { categorySlug: slug, categoryId: category?.id, initialProductsData: productsResult ?? undefined, initialBundles: bundlesResult ?? [], counts: {
|
|
72
|
+
products: productCount,
|
|
73
|
+
auctions: auctionCount,
|
|
74
|
+
preOrders: preOrderCount,
|
|
75
|
+
prizeDraws: prizeDrawCount,
|
|
76
|
+
bundles: bundleCount,
|
|
77
|
+
} }) }) })] }));
|
|
53
78
|
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import type { CategoryDocument } from "../schemas";
|
|
1
2
|
export interface CategoryDetailTabsProps {
|
|
2
3
|
categorySlug: string;
|
|
3
4
|
categoryId?: string;
|
|
4
5
|
initialProductsData?: any;
|
|
6
|
+
initialBundles?: CategoryDocument[];
|
|
5
7
|
counts?: {
|
|
6
8
|
products?: number;
|
|
7
9
|
auctions?: number;
|
|
8
10
|
preOrders?: number;
|
|
11
|
+
prizeDraws?: number;
|
|
12
|
+
bundles?: number;
|
|
9
13
|
};
|
|
10
14
|
}
|
|
11
|
-
export declare function CategoryDetailTabs({ categorySlug, categoryId, initialProductsData, counts }: CategoryDetailTabsProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare function CategoryDetailTabs({ categorySlug, categoryId, initialProductsData, initialBundles, counts, }: CategoryDetailTabsProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -4,19 +4,33 @@ import { useState } from "react";
|
|
|
4
4
|
import { CategoryProductsListing } from "./CategoryProductsListing";
|
|
5
5
|
import { AuctionsIndexListing } from "../../products/components/AuctionsIndexListing";
|
|
6
6
|
import { PreOrdersIndexListing } from "../../pre-orders/components/PreOrdersIndexListing";
|
|
7
|
+
import { PrizeDrawsIndexListing } from "../../products/components/PrizeDrawsIndexListing";
|
|
8
|
+
import { CategoryBundlesListing } from "./CategoryBundlesListing";
|
|
9
|
+
import { CATEGORY_PAGE_TABS } from "../../products/constants/listing-tabs";
|
|
7
10
|
function tabLabel(label, count) {
|
|
8
11
|
if (!count)
|
|
9
12
|
return label;
|
|
10
13
|
return `${label} (${count.toLocaleString()})`;
|
|
11
14
|
}
|
|
12
|
-
export function CategoryDetailTabs({ categorySlug, categoryId, initialProductsData, counts }) {
|
|
15
|
+
export function CategoryDetailTabs({ categorySlug, categoryId, initialProductsData, initialBundles = [], counts, }) {
|
|
13
16
|
const [activeTab, setActiveTab] = useState("products");
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
const countFor = (id) => {
|
|
18
|
+
switch (id) {
|
|
19
|
+
case "products":
|
|
20
|
+
return counts?.products;
|
|
21
|
+
case "auctions":
|
|
22
|
+
return counts?.auctions;
|
|
23
|
+
case "pre-orders":
|
|
24
|
+
return counts?.preOrders;
|
|
25
|
+
case "prize-draws":
|
|
26
|
+
return counts?.prizeDraws;
|
|
27
|
+
case "bundles":
|
|
28
|
+
return counts?.bundles;
|
|
29
|
+
default:
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
return (_jsxs("div", { children: [_jsx("div", { className: "flex border-b border-zinc-200 dark:border-slate-700 mb-6 overflow-x-auto", children: CATEGORY_PAGE_TABS.map((t) => (_jsx("button", { type: "button", onClick: () => setActiveTab(t.id), className: `px-5 py-2.5 text-sm font-medium whitespace-nowrap transition-colors -mb-px border-b-2 ${activeTab === t.id
|
|
20
34
|
? "border-primary text-primary"
|
|
21
|
-
: "border-transparent text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-100"}`, children: t.label }, t.id))) }), activeTab === "products" && (_jsx(CategoryProductsListing, { categorySlug: categorySlug, categoryId: categoryId, initialData: initialProductsData })), activeTab === "auctions" && (_jsx(AuctionsIndexListing, { categorySlug: categorySlug })), activeTab === "pre-orders" && (_jsx(PreOrdersIndexListing, { categorySlug: categorySlug }))] }));
|
|
35
|
+
: "border-transparent text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-100"}`, children: tabLabel(t.label, countFor(t.id)) }, t.id))) }), activeTab === "products" && (_jsx(CategoryProductsListing, { categorySlug: categorySlug, categoryId: categoryId, initialData: initialProductsData })), activeTab === "auctions" && (_jsx(AuctionsIndexListing, { categorySlug: categorySlug })), activeTab === "pre-orders" && (_jsx(PreOrdersIndexListing, { categorySlug: categorySlug })), activeTab === "prize-draws" && (_jsx(PrizeDrawsIndexListing, { categorySlug: categorySlug })), activeTab === "bundles" && (_jsx(CategoryBundlesListing, { initialBundles: initialBundles }))] }));
|
|
22
36
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// SB-UNI X3 — migrations shell for category.
|
|
2
|
+
// v1 schema is current; no migrations needed yet.
|
|
3
|
+
//
|
|
4
|
+
// To add a migration:
|
|
5
|
+
// 1. Bump the relevant SCHEMA_VERSIONS entry in
|
|
6
|
+
// `_internal/shared/schema-versions.ts` (e.g. `categories: 2`)
|
|
7
|
+
// 2. Add a row below keyed by the FROM-version, returning the upgraded doc
|
|
8
|
+
// 3. Repository wraps reads with `migrateDocument(doc, current, migrations)`
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
export const migrations = {};
|
|
@@ -26,5 +26,34 @@ export declare class CategoriesRepository extends BaseRepository<CategoryDocumen
|
|
|
26
26
|
* Cloud Functions: full-overwrite category metrics (used by nightly reconciliation job).
|
|
27
27
|
*/
|
|
28
28
|
setMetrics(categoryId: string, productCount: number, auctionCount: number, productIds: string[], auctionIds: string[]): Promise<void>;
|
|
29
|
+
/** SB-UNI — discriminator-keyed listing (sublistings, brands, bundles). */
|
|
30
|
+
listByType(type: import("../types").CategoryType, opts?: {
|
|
31
|
+
limit?: number;
|
|
32
|
+
activeOnly?: boolean;
|
|
33
|
+
}): Promise<CategoryDocument[]>;
|
|
34
|
+
/** SB-UNI — locate a category by slug, optionally constrained by discriminator. */
|
|
35
|
+
findBySlugAndType(slug: string, type?: import("../types").CategoryType): Promise<CategoryDocument | null>;
|
|
36
|
+
/**
|
|
37
|
+
* SB-UNI-B — fetch products linked to a sublisting category (legacy
|
|
38
|
+
* sublistingCategoryId field on ProductDocument), ordered by price asc.
|
|
39
|
+
* Used by /api/sublisting-categories/[slug]/listings.
|
|
40
|
+
*/
|
|
41
|
+
getSublistingListings(sublistingId: string, limit?: number): Promise<Record<string, unknown>[]>;
|
|
42
|
+
/**
|
|
43
|
+
* SB-UNI-B — delete a sublisting category and unlink all products
|
|
44
|
+
* that referenced it. Mirrors the cascade behavior of the old
|
|
45
|
+
* SublistingCategoriesRepository.delete().
|
|
46
|
+
*/
|
|
47
|
+
deleteWithSublistingUnlink(categoryId: string): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* SB-UNI-C — list active brand categories ordered by displayOrder.
|
|
50
|
+
* Replaces the old `brandsRepository.findActive()` call site.
|
|
51
|
+
*/
|
|
52
|
+
findActiveBrands(): Promise<CategoryDocument[]>;
|
|
53
|
+
/**
|
|
54
|
+
* SB-UNI-B — derive the canonical `sublisting-{slug}` ID from a
|
|
55
|
+
* human-entered category name.
|
|
56
|
+
*/
|
|
57
|
+
generateSublistingId(name: string): string;
|
|
29
58
|
}
|
|
30
59
|
export declare const categoriesRepository: CategoriesRepository;
|
|
@@ -364,6 +364,88 @@ export class CategoriesRepository extends BaseRepository {
|
|
|
364
364
|
updatedAt: new Date(),
|
|
365
365
|
});
|
|
366
366
|
}
|
|
367
|
+
/** SB-UNI — discriminator-keyed listing (sublistings, brands, bundles). */
|
|
368
|
+
async listByType(type, opts = {}) {
|
|
369
|
+
let q = this.db
|
|
370
|
+
.collection(this.collection)
|
|
371
|
+
.where("categoryType", "==", type);
|
|
372
|
+
if (opts.activeOnly)
|
|
373
|
+
q = q.where("isActive", "==", true);
|
|
374
|
+
if (opts.limit)
|
|
375
|
+
q = q.limit(opts.limit);
|
|
376
|
+
const snap = await q.get();
|
|
377
|
+
return snap.docs.map((d) => this.mapDoc(d));
|
|
378
|
+
}
|
|
379
|
+
/** SB-UNI — locate a category by slug, optionally constrained by discriminator. */
|
|
380
|
+
async findBySlugAndType(slug, type) {
|
|
381
|
+
let q = this.db
|
|
382
|
+
.collection(this.collection)
|
|
383
|
+
.where("slug", "==", slug)
|
|
384
|
+
.limit(1);
|
|
385
|
+
if (type)
|
|
386
|
+
q = q.where("categoryType", "==", type);
|
|
387
|
+
const snap = await q.get();
|
|
388
|
+
if (snap.empty)
|
|
389
|
+
return null;
|
|
390
|
+
return this.mapDoc(snap.docs[0]);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* SB-UNI-B — fetch products linked to a sublisting category (legacy
|
|
394
|
+
* sublistingCategoryId field on ProductDocument), ordered by price asc.
|
|
395
|
+
* Used by /api/sublisting-categories/[slug]/listings.
|
|
396
|
+
*/
|
|
397
|
+
async getSublistingListings(sublistingId, limit = 20) {
|
|
398
|
+
const snap = await this.db
|
|
399
|
+
.collection("products")
|
|
400
|
+
.where("sublistingCategoryId", "==", sublistingId)
|
|
401
|
+
.where("status", "==", "published")
|
|
402
|
+
.orderBy("price", "asc")
|
|
403
|
+
.limit(limit)
|
|
404
|
+
.get();
|
|
405
|
+
return snap.docs.map((d) => ({ id: d.id, ...d.data() }));
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* SB-UNI-B — delete a sublisting category and unlink all products
|
|
409
|
+
* that referenced it. Mirrors the cascade behavior of the old
|
|
410
|
+
* SublistingCategoriesRepository.delete().
|
|
411
|
+
*/
|
|
412
|
+
async deleteWithSublistingUnlink(categoryId) {
|
|
413
|
+
const batch = this.db.batch();
|
|
414
|
+
const productsSnap = await this.db
|
|
415
|
+
.collection("products")
|
|
416
|
+
.where("sublistingCategoryId", "==", categoryId)
|
|
417
|
+
.get();
|
|
418
|
+
for (const doc of productsSnap.docs) {
|
|
419
|
+
batch.update(doc.ref, { sublistingCategoryId: null, updatedAt: new Date() });
|
|
420
|
+
}
|
|
421
|
+
batch.delete(this.db.collection(this.collection).doc(categoryId));
|
|
422
|
+
await batch.commit();
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* SB-UNI-C — list active brand categories ordered by displayOrder.
|
|
426
|
+
* Replaces the old `brandsRepository.findActive()` call site.
|
|
427
|
+
*/
|
|
428
|
+
async findActiveBrands() {
|
|
429
|
+
const snap = await this.db
|
|
430
|
+
.collection(this.collection)
|
|
431
|
+
.where("categoryType", "==", "brand")
|
|
432
|
+
.where("isActive", "==", true)
|
|
433
|
+
.orderBy("order", "asc")
|
|
434
|
+
.get();
|
|
435
|
+
return snap.docs.map((d) => this.mapDoc(d));
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* SB-UNI-B — derive the canonical `sublisting-{slug}` ID from a
|
|
439
|
+
* human-entered category name.
|
|
440
|
+
*/
|
|
441
|
+
generateSublistingId(name) {
|
|
442
|
+
const base = name
|
|
443
|
+
.toLowerCase()
|
|
444
|
+
.trim()
|
|
445
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
446
|
+
.replace(/^-+|-+$/g, "");
|
|
447
|
+
return base.startsWith("sublisting-") ? base : `sublisting-${base}`;
|
|
448
|
+
}
|
|
367
449
|
}
|
|
368
450
|
CategoriesRepository.SIEVE_FIELDS = {
|
|
369
451
|
name: { canFilter: true, canSort: true },
|
|
@@ -372,6 +454,7 @@ CategoriesRepository.SIEVE_FIELDS = {
|
|
|
372
454
|
isActive: { canFilter: true, canSort: false },
|
|
373
455
|
isFeatured: { canFilter: true, canSort: false },
|
|
374
456
|
isBrand: { canFilter: true, canSort: false },
|
|
457
|
+
categoryType: { canFilter: true, canSort: false },
|
|
375
458
|
isSearchable: { canFilter: true, canSort: false },
|
|
376
459
|
parentId: { canFilter: true, canSort: false, path: "parentIds" },
|
|
377
460
|
order: { canFilter: true, canSort: true },
|
|
@@ -6,6 +6,32 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { CategoryAncestor, CategoryMetrics, CategoryDisplay } from "../types";
|
|
8
8
|
export type { CategoryAncestor, CategoryMetrics };
|
|
9
|
+
/**
|
|
10
|
+
* How a bundle resolves its member products.
|
|
11
|
+
*
|
|
12
|
+
* `static` — hand-picked product IDs. The admin editor multi-select writes
|
|
13
|
+
* here; bundleProductIds mirrors for index-friendly queries.
|
|
14
|
+
*
|
|
15
|
+
* `dynamic` — publisher-pack style: filter against the products collection
|
|
16
|
+
* (e.g. "all Pokémon TCG products in this category, ordered by price asc,
|
|
17
|
+
* limit N"). Resolved by onProductStockChange + a scheduled job; the
|
|
18
|
+
* resolved IDs are cached on `bundleProductIds` with a `bundleQueryResolvedAt`
|
|
19
|
+
* timestamp.
|
|
20
|
+
*/
|
|
21
|
+
export type BundleQueryRule = {
|
|
22
|
+
type: "static";
|
|
23
|
+
productIds: string[];
|
|
24
|
+
} | {
|
|
25
|
+
type: "dynamic";
|
|
26
|
+
filter: {
|
|
27
|
+
categorySlug?: string;
|
|
28
|
+
brandSlug?: string;
|
|
29
|
+
tags?: string[];
|
|
30
|
+
listingType?: "standard" | "pre-order";
|
|
31
|
+
};
|
|
32
|
+
orderBy?: "price-asc" | "price-desc" | "createdAt-desc";
|
|
33
|
+
limit: number;
|
|
34
|
+
};
|
|
9
35
|
/** Full Firestore category document (includes server-only fields) */
|
|
10
36
|
export interface CategoryDocumentSEO {
|
|
11
37
|
title: string;
|
|
@@ -56,8 +82,34 @@ export interface CategoryDocument {
|
|
|
56
82
|
metrics: CategoryDocumentMetrics;
|
|
57
83
|
isFeatured: boolean;
|
|
58
84
|
featuredPriority?: number;
|
|
85
|
+
/** @deprecated Use categoryType === "brand". Will be removed once seed + admin form fully migrate. */
|
|
59
86
|
isBrand?: boolean;
|
|
60
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Discriminator — distinguishes plain categories from sublistings (tier-4
|
|
89
|
+
* leaf groups under a parent category) and brands (storefront-facing
|
|
90
|
+
* brand pages). SB-UNI-D will add `"bundle"` to this union.
|
|
91
|
+
*/
|
|
92
|
+
categoryType?: import("../types").CategoryType;
|
|
93
|
+
/** Sublisting/grading code (e.g. "108/120", "PSA 10") — categoryType==="sublisting". */
|
|
94
|
+
itemCode?: string;
|
|
95
|
+
/** Brand homepage URL — categoryType==="brand". */
|
|
96
|
+
brandWebsite?: string;
|
|
97
|
+
/** Brand country of origin — categoryType==="brand". */
|
|
98
|
+
brandCountry?: string;
|
|
99
|
+
/** Brand founding year — categoryType==="brand". */
|
|
100
|
+
brandFounded?: number;
|
|
101
|
+
/** Banner image for the brand storefront page — categoryType==="brand". */
|
|
102
|
+
brandBannerImage?: string;
|
|
103
|
+
/** Discounted bundle price in paise. */
|
|
104
|
+
bundlePriceInPaise?: number;
|
|
105
|
+
/** Rule resolving the bundle's member products — static list or live query. */
|
|
106
|
+
bundleQueryRule?: BundleQueryRule;
|
|
107
|
+
/** Snapshot stock state — recomputed by onProductStockChange. */
|
|
108
|
+
bundleStockStatus?: "in_stock" | "partial" | "out_of_stock";
|
|
109
|
+
/** Timestamp of the last dynamic-rule resolution. */
|
|
110
|
+
bundleQueryResolvedAt?: Date;
|
|
111
|
+
/** Hand-picked products list (mirror of bundleQueryRule for static rules); kept for index-friendly queries. */
|
|
112
|
+
bundleProductIds?: string[];
|
|
61
113
|
seo: CategoryDocumentSEO;
|
|
62
114
|
display: CategoryDocumentDisplay;
|
|
63
115
|
isActive: boolean;
|
|
@@ -75,7 +127,7 @@ export interface CategoryDocument {
|
|
|
75
127
|
ancestors: CategoryAncestor[];
|
|
76
128
|
}
|
|
77
129
|
export declare const CATEGORIES_COLLECTION: "categories";
|
|
78
|
-
export declare const CATEGORIES_INDEXED_FIELDS: readonly ["slug", "rootId", "tier", "parentIds", "isLeaf", "isFeatured", "featuredPriority", "isBrand", "isActive", "isSearchable", "showOnHomepage", "createdBy", "createdByType", "createdByStoreId", "createdAt"];
|
|
130
|
+
export declare const CATEGORIES_INDEXED_FIELDS: readonly ["slug", "rootId", "tier", "parentIds", "isLeaf", "isFeatured", "featuredPriority", "isBrand", "categoryType", "isActive", "isSearchable", "showOnHomepage", "createdBy", "createdByType", "createdByStoreId", "createdAt"];
|
|
79
131
|
export declare const MIN_ITEMS_FOR_FEATURED: 8;
|
|
80
132
|
export declare const MAX_FEATURED_CATEGORIES: 4;
|
|
81
133
|
export declare const DEFAULT_CATEGORY_DATA: Partial<CategoryDocument>;
|
|
@@ -103,7 +155,12 @@ export declare const categoryQueryHelpers: {
|
|
|
103
155
|
readonly byRootId: (rootId: string) => readonly ["rootId", "==", string];
|
|
104
156
|
readonly children: (parentId: string) => readonly ["parentIds", "array-contains", string];
|
|
105
157
|
readonly featured: () => readonly ["isFeatured", "==", true];
|
|
158
|
+
/** @deprecated Use byCategoryType("brand"). */
|
|
106
159
|
readonly brands: () => readonly ["isBrand", "==", true];
|
|
160
|
+
/** SB-UNI B + C + D — discriminator-based listing. */
|
|
161
|
+
readonly byCategoryType: (type: import("../types").CategoryType) => readonly ["categoryType", "==", import("..").CategoryType];
|
|
162
|
+
readonly sublistings: () => readonly ["categoryType", "==", "sublisting"];
|
|
163
|
+
readonly brandPages: () => readonly ["categoryType", "==", "brand"];
|
|
107
164
|
readonly active: () => readonly ["isActive", "==", true];
|
|
108
165
|
readonly searchable: () => readonly ["isSearchable", "==", true];
|
|
109
166
|
readonly byCreator: (userId: string) => readonly ["createdBy", "==", string];
|