@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
|
@@ -16,6 +16,7 @@ export const CATEGORIES_INDEXED_FIELDS = [
|
|
|
16
16
|
"isFeatured",
|
|
17
17
|
"featuredPriority",
|
|
18
18
|
"isBrand",
|
|
19
|
+
"categoryType",
|
|
19
20
|
"isActive",
|
|
20
21
|
"isSearchable",
|
|
21
22
|
"showOnHomepage",
|
|
@@ -97,7 +98,12 @@ export const categoryQueryHelpers = {
|
|
|
97
98
|
byRootId: (rootId) => ["rootId", "==", rootId],
|
|
98
99
|
children: (parentId) => ["parentIds", "array-contains", parentId],
|
|
99
100
|
featured: () => ["isFeatured", "==", true],
|
|
101
|
+
/** @deprecated Use byCategoryType("brand"). */
|
|
100
102
|
brands: () => ["isBrand", "==", true],
|
|
103
|
+
/** SB-UNI B + C + D — discriminator-based listing. */
|
|
104
|
+
byCategoryType: (type) => ["categoryType", "==", type],
|
|
105
|
+
sublistings: () => ["categoryType", "==", "sublisting"],
|
|
106
|
+
brandPages: () => ["categoryType", "==", "brand"],
|
|
101
107
|
active: () => ["isActive", "==", true],
|
|
102
108
|
searchable: () => ["isSearchable", "==", true],
|
|
103
109
|
byCreator: (userId) => ["createdBy", "==", userId],
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type CategoryType = "category" | "
|
|
1
|
+
export type CategoryType = "category" | "sublisting" | "brand" | "bundle";
|
|
2
2
|
export interface CategorySeo {
|
|
3
3
|
title?: string;
|
|
4
4
|
description?: string;
|
|
@@ -24,7 +24,15 @@ export interface CategoryAncestor {
|
|
|
24
24
|
}
|
|
25
25
|
export interface CategoryItem {
|
|
26
26
|
id: string;
|
|
27
|
-
|
|
27
|
+
categoryType?: CategoryType;
|
|
28
|
+
/** Sublisting/grading code, e.g. "108/120" or "PSA 10" — only set when categoryType==="sublisting". */
|
|
29
|
+
itemCode?: string;
|
|
30
|
+
/** Brand website — only set when categoryType==="brand". */
|
|
31
|
+
brandWebsite?: string;
|
|
32
|
+
/** Brand country — only set when categoryType==="brand". */
|
|
33
|
+
brandCountry?: string;
|
|
34
|
+
/** Brand founded year — only set when categoryType==="brand". */
|
|
35
|
+
brandFounded?: number;
|
|
28
36
|
name: string;
|
|
29
37
|
slug: string;
|
|
30
38
|
description?: string;
|
|
@@ -38,7 +46,7 @@ export interface CategoryItem {
|
|
|
38
46
|
metrics?: CategoryMetrics;
|
|
39
47
|
isFeatured?: boolean;
|
|
40
48
|
featuredPriority?: number;
|
|
41
|
-
/** @deprecated Use
|
|
49
|
+
/** @deprecated Use categoryType === "brand" */ isBrand?: boolean;
|
|
42
50
|
seo?: CategorySeo;
|
|
43
51
|
display?: CategoryDisplay;
|
|
44
52
|
ancestors?: CategoryAncestor[];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// SB-UNI X3 — migrations shell for event/eventEntry.
|
|
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. `events: 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 = {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// SB-UNI X3 — migrations shell for faq.
|
|
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. `faq: 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 = {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// SB-UNI X3 — migrations shell for groupedListing.
|
|
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. `grouped: 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 = {};
|
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Grouped Listings Firestore Document Types & Constants
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* SB-UNI-V re-scoped this from a "bundle-like" pricing construct (with
|
|
5
|
+
* `bundlePrice` / `originalPrice` / `discountPercent`) to a **theme group**
|
|
6
|
+
* — a horizontal scroller that links related listings on the detail page
|
|
7
|
+
* without any pricing semantics. Pricing-aware bundles now live on the
|
|
8
|
+
* categories collection with `categoryType:"bundle"` (SB-UNI-D).
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
+
* A grouped listing survives partial sellout: when the count of currently-
|
|
11
|
+
* active members drops below `minActiveMembers`, `visibilityStatus` flips
|
|
12
|
+
* to "hidden" (recomputed by `onProductStockChange`).
|
|
10
13
|
*/
|
|
14
|
+
export type GroupTheme =
|
|
15
|
+
/** "Other anime figures you might like" */
|
|
16
|
+
"related"
|
|
17
|
+
/** "Same character across multiple lines" */
|
|
18
|
+
| "character"
|
|
19
|
+
/** "All HG kits in this lineage" */
|
|
20
|
+
| "lineage"
|
|
21
|
+
/** "From the same set / wave / drop" */
|
|
22
|
+
| "set"
|
|
23
|
+
/** Default / unspecified theme */
|
|
24
|
+
| "generic";
|
|
25
|
+
export type GroupVisibility = "visible" | "hidden";
|
|
11
26
|
export interface GroupedListingDocument {
|
|
12
27
|
id: string;
|
|
13
28
|
slug: string;
|
|
@@ -15,10 +30,14 @@ export interface GroupedListingDocument {
|
|
|
15
30
|
description?: string;
|
|
16
31
|
productIds: string[];
|
|
17
32
|
coverImage?: string;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
33
|
+
/** SB-UNI-V — semantic label for the group (drives section heading copy). */
|
|
34
|
+
groupTheme: GroupTheme;
|
|
35
|
+
/** Hide the group if fewer than this many members are active. Default 2. */
|
|
36
|
+
minActiveMembers: number;
|
|
37
|
+
/** Count of currently-active members. Maintained by onProductStockChange. */
|
|
38
|
+
activeMemberCount: number;
|
|
39
|
+
/** Computed from activeMemberCount < minActiveMembers. */
|
|
40
|
+
visibilityStatus: GroupVisibility;
|
|
22
41
|
isActive: boolean;
|
|
23
42
|
isFeatured: boolean;
|
|
24
43
|
storeId?: string;
|
|
@@ -29,4 +48,4 @@ export interface GroupedListingDocument {
|
|
|
29
48
|
updatedAt: Date;
|
|
30
49
|
}
|
|
31
50
|
export declare const GROUPED_LISTINGS_COLLECTION: "groupedListings";
|
|
32
|
-
export declare const GROUPED_LISTINGS_INDEXED_FIELDS: readonly ["storeId", "brandSlug", "categorySlug", "isActive", "isFeatured", "createdAt"];
|
|
51
|
+
export declare const GROUPED_LISTINGS_INDEXED_FIELDS: readonly ["storeId", "brandSlug", "categorySlug", "isActive", "isFeatured", "groupTheme", "visibilityStatus", "createdAt"];
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Grouped Listings Firestore Document Types & Constants
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* SB-UNI-V re-scoped this from a "bundle-like" pricing construct (with
|
|
5
|
+
* `bundlePrice` / `originalPrice` / `discountPercent`) to a **theme group**
|
|
6
|
+
* — a horizontal scroller that links related listings on the detail page
|
|
7
|
+
* without any pricing semantics. Pricing-aware bundles now live on the
|
|
8
|
+
* categories collection with `categoryType:"bundle"` (SB-UNI-D).
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
+
* A grouped listing survives partial sellout: when the count of currently-
|
|
11
|
+
* active members drops below `minActiveMembers`, `visibilityStatus` flips
|
|
12
|
+
* to "hidden" (recomputed by `onProductStockChange`).
|
|
10
13
|
*/
|
|
11
14
|
export const GROUPED_LISTINGS_COLLECTION = "groupedListings";
|
|
12
15
|
export const GROUPED_LISTINGS_INDEXED_FIELDS = [
|
|
@@ -15,5 +18,7 @@ export const GROUPED_LISTINGS_INDEXED_FIELDS = [
|
|
|
15
18
|
"categorySlug",
|
|
16
19
|
"isActive",
|
|
17
20
|
"isFeatured",
|
|
21
|
+
"groupTheme",
|
|
22
|
+
"visibilityStatus",
|
|
18
23
|
"createdAt",
|
|
19
24
|
];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// SB-UNI X3 — migrations shell for history.
|
|
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. `history: 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 = {};
|
|
@@ -10,12 +10,12 @@ export function useFeaturedAuctions(options) {
|
|
|
10
10
|
queryKey: ["auctions", "featured", options?.filterByBrand ?? "all"],
|
|
11
11
|
initialData: options?.initialData,
|
|
12
12
|
queryFn: async () => {
|
|
13
|
-
const promotedRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?filters=
|
|
13
|
+
const promotedRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?filters=listingType%3D%3Dauction%2Cstatus%3D%3Dpublished%2CisPromoted%3D%3Dtrue${brandFilter}&pageSize=18`);
|
|
14
14
|
const promoted = promotedRes?.items ?? [];
|
|
15
15
|
if (promoted.length >= MIN_COUNT)
|
|
16
16
|
return promoted;
|
|
17
17
|
const remaining = MIN_COUNT - promoted.length;
|
|
18
|
-
const latestRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?filters=
|
|
18
|
+
const latestRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?filters=listingType%3D%3Dauction%2Cstatus%3D%3Dpublished${brandFilter}&sorts=-createdAt&pageSize=${remaining + promoted.length}`);
|
|
19
19
|
const latest = latestRes?.items ?? [];
|
|
20
20
|
const existingIds = new Set(promoted.map((a) => a.id));
|
|
21
21
|
const filler = latest
|
|
@@ -10,12 +10,12 @@ export function useFeaturedPreOrders(options) {
|
|
|
10
10
|
queryKey: ["pre-orders", "featured", options?.filterByBrand ?? "all"],
|
|
11
11
|
initialData: options?.initialData,
|
|
12
12
|
queryFn: async () => {
|
|
13
|
-
const featuredRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?filters=
|
|
13
|
+
const featuredRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?filters=listingType%3D%3Dpre-order%2Cstatus%3D%3Dpublished${brandFilter}&sorts=preOrderDeliveryDate&pageSize=6`);
|
|
14
14
|
const featured = featuredRes?.items ?? [];
|
|
15
15
|
if (featured.length >= MIN_COUNT)
|
|
16
16
|
return featured;
|
|
17
17
|
const remaining = MIN_COUNT - featured.length;
|
|
18
|
-
const latestRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?filters=
|
|
18
|
+
const latestRes = await apiClient.get(`${PRODUCT_ENDPOINTS.LIST}?filters=listingType%3D%3Dpre-order%2Cstatus%3D%3Dpublished${brandFilter}&sorts=-createdAt&pageSize=${remaining + featured.length}`);
|
|
19
19
|
const latest = latestRes?.items ?? [];
|
|
20
20
|
const existingIds = new Set(featured.map((p) => p.id));
|
|
21
21
|
const filler = latest
|
|
@@ -25,7 +25,8 @@ import { BrandsSection } from "../components/BrandsSection";
|
|
|
25
25
|
import { SocialFeedSection } from "../components/SocialFeedSection";
|
|
26
26
|
import { CustomCardsSection } from "../components/CustomCardsSection";
|
|
27
27
|
import { GoogleReviewsSection } from "../components/GoogleReviewsSection";
|
|
28
|
-
|
|
28
|
+
// SB-UNI-V — FeaturedBundlesSection deleted; bundle homepage section to be
|
|
29
|
+
// rebuilt against categoryType:"bundle" queries in a follow-up cohort.
|
|
29
30
|
import { PrizeDrawsSection } from "../../products/components/PrizeDrawsSection";
|
|
30
31
|
import { EventRafflesSection } from "../../events/components/EventRafflesSection";
|
|
31
32
|
import { CollectionCardsSection } from "../components/CollectionCardsSection";
|
|
@@ -174,8 +175,9 @@ function renderSectionElement(section, newsletterFormSlot, faqItems, slides, liv
|
|
|
174
175
|
return (_jsx(GoogleReviewsSection, { placeId: cfg?.placeId ?? "", maxReviews: cfg?.maxReviews ?? 6, minRating: cfg?.minRating ?? 0, layout: cfg?.layout ?? "grid", showRating: cfg?.showRating ?? true, showDate: cfg?.showDate ?? true, linkToGoogleMaps: cfg?.linkToGoogleMaps ?? true, googleMapsUrl: cfg?.googleMapsUrl }));
|
|
175
176
|
}
|
|
176
177
|
case "featured-bundles": {
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
// SB-UNI-V — FeaturedBundlesSection deleted with features/bundles/. The
|
|
179
|
+
// category-based replacement lands in a follow-up.
|
|
180
|
+
return null;
|
|
179
181
|
}
|
|
180
182
|
case "prize-draws": {
|
|
181
183
|
const cfg = config;
|
|
@@ -24,7 +24,7 @@ export function AvatarUpload({ currentPhotoURL, currentCropData, userId, display
|
|
|
24
24
|
const [pendingUploadFile, setPendingUploadFile] = useState(null);
|
|
25
25
|
const fileInputRef = useRef(null);
|
|
26
26
|
const { showToast } = useToast();
|
|
27
|
-
const {
|
|
27
|
+
const { upload: uploadMedia, isPending: isUploading, error: uploadApiError, } = useMediaUpload();
|
|
28
28
|
useEffect(() => {
|
|
29
29
|
const hasPending = pendingCropData !== null && pendingUploadFile !== null;
|
|
30
30
|
onPendingStateChange?.(hasPending);
|
|
@@ -53,37 +53,15 @@ export function AvatarUpload({ currentPhotoURL, currentCropData, userId, display
|
|
|
53
53
|
const handleConfirmSave = async () => {
|
|
54
54
|
if (!pendingUploadFile || !pendingCropData)
|
|
55
55
|
return;
|
|
56
|
-
const formData = new FormData();
|
|
57
|
-
formData.append("file", pendingUploadFile);
|
|
58
56
|
const nameParts = (displayName ?? "").trim().split(/\s+/).filter(Boolean);
|
|
59
57
|
const firstName = nameParts[0] || userId;
|
|
60
58
|
const lastName = nameParts.slice(1).join("-") || firstName;
|
|
61
|
-
formData.append("context", JSON.stringify({
|
|
62
|
-
type: "user-avatar",
|
|
63
|
-
firstName,
|
|
64
|
-
lastName,
|
|
65
|
-
}));
|
|
66
|
-
formData.append("folder", "users");
|
|
67
|
-
formData.append("public", "true");
|
|
68
|
-
formData.append("metadata", JSON.stringify({
|
|
69
|
-
cropX: 0,
|
|
70
|
-
cropY: 0,
|
|
71
|
-
cropWidth: 400,
|
|
72
|
-
cropHeight: 400,
|
|
73
|
-
zoom: pendingCropData.zoom ?? 1,
|
|
74
|
-
focalX: (pendingCropData.position?.x ?? 50) / 100,
|
|
75
|
-
focalY: (pendingCropData.position?.y ?? 50) / 100,
|
|
76
|
-
aspectRatio: "1:1",
|
|
77
|
-
objectFit: "cover",
|
|
78
|
-
displayMode: "avatar",
|
|
79
|
-
originalWidth: 400,
|
|
80
|
-
originalHeight: 400,
|
|
81
|
-
mimeType: pendingUploadFile.type,
|
|
82
|
-
fileSize: pendingUploadFile.size,
|
|
83
|
-
}));
|
|
84
59
|
try {
|
|
85
|
-
const
|
|
86
|
-
|
|
60
|
+
const downloadURL = await uploadMedia(pendingUploadFile, "users", true, {
|
|
61
|
+
type: "user-avatar",
|
|
62
|
+
firstName,
|
|
63
|
+
lastName,
|
|
64
|
+
});
|
|
87
65
|
await onUploadSuccess?.(downloadURL, pendingCropData);
|
|
88
66
|
setPreviewUrl(downloadURL);
|
|
89
67
|
setCropData(pendingCropData);
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useMedia — upload/crop/trim hooks for @mohasinac/feat-media.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* useMediaUpload uses the signed-URL flow:
|
|
5
|
+
* 1. POST /api/media/sign — server issues v4 signed PUT URL
|
|
6
|
+
* 2. PUT directly to GCS — bytes bypass the Vercel function (Rule #6)
|
|
7
|
+
* 3. POST /api/media/finalize — server runs magic-byte check + returns URL
|
|
8
|
+
*
|
|
9
|
+
* The hook surface (`upload(file, folder, isPublic, context) => Promise<string>`)
|
|
10
|
+
* is unchanged so existing field components (MediaUploadField, MediaUploadList,
|
|
11
|
+
* ImageUpload, MediaPickerModal) keep working without modification.
|
|
6
12
|
*/
|
|
7
13
|
import type { MediaFilenameContext } from "../../../utils/id-generators";
|
|
8
14
|
export interface MediaUploadResult {
|
|
@@ -12,6 +18,12 @@ export interface MediaUploadResult {
|
|
|
12
18
|
size?: number;
|
|
13
19
|
type?: string;
|
|
14
20
|
}
|
|
21
|
+
interface UploadVariables {
|
|
22
|
+
file: File;
|
|
23
|
+
folder: string;
|
|
24
|
+
isPublic: boolean;
|
|
25
|
+
context?: MediaFilenameContext | Record<string, unknown>;
|
|
26
|
+
}
|
|
15
27
|
export interface MediaCropInput {
|
|
16
28
|
sourceUrl: string;
|
|
17
29
|
x: number;
|
|
@@ -31,13 +43,16 @@ export interface MediaTrimInput {
|
|
|
31
43
|
quality?: "low" | "medium" | "high";
|
|
32
44
|
}
|
|
33
45
|
/**
|
|
34
|
-
* useMediaUpload — uploads a file via
|
|
46
|
+
* useMediaUpload — uploads a file via the signed-URL flow.
|
|
35
47
|
*
|
|
36
48
|
* @example
|
|
37
49
|
* const { upload, isPending } = useMediaUpload();
|
|
38
50
|
* const url = await upload(file, "products", true);
|
|
39
51
|
*/
|
|
40
|
-
export declare function useMediaUpload(
|
|
52
|
+
export declare function useMediaUpload(endpoints?: {
|
|
53
|
+
sign?: string;
|
|
54
|
+
finalize?: string;
|
|
55
|
+
}): {
|
|
41
56
|
upload: (file: File, folder?: string, isPublic?: boolean, context?: MediaFilenameContext | Record<string, unknown>) => Promise<string>;
|
|
42
57
|
data: undefined;
|
|
43
58
|
variables: undefined;
|
|
@@ -47,68 +62,68 @@ export declare function useMediaUpload(endpoint?: string): {
|
|
|
47
62
|
isPending: false;
|
|
48
63
|
isSuccess: false;
|
|
49
64
|
status: "idle";
|
|
50
|
-
mutate: import("@tanstack/react-query").UseMutateFunction<MediaUploadResult, Error,
|
|
65
|
+
mutate: import("@tanstack/react-query").UseMutateFunction<MediaUploadResult, Error, UploadVariables, unknown>;
|
|
51
66
|
reset: () => void;
|
|
52
67
|
context: unknown;
|
|
53
68
|
failureCount: number;
|
|
54
69
|
failureReason: Error | null;
|
|
55
70
|
isPaused: boolean;
|
|
56
71
|
submittedAt: number;
|
|
57
|
-
mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<MediaUploadResult, Error,
|
|
72
|
+
mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<MediaUploadResult, Error, UploadVariables, unknown>;
|
|
58
73
|
} | {
|
|
59
74
|
upload: (file: File, folder?: string, isPublic?: boolean, context?: MediaFilenameContext | Record<string, unknown>) => Promise<string>;
|
|
60
75
|
data: undefined;
|
|
61
|
-
variables:
|
|
76
|
+
variables: UploadVariables;
|
|
62
77
|
error: null;
|
|
63
78
|
isError: false;
|
|
64
79
|
isIdle: false;
|
|
65
80
|
isPending: true;
|
|
66
81
|
isSuccess: false;
|
|
67
82
|
status: "pending";
|
|
68
|
-
mutate: import("@tanstack/react-query").UseMutateFunction<MediaUploadResult, Error,
|
|
83
|
+
mutate: import("@tanstack/react-query").UseMutateFunction<MediaUploadResult, Error, UploadVariables, unknown>;
|
|
69
84
|
reset: () => void;
|
|
70
85
|
context: unknown;
|
|
71
86
|
failureCount: number;
|
|
72
87
|
failureReason: Error | null;
|
|
73
88
|
isPaused: boolean;
|
|
74
89
|
submittedAt: number;
|
|
75
|
-
mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<MediaUploadResult, Error,
|
|
90
|
+
mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<MediaUploadResult, Error, UploadVariables, unknown>;
|
|
76
91
|
} | {
|
|
77
92
|
upload: (file: File, folder?: string, isPublic?: boolean, context?: MediaFilenameContext | Record<string, unknown>) => Promise<string>;
|
|
78
93
|
data: undefined;
|
|
79
94
|
error: Error;
|
|
80
|
-
variables:
|
|
95
|
+
variables: UploadVariables;
|
|
81
96
|
isError: true;
|
|
82
97
|
isIdle: false;
|
|
83
98
|
isPending: false;
|
|
84
99
|
isSuccess: false;
|
|
85
100
|
status: "error";
|
|
86
|
-
mutate: import("@tanstack/react-query").UseMutateFunction<MediaUploadResult, Error,
|
|
101
|
+
mutate: import("@tanstack/react-query").UseMutateFunction<MediaUploadResult, Error, UploadVariables, unknown>;
|
|
87
102
|
reset: () => void;
|
|
88
103
|
context: unknown;
|
|
89
104
|
failureCount: number;
|
|
90
105
|
failureReason: Error | null;
|
|
91
106
|
isPaused: boolean;
|
|
92
107
|
submittedAt: number;
|
|
93
|
-
mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<MediaUploadResult, Error,
|
|
108
|
+
mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<MediaUploadResult, Error, UploadVariables, unknown>;
|
|
94
109
|
} | {
|
|
95
110
|
upload: (file: File, folder?: string, isPublic?: boolean, context?: MediaFilenameContext | Record<string, unknown>) => Promise<string>;
|
|
96
111
|
data: MediaUploadResult;
|
|
97
112
|
error: null;
|
|
98
|
-
variables:
|
|
113
|
+
variables: UploadVariables;
|
|
99
114
|
isError: false;
|
|
100
115
|
isIdle: false;
|
|
101
116
|
isPending: false;
|
|
102
117
|
isSuccess: true;
|
|
103
118
|
status: "success";
|
|
104
|
-
mutate: import("@tanstack/react-query").UseMutateFunction<MediaUploadResult, Error,
|
|
119
|
+
mutate: import("@tanstack/react-query").UseMutateFunction<MediaUploadResult, Error, UploadVariables, unknown>;
|
|
105
120
|
reset: () => void;
|
|
106
121
|
context: unknown;
|
|
107
122
|
failureCount: number;
|
|
108
123
|
failureReason: Error | null;
|
|
109
124
|
isPaused: boolean;
|
|
110
125
|
submittedAt: number;
|
|
111
|
-
mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<MediaUploadResult, Error,
|
|
126
|
+
mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<MediaUploadResult, Error, UploadVariables, unknown>;
|
|
112
127
|
};
|
|
113
128
|
/**
|
|
114
129
|
* useMediaCrop — sends pixel-crop params to /api/media/crop.
|
|
@@ -202,3 +217,4 @@ export declare function useMediaCleanup(endpoint?: string): {
|
|
|
202
217
|
submittedAt: number;
|
|
203
218
|
mutateAsync: import("@tanstack/react-query").UseMutateAsyncFunction<void, Error, string[], unknown>;
|
|
204
219
|
};
|
|
220
|
+
export {};
|
|
@@ -1,33 +1,68 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useMedia — upload/crop/trim hooks for @mohasinac/feat-media.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* useMediaUpload uses the signed-URL flow:
|
|
5
|
+
* 1. POST /api/media/sign — server issues v4 signed PUT URL
|
|
6
|
+
* 2. PUT directly to GCS — bytes bypass the Vercel function (Rule #6)
|
|
7
|
+
* 3. POST /api/media/finalize — server runs magic-byte check + returns URL
|
|
8
|
+
*
|
|
9
|
+
* The hook surface (`upload(file, folder, isPublic, context) => Promise<string>`)
|
|
10
|
+
* is unchanged so existing field components (MediaUploadField, MediaUploadList,
|
|
11
|
+
* ImageUpload, MediaPickerModal) keep working without modification.
|
|
6
12
|
*/
|
|
7
13
|
import { useMutation } from "@tanstack/react-query";
|
|
8
14
|
import { apiClient } from "../../../http";
|
|
15
|
+
import { classifyMime, MAX_BYTES, MAX_LABEL, isAllowedMime, } from "../../../_internal/shared/media/limits";
|
|
9
16
|
import { MEDIA_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
17
|
// --- Hooks --------------------------------------------------------------------
|
|
11
18
|
/**
|
|
12
|
-
* useMediaUpload — uploads a file via
|
|
19
|
+
* useMediaUpload — uploads a file via the signed-URL flow.
|
|
13
20
|
*
|
|
14
21
|
* @example
|
|
15
22
|
* const { upload, isPending } = useMediaUpload();
|
|
16
23
|
* const url = await upload(file, "products", true);
|
|
17
24
|
*/
|
|
18
|
-
export function useMediaUpload(
|
|
25
|
+
export function useMediaUpload(endpoints = {}) {
|
|
26
|
+
const signEndpoint = endpoints.sign ?? MEDIA_ENDPOINTS.SIGN;
|
|
27
|
+
const finalizeEndpoint = endpoints.finalize ?? MEDIA_ENDPOINTS.FINALIZE;
|
|
19
28
|
const mutation = useMutation({
|
|
20
|
-
mutationFn: (
|
|
29
|
+
mutationFn: async ({ file, folder, isPublic, context }) => {
|
|
30
|
+
const contentType = file.type;
|
|
31
|
+
// Client-side precheck — fail fast before any network call. Catches
|
|
32
|
+
// image-vs-video kind mismatch and oversize files with the same
|
|
33
|
+
// user-facing message the server would have returned.
|
|
34
|
+
if (!isAllowedMime(contentType)) {
|
|
35
|
+
throw new Error(`Unsupported file type: ${contentType || "unknown"}`);
|
|
36
|
+
}
|
|
37
|
+
const kind = classifyMime(contentType);
|
|
38
|
+
if (file.size > MAX_BYTES[kind]) {
|
|
39
|
+
throw new Error(`File too large — ${kind} uploads must be ≤ ${MAX_LABEL[kind]}`);
|
|
40
|
+
}
|
|
41
|
+
const signResponse = await apiClient.post(signEndpoint, {
|
|
42
|
+
contentType,
|
|
43
|
+
size: file.size,
|
|
44
|
+
folder,
|
|
45
|
+
isPublic,
|
|
46
|
+
context,
|
|
47
|
+
});
|
|
48
|
+
// PUT bytes directly to Cloud Storage. The browser does NOT send
|
|
49
|
+
// credentials and the bucket must allow the request origin via CORS.
|
|
50
|
+
const putResponse = await fetch(signResponse.uploadUrl, {
|
|
51
|
+
method: "PUT",
|
|
52
|
+
body: file,
|
|
53
|
+
headers: { "Content-Type": signResponse.contentType },
|
|
54
|
+
});
|
|
55
|
+
if (!putResponse.ok) {
|
|
56
|
+
throw new Error(`Storage PUT failed with status ${putResponse.status}`);
|
|
57
|
+
}
|
|
58
|
+
return apiClient.post(finalizeEndpoint, {
|
|
59
|
+
storagePath: signResponse.storagePath,
|
|
60
|
+
isPublic,
|
|
61
|
+
});
|
|
62
|
+
},
|
|
21
63
|
});
|
|
22
64
|
const upload = async (file, folder = "uploads", isPublic = true, context) => {
|
|
23
|
-
const
|
|
24
|
-
formData.append("file", file);
|
|
25
|
-
formData.append("folder", folder);
|
|
26
|
-
formData.append("public", isPublic.toString());
|
|
27
|
-
if (context) {
|
|
28
|
-
formData.append("context", JSON.stringify(context));
|
|
29
|
-
}
|
|
30
|
-
const data = await mutation.mutateAsync(formData);
|
|
65
|
+
const data = await mutation.mutateAsync({ file, folder, isPublic, context });
|
|
31
66
|
return data.url;
|
|
32
67
|
};
|
|
33
68
|
return { ...mutation, upload };
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
/**
|
|
4
4
|
* ImageUpload — canonical image upload component for @mohasinac/feat-media.
|
|
5
5
|
*
|
|
6
|
-
* Stage locally → caller-provided onUpload() →
|
|
6
|
+
* Stage locally → caller-provided onUpload() → sign+PUT+finalize signed-URL flow.
|
|
7
7
|
* Optional focal-point crop (enableCrop, default true).
|
|
8
8
|
* Optional camera capture (captureSource="both").
|
|
9
9
|
*/
|
|
@@ -4,7 +4,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
4
4
|
* MediaUploadField — single-file upload field for @mohasinac/feat-media.
|
|
5
5
|
*
|
|
6
6
|
* For video: optionally opens VideoTrimModal then VideoThumbnailSelector after upload.
|
|
7
|
-
* Stage locally → caller-provided onUpload() →
|
|
7
|
+
* Stage locally → caller-provided onUpload() → sign+PUT+finalize signed-URL flow.
|
|
8
8
|
*/
|
|
9
9
|
import { useState, useRef, useEffect } from "react";
|
|
10
10
|
import { useTranslations } from "next-intl";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// SB-UNI X3 — migrations shell for conversation.
|
|
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. `messages: 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 = {};
|
|
@@ -23,9 +23,18 @@ export function OrderCard({ order, onClick, labels = {} }) {
|
|
|
23
23
|
})
|
|
24
24
|
: "";
|
|
25
25
|
const statusColor = STATUS_COLORS[order.orderStatus] ?? "bg-neutral-100 text-neutral-700";
|
|
26
|
+
// SB8-F — count unrevealed prize-draw entries to surface a "reveals pending" badge.
|
|
27
|
+
const unrevealedPrizeDraws = order.items.filter((it) => it.listingType === "prize-draw" &&
|
|
28
|
+
it.prizeRevealStatus !== undefined &&
|
|
29
|
+
it.prizeRevealStatus !== "revealed");
|
|
30
|
+
const revealsRemaining = unrevealedPrizeDraws.length;
|
|
31
|
+
const earliestDeadline = unrevealedPrizeDraws
|
|
32
|
+
.map((it) => it.prizeRevealDeadline)
|
|
33
|
+
.filter((d) => !!d)
|
|
34
|
+
.sort()[0];
|
|
26
35
|
return (_jsxs(Div, { role: onClick ? "button" : undefined, tabIndex: onClick ? 0 : undefined, onKeyDown: onClick
|
|
27
36
|
? (e) => (e.key === "Enter" || e.key === " ") && onClick(order)
|
|
28
|
-
: undefined, onClick: onClick ? () => onClick(order) : undefined, className: `rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900 p-5 ${onClick ? "cursor-pointer transition hover:shadow-md" : ""}`, children: [_jsxs(Row, { wrap: true, align: "start", justify: "between", gap: "3", children: [_jsxs(Div, { children: [_jsxs(Text, { className: "text-xs text-neutral-500 dark:text-zinc-400", children: ["Order #", order.id.slice(-8).toUpperCase()] }), date && (_jsx(Text, { className: "mt-0.5 text-xs text-neutral-400 dark:text-zinc-500", children: date }))] }), _jsx(Span, { className: `rounded-full px-3 py-1 text-xs font-semibold capitalize ${statusColor}`, children: labels[order.orderStatus] ?? order.orderStatus.replace(/_/g, " ") })] }), _jsxs(Row, { wrap: true, gap: "3", className: "mt-4", children: [order.items.slice(0, 3).map((item, i) => (_jsxs(Row, { className: "gap-2", children: [item.image && (_jsx(Div, { role: "img", "aria-label": item.title, className: "h-10 w-10 rounded-lg bg-center bg-cover", style: { backgroundImage: `url(${item.image})` } })), _jsxs(Div, { children: [_jsx(Text, { className: `text-sm font-medium text-neutral-900 dark:text-zinc-100 ${THEME_CONSTANTS.utilities.textClamp1}`, children: item.title }), _jsxs(Text, { className: "text-xs text-neutral-400 dark:text-zinc-500", children: ["\u00D7", item.quantity] })] })] }, i))), order.items.length > 3 && (_jsxs(Span, { className: "self-center text-xs text-neutral-400 dark:text-zinc-500", children: ["+", order.items.length - 3, " more"] }))] }), _jsxs(Row, { justify: "between", className: "mt-4 border-t border-neutral-100 dark:border-slate-700 pt-3", children: [_jsxs(Span, { className: "text-sm text-neutral-500 dark:text-zinc-400", children: [order.currency ?? "", " Total"] }), _jsx(Span, { className: "font-semibold text-neutral-900 dark:text-zinc-100", children: formatCurrency(order.total, order.currency) })] })] }));
|
|
37
|
+
: undefined, onClick: onClick ? () => onClick(order) : undefined, className: `rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900 p-5 ${onClick ? "cursor-pointer transition hover:shadow-md" : ""}`, children: [_jsxs(Row, { wrap: true, align: "start", justify: "between", gap: "3", children: [_jsxs(Div, { children: [_jsxs(Text, { className: "text-xs text-neutral-500 dark:text-zinc-400", children: ["Order #", order.id.slice(-8).toUpperCase()] }), date && (_jsx(Text, { className: "mt-0.5 text-xs text-neutral-400 dark:text-zinc-500", children: date }))] }), _jsx(Span, { className: `rounded-full px-3 py-1 text-xs font-semibold capitalize ${statusColor}`, children: labels[order.orderStatus] ?? order.orderStatus.replace(/_/g, " ") })] }), revealsRemaining > 0 && (_jsxs(Row, { gap: "sm", className: "mt-2", children: [_jsxs(Span, { className: "inline-flex items-center rounded-full bg-fuchsia-100 dark:bg-fuchsia-900/30 px-2.5 py-0.5 text-xs font-semibold text-fuchsia-700 dark:text-fuchsia-300", children: [revealsRemaining, " ", revealsRemaining === 1 ? "reveal" : "reveals", " pending"] }), earliestDeadline && (_jsxs(Span, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: ["before ", new Date(earliestDeadline).toLocaleDateString(getDefaultLocale(), { month: "short", day: "numeric" })] }))] })), _jsxs(Row, { wrap: true, gap: "3", className: "mt-4", children: [order.items.slice(0, 3).map((item, i) => (_jsxs(Row, { className: "gap-2", children: [item.image && (_jsx(Div, { role: "img", "aria-label": item.title, className: "h-10 w-10 rounded-lg bg-center bg-cover", style: { backgroundImage: `url(${item.image})` } })), _jsxs(Div, { children: [_jsx(Text, { className: `text-sm font-medium text-neutral-900 dark:text-zinc-100 ${THEME_CONSTANTS.utilities.textClamp1}`, children: item.title }), _jsxs(Text, { className: "text-xs text-neutral-400 dark:text-zinc-500", children: ["\u00D7", item.quantity] })] })] }, i))), order.items.length > 3 && (_jsxs(Span, { className: "self-center text-xs text-neutral-400 dark:text-zinc-500", children: ["+", order.items.length - 3, " more"] }))] }), _jsxs(Row, { justify: "between", className: "mt-4 border-t border-neutral-100 dark:border-slate-700 pt-3", children: [_jsxs(Span, { className: "text-sm text-neutral-500 dark:text-zinc-400", children: [order.currency ?? "", " Total"] }), _jsx(Span, { className: "font-semibold text-neutral-900 dark:text-zinc-100", children: formatCurrency(order.total, order.currency) })] })] }));
|
|
29
38
|
}
|
|
30
39
|
export function OrdersList({ orders, isLoading, onOrderClick, totalPages = 1, currentPage = 1, onPageChange, emptyLabel = "No orders found", }) {
|
|
31
40
|
if (isLoading) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// SB-UNI X3 — migrations shell for order.
|
|
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. `orders: 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 = {};
|