@mohasinac/appkit 4.26.0 → 4.28.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/LotteryAdminEditView.d.ts +16 -16
- package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +29 -6
- package/dist/_internal/client/features/lottery/LotteryListView.js +13 -1
- package/dist/_internal/server/features/bundles/data.d.ts +19 -0
- package/dist/_internal/server/features/bundles/data.js +32 -0
- package/dist/_internal/server/features/bundles/index.d.ts +1 -1
- package/dist/_internal/server/features/bundles/index.js +1 -1
- package/dist/_internal/server/features/checkout/actions.js +6 -4
- package/dist/_internal/server/features/classified/metadata.js +3 -1
- package/dist/_internal/server/features/digital-code/metadata.js +3 -1
- package/dist/_internal/server/features/live/metadata.js +3 -1
- package/dist/_internal/server/features/refunds/actions.js +3 -2
- package/dist/_internal/server/features/reviews/actions.js +3 -2
- package/dist/_internal/server/features/seo/sitemap.js +189 -40
- package/dist/_internal/server/jobs/core/listingProcessor.d.ts +0 -1
- package/dist/_internal/server/jobs/core/listingProcessor.js +19 -1
- package/dist/_internal/shared/actions/action-registry.js +14 -0
- package/dist/_internal/shared/checkout/order-math.d.ts +25 -3
- package/dist/_internal/shared/features/checkout/config.d.ts +3 -3
- package/dist/_internal/shared/features/checkout/config.js +6 -3
- package/dist/client.d.ts +32 -2
- package/dist/client.js +37 -2
- package/dist/constants/api-endpoints.d.ts +21 -3
- package/dist/constants/api-endpoints.js +14 -1
- package/dist/constants/field-names.d.ts +2 -2
- package/dist/constants/field-names.js +2 -2
- package/dist/core/newsletter.repository.js +14 -1
- package/dist/features/about/components/FAQPageView.js +1 -1
- package/dist/features/about/components/FeesView.js +1 -1
- package/dist/features/account/components/AddressForm.d.ts +2 -0
- package/dist/features/account/components/AddressForm.js +5 -1
- package/dist/features/account/components/UserBidsView.js +1 -1
- package/dist/features/account/components/UserSidebar.js +1 -1
- package/dist/features/account/components/UserSupportView.js +24 -2
- package/dist/features/account/hooks/useAddresses.d.ts +29 -1
- package/dist/features/account/hooks/useAddresses.js +4 -1
- package/dist/features/account/hooks/useProfile.d.ts +1 -1
- package/dist/features/account/hooks/useProfile.js +1 -1
- package/dist/features/addresses/repository/addresses.repository.d.ts +13 -0
- package/dist/features/addresses/repository/addresses.repository.js +19 -0
- package/dist/features/admin/actions/chat-actions.js +5 -1
- package/dist/features/admin/components/AdminAdsView.js +1 -1
- package/dist/features/admin/components/AdminBidsView.js +6 -1
- package/dist/features/admin/components/AdminCarouselGroupEditorView.d.ts +9 -1
- package/dist/features/admin/components/AdminCarouselGroupEditorView.js +42 -10
- package/dist/features/admin/components/AdminCarouselView.js +1 -1
- package/dist/features/admin/components/AdminFeatureEditorView.js +1 -1
- package/dist/features/admin/components/AdminGroupedListingsView.js +14 -2
- package/dist/features/admin/components/AdminSidebar.js +1 -1
- package/dist/features/admin/components/AdminViewCards.js +2 -2
- package/dist/features/admin/components/DataTable.js +2 -2
- package/dist/features/admin/schemas/firestore.js +9 -3
- package/dist/features/auctions/actions/bid-actions.js +45 -4
- package/dist/features/auctions/components/AuctionBottomActions.d.ts +21 -6
- package/dist/features/auctions/components/AuctionBottomActions.js +42 -11
- package/dist/features/auctions/components/AuctionDetailPageView.d.ts +8 -3
- package/dist/features/auctions/components/AuctionDetailPageView.js +2 -2
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +4 -5
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +27 -2
- package/dist/features/auctions/components/PlaceBidFormClient.js +42 -20
- package/dist/features/auth/components/SocialAuthButtons.js +1 -1
- package/dist/features/auth/hooks/useAuth.d.ts +23 -1
- package/dist/features/auth/hooks/useAuth.js +27 -1
- package/dist/features/auth/permissions/constants.d.ts +1 -1
- package/dist/features/auth/permissions/constants.js +3 -0
- package/dist/features/auth/repository/session.repository.d.ts +16 -1
- package/dist/features/auth/repository/session.repository.js +23 -0
- package/dist/features/auth/repository/user.repository.js +13 -2
- package/dist/features/blog/components/BlogListView.js +2 -2
- package/dist/features/cart/components/CartPriceBreakdown.d.ts +11 -1
- package/dist/features/cart/components/CartPriceBreakdown.js +3 -3
- package/dist/features/cart/components/StoreAddonsPicker.d.ts +9 -0
- package/dist/features/cart/components/StoreAddonsPicker.js +15 -5
- package/dist/features/cart/components/index.d.ts +1 -1
- package/dist/features/cart/components/index.js +1 -1
- package/dist/features/cart/hooks/useGuestCartMerge.d.ts +8 -1
- package/dist/features/cart/hooks/useGuestCartMerge.js +2 -1
- package/dist/features/cart/utils/line-total.d.ts +25 -0
- package/dist/features/cart/utils/line-total.js +26 -0
- package/dist/features/catalogue/repository/catalogue.repository.d.ts +12 -0
- package/dist/features/catalogue/repository/catalogue.repository.js +15 -0
- package/dist/features/categories/components/CategoryTree.js +2 -2
- package/dist/features/contact/components/ContactForm.js +1 -1
- package/dist/features/copilot/hooks/useCopilotFeedback.d.ts +1 -1
- package/dist/features/copilot/hooks/useCopilotFeedback.js +1 -1
- package/dist/features/events/components/AdminEventEditorView.js +16 -9
- package/dist/features/events/components/AdminEventEntriesView.js +7 -13
- package/dist/features/events/components/EventRafflesSection.js +14 -2
- package/dist/features/events/components/EventStatusBadge.js +1 -1
- package/dist/features/events/components/event-entry-detail-fields.d.ts +24 -0
- package/dist/features/events/components/event-entry-detail-fields.js +13 -0
- package/dist/features/faq/components/ContactCTA.js +1 -1
- package/dist/features/faq/components/FAQHelpfulButtons.js +2 -2
- package/dist/features/faq/repository/faqs.repository.d.ts +16 -1
- package/dist/features/faq/repository/faqs.repository.js +64 -54
- package/dist/features/faq/schemas/firestore.d.ts +3 -2
- package/dist/features/faq/schemas/firestore.js +2 -2
- package/dist/features/faq/schemas/index.d.ts +3 -3
- package/dist/features/faq/schemas/index.js +1 -1
- package/dist/features/filters/FilterFacetSection.js +2 -2
- package/dist/features/forms/Toggle.js +1 -1
- package/dist/features/grouped/components/GroupedListingEditorView.d.ts +18 -0
- package/dist/features/grouped/components/GroupedListingEditorView.js +192 -0
- package/dist/features/homepage/components/AdvertisementBanner.js +2 -2
- package/dist/features/homepage/components/BrandsSection.js +1 -1
- package/dist/features/homepage/components/CTABannerSection.js +1 -1
- package/dist/features/homepage/components/HomepageCustomerReviewsSection.js +1 -1
- package/dist/features/homepage/components/HomepageSectionBoundary.d.ts +39 -0
- package/dist/features/homepage/components/HomepageSectionBoundary.js +49 -0
- package/dist/features/homepage/components/ShopByCategorySection.js +1 -1
- package/dist/features/homepage/components/WelcomeSection.js +1 -1
- package/dist/features/homepage/lib/section-renderer.js +2 -1
- package/dist/features/layout/AppLayoutShell.js +5 -5
- package/dist/features/layout/BottomActions.js +5 -5
- package/dist/features/layout/BottomSheet.js +1 -1
- package/dist/features/layout/MainNavbar.js +1 -1
- package/dist/features/layout/NavbarLayout.js +1 -1
- package/dist/features/layout/TitleBarLayout.js +3 -3
- package/dist/features/layout/hooks/useFormBottomActions.d.ts +11 -1
- package/dist/features/layout/hooks/useFormBottomActions.js +16 -2
- package/dist/features/lottery/schemas/config-write.d.ts +141 -0
- package/dist/features/lottery/schemas/config-write.js +139 -0
- package/dist/features/media/MediaPickerModal.js +3 -1
- package/dist/features/media/MediaVideo.js +4 -2
- package/dist/features/media/components/MediaSlider.js +1 -1
- package/dist/features/orders/components/OrdersList.js +2 -2
- package/dist/features/payments/repository/payout.repository.d.ts +11 -1
- package/dist/features/payments/repository/payout.repository.js +14 -6
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -2
- package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
- package/dist/features/products/columns/productTableColumns.js +3 -3
- package/dist/features/products/components/CompareOverlay.js +1 -1
- package/dist/features/products/components/PrizeDrawDetailPageView.js +1 -1
- package/dist/features/products/components/PrizeDrawEntryActions.js +2 -2
- package/dist/features/products/components/PrizeDrawsSection.js +5 -1
- package/dist/features/products/components/ProductDetailActions.js +11 -7
- package/dist/features/products/components/ProductDetailPageView.js +3 -3
- package/dist/features/products/components/ProductFeatureBadges.js +2 -2
- package/dist/features/products/components/ProductFeaturesSelector.js +1 -1
- package/dist/features/products/components/ProductFilters.js +15 -8
- package/dist/features/products/components/ProductGrid.js +8 -17
- package/dist/features/products/components/ProductTabs.js +7 -4
- package/dist/features/products/components/ProductTabsShell.js +2 -7
- package/dist/features/products/constants/action-defs.d.ts +21 -4
- package/dist/features/products/constants/action-defs.js +7 -3
- package/dist/features/products/hooks/useRelatedProducts.js +2 -1
- package/dist/features/products/repository/products.repository.d.ts +1 -1
- package/dist/features/products/repository/products.repository.js +27 -13
- package/dist/features/products/schemas/firestore.d.ts +3 -2
- package/dist/features/products/schemas/firestore.js +1 -1
- package/dist/features/reviews/actions/review-actions.d.ts +8 -1
- package/dist/features/reviews/actions/review-actions.js +14 -3
- package/dist/features/reviews/components/ReviewDetailShell.js +10 -4
- package/dist/features/reviews/components/ReviewModal.js +1 -1
- package/dist/features/reviews/repository/reviews.repository.d.ts +25 -1
- package/dist/features/reviews/repository/reviews.repository.js +53 -13
- package/dist/features/reviews/schemas/firestore.d.ts +6 -0
- package/dist/features/scams/components/ScamRegistryView.js +2 -2
- package/dist/features/search/components/Search.js +3 -3
- package/dist/features/search/components/SearchResultsSection.js +1 -1
- package/dist/features/seller/components/SellerAddressesView.js +3 -1
- package/dist/features/seller/components/SellerBidsView.js +6 -1
- package/dist/features/seller/components/SellerSidebar.js +1 -1
- package/dist/features/shell/SectionForm.d.ts +21 -1
- package/dist/features/shell/SectionForm.js +15 -3
- package/dist/features/shell/build-sections.js +10 -1
- package/dist/features/shell/field-ui-meta.d.ts +21 -0
- package/dist/features/shipments/components/AdminShipmentLotItemsView.js +23 -6
- package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
- package/dist/features/store-extensions/repository/store-extensions.repositories.d.ts +21 -4
- package/dist/features/store-extensions/repository/store-extensions.repositories.js +24 -4
- package/dist/features/store-extensions/schemas/shipping-config-form.js +13 -0
- package/dist/features/store-extensions/schemas/store-category-form.js +1 -1
- package/dist/features/stores/actions/store-query-actions.js +25 -1
- package/dist/features/stores/components/InteractiveStoreCard.js +1 -1
- package/dist/features/stores/components/StoreHeader.js +1 -1
- package/dist/features/stores/components/StoreNavTabs.js +20 -8
- package/dist/features/support/repository/support.repository.d.ts +12 -0
- package/dist/features/support/repository/support.repository.js +15 -0
- package/dist/features/support/schemas/ticket-create-form.d.ts +33 -0
- package/dist/features/support/schemas/ticket-create-form.js +82 -0
- package/dist/features/tester/seed-data/products-tester-seed-data.js +108 -2
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +235 -13
- package/dist/features/wishlist/components/WishlistHeartButton.d.ts +55 -0
- package/dist/features/wishlist/components/WishlistHeartButton.js +52 -0
- package/dist/features/wishlist/components/index.d.ts +2 -1
- package/dist/features/wishlist/components/index.js +1 -1
- package/dist/index.d.ts +10 -3
- package/dist/index.js +19 -4
- package/dist/monitoring/error-tracking.js +39 -5
- package/dist/monitoring/server-logger.js +70 -32
- package/dist/next/components/ErrorView.js +1 -1
- package/dist/next/routing/route-map.d.ts +39 -7
- package/dist/next/routing/route-map.js +34 -4
- package/dist/providers/db-firebase/base.repository.d.ts +30 -0
- package/dist/providers/db-firebase/base.repository.js +56 -9
- package/dist/react/ErrorBoundary.js +9 -1
- package/dist/react/contexts/SessionContext.js +6 -2
- package/dist/react/hooks/useAuthGate.d.ts +22 -0
- package/dist/react/hooks/useAuthGate.js +86 -7
- package/dist/schemas/registry.d.ts +3 -3
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/pii-encrypt.d.ts +13 -5
- package/dist/security/pii-encrypt.js +132 -6
- package/dist/security/pii-schemas.d.ts +20 -4
- package/dist/security/pii-schemas.js +23 -5
- package/dist/seed/_helpers/product-search-txt.d.ts +18 -0
- package/dist/seed/_helpers/product-search-txt.js +33 -0
- package/dist/seed/faq-seed-data.d.ts +16 -0
- package/dist/seed/faq-seed-data.js +22 -1
- package/dist/seed/products-art-seed-data.js +49 -7
- package/dist/seed/products-auctions-seed-data.js +12 -4
- package/dist/seed/products-classifieds-seed-data.d.ts +5 -0
- package/dist/seed/products-classifieds-seed-data.js +65 -9
- package/dist/seed/products-digital-codes-seed-data.d.ts +5 -0
- package/dist/seed/products-digital-codes-seed-data.js +65 -9
- package/dist/seed/products-live-items-seed-data.d.ts +5 -0
- package/dist/seed/products-live-items-seed-data.js +33 -5
- package/dist/seed/products-preorders-seed-data.d.ts +1 -1
- package/dist/seed/products-preorders-seed-data.js +6 -2
- package/dist/seed/products-prize-draws-seed-data.d.ts +5 -0
- package/dist/seed/products-prize-draws-seed-data.js +57 -8
- package/dist/seed/products-standard-seed-data.js +6 -2
- package/dist/seed/products-stickers-seed-data.js +49 -7
- package/dist/seed/site-settings-seed-data.js +5 -3
- package/dist/seo/json-ld.d.ts +44 -2
- package/dist/seo/json-ld.js +57 -11
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/dist/styles.css +368 -90
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/index.d.ts +1 -1
- package/dist/tokens/index.js +1 -1
- package/dist/tokens/themes/default-dark.js +6 -0
- package/dist/tokens/themes/default-light.js +5 -0
- package/dist/tokens/tokens.css +84 -0
- package/dist/ui/DataTable.js +1 -1
- package/dist/ui/DataTable.style.css +3 -3
- package/dist/ui/components/BulkActionBar.style.css +1 -3
- package/dist/ui/components/Button.js +15 -6
- package/dist/ui/components/Dropdown.style.css +1 -4
- package/dist/ui/components/FilterChipGroup.js +1 -1
- package/dist/ui/components/FilterDrawer.style.css +1 -1
- package/dist/ui/components/Icon.d.ts +53 -0
- package/dist/ui/components/Icon.js +41 -0
- package/dist/ui/components/IconButton.d.ts +13 -0
- package/dist/ui/components/IconButton.js +13 -0
- package/dist/ui/components/IconButton.style.css +18 -10
- package/dist/ui/components/ListingLayout.style.css +2 -10
- package/dist/ui/components/ListingToolbar.js +6 -6
- package/dist/ui/components/Modal.js +1 -1
- package/dist/ui/components/PaginatedSelect.style.css +3 -3
- package/dist/ui/components/Pagination.style.css +1 -6
- package/dist/ui/components/RichTextEditor.js +1 -1
- package/dist/ui/components/RowActionMenu.style.css +1 -1
- package/dist/ui/components/SideDrawer.style.css +1 -1
- package/dist/ui/components/Slider.js +1 -1
- package/dist/ui/components/TabBarShell.d.ts +78 -0
- package/dist/ui/components/TabBarShell.js +95 -0
- package/dist/ui/components/Tabs.js +12 -8
- package/dist/ui/components/Tabs.style.css +249 -41
- package/dist/ui/components/ViewToggle.style.css +1 -5
- package/dist/ui/components/tabs-overflow.d.ts +17 -0
- package/dist/ui/components/tabs-overflow.js +142 -0
- package/dist/ui/icons/icon-registry.d.ts +38 -0
- package/dist/ui/icons/icon-registry.js +37 -0
- package/dist/ui/index.d.ts +6 -0
- package/dist/ui/index.js +3 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/media-url.d.ts +27 -4
- package/dist/utils/media-url.js +37 -6
- package/dist/utils/search-txt.d.ts +71 -0
- package/dist/utils/search-txt.js +118 -0
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import type { LotteryConfigWriteInput } from "../../../../features/lottery/schemas/config-write";
|
|
2
3
|
interface LotterySlotRow {
|
|
3
4
|
slotNumber: number;
|
|
4
5
|
name: string;
|
|
@@ -6,26 +7,25 @@ interface LotterySlotRow {
|
|
|
6
7
|
/** Prize photo — rendered publicly in the prize collage on the detail page. */
|
|
7
8
|
image?: string;
|
|
8
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* What this form hands its caller.
|
|
12
|
+
*
|
|
13
|
+
* `lotteryConfig` is the WRITE input — deliberately not `LotteryConfig`. It
|
|
14
|
+
* carries no `isBooked`, no `bookedBy*` and no `weight`, because an admin
|
|
15
|
+
* editing a lottery is describing prizes, not attendance. The stored config
|
|
16
|
+
* gains those in `mergeLotteryConfig`, server-side, from the slots that are
|
|
17
|
+
* already there.
|
|
18
|
+
*
|
|
19
|
+
* `totalSlots` is absent for the same reason: derived from `slots.length` by
|
|
20
|
+
* the merge, never sent. A caller-supplied count that disagrees with the array
|
|
21
|
+
* is the mirror-drift trap (Root Cause #42), and the count is what the
|
|
22
|
+
* fullness check reads.
|
|
23
|
+
*/
|
|
9
24
|
interface LotteryEventFormData {
|
|
10
25
|
title: string;
|
|
11
26
|
description?: string;
|
|
12
27
|
type: "lottery";
|
|
13
|
-
lotteryConfig:
|
|
14
|
-
slots: Array<{
|
|
15
|
-
slotNumber: number;
|
|
16
|
-
name: string;
|
|
17
|
-
image?: string;
|
|
18
|
-
price: number;
|
|
19
|
-
weight: number;
|
|
20
|
-
isBooked: boolean;
|
|
21
|
-
}>;
|
|
22
|
-
totalSlots: number;
|
|
23
|
-
pricingMode: "uniform" | "variable";
|
|
24
|
-
uniformPrice?: number;
|
|
25
|
-
drawWindowDurationMinutes: number;
|
|
26
|
-
maxPullsPerTransaction: number;
|
|
27
|
-
maxPullsPerUser: number;
|
|
28
|
-
};
|
|
28
|
+
lotteryConfig: LotteryConfigWriteInput;
|
|
29
29
|
}
|
|
30
30
|
interface LotteryAdminEditViewProps {
|
|
31
31
|
eventId?: string;
|
|
@@ -5,6 +5,7 @@ import { normalizeError } from "../../../../errors/normalize";
|
|
|
5
5
|
import { Stack, Row, Text, Heading, Section, Container, Button, DataTable, } from "../../../../ui";
|
|
6
6
|
import { Form, FieldInput, FieldSelect, Input } from "../../../../ui";
|
|
7
7
|
import { lotteryConfigFormSchema } from "../../../shared/features/lottery/config-form";
|
|
8
|
+
import { lotteryConfigWriteSchema } from "../../../../features/lottery/schemas/config-write";
|
|
8
9
|
import { FormErrorSummary } from "../../../../ui/forms/FormErrorSummary";
|
|
9
10
|
export function LotteryAdminEditView({ eventId, initialData, onSubmit, }) {
|
|
10
11
|
const [isPending, startTransition] = useTransition();
|
|
@@ -35,23 +36,45 @@ export function LotteryAdminEditView({ eventId, initialData, onSubmit, }) {
|
|
|
35
36
|
setError(null);
|
|
36
37
|
startTransition(async () => {
|
|
37
38
|
try {
|
|
38
|
-
|
|
39
|
+
/*
|
|
40
|
+
* 🛑 This used to send `isBooked: false` and `weight: 0` on every slot,
|
|
41
|
+
* and drop `bookedByUserId` / `bookedByDisplayName` /
|
|
42
|
+
* `bookedByUserLotteryNumber` entirely — against a `.passthrough()`
|
|
43
|
+
* PATCH, which made the first save of a live lottery erase every
|
|
44
|
+
* purchased slot, silently, with a 200.
|
|
45
|
+
*
|
|
46
|
+
* The write shape now has NO booking fields at all and is `.strict()`,
|
|
47
|
+
* so a slot's buyer is not something this form can express, let alone
|
|
48
|
+
* overwrite. `PUT /lottery-config` merges by `slotNumber` against the
|
|
49
|
+
* stored config and is the only writer.
|
|
50
|
+
*/
|
|
51
|
+
const candidate = {
|
|
39
52
|
slots: slots.map((s) => ({
|
|
40
53
|
slotNumber: s.slotNumber,
|
|
41
54
|
name: s.name,
|
|
42
55
|
image: s.image?.trim() || undefined,
|
|
43
56
|
price: s.price,
|
|
44
|
-
weight: 0,
|
|
45
|
-
isBooked: false,
|
|
46
57
|
})),
|
|
47
|
-
totalSlots: slots.length,
|
|
48
58
|
pricingMode,
|
|
49
|
-
uniformPrice: pricingMode === "uniform"
|
|
59
|
+
uniformPrice: pricingMode === "uniform"
|
|
60
|
+
? Math.round(parseFloat(uniformPrice) * 100) / 100 || 0
|
|
61
|
+
: undefined,
|
|
50
62
|
drawWindowDurationMinutes: parseInt(drawWindowMinutes, 10) || 5,
|
|
51
63
|
maxPullsPerTransaction: parseInt(maxPullsPerTx, 10) || 1,
|
|
52
64
|
maxPullsPerUser: parseInt(maxPullsPerUser, 10) || 1,
|
|
53
65
|
};
|
|
54
|
-
|
|
66
|
+
/*
|
|
67
|
+
* The schema was passed to `<Form>` and never actually run — so the
|
|
68
|
+
* `parseFloat(x) || 0` coercions above, which it exists to catch, were
|
|
69
|
+
* still live. Parsed here so a bad slot is refused on the client
|
|
70
|
+
* rather than discovered as a 400.
|
|
71
|
+
*/
|
|
72
|
+
const parsed = lotteryConfigWriteSchema.safeParse(candidate);
|
|
73
|
+
if (!parsed.success) {
|
|
74
|
+
setError(parsed.error.issues[0]?.message ?? "Check the lottery settings.");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
await onSubmit({ title, description, type: "lottery", lotteryConfig: parsed.data });
|
|
55
78
|
}
|
|
56
79
|
catch (err) {
|
|
57
80
|
void normalizeError(err);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Stack, Row, Text, Heading, Badge, Card, Section, Container, Anchor, Div, Grid, } from "../../../../ui";
|
|
4
4
|
import { MediaImage } from "../../../../features/media/MediaImage";
|
|
5
|
+
import { ROUTES } from "../../../../next/routing/route-map";
|
|
5
6
|
/**
|
|
6
7
|
* Card grid of lotteries.
|
|
7
8
|
* Admin sees all statuses; public sees active only.
|
|
@@ -17,7 +18,18 @@ export function LotteryListView({ items, adminMode = false }) {
|
|
|
17
18
|
const claimed = event.lotteryConfig?.slots.filter((s) => s.isBooked).length ?? 0;
|
|
18
19
|
const total = event.lotteryConfig?.totalSlots ?? 0;
|
|
19
20
|
const slotsLeft = total - claimed;
|
|
20
|
-
return (_jsx(Anchor, {
|
|
21
|
+
return (_jsx(Anchor, {
|
|
22
|
+
/*
|
|
23
|
+
* In adminMode this used to link to the PUBLIC lottery page,
|
|
24
|
+
* so an admin on the admin list had no route to slot
|
|
25
|
+
* configuration at all — which went unnoticed because the
|
|
26
|
+
* editor was orphaned and there was nowhere to link TO.
|
|
27
|
+
* Nested anchors are invalid, so the card itself changes
|
|
28
|
+
* target rather than gaining a second link.
|
|
29
|
+
*/
|
|
30
|
+
href: adminMode
|
|
31
|
+
? String(ROUTES.ADMIN.LOTTERY_CONFIG(event.id))
|
|
32
|
+
: `/lottery/${event.id}`, tone: "brand", rounded: "2xl", className: "block focus:outline-none focus-visible:ring-2 focus-visible:ring-primary", "aria-label": `${event.title} lottery`, children: _jsxs(Card, { variant: "default", padding: "none", animate: "hoverLift", className: "overflow-hidden", children: [event.coverImageUrl || event.coverImage?.url ? (_jsx(Div, { className: "relative aspect-video overflow-hidden", children: _jsx(MediaImage, { src: event.coverImageUrl || event.coverImage?.url || "", alt: event.title, size: "card" }) })) : (_jsx(Div, { className: "aspect-video", surface: "muted", align: "center", justify: "center", children: _jsx(Text, { size: "3xl", children: "\uD83C\uDFB0" }) })), _jsxs(Stack, { gap: "xs", padding: "md", children: [_jsxs(Row, { gap: "xs", align: "center", wrap: true, children: [_jsx(Badge, { variant: event.status === "active"
|
|
21
33
|
? "success"
|
|
22
34
|
: event.status === "ended"
|
|
23
35
|
? "danger"
|
|
@@ -40,6 +40,25 @@ export declare const listBundleMembers: (bundle: Pick<CategoryDocument, "bundleP
|
|
|
40
40
|
* displayed discount.
|
|
41
41
|
*/
|
|
42
42
|
export declare function resolveBundleOriginalTotal(productIds: string[]): Promise<number | undefined>;
|
|
43
|
+
/**
|
|
44
|
+
* Reject a bundle whose members belong to more than one seller.
|
|
45
|
+
*
|
|
46
|
+
* `storeId` is a SCALAR on the cart line, and it is simultaneously the
|
|
47
|
+
* order-splitting key and the key that per-store add-ons, coupons, shipping and
|
|
48
|
+
* payout all hang off. A bundle spanning two sellers therefore produces one
|
|
49
|
+
* order belonging to one of them while containing the other's products — the
|
|
50
|
+
* second seller gets no notification, no shipping resolution and no payout.
|
|
51
|
+
*
|
|
52
|
+
* Grouped cart lines refuse this at both the picker and the add-to-cart action.
|
|
53
|
+
* Bundles had no equivalent guard, so the only reason nothing was broken was
|
|
54
|
+
* that every bundle anyone had built happened to be single-store. This closes
|
|
55
|
+
* that at the point a bundle is SAVED, which is the cheapest place to say no:
|
|
56
|
+
* bundles already in the database keep working, and checkout is untouched.
|
|
57
|
+
*
|
|
58
|
+
* Returns the offending store ids (empty when fine) rather than throwing, so
|
|
59
|
+
* each route can phrase its own 400.
|
|
60
|
+
*/
|
|
61
|
+
export declare function findBundleMemberStores(productIds: string[]): Promise<string[]>;
|
|
43
62
|
/**
|
|
44
63
|
* Union of the member products' `categorySlugs` — the `bundleCategorySlugs`
|
|
45
64
|
* mirror that lets a category page scope its Bundles tab.
|
|
@@ -68,6 +68,38 @@ export async function resolveBundleOriginalTotal(productIds) {
|
|
|
68
68
|
const total = prices.reduce((sum, p) => sum + p, 0);
|
|
69
69
|
return Math.round(total * 100) / 100;
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Reject a bundle whose members belong to more than one seller.
|
|
73
|
+
*
|
|
74
|
+
* `storeId` is a SCALAR on the cart line, and it is simultaneously the
|
|
75
|
+
* order-splitting key and the key that per-store add-ons, coupons, shipping and
|
|
76
|
+
* payout all hang off. A bundle spanning two sellers therefore produces one
|
|
77
|
+
* order belonging to one of them while containing the other's products — the
|
|
78
|
+
* second seller gets no notification, no shipping resolution and no payout.
|
|
79
|
+
*
|
|
80
|
+
* Grouped cart lines refuse this at both the picker and the add-to-cart action.
|
|
81
|
+
* Bundles had no equivalent guard, so the only reason nothing was broken was
|
|
82
|
+
* that every bundle anyone had built happened to be single-store. This closes
|
|
83
|
+
* that at the point a bundle is SAVED, which is the cheapest place to say no:
|
|
84
|
+
* bundles already in the database keep working, and checkout is untouched.
|
|
85
|
+
*
|
|
86
|
+
* Returns the offending store ids (empty when fine) rather than throwing, so
|
|
87
|
+
* each route can phrase its own 400.
|
|
88
|
+
*/
|
|
89
|
+
export async function findBundleMemberStores(productIds) {
|
|
90
|
+
if (productIds.length === 0)
|
|
91
|
+
return [];
|
|
92
|
+
const results = await Promise.all(productIds.map((id) => productRepository.findById(id).catch(() => null)));
|
|
93
|
+
const stores = new Set();
|
|
94
|
+
for (const p of results) {
|
|
95
|
+
// An unresolvable member says nothing about seller spread — the price
|
|
96
|
+
// helper above already refuses to guess in that case, and inventing a
|
|
97
|
+
// store here would produce a false positive that blocks a valid save.
|
|
98
|
+
if (p?.storeId)
|
|
99
|
+
stores.add(p.storeId);
|
|
100
|
+
}
|
|
101
|
+
return [...stores];
|
|
102
|
+
}
|
|
71
103
|
/**
|
|
72
104
|
* Union of the member products' `categorySlugs` — the `bundleCategorySlugs`
|
|
73
105
|
* mirror that lets a category page scope its Bundles tab.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { getBundleForDetail, listBundleMembers, listFeaturedBundles, getRelatedBundles, resolveBundleMemberIds, resolveBundleOriginalTotal, resolveBundleCategorySlugs, type BundleDataOptions, } from "./data";
|
|
1
|
+
export { getBundleForDetail, listBundleMembers, listFeaturedBundles, getRelatedBundles, resolveBundleMemberIds, resolveBundleOriginalTotal, findBundleMemberStores, resolveBundleCategorySlugs, type BundleDataOptions, } from "./data";
|
|
2
2
|
export { buildBundleMetadata, type BundleMetadataOptions } from "./metadata";
|
|
3
3
|
export { renderBundleOg, renderBundleOgImage, type BundleOgData, } from "./og";
|
|
4
4
|
export { addBundleToCartAction } from "./actions";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { getBundleForDetail, listBundleMembers, listFeaturedBundles, getRelatedBundles, resolveBundleMemberIds, resolveBundleOriginalTotal, resolveBundleCategorySlugs, } from "./data";
|
|
1
|
+
export { getBundleForDetail, listBundleMembers, listFeaturedBundles, getRelatedBundles, resolveBundleMemberIds, resolveBundleOriginalTotal, findBundleMemberStores, resolveBundleCategorySlugs, } from "./data";
|
|
2
2
|
export { buildBundleMetadata } from "./metadata";
|
|
3
3
|
// S-SBUNI-4 2026-05-13 — bundle OG renderer.
|
|
4
4
|
export { renderBundleOg, renderBundleOgImage, } from "./og";
|
|
@@ -80,7 +80,7 @@ async function claimDigitalCodeForOrder(db, productId, orderId, userId, opts) {
|
|
|
80
80
|
userName: opts.userName ?? "there",
|
|
81
81
|
productTitle: opts.productTitle ?? "your product",
|
|
82
82
|
orderId,
|
|
83
|
-
}).catch((e) => serverLogger.error("claimDigitalCode: email failed", e));
|
|
83
|
+
}).catch((e) => serverLogger.error("claimDigitalCode: email failed", { error: normalizeError(e).message }));
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
@@ -550,7 +550,7 @@ async function createOrderForGroup(group, orderType, ctx) {
|
|
|
550
550
|
userEmail: userEmail || undefined,
|
|
551
551
|
userName,
|
|
552
552
|
productTitle: firstItem.productTitle,
|
|
553
|
-
}).catch((e) => serverLogger.error("claimDigitalCode", e));
|
|
553
|
+
}).catch((e) => serverLogger.error("claimDigitalCode", { error: normalizeError(e).message }));
|
|
554
554
|
}
|
|
555
555
|
for (const code of groupCouponCodes) {
|
|
556
556
|
const entry = couponUsageAccumulator.get(code);
|
|
@@ -892,8 +892,10 @@ export async function previewCheckoutPricing(input) {
|
|
|
892
892
|
// A preview never throws — it just shows the lane the buyer can actually
|
|
893
893
|
// check out, matching what the checkout page's active tab displays.
|
|
894
894
|
(!previewLane || laneOf(item) === previewLane));
|
|
895
|
+
// A fresh object, not the module singleton — an in-process caller mutating
|
|
896
|
+
// the returned preview would otherwise poison every later empty result.
|
|
895
897
|
if (cartItems.length === 0)
|
|
896
|
-
return EMPTY_PRICING_PREVIEW;
|
|
898
|
+
return { ...EMPTY_PRICING_PREVIEW, stores: [] };
|
|
897
899
|
// Best-effort product lookup — a preview never throws on a since-unpublished
|
|
898
900
|
// product, it just falls back to the item's cart-snapshot price for that
|
|
899
901
|
// line (same fallback cartRepository.getSubtotal() implicitly relies on).
|
|
@@ -1268,7 +1270,7 @@ async function createRazorpayGroupOrder(group, orderType, ctx) {
|
|
|
1268
1270
|
userEmail: userEmail || undefined,
|
|
1269
1271
|
userName,
|
|
1270
1272
|
productTitle: firstItem.productTitle,
|
|
1271
|
-
}).catch((e) => serverLogger.error("claimDigitalCode", e));
|
|
1273
|
+
}).catch((e) => serverLogger.error("claimDigitalCode", { error: normalizeError(e).message }));
|
|
1272
1274
|
}
|
|
1273
1275
|
for (const code of groupCouponCodes) {
|
|
1274
1276
|
const entry = couponUsageAccumulator.get(code);
|
|
@@ -21,7 +21,9 @@ export function buildClassifiedMetadata(product, opts) {
|
|
|
21
21
|
: product.description?.slice(0, 160) ?? "";
|
|
22
22
|
const image = opts?.ogImageUrl ?? (product.mainImage || product.images?.[0] || null);
|
|
23
23
|
const siteUrl = opts?.siteUrl?.replace(/\/+$/, "");
|
|
24
|
-
|
|
24
|
+
// Prefer `slug` — the route is `/classified/[slug]`, so a canonical built from
|
|
25
|
+
// `id` names a URL the router does not serve whenever the two differ.
|
|
26
|
+
const canonical = siteUrl ? `${siteUrl}/classified/${product.slug ?? product.id}` : undefined;
|
|
25
27
|
return {
|
|
26
28
|
title,
|
|
27
29
|
description,
|
|
@@ -14,7 +14,9 @@ export function buildDigitalCodeMetadata(product, opts) {
|
|
|
14
14
|
: product.description?.slice(0, 160) ?? "";
|
|
15
15
|
const image = opts?.ogImageUrl ?? (product.mainImage || product.images?.[0] || null);
|
|
16
16
|
const siteUrl = opts?.siteUrl?.replace(/\/+$/, "");
|
|
17
|
-
|
|
17
|
+
// Prefer `slug` — the route is `/digital-codes/[slug]`, so a canonical built
|
|
18
|
+
// from `id` names a URL the router does not serve whenever the two differ.
|
|
19
|
+
const canonical = siteUrl ? `${siteUrl}/digital-codes/${product.slug ?? product.id}` : undefined;
|
|
18
20
|
return {
|
|
19
21
|
title,
|
|
20
22
|
description,
|
|
@@ -17,7 +17,9 @@ export function buildLiveItemMetadata(product, opts) {
|
|
|
17
17
|
: product.description?.slice(0, 160) ?? "";
|
|
18
18
|
const image = opts?.ogImageUrl ?? (product.mainImage || product.images?.[0] || null);
|
|
19
19
|
const siteUrl = opts?.siteUrl?.replace(/\/+$/, "");
|
|
20
|
-
|
|
20
|
+
// Prefer `slug` — the route is `/live/[slug]`, so a canonical built from `id`
|
|
21
|
+
// names a URL the router does not serve whenever the two differ.
|
|
22
|
+
const canonical = siteUrl ? `${siteUrl}/live/${product.slug ?? product.id}` : undefined;
|
|
21
23
|
return {
|
|
22
24
|
title,
|
|
23
25
|
description,
|
|
@@ -16,6 +16,7 @@ import { wrapAction } from "@mohasinac/appkit/server";
|
|
|
16
16
|
*/
|
|
17
17
|
import { randomUUID } from "crypto";
|
|
18
18
|
import { getProviders } from "../../../../contracts/registry";
|
|
19
|
+
import { normalizeError } from "../../../../errors/normalize";
|
|
19
20
|
import { rupeesToPaise } from "../../../../providers/payment-razorpay/index";
|
|
20
21
|
import { orderRepository } from "../../../..";
|
|
21
22
|
import { ORDER_FIELDS } from "../../../../constants/field-names";
|
|
@@ -65,7 +66,7 @@ function revokeAvailableDigitalCodes(orderId, items) {
|
|
|
65
66
|
.limit(1)
|
|
66
67
|
.get()
|
|
67
68
|
.then((snap) => revokeFirstSnapDoc(snap))
|
|
68
|
-
.catch((e) => serverLogger.error("refund: code query failed", e));
|
|
69
|
+
.catch((e) => serverLogger.error("refund: code query failed", { error: normalizeError(e).message }));
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
function revokeFirstSnapDoc(snap) {
|
|
@@ -73,7 +74,7 @@ function revokeFirstSnapDoc(snap) {
|
|
|
73
74
|
return;
|
|
74
75
|
snap.docs[0].ref
|
|
75
76
|
.update({ status: "revoked", updatedAt: new Date() })
|
|
76
|
-
.catch((e) => serverLogger.error("refund: code revoke failed", e));
|
|
77
|
+
.catch((e) => serverLogger.error("refund: code revoke failed", { error: normalizeError(e).message }));
|
|
77
78
|
}
|
|
78
79
|
export async function processRefundAction(input) {
|
|
79
80
|
return wrapAction(async () => {
|
|
@@ -71,8 +71,9 @@ export async function deleteReviewAction(input) {
|
|
|
71
71
|
}
|
|
72
72
|
export async function markReviewHelpfulAction(reviewId) {
|
|
73
73
|
return wrapAction(async () => {
|
|
74
|
-
await requireRoleUser(["buyer", "seller", "admin"]);
|
|
74
|
+
const user = await requireRoleUser(["buyer", "seller", "admin"]);
|
|
75
75
|
await getReviewOrThrow(reviewId);
|
|
76
|
-
|
|
76
|
+
// Keyed on the voter so a repeated call is a no-op rather than another +1.
|
|
77
|
+
return reviewRepository.voteHelpful(reviewId, user.uid);
|
|
77
78
|
});
|
|
78
79
|
}
|