@mohasinac/appkit 4.42.18 → 4.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/lottery/LotteryDetailView.js +22 -1
- package/dist/_internal/server/features/categories/adapters.d.ts +47 -0
- package/dist/_internal/server/features/categories/adapters.js +235 -0
- package/dist/_internal/server/features/checkout/actions.js +161 -22
- package/dist/_internal/server/features/checkout/expand-order-items.js +34 -0
- package/dist/_internal/server/features/orders/adapters.js +21 -0
- package/dist/_internal/server/features/products/list-public.d.ts +24 -0
- package/dist/_internal/server/features/products/list-public.js +10 -2
- package/dist/_internal/server/features/refunds/actions.js +1 -1
- package/dist/_internal/server/features/seo/sitemap.js +54 -15
- package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
- package/dist/_internal/server/features/stores/adapters.js +6 -0
- package/dist/_internal/server/functions/scheduled.d.ts +4 -2
- package/dist/_internal/server/functions/scheduled.js +64 -6
- package/dist/_internal/server/jobs/core/adminAnalytics.d.ts +0 -1
- package/dist/_internal/server/jobs/core/adminAnalytics.js +9 -1
- package/dist/_internal/server/jobs/core/analyticsAlertEvaluate.d.ts +47 -0
- package/dist/_internal/server/jobs/core/analyticsAlertEvaluate.js +468 -0
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +14 -1
- package/dist/_internal/server/jobs/core/countersReconcile.js +24 -8
- package/dist/_internal/server/jobs/core/onCategoryWrite.js +59 -0
- package/dist/_internal/server/jobs/core/onProductWrite.js +31 -0
- package/dist/_internal/server/jobs/core/onReviewWrite.js +8 -0
- package/dist/_internal/server/jobs/core/onStoreWrite.d.ts +1 -1
- package/dist/_internal/server/jobs/core/onStoreWrite.js +8 -2
- package/dist/_internal/server/jobs/core/priceIndexRollup.d.ts +47 -0
- package/dist/_internal/server/jobs/core/priceIndexRollup.js +234 -0
- package/dist/_internal/server/jobs/core/revalidateNotify.d.ts +28 -0
- package/dist/_internal/server/jobs/core/revalidateNotify.js +125 -0
- package/dist/_internal/server/jobs/core/{testerSandboxCleanup.d.ts → testDataCleanup.d.ts} +3 -3
- package/dist/_internal/server/jobs/core/{testerSandboxCleanup.js → testDataCleanup.js} +3 -3
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +5 -1
- package/dist/_internal/server/jobs/handlers/analyticsAlertEvaluate.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/analyticsAlertEvaluate.js +2 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +3 -1
- package/dist/_internal/server/jobs/handlers/index.js +3 -1
- package/dist/_internal/server/jobs/handlers/messages.d.ts +5 -2
- package/dist/_internal/server/jobs/handlers/messages.js +5 -2
- package/dist/_internal/server/jobs/handlers/priceIndexRollup.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/priceIndexRollup.js +2 -0
- package/dist/_internal/server/jobs/handlers/testDataCleanup.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/testDataCleanup.js +2 -0
- package/dist/_internal/shared/actions/action-registry.js +34 -0
- package/dist/_internal/shared/cache/revalidate-targets.d.ts +49 -0
- package/dist/_internal/shared/cache/revalidate-targets.js +157 -0
- package/dist/_internal/shared/checkout/order-math.d.ts +113 -3
- package/dist/_internal/shared/checkout/order-math.js +194 -7
- package/dist/_internal/shared/features/categories/price-guidance.d.ts +110 -0
- package/dist/_internal/shared/features/categories/price-guidance.js +136 -0
- package/dist/_internal/shared/features/site-settings/gst-registration.d.ts +90 -0
- package/dist/_internal/shared/features/site-settings/gst-registration.js +126 -0
- package/dist/_internal/shared/fees/calculator.d.ts +105 -0
- package/dist/_internal/shared/fees/calculator.js +143 -0
- package/dist/_internal/shared/listing-types/art/config.d.ts +0 -6
- package/dist/_internal/shared/listing-types/classified/config.d.ts +0 -6
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +0 -6
- package/dist/_internal/shared/listing-types/live/config.d.ts +0 -6
- package/dist/_internal/shared/listing-types/standard/config.d.ts +0 -6
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +0 -6
- package/dist/_internal/shared/templating/placeholders.d.ts +70 -0
- package/dist/_internal/shared/templating/placeholders.js +73 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +1 -0
- package/dist/constants/api-endpoints.d.ts +42 -0
- package/dist/constants/api-endpoints.js +14 -0
- package/dist/features/admin/actions/admin-actions.d.ts +1 -1
- package/dist/features/admin/actions/admin-actions.js +1 -1
- package/dist/features/admin/components/AdminCategoryEditorView.js +269 -3
- package/dist/features/admin/components/AdminProductEditorView.js +8 -1
- package/dist/features/admin/components/AdminProductsView.js +78 -7
- package/dist/features/admin/components/AdminSiteSettingsView.js +20 -1
- package/dist/features/admin/components/AdminTaxCodeEditorView.d.ts +10 -0
- package/dist/features/admin/components/AdminTaxCodeEditorView.js +160 -0
- package/dist/features/admin/components/AdminTaxCodesView.d.ts +13 -0
- package/dist/features/admin/components/AdminTaxCodesView.js +86 -0
- package/dist/features/admin/components/AdminTesterChecklistView.js +1 -1
- package/dist/features/admin/components/analytics/AdminTopProductsTable.js +1 -1
- package/dist/features/auctions/components/AuctionBottomActions.js +10 -1
- package/dist/features/auctions/components/AuctionDetailPageView.js +2 -1
- package/dist/features/auctions/components/AuctionLiveStatusBadge.d.ts +20 -0
- package/dist/features/auctions/components/AuctionLiveStatusBadge.js +21 -0
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +1 -1
- package/dist/features/auctions/components/PlaceBidFormClient.js +20 -2
- package/dist/features/auctions/hooks/useIsAuctionEnded.d.ts +41 -0
- package/dist/features/auctions/hooks/useIsAuctionEnded.js +76 -0
- package/dist/features/auth/schemas/firestore.d.ts +0 -1
- package/dist/features/blog/api/route.js +16 -1
- package/dist/features/blog/schemas/firestore.d.ts +2 -2
- package/dist/features/catalogue/components/CatalogueItemEditorView.js +14 -3
- package/dist/features/categories/actions/category-actions.d.ts +2 -2
- package/dist/features/categories/actions/category-actions.js +22 -10
- package/dist/features/categories/api/[id]/description-template/route.d.ts +45 -0
- package/dist/features/categories/api/[id]/description-template/route.js +252 -0
- package/dist/features/categories/api/route.js +156 -11
- package/dist/features/categories/components/CategoriesIndexListing.d.ts +1 -3
- package/dist/features/categories/components/CategoriesIndexListing.js +1 -1
- package/dist/features/categories/components/CategoriesIndexPageView.d.ts +27 -1
- package/dist/features/categories/components/CategoriesIndexPageView.js +24 -33
- package/dist/features/categories/components/CategoryDetailPageView.js +43 -10
- package/dist/features/categories/components/CategoryTemplatePicker.d.ts +65 -0
- package/dist/features/categories/components/CategoryTemplatePicker.js +170 -0
- package/dist/features/categories/constants/listing-categories.d.ts +17 -0
- package/dist/features/categories/constants/listing-categories.js +53 -0
- package/dist/features/categories/hooks/useCategoryTree.js +28 -2
- package/dist/features/categories/repository/categories.repository.d.ts +73 -12
- package/dist/features/categories/repository/categories.repository.js +203 -42
- package/dist/features/categories/repository/price-index.repository.d.ts +115 -0
- package/dist/features/categories/repository/price-index.repository.js +85 -0
- package/dist/features/categories/schemas/category-content.d.ts +206 -0
- package/dist/features/categories/schemas/category-content.js +28 -0
- package/dist/features/categories/schemas/firestore.d.ts +74 -5
- package/dist/features/categories/schemas/firestore.js +74 -3
- package/dist/features/categories/schemas/index.d.ts +1047 -0
- package/dist/features/categories/schemas/index.js +96 -0
- package/dist/features/categories/server.d.ts +1 -0
- package/dist/features/categories/server.js +7 -0
- package/dist/features/categories/types/index.d.ts +9 -0
- package/dist/features/events/schemas/firestore.d.ts +2 -2
- package/dist/features/faq/schemas/firestore.d.ts +2 -1
- package/dist/features/faq/schemas/firestore.js +20 -8
- package/dist/features/faq/utils/getHomepageFaqsCached.d.ts +20 -0
- package/dist/features/faq/utils/getHomepageFaqsCached.js +22 -0
- package/dist/features/homepage/components/MarketplaceHomepageView.js +13 -3
- package/dist/features/homepage/lib/live-stats.js +18 -8
- package/dist/features/media/MediaImage.js +19 -2
- package/dist/features/orders/schemas/firestore.d.ts +70 -0
- package/dist/features/orders/types/index.d.ts +55 -0
- package/dist/features/products/actions/product-actions.d.ts +0 -9
- package/dist/features/products/actions/product-actions.js +25 -0
- package/dist/features/products/columns/index.d.ts +0 -1
- package/dist/features/products/columns/index.js +5 -1
- package/dist/features/products/components/ArtStickersListView.js +7 -1
- package/dist/features/products/components/PrizeDrawsSection.js +5 -0
- package/dist/features/products/components/ProductFilters.d.ts +0 -9
- package/dist/features/products/components/ProductForm.d.ts +27 -1
- package/dist/features/products/components/ProductForm.js +26 -12
- package/dist/features/products/components/ProductsIndexPageView.js +1 -1
- package/dist/features/products/constants/sieve.d.ts +0 -6
- package/dist/features/products/constants/sieve.js +35 -2
- package/dist/features/products/repository/products.repository.d.ts +115 -1
- package/dist/features/products/repository/products.repository.js +491 -39
- package/dist/features/products/schemas/firestore.d.ts +65 -5
- package/dist/features/products/schemas/firestore.js +9 -0
- package/dist/features/products/schemas/product-features.d.ts +140 -2
- package/dist/features/products/schemas/product-features.js +62 -1
- package/dist/features/promotions/repository/coupons.repository.js +6 -1
- package/dist/features/promotions/schemas/firestore.d.ts +44 -1
- package/dist/features/promotions/schemas/firestore.js +76 -2
- package/dist/features/reviews/repository/reviews.repository.d.ts +22 -0
- package/dist/features/reviews/repository/reviews.repository.js +35 -0
- package/dist/features/seller/actions/seller-actions.js +17 -2
- package/dist/features/seller/components/PrintCenterView.d.ts +3 -2
- package/dist/features/seller/components/PrintCenterView.js +3 -2
- package/dist/features/seller/components/SellerAnalyticsAlertsView.js +21 -11
- package/dist/features/seller/components/SellerOrdersView.js +6 -1
- package/dist/features/seller/components/SellerPayoutSettingsView.js +19 -3
- package/dist/features/seller/components/SellerProductShell.d.ts +50 -2
- package/dist/features/seller/components/SellerProductShell.js +101 -9
- package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +13 -1
- package/dist/features/seller/components/SellerProductsFilterDrawer.js +2 -2
- package/dist/features/seller/components/SellerProductsView.js +108 -3
- package/dist/features/seller/types/index.d.ts +17 -0
- package/dist/features/seller/utils/product-draft-mapping.d.ts +13 -0
- package/dist/features/seller/utils/product-draft-mapping.js +17 -0
- package/dist/features/store-extensions/constants/alert-metrics.d.ts +86 -0
- package/dist/features/store-extensions/constants/alert-metrics.js +142 -0
- package/dist/features/store-extensions/schemas/firestore.d.ts +19 -1
- package/dist/features/stores/schemas/firestore.d.ts +31 -2
- package/dist/features/tax-codes/repository/tax-codes.repository.d.ts +55 -0
- package/dist/features/tax-codes/repository/tax-codes.repository.js +131 -0
- package/dist/features/tax-codes/schemas/firestore.d.ts +125 -0
- package/dist/features/tax-codes/schemas/firestore.js +82 -0
- package/dist/features/tax-codes/schemas/tax-code-form.d.ts +63 -0
- package/dist/features/tax-codes/schemas/tax-code-form.js +152 -0
- package/dist/features/tax-codes/schemas/tax-codes.validators.d.ts +107 -0
- package/dist/features/tax-codes/schemas/tax-codes.validators.js +85 -0
- package/dist/features/tester/actions/index.d.ts +0 -1
- package/dist/features/tester/actions/index.js +0 -1
- package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +1 -1
- package/dist/features/tester/components/AdminTesterFeedbackListView.js +1 -1
- package/dist/features/tester/components/index.d.ts +0 -4
- package/dist/features/tester/components/index.js +0 -2
- package/dist/features/tester/repository/tester-checklist-item.repository.d.ts +1 -20
- package/dist/features/tester/repository/tester-checklist-item.repository.js +0 -52
- package/dist/features/tester/schemas/firestore.d.ts +0 -6
- package/dist/features/tester/seed-data/authored/admin__admin-detail-round-trips.js +3 -3
- package/dist/features/tester/seed-data/authored/admin__site-system.js +4 -5
- package/dist/features/tester/seed-data/authored/content-discovery__category-counts-and-rollup.js +2 -2
- package/dist/features/tester/seed-data/authored/index.js +0 -2
- package/dist/features/tester/seed-data/authored/page-wiring__reachability.js +0 -19
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +10 -29
- package/dist/index.d.ts +21 -7
- package/dist/index.js +53 -7
- package/dist/next/routing/route-map.d.ts +2 -4
- package/dist/next/routing/route-map.js +1 -2
- package/dist/repositories/index.d.ts +1 -0
- package/dist/repositories/index.js +1 -0
- package/dist/schemas/registry.d.ts +506 -0
- package/dist/seed/_helpers/brand-rows.d.ts +35 -0
- package/dist/seed/_helpers/brand-rows.js +576 -0
- package/dist/seed/_helpers/category-content.d.ts +39 -0
- package/dist/seed/_helpers/category-content.js +634 -0
- package/dist/seed/_helpers/category-forest.d.ts +2 -0
- package/dist/seed/_helpers/category-forest.js +585 -243
- package/dist/seed/_helpers/category-models.d.ts +5 -0
- package/dist/seed/_helpers/category-models.js +1364 -0
- package/dist/seed/_helpers/category-tree.js +17 -1
- package/dist/seed/_helpers/character-features.d.ts +10 -0
- package/dist/seed/_helpers/character-features.js +118 -0
- package/dist/seed/_helpers/media.d.ts +1 -1
- package/dist/seed/_helpers/media.js +44 -8
- package/dist/seed/_helpers/search-txt-wrappers.d.ts +10 -0
- package/dist/seed/_helpers/search-txt-wrappers.js +12 -1
- package/dist/seed/categories-seed-data.js +147 -169
- package/dist/seed/index.d.ts +1 -0
- package/dist/seed/index.js +1 -0
- package/dist/seed/manifest.d.ts +1 -1
- package/dist/seed/manifest.js +5 -0
- package/dist/seed/orders-seed-data.js +5 -3
- package/dist/seed/product-features-seed-data.d.ts +1 -8
- package/dist/seed/product-features-seed-data.js +358 -154
- package/dist/seed/products-art-seed-data.js +16 -4
- package/dist/seed/products-auctions-seed-data.js +6 -6
- package/dist/seed/products-classifieds-seed-data.js +17 -7
- package/dist/seed/products-digital-codes-seed-data.js +12 -12
- package/dist/seed/products-preorders-seed-data.js +8 -8
- package/dist/seed/products-prize-draws-seed-data.js +25 -8
- package/dist/seed/products-standard-seed-data.js +10 -60
- package/dist/seed/products-stickers-seed-data.js +6 -6
- package/dist/seed/site-settings-seed-data.js +24 -0
- package/dist/seed/store-extensions-seed-data.js +10 -4
- package/dist/seed/tax-codes-seed-data.d.ts +34 -0
- package/dist/seed/tax-codes-seed-data.js +100 -0
- package/dist/seed/test-data-ttl.d.ts +29 -0
- package/dist/seed/test-data-ttl.js +31 -0
- package/dist/server-entry.d.ts +1 -1
- package/dist/server-entry.js +6 -1
- package/dist/server.d.ts +4 -1
- package/dist/server.js +11 -2
- package/dist/styles.css +40 -0
- package/dist/ui/components/PaginatedSelect.d.ts +14 -0
- package/dist/ui/components/PaginatedSelect.js +0 -0
- package/dist/ui/components/PaginatedSelect.style.css +40 -0
- package/dist/utils/media-url.d.ts +26 -0
- package/dist/utils/media-url.js +144 -0
- package/dist/utils/physical-location.d.ts +23 -0
- package/dist/utils/physical-location.js +52 -0
- package/dist/utils/search-txt-builders.d.ts +20 -0
- package/dist/utils/search-txt-builders.js +36 -0
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +4 -1
- package/dist/_internal/server/jobs/handlers/testerSandboxCleanup.d.ts +0 -2
- package/dist/_internal/server/jobs/handlers/testerSandboxCleanup.js +0 -2
- package/dist/features/tester/actions/leaderboard-actions.d.ts +0 -3
- package/dist/features/tester/actions/leaderboard-actions.js +0 -5
- package/dist/features/tester/components/BugHunterLeaderboardView.d.ts +0 -23
- package/dist/features/tester/components/BugHunterLeaderboardView.js +0 -18
- package/dist/features/tester/components/TesterHubView.d.ts +0 -5
- package/dist/features/tester/components/TesterHubView.js +0 -110
- package/dist/features/tester/seed-data/authored/public-pages__bug-hunters.d.ts +0 -2
- package/dist/features/tester/seed-data/authored/public-pages__bug-hunters.js +0 -78
- package/dist/features/tester/seed-data/tester-ttl.d.ts +0 -4
- package/dist/features/tester/seed-data/tester-ttl.js +0 -6
|
@@ -34,6 +34,11 @@ export async function PrizeDrawsSection({ config, }) {
|
|
|
34
34
|
sorts: sortBy(PRODUCT_FIELDS.CREATED_AT, "DESC"),
|
|
35
35
|
page: 1,
|
|
36
36
|
pageSize: config.maxItems ?? 8,
|
|
37
|
+
// Fixed-size strip that never pages — cap the bounded window instead of
|
|
38
|
+
// inheriting the 50-document default. Headroom over `pageSize` because
|
|
39
|
+
// availability and test-data are per-row predicates applied after the fetch.
|
|
40
|
+
// See `featuredWindow` in features/products/actions/product-actions.ts.
|
|
41
|
+
windowSize: Math.max((config.maxItems ?? 8) * 2, (config.maxItems ?? 8) + 8),
|
|
37
42
|
});
|
|
38
43
|
const draws = (result?.items ?? []);
|
|
39
44
|
if (draws.length === 0)
|
|
@@ -37,9 +37,6 @@ export declare const PRODUCT_ADMIN_SORT_OPTIONS: readonly [{
|
|
|
37
37
|
}, {
|
|
38
38
|
readonly value: string;
|
|
39
39
|
readonly label: "Recently Updated";
|
|
40
|
-
}, {
|
|
41
|
-
readonly value: string;
|
|
42
|
-
readonly label: "Most Viewed";
|
|
43
40
|
}];
|
|
44
41
|
export declare const PRODUCT_SELLER_SORT_OPTIONS: readonly [{
|
|
45
42
|
readonly value: string;
|
|
@@ -68,9 +65,6 @@ export declare const PRODUCT_SELLER_SORT_OPTIONS: readonly [{
|
|
|
68
65
|
}, {
|
|
69
66
|
readonly value: string;
|
|
70
67
|
readonly label: "Recently Updated";
|
|
71
|
-
}, {
|
|
72
|
-
readonly value: string;
|
|
73
|
-
readonly label: "Most Viewed";
|
|
74
68
|
}];
|
|
75
69
|
export declare const PRODUCT_PUBLIC_SORT_OPTIONS: readonly [{
|
|
76
70
|
readonly value: string;
|
|
@@ -90,9 +84,6 @@ export declare const PRODUCT_PUBLIC_SORT_OPTIONS: readonly [{
|
|
|
90
84
|
}, {
|
|
91
85
|
readonly value: string;
|
|
92
86
|
readonly label: "Name: Z–A";
|
|
93
|
-
}, {
|
|
94
|
-
readonly value: string;
|
|
95
|
-
readonly label: "Most Viewed";
|
|
96
87
|
}];
|
|
97
88
|
export declare function getProductFilterKeys(variant: ProductFilterVariant): readonly string[];
|
|
98
89
|
export declare function getProductSortOptions(variant: ProductFilterVariant): ReadonlyArray<{
|
|
@@ -33,6 +33,32 @@ export interface ProductFormProps {
|
|
|
33
33
|
product: ProductFormValue;
|
|
34
34
|
onChange: (updated: ProductFormValue) => void;
|
|
35
35
|
isReadonly?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Show the Tax & GST section (`gstRate` + `hsnCode`). **Admin only.**
|
|
38
|
+
*
|
|
39
|
+
* 🛑 Defaults to `false`, and the default is the point. LetItRip is the
|
|
40
|
+
* seller of record and invoices under its OWN GSTIN, so a supplier choosing
|
|
41
|
+
* the tax rate on goods we invoice is a compliance hole, not a convenience.
|
|
42
|
+
* The rate is a tax determination: it belongs to the category (via
|
|
43
|
+
* `productDefaults.taxCodeId`, resolved server-side by `deriveTaxonomy`) and
|
|
44
|
+
* to the admin who maintains the tax codes.
|
|
45
|
+
*
|
|
46
|
+
* There is also a one-click foot-gun behind it. The select renders
|
|
47
|
+
* `String(gstRate ?? 0)`, so an undetermined rate DISPLAYS as "0%" — and
|
|
48
|
+
* because `0` is a deliberate exemption while `undefined` means "nobody has
|
|
49
|
+
* said" (see `ProductDocument.gstRate`), a seller who touches that control
|
|
50
|
+
* at all, even re-picking the 0% already shown, writes a hard `0` and
|
|
51
|
+
* **permanently suppresses the category derivation**: `deriveTaxonomy` only
|
|
52
|
+
* derives when `input.gstRate == null`.
|
|
53
|
+
*
|
|
54
|
+
* 🛑 The display default does NOT corrupt the field on its own. `FormField`'s
|
|
55
|
+
* select fires `onChange` from a real DOM event only, and the draft is
|
|
56
|
+
* seeded from the stored document — so an untouched save preserves
|
|
57
|
+
* `undefined`. Verified rather than assumed; an earlier plan revision
|
|
58
|
+
* asserted every save converted "nobody has said" into "exempt", and that
|
|
59
|
+
* was not true of this code.
|
|
60
|
+
*/
|
|
61
|
+
canEditTax?: boolean;
|
|
36
62
|
/**
|
|
37
63
|
* Render a custom rich-text description editor.
|
|
38
64
|
* If omitted, a standard textarea field is used.
|
|
@@ -76,5 +102,5 @@ export interface ProductFormProps {
|
|
|
76
102
|
disabled: boolean;
|
|
77
103
|
}) => React.ReactNode;
|
|
78
104
|
}
|
|
79
|
-
export declare function ProductForm({ product, onChange, isReadonly, renderDescriptionEditor, renderCategorySelector, renderBrandSelector, renderStoreAddressSelector, onMediaAbort, currencyPrefix, renderGroupSettings, renderGroupJoinField, }: ProductFormProps): React.JSX.Element;
|
|
105
|
+
export declare function ProductForm({ product, onChange, isReadonly, canEditTax, renderDescriptionEditor, renderCategorySelector, renderBrandSelector, renderStoreAddressSelector, onMediaAbort, currencyPrefix, renderGroupSettings, renderGroupJoinField, }: ProductFormProps): React.JSX.Element;
|
|
80
106
|
export {};
|
|
@@ -50,7 +50,9 @@ function readFileAsDataUrl(file) {
|
|
|
50
50
|
reader.readAsDataURL(file);
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
|
-
export function ProductForm({ product, onChange, isReadonly = false,
|
|
53
|
+
export function ProductForm({ product, onChange, isReadonly = false,
|
|
54
|
+
/* Defaults to false — the seller path must not reach it. See the prop's docs. */
|
|
55
|
+
canEditTax = false, renderDescriptionEditor, renderCategorySelector, renderBrandSelector, renderStoreAddressSelector, onMediaAbort, currencyPrefix = "", renderGroupSettings, renderGroupJoinField, }) {
|
|
54
56
|
const t = useTranslations("adminProducts");
|
|
55
57
|
const { upload } = useMediaUpload();
|
|
56
58
|
const galleryIndexRef = useRef(0);
|
|
@@ -179,17 +181,29 @@ export function ProductForm({ product, onChange, isReadonly = false, renderDescr
|
|
|
179
181
|
minOfferPercent: e.target.checked
|
|
180
182
|
? product.minOfferPercent ?? DEFAULT_MIN_OFFER_PERCENT
|
|
181
183
|
: undefined,
|
|
182
|
-
}), disabled: isReadonly })] }), product.allowOffers && (_jsxs(_Fragment, { children: [_jsx(Alert, { variant: "info", title: t("formAllowOffersHelp"), children: t("formAllowOffersHelp") }), _jsx(FormField, { name: "minOfferPercent", label: t("formMinOfferPercent"), type: "number", value: String(product.minOfferPercent ?? ""), onChange: (value) => update({ minOfferPercent: Number(value) }), disabled: isReadonly, placeholder: String(DEFAULT_MIN_OFFER_PERCENT) })] })), _jsx(Heading, { level: 4, className: "mt-4", children: t("sectionTaxGst") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "gstRate", label: t("formGstRate"), type: "select",
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
184
|
+
}), disabled: isReadonly })] }), product.allowOffers && (_jsxs(_Fragment, { children: [_jsx(Alert, { variant: "info", title: t("formAllowOffersHelp"), children: t("formAllowOffersHelp") }), _jsx(FormField, { name: "minOfferPercent", label: t("formMinOfferPercent"), type: "number", value: String(product.minOfferPercent ?? ""), onChange: (value) => update({ minOfferPercent: Number(value) }), disabled: isReadonly, placeholder: String(DEFAULT_MIN_OFFER_PERCENT) })] })), canEditTax ? (_jsxs(_Fragment, { children: [_jsx(Heading, { level: 4, className: "mt-4", children: t("sectionTaxGst") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "gstRate", label: t("formGstRate"), type: "select",
|
|
185
|
+
/*
|
|
186
|
+
* 🛑 "" is the UNSET option, and it is not cosmetic.
|
|
187
|
+
* `ProductDocument.gstRate` documents that `undefined` and `0`
|
|
188
|
+
* mean different things — zero is a deliberate exemption (live
|
|
189
|
+
* plants and animals), undefined is "nobody has said" — and
|
|
190
|
+
* `deriveTaxonomy` only derives the category's rate while it is
|
|
191
|
+
* `== null`. Collapsing unset to "0%", which this control did
|
|
192
|
+
* until 2026-10-10, made an undetermined rate read as exempt and
|
|
193
|
+
* put a hard `0` one click away.
|
|
194
|
+
*/
|
|
195
|
+
value: product.gstRate == null ? "" : String(product.gstRate), onChange: (value) => update({
|
|
196
|
+
gstRate: value === ""
|
|
197
|
+
? undefined
|
|
198
|
+
: Number(value),
|
|
199
|
+
}), disabled: isReadonly, options: [
|
|
200
|
+
{ value: "", label: t("formGstRateUnset") },
|
|
201
|
+
{ value: "0", label: "0%" },
|
|
202
|
+
{ value: "5", label: "5%" },
|
|
203
|
+
{ value: "12", label: "12%" },
|
|
204
|
+
{ value: "18", label: "18%" },
|
|
205
|
+
{ value: "28", label: "28%" },
|
|
206
|
+
] }), _jsx(FormField, { name: "hsnCode", label: t("formHsnCode"), type: "text", value: product.hsnCode ?? "", onChange: (value) => update({ hsnCode: value }), disabled: isReadonly, placeholder: "e.g. 9503" })] })] })) : null, _jsx(Heading, { level: 4, className: "mt-4", children: t("sectionConditionShipping") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "condition", label: t("formCondition"), type: "select", value: product.condition || "new", onChange: (value) => update({
|
|
193
207
|
condition: value,
|
|
194
208
|
}), disabled: isReadonly, options: [
|
|
195
209
|
{ value: "new", label: t("formConditionNew") },
|
|
@@ -53,5 +53,5 @@ export async function ProductsIndexPageView({ searchParams = {} }) {
|
|
|
53
53
|
sorts: DEFAULT_SORT,
|
|
54
54
|
...defaultAvailabilityForListingTypes(effectiveTypes),
|
|
55
55
|
}));
|
|
56
|
-
return (_jsxs(Main, { children: [_jsx(Section, { border: "bottom-subtle", paddingY: "b-md", padding: "t-xl", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, size: "3xl", weight: "bold", color: "primary", children: "Products" }), _jsx(Text, { className: "mt-1", color: "muted", size: "sm", children: "Discover amazing products and deals" }), _jsx(Div, { className: "mt-3", children: _jsx(TextLink, { variant: "bare", href: String(ROUTES.PUBLIC.AUCTIONS), rounded: "full", paddingX: "sm", paddingY: "2xs", size: "xs", weight: "medium", layout: "inline-flex", align: "center", gap: "sm", className: "border border-primary/30 bg-primary/10 text-primary-700 dark:text-primary-400 hover:bg-primary/15 transition-colors", children: "\uD83C\uDFF7\uFE0F Looking for unique deals? Browse Auctions \u2192" }) })] }) }), _jsxs(Container, { size: "xl", padding: "x-md", children: [_jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-4 mt-4" }), _jsx(ProductsIndexListing, { initialData: products }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] })] }));
|
|
56
|
+
return (_jsxs(Main, { children: [_jsx(Section, { border: "bottom-subtle", paddingY: "b-md", padding: "t-xl", children: _jsxs(Container, { size: "xl", children: [_jsx(Heading, { level: 1, size: "3xl", weight: "bold", color: "primary", children: "Products" }), _jsx(Text, { className: "mt-1", color: "muted", size: "sm", children: "Discover amazing products and deals" }), _jsx(Div, { className: "mt-3", children: _jsx(TextLink, { variant: "bare", href: String(ROUTES.PUBLIC.AUCTIONS), rounded: "full", paddingX: "sm", paddingY: "2xs", size: "xs", weight: "medium", layout: "inline-flex", align: "center", gap: "sm", className: "border border-primary/30 bg-primary/10 text-primary-700 dark:text-primary-400 hover:bg-primary/15 transition-colors", children: "\uD83C\uDFF7\uFE0F Looking for unique deals? Browse Auctions \u2192" }) })] }) }), _jsxs(Container, { size: "xl", padding: "x-md", children: [_jsx(AdSlot, { id: "listing-sidebar-top", className: "mb-4 mt-4" }), _jsx(ProductsIndexListing, { initialData: products ?? undefined }), _jsx(AdSlot, { id: "listing-sidebar-bottom", className: "mt-8" })] })] }));
|
|
57
57
|
}
|
|
@@ -57,9 +57,6 @@ export declare const STANDARD_SORT_OPTIONS: readonly [{
|
|
|
57
57
|
}, {
|
|
58
58
|
readonly value: string;
|
|
59
59
|
readonly label: "Recently Updated";
|
|
60
|
-
}, {
|
|
61
|
-
readonly value: string;
|
|
62
|
-
readonly label: "Most Viewed";
|
|
63
60
|
}];
|
|
64
61
|
export declare const STANDARD_PUBLIC_SORT_OPTIONS: readonly [{
|
|
65
62
|
readonly value: string;
|
|
@@ -79,9 +76,6 @@ export declare const STANDARD_PUBLIC_SORT_OPTIONS: readonly [{
|
|
|
79
76
|
}, {
|
|
80
77
|
readonly value: string;
|
|
81
78
|
readonly label: "Name: Z–A";
|
|
82
|
-
}, {
|
|
83
|
-
readonly value: string;
|
|
84
|
-
readonly label: "Most Viewed";
|
|
85
79
|
}];
|
|
86
80
|
export declare const AUCTION_SORT_OPTIONS: readonly [{
|
|
87
81
|
readonly value: string;
|
|
@@ -68,7 +68,31 @@ export const STANDARD_SORT_OPTIONS = [
|
|
|
68
68
|
{ value: sortBy(PRODUCT_FIELDS.FEATURED), label: "Featured First" },
|
|
69
69
|
{ value: sortBy(PRODUCT_FIELDS.IS_PROMOTED), label: "Promoted First" },
|
|
70
70
|
{ value: sortBy(PRODUCT_FIELDS.UPDATED_AT), label: "Recently Updated" },
|
|
71
|
-
|
|
71
|
+
/*
|
|
72
|
+
* 🛑 "Most Viewed" REMOVED 2026-10-10 — it returned zero products, always.
|
|
73
|
+
*
|
|
74
|
+
* It sorted by `viewCount`, and **no document has that field**: measured on
|
|
75
|
+
* production, 0 of 72 published products and 0 of 58 categories carry it.
|
|
76
|
+
* Nothing writes it either — `incrementViewCount` was removed from the
|
|
77
|
+
* render path on 2026-08-31 (correctly: a Firestore write per render) and
|
|
78
|
+
* both repositories' copies have had zero callers since.
|
|
79
|
+
*
|
|
80
|
+
* A Firestore `orderBy` EXCLUDES every document that lacks the ordering
|
|
81
|
+
* field, so this was Root Cause #100 verbatim. Confirmed against the real
|
|
82
|
+
* query shape — `status==published` + `listingType==standard`:
|
|
83
|
+
*
|
|
84
|
+
* no sort -> 16 rows
|
|
85
|
+
* orderBy title -> 16 rows
|
|
86
|
+
* orderBy viewCount -> 0 rows <- and the index EXISTS
|
|
87
|
+
*
|
|
88
|
+
* So it was not a missing index. It was a served query, costing a real
|
|
89
|
+
* round trip, guaranteed to return nothing — and picking it emptied the
|
|
90
|
+
* catalogue with no error anywhere.
|
|
91
|
+
*
|
|
92
|
+
* Real view counts arrive from the `pageViews` collection (already
|
|
93
|
+
* collected on ~30 surfaces) as `metrics.views7d`/`views30d`, rolled up by
|
|
94
|
+
* the daily job. A sort can come back then, against a field that exists.
|
|
95
|
+
*/
|
|
72
96
|
];
|
|
73
97
|
export const STANDARD_PUBLIC_SORT_OPTIONS = [
|
|
74
98
|
STANDARD_SORT_OPTIONS[0],
|
|
@@ -77,7 +101,16 @@ export const STANDARD_PUBLIC_SORT_OPTIONS = [
|
|
|
77
101
|
STANDARD_SORT_OPTIONS[3],
|
|
78
102
|
STANDARD_SORT_OPTIONS[4],
|
|
79
103
|
STANDARD_SORT_OPTIONS[5],
|
|
80
|
-
|
|
104
|
+
/*
|
|
105
|
+
* 🛑 `STANDARD_SORT_OPTIONS[9]` removed — it WAS "Most Viewed", and its
|
|
106
|
+
* presence here means the zero-result sort was offered on the PUBLIC
|
|
107
|
+
* products page, not only in admin. A shopper picking it emptied the
|
|
108
|
+
* catalogue.
|
|
109
|
+
*
|
|
110
|
+
* Indices 6–8 (Featured First / Promoted First / Recently Updated) are
|
|
111
|
+
* omitted deliberately and always have been — this list is a curated public
|
|
112
|
+
* subset, not a prefix.
|
|
113
|
+
*/
|
|
81
114
|
];
|
|
82
115
|
// ---------------------------------------------------------------------------
|
|
83
116
|
// Auctions — auction-specific base puts auctionEndDate primary
|
|
@@ -45,8 +45,123 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
|
|
|
45
45
|
* (Root Cause #6/#18) — a raw collection read has neither problem.
|
|
46
46
|
*/
|
|
47
47
|
private deriveTaxonomy;
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the GST rate floor, and gross up an ex-tax price if the seller
|
|
50
|
+
* authors that way.
|
|
51
|
+
*
|
|
52
|
+
* ## The rate resolution order, in full
|
|
53
|
+
*
|
|
54
|
+
* 1. the product's own `gstRate`, if a human set one — including `0`, a
|
|
55
|
+
* deliberate exemption;
|
|
56
|
+
* 2. the CATEGORY's rate via `taxCodeId` → `taxCodes`, applied just above
|
|
57
|
+
* by `applyCategoryDefaults`. **This is the mechanism**, and the ~330-node
|
|
58
|
+
* forest carries a tax code on every listing branch, so it answers for
|
|
59
|
+
* effectively the whole catalogue;
|
|
60
|
+
* 3. `DEFAULT_GST_RATE` — a floor, warned about, never a feature.
|
|
61
|
+
*
|
|
62
|
+
* 🛑 Step 3 fires only when steps 1 and 2 both came up empty, which means
|
|
63
|
+
* the category has no tax code — and THAT is the defect to fix. The measure
|
|
64
|
+
* of success is that this warning never appears. It exists because the
|
|
65
|
+
* alternative fails asymmetrically: with prices tax-inclusive, treating an
|
|
66
|
+
* unrated item as 0% hands the seller the full amount and leaves us owing
|
|
67
|
+
* tax on money already paid out, which is C0's bug in miniature.
|
|
68
|
+
*
|
|
69
|
+
* ## The gross-up
|
|
70
|
+
*
|
|
71
|
+
* A store with `pricesIncludeGst: false` types EX-TAX prices, so the typed
|
|
72
|
+
* number is converted ONCE here and stored tax-inclusive like every other
|
|
73
|
+
* price. No money path branches on the flag; see `taxableFromInclusive`.
|
|
74
|
+
*
|
|
75
|
+
* Gated on the write actually carrying a price, so a stock decrement or a
|
|
76
|
+
* status flip pays nothing for this — the same discipline `update` already
|
|
77
|
+
* applies to the taxonomy derivation itself.
|
|
78
|
+
*/
|
|
79
|
+
private applyInclusivePricing;
|
|
80
|
+
/**
|
|
81
|
+
* Hand down what the category declares: the tax code, the spec rows, the
|
|
82
|
+
* in-the-box list, the inherited features and the SEO templates.
|
|
83
|
+
*
|
|
84
|
+
* ## 🛑 Why this is server-side and not a form default
|
|
85
|
+
*
|
|
86
|
+
* GST is tax CORRECTNESS. A client-side default is bypassable by all ~14
|
|
87
|
+
* write paths, and the defect this closes is recorded in our own source:
|
|
88
|
+
* *"0 of 40 live orders had a `gstAmount`, and the order page's Tax row had
|
|
89
|
+
* never rendered."* The rate has to be applied where every path funnels, and
|
|
90
|
+
* that is here.
|
|
91
|
+
*
|
|
92
|
+
* ## 🛑 The three behaviours are different, and the difference is the design
|
|
93
|
+
*
|
|
94
|
+
* WRITTEN tax, specifications, inTheBox, features — written when the
|
|
95
|
+
* product omits them.
|
|
96
|
+
* FILL-BLANK seo* — fills only an empty field, never overwrites a
|
|
97
|
+
* seller's own words.
|
|
98
|
+
* ADVISORY priceGuidance — NEVER touched here. It renders as a hint
|
|
99
|
+
* beside the price input and nothing else. Writing a suggested
|
|
100
|
+
* price onto `price` would be the platform setting a seller's
|
|
101
|
+
* price, which is not a default, it is a decision.
|
|
102
|
+
*
|
|
103
|
+
* ## 🛑 Resolution is PER FIELD across the ancestor chain
|
|
104
|
+
*
|
|
105
|
+
* Not "the nearest category that declares a `productDefaults` object wins".
|
|
106
|
+
* B5's named-model leaves carry `defaultFeatures` (the blader, the battle
|
|
107
|
+
* type, the spin) while the tax code, the spec rows and the price band live
|
|
108
|
+
* on the tier-3 LINE above them. An object-level "nearest wins" would hand a
|
|
109
|
+
* Dragoon G its blader and NO TAX CODE — silently deriving no GST, which is
|
|
110
|
+
* the quiet under-charging failure the whole tax-code indirection exists to
|
|
111
|
+
* prevent.
|
|
112
|
+
*
|
|
113
|
+
* ## Cost
|
|
114
|
+
*
|
|
115
|
+
* One `getAll` over at most four ancestors, fired only when the leaf leaves
|
|
116
|
+
* something unresolved, plus one read for the tax code. With the leaf+brand
|
|
117
|
+
* read that is three round trips worst case against Rule #6's ~3 budget —
|
|
118
|
+
* and `update` only re-derives when a write names a taxonomy field, so a
|
|
119
|
+
* stock decrement or a status flip pays none of it.
|
|
120
|
+
*/
|
|
121
|
+
private applyCategoryDefaults;
|
|
48
122
|
/** The keys whose presence means a write could change the product's taxonomy. */
|
|
49
123
|
private static readonly TAXONOMY_KEYS;
|
|
124
|
+
/**
|
|
125
|
+
* Keep `availableQuantity` in step when a seller edits `stockQuantity`.
|
|
126
|
+
*
|
|
127
|
+
* 🛑 THE BUG THIS CLOSES: A SELLER COULD NOT RESTOCK.
|
|
128
|
+
*
|
|
129
|
+
* There are two stock numbers. `stockQuantity` is seller-authored and is the
|
|
130
|
+
* only one any of the three product forms exposes. `availableQuantity` is
|
|
131
|
+
* system-owned, seeded from `stockQuantity` **once, at create**, and
|
|
132
|
+
* thereafter driven by checkout decrements and timeout restores.
|
|
133
|
+
*
|
|
134
|
+
* Nothing re-synced them. `updateProduct` is a bare spread, and grep for
|
|
135
|
+
* `availableQuantity` across `src/app/api/store/products/**` and
|
|
136
|
+
* `seller-actions.ts` returns ZERO. So: a product sells out
|
|
137
|
+
* (`availableQuantity: 0`), the seller edits `stockQuantity` 0 → 10, and the
|
|
138
|
+
* product STAYS UNBUYABLE — purchasability gates on `availableQuantity > 0`
|
|
139
|
+
* in three independent places (`service.ts`, the cart route, and the public
|
|
140
|
+
* list filter). The only field the seller can reach is the one that does not
|
|
141
|
+
* control availability.
|
|
142
|
+
*
|
|
143
|
+
* With 76–93% of competitor catalogues sold out, restock is the main
|
|
144
|
+
* lifecycle event, not an edge case.
|
|
145
|
+
*
|
|
146
|
+
* WHY A DELTA AND NOT `availableQuantity = stockQuantity`: the difference
|
|
147
|
+
* between the two IS the outstanding reservation — units held by placed-but-
|
|
148
|
+
* unpaid orders. Overwriting would hand those units back to the shelf and
|
|
149
|
+
* oversell them. So carry the reservation forward:
|
|
150
|
+
*
|
|
151
|
+
* reserved = prevStock - prevAvailable (clamped at 0)
|
|
152
|
+
* available = nextStock - reserved (clamped at 0)
|
|
153
|
+
*
|
|
154
|
+
* Examples: 10 stock / 10 available, edit to 12 → 12. 10 stock / 7 available
|
|
155
|
+
* (3 reserved), edit to 12 → 9. Sold out at 0/0, edit to 10 → 10.
|
|
156
|
+
*
|
|
157
|
+
* 🛑 Returns `{}` — not a value — whenever the write does not name
|
|
158
|
+
* `stockQuantity`, or names `availableQuantity` explicitly. The second case
|
|
159
|
+
* is what keeps the checkout decrement, the stock-restore job and
|
|
160
|
+
* `updateAvailableQuantity` authoritative: they write `availableQuantity`
|
|
161
|
+
* directly and must never have it recomputed from a stale `stockQuantity`
|
|
162
|
+
* underneath them.
|
|
163
|
+
*/
|
|
164
|
+
private static resyncAvailableQuantity;
|
|
50
165
|
update(id: string, data: Partial<ProductDocument>): Promise<ProductDocument>;
|
|
51
166
|
create(input: ProductCreateInput): Promise<ProductDocument>;
|
|
52
167
|
findByStore(storeId: string): Promise<ProductDocument[]>;
|
|
@@ -403,7 +518,6 @@ export declare class ProductRepository extends BaseRepository<ProductDocument> {
|
|
|
403
518
|
categoriesIn?: string[];
|
|
404
519
|
brandName?: string;
|
|
405
520
|
}): Promise<Record<string, number | undefined>>;
|
|
406
|
-
incrementViewCount(productId: string): Promise<void>;
|
|
407
521
|
/**
|
|
408
522
|
* Cloud Functions: find published auctions whose end date has already passed.
|
|
409
523
|
* Used by the auction settlement job to finalize expired auctions.
|