@mohasinac/appkit 4.59.0 → 4.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.build-stamp.json +3 -3
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +62 -5
- package/dist/_internal/client/features/maintenance/views/MaintenanceDashboardView.js +2 -2
- package/dist/_internal/server/features/brands/actions.js +78 -4
- package/dist/_internal/server/features/checkout/actions.d.ts +36 -13
- package/dist/_internal/server/features/checkout/actions.js +668 -183
- package/dist/_internal/server/features/checkout/supplier-identity.js +18 -1
- package/dist/_internal/server/features/gateway-events/index.d.ts +10 -0
- package/dist/_internal/server/features/gateway-events/index.js +10 -0
- package/dist/_internal/server/features/gateway-events/ingest.d.ts +127 -0
- package/dist/_internal/server/features/gateway-events/ingest.js +188 -0
- package/dist/_internal/server/features/gateway-events/redact.d.ts +66 -0
- package/dist/_internal/server/features/gateway-events/redact.js +151 -0
- package/dist/_internal/server/features/grievance/actions.d.ts +63 -0
- package/dist/_internal/server/features/grievance/actions.js +173 -0
- package/dist/_internal/server/features/grievance/index.d.ts +8 -0
- package/dist/_internal/server/features/grievance/index.js +8 -0
- package/dist/_internal/server/features/grouped/data.js +28 -3
- package/dist/_internal/server/features/lottery/actions.js +99 -4
- package/dist/_internal/server/features/order-completion/actions.d.ts +92 -0
- package/dist/_internal/server/features/order-completion/actions.js +336 -0
- package/dist/_internal/server/features/order-completion/index.d.ts +7 -0
- package/dist/_internal/server/features/order-completion/index.js +7 -0
- package/dist/_internal/server/features/orders/actions.d.ts +13 -0
- package/dist/_internal/server/features/orders/actions.js +56 -2
- package/dist/_internal/server/features/orders/adapters.js +26 -0
- package/dist/_internal/server/features/orders/cancellation-refund.d.ts +116 -0
- package/dist/_internal/server/features/orders/cancellation-refund.js +466 -0
- package/dist/_internal/server/features/orders/index.d.ts +2 -1
- package/dist/_internal/server/features/orders/index.js +1 -1
- package/dist/_internal/server/features/orders/service.d.ts +60 -0
- package/dist/_internal/server/features/orders/service.js +74 -0
- package/dist/_internal/server/features/payment-cases/disputes.d.ts +76 -0
- package/dist/_internal/server/features/payment-cases/disputes.js +241 -0
- package/dist/_internal/server/features/payment-cases/index.d.ts +9 -0
- package/dist/_internal/server/features/payment-cases/index.js +9 -0
- package/dist/_internal/server/features/payouts/actions.d.ts +28 -3
- package/dist/_internal/server/features/payouts/actions.js +84 -10
- package/dist/_internal/server/features/points/adjust.d.ts +61 -0
- package/dist/_internal/server/features/points/adjust.js +121 -0
- package/dist/_internal/server/features/points/award.d.ts +65 -0
- package/dist/_internal/server/features/points/award.js +145 -0
- package/dist/_internal/server/features/price-index/market.d.ts +33 -0
- package/dist/_internal/server/features/price-index/market.js +54 -1
- package/dist/_internal/server/features/products/data.js +40 -22
- package/dist/_internal/server/features/protection-claims/actions.d.ts +72 -0
- package/dist/_internal/server/features/protection-claims/actions.js +228 -0
- package/dist/_internal/server/features/protection-claims/index.d.ts +8 -0
- package/dist/_internal/server/features/protection-claims/index.js +8 -0
- package/dist/_internal/server/features/refunds/actions.d.ts +20 -0
- package/dist/_internal/server/features/refunds/actions.js +259 -17
- package/dist/_internal/server/features/refunds/capacity.d.ts +29 -0
- package/dist/_internal/server/features/refunds/capacity.js +37 -0
- package/dist/_internal/server/features/reviews/actions.js +3 -2
- package/dist/_internal/server/features/search/index-build/sources.js +20 -1
- package/dist/_internal/server/features/shipping/reconcile.d.ts +10 -1
- package/dist/_internal/server/features/shipping/reconcile.js +34 -3
- package/dist/_internal/server/features/shipping/reimbursement.d.ts +117 -0
- package/dist/_internal/server/features/shipping/reimbursement.js +223 -0
- package/dist/_internal/server/features/stores/tabs.d.ts +19 -4
- package/dist/_internal/server/features/stores/tabs.js +24 -4
- package/dist/_internal/server/functions/https.d.ts +31 -1
- package/dist/_internal/server/functions/https.js +52 -1
- package/dist/_internal/server/functions/scheduled.d.ts +72 -1
- package/dist/_internal/server/functions/scheduled.js +124 -1
- package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +62 -16
- package/dist/_internal/server/jobs/core/boxRollupReconcile.d.ts +46 -0
- package/dist/_internal/server/jobs/core/boxRollupReconcile.js +142 -0
- package/dist/_internal/server/jobs/core/dailyDataCleanup.js +149 -1
- package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +14 -0
- package/dist/_internal/server/jobs/core/dailyStatusDigest.js +79 -1
- package/dist/_internal/server/jobs/core/index.d.ts +2 -0
- package/dist/_internal/server/jobs/core/index.js +2 -0
- package/dist/_internal/server/jobs/core/marketPriceRollup.d.ts +64 -0
- package/dist/_internal/server/jobs/core/marketPriceRollup.js +153 -0
- package/dist/_internal/server/jobs/core/obligationSweep.d.ts +36 -0
- package/dist/_internal/server/jobs/core/obligationSweep.js +503 -0
- package/dist/_internal/server/jobs/core/paymentReconcile.d.ts +79 -0
- package/dist/_internal/server/jobs/core/paymentReconcile.js +614 -0
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +34 -0
- package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +35 -4
- package/dist/_internal/server/jobs/handlers/boxRollupReconcile.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/boxRollupReconcile.js +15 -0
- package/dist/_internal/server/jobs/handlers/countersReconcileNow.d.ts +65 -0
- package/dist/_internal/server/jobs/handlers/countersReconcileNow.js +89 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +5 -0
- package/dist/_internal/server/jobs/handlers/index.js +5 -0
- package/dist/_internal/server/jobs/handlers/marketPriceRollup.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/marketPriceRollup.js +2 -0
- package/dist/_internal/server/jobs/handlers/obligationSweep.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/obligationSweep.js +15 -0
- package/dist/_internal/server/jobs/handlers/onShipmentAllocationSync.js +30 -1
- package/dist/_internal/server/jobs/handlers/paymentReconcile.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/paymentReconcile.js +6 -0
- package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
- package/dist/_internal/shared/actions/action-registry.js +71 -0
- package/dist/_internal/shared/checkout/coupon-discount.d.ts +73 -0
- package/dist/_internal/shared/checkout/coupon-discount.js +103 -0
- package/dist/_internal/shared/checkout/lanes.d.ts +42 -0
- package/dist/_internal/shared/checkout/lanes.js +46 -0
- package/dist/_internal/shared/checkout/order-math.d.ts +149 -1
- package/dist/_internal/shared/checkout/order-math.js +251 -3
- package/dist/_internal/shared/checkout/refundable-base.d.ts +116 -0
- package/dist/_internal/shared/checkout/refundable-base.js +122 -0
- package/dist/_internal/shared/features/auctions/config.d.ts +14 -1
- package/dist/_internal/shared/features/auctions/config.js +30 -1
- package/dist/_internal/shared/features/brands/schema.d.ts +42 -0
- package/dist/_internal/shared/features/brands/schema.js +18 -0
- package/dist/_internal/shared/features/orders/refund-copy.d.ts +22 -0
- package/dist/_internal/shared/features/orders/refund-copy.js +34 -0
- package/dist/_internal/shared/features/print/label-profiles.d.ts +198 -0
- package/dist/_internal/shared/features/print/label-profiles.js +350 -0
- package/dist/_internal/shared/features/products/to-product-item.js +33 -5
- package/dist/_internal/shared/fees/calculator.d.ts +31 -2
- package/dist/_internal/shared/fees/calculator.js +103 -5
- package/dist/_internal/shared/fees/india-states.d.ts +195 -0
- package/dist/_internal/shared/fees/india-states.js +190 -0
- package/dist/_internal/shared/fees/payout-hold.d.ts +80 -0
- package/dist/_internal/shared/fees/payout-hold.js +74 -0
- package/dist/_internal/shared/obligations/deadlines.d.ts +94 -0
- package/dist/_internal/shared/obligations/deadlines.js +155 -0
- package/dist/_internal/shared/shipping/quote.d.ts +96 -0
- package/dist/_internal/shared/shipping/quote.js +67 -0
- package/dist/_internal/shared/time/business-days.d.ts +91 -0
- package/dist/_internal/shared/time/business-days.js +185 -0
- package/dist/cli/index.js +0 -2
- package/dist/client.d.ts +9 -4
- package/dist/client.js +26 -3
- package/dist/configs/tailwind.d.ts +0 -12
- package/dist/configs/tailwind.js +28 -15
- package/dist/constants/api-endpoints.d.ts +51 -9
- package/dist/constants/api-endpoints.js +23 -7
- package/dist/constants/index.d.ts +1 -1
- package/dist/constants/index.js +1 -1
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/payment.d.ts +131 -0
- package/dist/features/about/components/AboutView.js +1 -1
- package/dist/features/about/components/DeveloperView.js +5 -2
- package/dist/features/about/components/FAQPageView.js +2 -1
- package/dist/features/about/components/FeesView.js +2 -1
- package/dist/features/about/components/HelpPageView.js +2 -1
- package/dist/features/about/components/HowAuctionsWorkView.js +2 -1
- package/dist/features/about/components/HowCheckoutWorksView.js +2 -1
- package/dist/features/about/components/HowEmiWorksView.js +2 -1
- package/dist/features/about/components/HowOffersWorkView.js +2 -1
- package/dist/features/about/components/HowOrdersWorkView.js +2 -1
- package/dist/features/about/components/HowPayoutsWorkView.js +2 -1
- package/dist/features/about/components/HowPreOrdersWorkView.js +2 -1
- package/dist/features/about/components/HowReviewsWorkView.js +2 -1
- package/dist/features/about/components/PolicyPageView.js +2 -1
- package/dist/features/about/components/ProseHero.d.ts +73 -0
- package/dist/features/about/components/ProseHero.js +18 -0
- package/dist/features/about/components/SecurityPrivacyView.js +2 -1
- package/dist/features/about/components/ShippingPolicyView.js +2 -1
- package/dist/features/about/components/TrackOrderView.js +2 -1
- package/dist/features/about/components/UnauthorizedView.js +1 -1
- package/dist/features/about/index.d.ts +2 -0
- package/dist/features/about/index.js +5 -0
- package/dist/features/account/actions/address-actions.d.ts +26 -2
- package/dist/features/account/actions/address-actions.js +25 -1
- package/dist/features/account/components/BuyerShoppingGuideView.js +1 -1
- package/dist/features/account/components/NotificationPreferencesPanel.js +1 -1
- package/dist/features/account/components/UserOrdersView.js +17 -0
- package/dist/features/account/components/UserSidebar.js +1 -1
- package/dist/features/account/hooks/useNotifications.js +29 -0
- package/dist/features/admin/actions/admin-coupon-actions.js +36 -0
- package/dist/features/admin/components/AdminBeysView.js +2 -2
- package/dist/features/admin/components/AdminBlogEditorView.js +2 -2
- package/dist/features/admin/components/AdminBrandEditorView.js +32 -2
- package/dist/features/admin/components/AdminBundleEditorView.js +1 -1
- package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
- package/dist/features/admin/components/AdminCategoriesView.js +1 -1
- package/dist/features/admin/components/AdminCategoryEditorView.js +73 -17
- package/dist/features/admin/components/AdminContactEditorView.js +1 -1
- package/dist/features/admin/components/AdminCouponEditorView.js +26 -1
- package/dist/features/admin/components/AdminFaqEditorView.js +1 -1
- package/dist/features/admin/components/AdminFaqsView.js +2 -2
- package/dist/features/admin/components/AdminGatewayEventsView.d.ts +54 -0
- package/dist/features/admin/components/AdminGatewayEventsView.js +209 -0
- package/dist/features/admin/components/AdminGuideHubView.js +1 -1
- package/dist/features/admin/components/AdminMoneyCasesView.d.ts +60 -0
- package/dist/features/admin/components/AdminMoneyCasesView.js +287 -0
- package/dist/features/admin/components/AdminPartsView.js +2 -2
- package/dist/features/admin/components/AdminProductsView.js +75 -4
- package/dist/features/admin/components/AdminSidebar.js +2 -2
- package/dist/features/admin/components/AdminSiteSettingsView.js +65 -7
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +2 -0
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +1 -1
- package/dist/features/admin/components/AdminSupportTicketsView.js +2 -2
- package/dist/features/admin/components/AdminTesterChecklistView.js +2 -2
- package/dist/features/admin/components/AdminUsersView.js +72 -1
- package/dist/features/admin/components/AdminViewCards.d.ts +3 -1
- package/dist/features/admin/components/AdminViewCards.js +4 -4
- package/dist/features/admin/components/DataListingView.d.ts +28 -0
- package/dist/features/admin/components/DataListingView.js +175 -14
- package/dist/features/admin/components/DataTable.d.ts +12 -1
- package/dist/features/admin/components/DataTable.js +3 -3
- package/dist/features/admin/components/OwnerQuickActions.d.ts +1 -1
- package/dist/features/admin/components/OwnerQuickActions.js +60 -2
- package/dist/features/admin/components/RecordOwnerQuickActions.d.ts +1 -1
- package/dist/features/admin/components/RecordOwnerQuickActions.js +61 -14
- package/dist/features/admin/components/analytics/AdminStatCard.d.ts +35 -1
- package/dist/features/admin/components/analytics/AdminStatCard.js +77 -2
- package/dist/features/admin/components/index.d.ts +4 -0
- package/dist/features/admin/components/index.js +6 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +156 -0
- package/dist/features/admin/constants/filter-tabs.js +106 -0
- package/dist/features/admin/repository/site-settings.repository.d.ts +22 -0
- package/dist/features/admin/repository/site-settings.repository.js +82 -1
- package/dist/features/admin/schemas/admin-editor-forms.d.ts +21 -0
- package/dist/features/admin/schemas/admin-editor-forms.js +44 -0
- package/dist/features/admin/schemas/firestore.js +5 -1
- package/dist/features/admin/schemas/site-settings-form.js +30 -1
- package/dist/features/auctions/actions/bid-actions.js +13 -1
- package/dist/features/auctions/components/AuctionBottomActions.js +2 -2
- package/dist/features/auctions/components/AuctionDetailPageView.js +3 -1
- package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +11 -0
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +5 -3
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +8 -1
- package/dist/features/auctions/components/PlaceBidFormClient.js +2 -2
- package/dist/features/audit-log/schemas/firestore.d.ts +28 -0
- package/dist/features/audit-log/schemas/firestore.js +14 -0
- package/dist/features/auth/permissions/constants.d.ts +17 -1
- package/dist/features/auth/permissions/constants.js +18 -0
- package/dist/features/blog/components/BlogPostView.js +1 -1
- package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +1 -1
- package/dist/features/categories/api/[id]/description-template/route.js +107 -1
- package/dist/features/categories/components/BrandDetailPageView.js +2 -2
- package/dist/features/categories/components/BreadcrumbTrail.js +7 -1
- package/dist/features/categories/components/BundleDetailView.js +1 -1
- package/dist/features/categories/components/CategoryDetailPageView.js +2 -2
- package/dist/features/categories/components/CategoryForm.js +12 -1
- package/dist/features/categories/components/CategoryTemplatePicker.js +38 -5
- package/dist/features/categories/repository/market-price-index.repository.d.ts +123 -0
- package/dist/features/categories/repository/market-price-index.repository.js +105 -0
- package/dist/features/categories/repository/price-observation.repository.d.ts +166 -0
- package/dist/features/categories/repository/price-observation.repository.js +132 -0
- package/dist/features/events/components/EventSaleBanner.js +1 -1
- package/dist/features/events/components/EventStatusBadge.js +1 -1
- package/dist/features/faq/components/FAQAccordion.js +2 -1
- package/dist/features/faq/components/FAQCategorySidebar.js +2 -2
- package/dist/features/faq/components/FAQHelpfulButtons.js +4 -4
- package/dist/features/faq/components/RelatedFAQs.js +1 -1
- package/dist/features/filters/FilterFacetSection.js +1 -1
- package/dist/features/filters/SwitchFilter.js +1 -1
- package/dist/features/gateway-events/repository/gateway-events.repository.d.ts +117 -0
- package/dist/features/gateway-events/repository/gateway-events.repository.js +274 -0
- package/dist/features/gateway-events/schemas/firestore.d.ts +217 -0
- package/dist/features/gateway-events/schemas/firestore.js +125 -0
- package/dist/features/gateway-events/schemas/index.d.ts +81 -0
- package/dist/features/gateway-events/schemas/index.js +79 -0
- package/dist/features/grouped/components/GroupedListingDetailView.js +1 -1
- package/dist/features/homepage/components/AnnouncementBar.js +37 -1
- package/dist/features/homepage/components/AnnouncementBar.style.css +30 -0
- package/dist/features/homepage/components/BrandsSection.js +1 -1
- package/dist/features/homepage/components/CTABannerSection.js +1 -1
- package/dist/features/homepage/components/FeaturedBundlesSection.js +14 -24
- package/dist/features/homepage/components/HeroCarousel.js +2 -2
- package/dist/features/homepage/components/SocialPostCard.js +1 -1
- package/dist/features/inventory/components/AdminMoveLedgerView.d.ts +7 -0
- package/dist/features/inventory/components/AdminMoveLedgerView.js +157 -0
- package/dist/features/inventory/components/AdminStorageBoxesView.d.ts +11 -0
- package/dist/features/inventory/components/AdminStorageBoxesView.js +168 -0
- package/dist/features/inventory/components/ReceiveStationView.d.ts +39 -0
- package/dist/features/inventory/components/ReceiveStationView.js +413 -0
- package/dist/features/inventory/components/operator-message.d.ts +29 -0
- package/dist/features/inventory/components/operator-message.js +41 -0
- package/dist/features/inventory/index.d.ts +13 -0
- package/dist/features/inventory/index.js +28 -0
- package/dist/features/inventory/repository/inventory-movements.repository.d.ts +97 -0
- package/dist/features/inventory/repository/inventory-movements.repository.js +186 -0
- package/dist/features/inventory/repository/inventory-placements.repository.d.ts +179 -0
- package/dist/features/inventory/repository/inventory-placements.repository.js +345 -0
- package/dist/features/inventory/repository/storage-boxes.repository.d.ts +83 -0
- package/dist/features/inventory/repository/storage-boxes.repository.js +127 -0
- package/dist/features/inventory/schemas/firestore.d.ts +197 -0
- package/dist/features/inventory/schemas/firestore.js +159 -0
- package/dist/features/layout/AppLayoutShell.d.ts +12 -1
- package/dist/features/layout/AppLayoutShell.js +25 -12
- package/dist/features/layout/AutoBreadcrumbs.js +162 -3
- package/dist/features/layout/AutoBreadcrumbs.style.css +45 -0
- package/dist/features/layout/BottomNavbar.d.ts +1 -1
- package/dist/features/layout/BottomNavbar.js +2 -2
- package/dist/features/media/MediaAvatar.js +1 -1
- package/dist/features/media/MediaImage.d.ts +24 -4
- package/dist/features/media/MediaImage.js +44 -4
- package/dist/features/media/MediaLightbox.js +1 -1
- package/dist/features/media/modals/ImageCropModal.js +1 -1
- package/dist/features/media/modals/ImageEditor.js +1 -1
- package/dist/features/media/upload/MediaUploadField.js +91 -3
- package/dist/features/obligations/constants/policies.d.ts +92 -0
- package/dist/features/obligations/constants/policies.js +378 -0
- package/dist/features/obligations/repository/obligations.repository.d.ts +68 -0
- package/dist/features/obligations/repository/obligations.repository.js +158 -0
- package/dist/features/obligations/schemas/firestore.d.ts +160 -0
- package/dist/features/obligations/schemas/firestore.js +87 -0
- package/dist/features/orders/actions/order-actions.d.ts +43 -4
- package/dist/features/orders/actions/order-actions.js +60 -3
- package/dist/features/orders/actions/refund-actions.js +70 -10
- package/dist/features/orders/components/OrderCompletionPanel.d.ts +27 -0
- package/dist/features/orders/components/OrderCompletionPanel.js +192 -0
- package/dist/features/orders/components/OrdersList.js +2 -2
- package/dist/features/orders/components/RefundHistoryTable.js +3 -1
- package/dist/features/orders/components/index.d.ts +2 -0
- package/dist/features/orders/components/index.js +6 -0
- package/dist/features/orders/constants/transitions.d.ts +59 -0
- package/dist/features/orders/constants/transitions.js +104 -0
- package/dist/features/orders/repository/orders.repository.d.ts +109 -6
- package/dist/features/orders/repository/orders.repository.js +464 -21
- package/dist/features/orders/schemas/firestore.d.ts +270 -0
- package/dist/features/orders/schemas/firestore.js +24 -0
- package/dist/features/orders/schemas/index.d.ts +61 -0
- package/dist/features/orders/schemas/index.js +22 -0
- package/dist/features/orders/types/index.d.ts +79 -0
- package/dist/features/parts/components/PartsIndexPageView.js +8 -1
- package/dist/features/payment-cases/repository/payment-cases.repository.d.ts +170 -0
- package/dist/features/payment-cases/repository/payment-cases.repository.js +342 -0
- package/dist/features/payment-cases/schemas/firestore.d.ts +229 -0
- package/dist/features/payment-cases/schemas/firestore.js +105 -0
- package/dist/features/payment-cases/schemas/resolve-form.d.ts +97 -0
- package/dist/features/payment-cases/schemas/resolve-form.js +114 -0
- package/dist/features/payments/repository/payout.repository.d.ts +0 -1
- package/dist/features/payments/repository/payout.repository.js +0 -1
- package/dist/features/points/repository/points.repository.d.ts +122 -0
- package/dist/features/points/repository/points.repository.js +304 -1
- package/dist/features/points/schemas/adjust-form.d.ts +58 -0
- package/dist/features/points/schemas/adjust-form.js +63 -0
- package/dist/features/points/schemas/firestore.d.ts +108 -1
- package/dist/features/points/schemas/firestore.js +84 -0
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -3
- package/dist/features/pre-orders/components/PreOrderBadge.js +1 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
- package/dist/features/price-index/components/PriceIndexToolView.d.ts +23 -4
- package/dist/features/price-index/components/PriceIndexToolView.js +71 -6
- package/dist/features/price-index/market-stats.d.ts +82 -0
- package/dist/features/price-index/market-stats.js +230 -0
- package/dist/features/print/components/BoxLabelPrintView.d.ts +25 -0
- package/dist/features/print/components/BoxLabelPrintView.js +66 -0
- package/dist/features/print/components/LabelProfilePicker.d.ts +13 -0
- package/dist/features/print/components/LabelProfilePicker.js +38 -0
- package/dist/features/print/components/labels.d.ts +117 -0
- package/dist/features/print/components/labels.js +258 -0
- package/dist/features/print/components/labels.style.css +46 -0
- package/dist/features/print/index.d.ts +6 -0
- package/dist/features/print/index.js +16 -0
- package/dist/features/products/columns/productTableColumns.js +5 -4
- package/dist/features/products/components/MarketplaceBundleCard.js +2 -2
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +6 -6
- package/dist/features/products/components/PrizeDrawCollage.js +1 -1
- package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
- package/dist/features/products/components/PrizeRevealModal.js +2 -2
- package/dist/features/products/components/ProductDetailPageView.js +18 -4
- package/dist/features/products/components/ProductForm.js +34 -23
- package/dist/features/products/components/ProductGrid.js +86 -62
- package/dist/features/products/constants/card-badges.d.ts +97 -0
- package/dist/features/products/constants/card-badges.js +100 -0
- package/dist/features/products/repository/products.repository.js +34 -1
- package/dist/features/products/schemas/firestore.d.ts +46 -3
- package/dist/features/products/schemas/firestore.js +21 -0
- package/dist/features/promotions/components/PromotionsView.js +1 -1
- package/dist/features/promotions/repository/claimed-coupons.repository.d.ts +32 -0
- package/dist/features/promotions/repository/claimed-coupons.repository.js +58 -0
- package/dist/features/promotions/schemas/firestore.js +30 -3
- package/dist/features/promotions/schemas/index.d.ts +6 -6
- package/dist/features/protection-claims/repository/protection-claims.repository.d.ts +54 -0
- package/dist/features/protection-claims/repository/protection-claims.repository.js +174 -0
- package/dist/features/protection-claims/schemas/firestore.d.ts +168 -0
- package/dist/features/protection-claims/schemas/firestore.js +130 -0
- package/dist/features/reviews/actions/review-actions.js +3 -2
- package/dist/features/reviews/components/ReviewModal.js +1 -1
- package/dist/features/reviews/components/ReviewsList.js +1 -1
- package/dist/features/reviews/schemas/firestore.d.ts +20 -1
- package/dist/features/scams/constants/scam-types.d.ts +2 -0
- package/dist/features/scams/constants/scam-types.js +18 -0
- package/dist/features/scams/schemas/report-form.d.ts +3 -3
- package/dist/features/scams/schemas/report-form.js +26 -1
- package/dist/features/search/components/Search.js +9 -9
- package/dist/features/search/components/SearchResultsClient.js +18 -3
- package/dist/features/search/components/SearchResultsPageView.d.ts +1 -6
- package/dist/features/search/components/SearchResultsPageView.js +2 -2
- package/dist/features/seller/actions/offer-actions.js +16 -1
- package/dist/features/seller/actions/seller-actions.js +14 -3
- package/dist/features/seller/components/CategoryInlineSelect.js +75 -11
- package/dist/features/seller/components/PrintCenterView.d.ts +5 -0
- package/dist/features/seller/components/PrintCenterView.js +41 -30
- package/dist/features/seller/components/SellerOrdersView.js +1 -1
- package/dist/features/seller/components/SellerPayoutRequestView.js +16 -5
- package/dist/features/seller/components/SellerPayoutSettingsView.js +1 -1
- package/dist/features/seller/components/SellerProductShell.d.ts +16 -0
- package/dist/features/seller/components/SellerProductShell.js +123 -13
- package/dist/features/seller/components/SellerReviewsView.js +4 -2
- package/dist/features/seller/components/SellerSidebar.js +1 -1
- package/dist/features/seller/schemas/coupon-form.d.ts +26 -0
- package/dist/features/seller/schemas/coupon-form.js +27 -0
- package/dist/features/seller/schemas/product-form.d.ts +3 -0
- package/dist/features/seller/schemas/product-form.js +5 -0
- package/dist/features/shell/FormShell.js +2 -2
- package/dist/features/shell/QuickFormDrawer.js +14 -0
- package/dist/features/shell/SectionForm.d.ts +19 -0
- package/dist/features/shell/SectionForm.js +133 -9
- package/dist/features/shell/SectionForm.style.css +54 -0
- package/dist/features/shipments/components/AdminShipmentsView.js +16 -1
- package/dist/features/shipments/repository/shipment-items.repository.d.ts +9 -0
- package/dist/features/shipments/repository/shipment-items.repository.js +14 -0
- package/dist/features/site-settings/components/ActionPermissionsManager.js +1 -1
- package/dist/features/sticker-trace/components/StickerCommitPanel.js +1 -1
- package/dist/features/store-extensions/schemas/report-review-form.d.ts +8 -8
- package/dist/features/stores/components/StoreGuideHubView.js +1 -1
- package/dist/features/stores/components/StoreHeader.js +1 -1
- package/dist/features/support/schemas/firestore.d.ts +73 -2
- package/dist/features/support/schemas/firestore.js +59 -0
- package/dist/index.d.ts +16 -24
- package/dist/index.js +38 -47
- package/dist/next/ErrorBoundary.js +1 -1
- package/dist/next/routing/route-map.d.ts +24 -0
- package/dist/next/routing/route-map.js +40 -0
- package/dist/providers/payment-manual/index.d.ts +15 -1
- package/dist/providers/payment-manual/index.js +37 -0
- package/dist/providers/payment-phonepe/index.d.ts +14 -1
- package/dist/providers/payment-phonepe/index.js +36 -0
- package/dist/providers/payment-razorpay/index.d.ts +44 -1
- package/dist/providers/payment-razorpay/index.js +99 -0
- package/dist/providers/shipping-shiprocket/index.d.ts +18 -0
- package/dist/providers/shipping-shiprocket/index.js +18 -0
- package/dist/schemas/registry.d.ts +44 -11
- package/dist/server-entry.d.ts +2 -0
- package/dist/server-entry.js +27 -0
- package/dist/server.d.ts +42 -0
- package/dist/server.js +93 -0
- package/dist/styles.css +418 -149
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/themes/default-dark.js +15 -12
- package/dist/tokens/themes/default-light.js +14 -11
- package/dist/tokens/tokens.css +106 -23
- package/dist/ui/DataTable.style.css +4 -4
- package/dist/ui/columns/cell-renderers.d.ts +20 -0
- package/dist/ui/columns/cell-renderers.js +26 -1
- package/dist/ui/columns/index.d.ts +1 -1
- package/dist/ui/columns/index.js +4 -1
- package/dist/ui/components/Badge.style.css +1 -1
- package/dist/ui/components/Breadcrumb.js +19 -1
- package/dist/ui/components/BulkActionBar.style.css +4 -4
- package/dist/ui/components/Button.style.css +1 -1
- package/dist/ui/components/Checkbox.style.css +1 -1
- package/dist/ui/components/FallbackShell.js +1 -1
- package/dist/ui/components/Form.d.ts +14 -1
- package/dist/ui/components/Form.js +2 -2
- package/dist/ui/components/FormField.js +18 -4
- package/dist/ui/components/FormField.style.css +43 -3
- package/dist/ui/components/GatedPrice.style.css +1 -1
- package/dist/ui/components/IconButton.style.css +1 -1
- package/dist/ui/components/Input.js +22 -1
- package/dist/ui/components/Input.style.css +38 -22
- package/dist/ui/components/ListingFilterDrawer.js +1 -1
- package/dist/ui/components/ListingToolbar.js +1 -1
- package/dist/ui/components/PaginatedSelect.js +0 -0
- package/dist/ui/components/Radio.style.css +1 -1
- package/dist/ui/components/Repeater.d.ts +53 -0
- package/dist/ui/components/Repeater.js +7 -0
- package/dist/ui/components/RichTextEditor.d.ts +20 -1
- package/dist/ui/components/RichTextEditor.js +23 -2
- package/dist/ui/components/Select.style.css +7 -21
- package/dist/ui/components/Semantic.js +27 -3
- package/dist/ui/components/TagInput.style.css +4 -9
- package/dist/ui/components/Textarea.style.css +4 -18
- package/dist/ui/components/Toggle.style.css +1 -1
- package/dist/ui/components/Typography.d.ts +3 -3
- package/dist/ui/components/Typography.js +15 -15
- package/dist/ui/components/ViewToggle.style.css +4 -4
- package/dist/ui/forms/AddAnotherToggle.d.ts +16 -0
- package/dist/ui/forms/AddAnotherToggle.js +56 -0
- package/dist/ui/forms/ColorPickerField.js +15 -2
- package/dist/ui/forms/FieldCheckbox.js +15 -2
- package/dist/ui/forms/FieldInput.d.ts +22 -1
- package/dist/ui/forms/FieldInput.js +19 -5
- package/dist/ui/forms/FieldLabel.d.ts +35 -0
- package/dist/ui/forms/FieldLabel.js +70 -0
- package/dist/ui/forms/FieldSelect.d.ts +4 -1
- package/dist/ui/forms/FieldSelect.js +27 -5
- package/dist/ui/forms/FieldTextarea.d.ts +4 -1
- package/dist/ui/forms/FieldTextarea.js +22 -5
- package/dist/ui/forms/FormShell.d.ts +17 -1
- package/dist/ui/forms/FormShell.js +36 -2
- package/dist/ui/forms/FormShell.style.css +41 -33
- package/dist/ui/forms/index.d.ts +5 -1
- package/dist/ui/forms/index.js +2 -0
- package/dist/ui/icons/icon-registry.d.ts +1 -0
- package/dist/ui/icons/icon-registry.js +17 -1
- package/dist/utils/id-generators.d.ts +80 -0
- package/dist/utils/id-generators.js +93 -0
- package/dist/utils/search-txt-builders.d.ts +12 -0
- package/dist/utils/search-txt-builders.js +17 -0
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +106 -5
- package/dist/features/loyalty/api/admin/loyalty/grant/route.d.ts +0 -12
- package/dist/features/loyalty/api/admin/loyalty/grant/route.js +0 -38
- package/dist/features/loyalty/api/balance/route.d.ts +0 -12
- package/dist/features/loyalty/api/balance/route.js +0 -41
- package/dist/features/loyalty/api/earn/route.d.ts +0 -12
- package/dist/features/loyalty/api/earn/route.js +0 -38
- package/dist/features/loyalty/api/history/route.d.ts +0 -12
- package/dist/features/loyalty/api/history/route.js +0 -58
- package/dist/features/loyalty/api/redeem/route.d.ts +0 -12
- package/dist/features/loyalty/api/redeem/route.js +0 -38
- package/dist/features/loyalty/columns/index.d.ts +0 -17
- package/dist/features/loyalty/columns/index.js +0 -40
- package/dist/features/loyalty/components/CoinsDisplay.d.ts +0 -12
- package/dist/features/loyalty/components/CoinsDisplay.js +0 -10
- package/dist/features/loyalty/components/index.d.ts +0 -1
- package/dist/features/loyalty/components/index.js +0 -1
- package/dist/features/loyalty/hooks/useLoyaltyBalance.d.ts +0 -4
- package/dist/features/loyalty/hooks/useLoyaltyBalance.js +0 -25
- package/dist/features/loyalty/index.d.ts +0 -6
- package/dist/features/loyalty/index.js +0 -6
- package/dist/features/loyalty/manifest.d.ts +0 -2
- package/dist/features/loyalty/manifest.js +0 -16
- package/dist/features/loyalty/messages/en.json +0 -19
- package/dist/features/loyalty/repository/loyalty.repository.d.ts +0 -8
- package/dist/features/loyalty/repository/loyalty.repository.js +0 -22
- package/dist/features/loyalty/schemas/index.d.ts +0 -85
- package/dist/features/loyalty/schemas/index.js +0 -40
- package/dist/features/loyalty/server.d.ts +0 -6
- package/dist/features/loyalty/server.js +0 -6
- package/dist/features/loyalty/types/index.d.ts +0 -31
- package/dist/features/loyalty/types/index.js +0 -1
- package/dist/features/loyalty/types/loyalty-config.d.ts +0 -7
- package/dist/features/loyalty/types/loyalty-config.js +0 -1
- package/dist/features/loyalty/types/loyalty-math.d.ts +0 -9
- package/dist/features/loyalty/types/loyalty-math.js +0 -27
- package/dist/features/products/components/ProductOwnerQuickActions.d.ts +0 -11
- package/dist/features/products/components/ProductOwnerQuickActions.js +0 -120
|
@@ -81,6 +81,75 @@ function isImage(url) {
|
|
|
81
81
|
function isPdf(url) {
|
|
82
82
|
return /\.pdf(\?|$)/i.test(url);
|
|
83
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* 🛑 AN EXTENSION REGEX CANNOT CLASSIFY ANYTHING THIS APP UPLOADED.
|
|
86
|
+
*
|
|
87
|
+
* `POST /api/media/finalize` returns `/media/{shortId}` where the shortId is the
|
|
88
|
+
* filename with its extension deliberately STRIPPED:
|
|
89
|
+
*
|
|
90
|
+
* const baseShortId = filename.replace(/\.[^.]+$/, ""); // finalize route
|
|
91
|
+
*
|
|
92
|
+
* so `isImage` / `isVideo` / `isPdf` above were ALL false for every freshly
|
|
93
|
+
* uploaded file. `MediaPreviewPanel` fell through its three branches to the bare
|
|
94
|
+
* `<Anchor>` at the bottom — a text link where the preview belongs — and the
|
|
95
|
+
* "Edit video" button, gated on `isVideo(value)`, never appeared either. The
|
|
96
|
+
* upload itself always worked, which is why this read as a missing feature
|
|
97
|
+
* rather than a broken one.
|
|
98
|
+
*
|
|
99
|
+
* Three signals, in order of how much each actually knows:
|
|
100
|
+
*
|
|
101
|
+
* 1. a real extension — seed fixtures and `source: "external"` URLs, which
|
|
102
|
+
* never pass through finalize. Unchanged behaviour for those.
|
|
103
|
+
* 2. the slug's own kind token, read off `MEDIA_FILENAME_PATTERNS` in
|
|
104
|
+
* `id-generators.ts` — stripping the extension leaves the token intact.
|
|
105
|
+
* (`deriveContextTypeFromFilename` is NOT usable here even though it owns
|
|
106
|
+
* that table: every one of its patterns is anchored with `\.<ext>$`, so it
|
|
107
|
+
* returns undefined for precisely the refs this has to classify.)
|
|
108
|
+
* 3. nothing recognisable — `"file"`, i.e. the bare link it renders today. A
|
|
109
|
+
* wrong guess is worse than the status quo, so this never guesses.
|
|
110
|
+
*
|
|
111
|
+
* 🛑 The token is a SUFFIX for most families and a PREFIX for three of them, so
|
|
112
|
+
* both are tested. A suffix-only rule silently misses `catalogue-image-{slug}-3`
|
|
113
|
+
* (the word is at the front, the number at the back) and
|
|
114
|
+
* `tester-screenshot-{case}-{tester}-{date}` — verified by running the real
|
|
115
|
+
* table's outputs through it.
|
|
116
|
+
*
|
|
117
|
+
* The three `*-proof` families are deliberately absent: their generators accept
|
|
118
|
+
* any extension, so a proof is as likely to be a PDF as a screenshot and there
|
|
119
|
+
* is nothing in the name to separate them. They keep the existing link.
|
|
120
|
+
*
|
|
121
|
+
* The authoritative answer is the uploaded file's own MIME, which `afterUpload`
|
|
122
|
+
* has and now remembers; this resolver is for a value that arrives as a prop
|
|
123
|
+
* (reopening an editor on a saved record), where no MIME is available.
|
|
124
|
+
*/
|
|
125
|
+
function previewKindFor(url) {
|
|
126
|
+
if (isVideo(url))
|
|
127
|
+
return "video";
|
|
128
|
+
if (isPdf(url))
|
|
129
|
+
return "pdf";
|
|
130
|
+
if (isImage(url))
|
|
131
|
+
return "image";
|
|
132
|
+
const slug = url.split("?")[0].split("/").pop() ?? "";
|
|
133
|
+
if (/-video(-\d+)?$/i.test(slug))
|
|
134
|
+
return "video";
|
|
135
|
+
if (/-(image|logo|banner|avatar)(-\d+)?$/i.test(slug))
|
|
136
|
+
return "image";
|
|
137
|
+
if (/^(catalogue-image|tester-screenshot)-/i.test(slug))
|
|
138
|
+
return "image";
|
|
139
|
+
if (/^(invoice|payout-doc)-/i.test(slug))
|
|
140
|
+
return "pdf";
|
|
141
|
+
return "file";
|
|
142
|
+
}
|
|
143
|
+
/** The uploaded MIME wins over any name-based guess, for the url it was seen on. */
|
|
144
|
+
function previewKindFromMime(mimeType) {
|
|
145
|
+
if (mimeType.startsWith("video/"))
|
|
146
|
+
return "video";
|
|
147
|
+
if (mimeType.startsWith("image/"))
|
|
148
|
+
return "image";
|
|
149
|
+
if (mimeType === "application/pdf")
|
|
150
|
+
return "pdf";
|
|
151
|
+
return "file";
|
|
152
|
+
}
|
|
84
153
|
function isPdfAccept(accept) {
|
|
85
154
|
return accept
|
|
86
155
|
.toLowerCase()
|
|
@@ -120,8 +189,8 @@ function YoutubeTabPanel({ ytInput, setYtInput, ytError, setYtError, onApply, va
|
|
|
120
189
|
function ExternalUrlTabPanel({ extInput, setExtInput, extError, setExtError, onApply, }) {
|
|
121
190
|
return (_jsxs(Stack, { gap: "sm", children: [_jsxs(Row, { gap: "sm", children: [_jsx("input", { type: "url", value: extInput, onChange: (e) => { setExtInput(e.target.value); setExtError(""); }, placeholder: "https://example.com/image.jpg", className: "appkit-input flex-1" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", onClick: onApply, children: "Apply" })] }), extError && _jsx(Alert, { variant: "error", children: extError }), _jsx(Text, { size: "xs", variant: "secondary", children: "External URLs are stored as-is and are not watermarked." })] }));
|
|
122
191
|
}
|
|
123
|
-
function MediaPreviewPanel({ value, label, disabled, enableTrim, enableThumbnail, onEditVideo, onRemove, tUpload, tMediaEditor, }) {
|
|
124
|
-
return (_jsxs(Div, { padding: "sm", rounded: "lg", surface: "muted", border: "default", children: [
|
|
192
|
+
function MediaPreviewPanel({ value, kind, label, disabled, enableTrim, enableThumbnail, onEditVideo, onRemove, tUpload, tMediaEditor, }) {
|
|
193
|
+
return (_jsxs(Div, { padding: "sm", rounded: "lg", surface: "muted", border: "default", children: [kind === "video" ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Div, { className: "relative aspect-video", overflow: "hidden", rounded: "lg", children: _jsx(MediaVideo, { src: value, alt: label, controls: true, objectFit: "contain" }) }), _jsx(Text, { size: "xs", variant: "secondary", children: "Video uploaded successfully. Large videos may take longer to process." })] })) : kind === "image" ? (_jsx(Div, { className: "relative aspect-video", overflow: "hidden", rounded: "lg", children: _jsx(MediaImage, { src: value, alt: label, size: "card", objectFit: "contain", fill: true }) })) : kind === "pdf" ? (_jsxs(Stack, { gap: "sm", children: [_jsx(Div, { className: "relative w-full", overflow: "hidden", rounded: "lg", border: "default", style: { height: 280 }, children: _jsx("iframe", { src: value, title: filenameFromUrl(value), className: "w-full h-full border-0" }) }), _jsxs(Row, { gap: "md", align: "center", children: [_jsx(Div, { "aria-hidden": true, className: CLS_PDF_BADGE, children: _jsx(Span, { size: "xs", weight: "bold", children: "PDF" }) }), _jsx(Anchor, { href: value, className: CLS_PDF_NAME, children: filenameFromUrl(value) })] })] })) : (_jsx(Anchor, { href: value, className: CLS_PDF_LINK, children: filenameFromUrl(value) })), !disabled && (_jsxs(Row, { wrap: true, gap: "sm", className: "mt-2", children: [kind === "video" && (enableTrim || enableThumbnail) && (_jsx(Button, { type: "button", onClick: () => onEditVideo(value), variant: "secondary", size: "sm", children: tMediaEditor("editVideo") })), _jsx(Button, { type: "button", onClick: onRemove, variant: "danger", size: "sm", children: tUpload("remove") })] }))] }));
|
|
125
194
|
}
|
|
126
195
|
export function MediaUploadField({ label, value, onChange, onChangeField, onUpload,
|
|
127
196
|
// SB-UNI-Z5 2026-05-13 — `kind` derives accept/maxSizeMB when not set
|
|
@@ -139,6 +208,16 @@ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText
|
|
|
139
208
|
const [ytError, setYtError] = useState("");
|
|
140
209
|
const [extInput, setExtInput] = useState("");
|
|
141
210
|
const [extError, setExtError] = useState("");
|
|
211
|
+
/*
|
|
212
|
+
* The kind of the file we just uploaded, remembered WITH the url it belongs to.
|
|
213
|
+
*
|
|
214
|
+
* Keyed on the url rather than held as a bare kind so it cannot go stale: a
|
|
215
|
+
* parent that swaps `value` out from under us (switching records, a reset, the
|
|
216
|
+
* trim flow replacing the url) simply stops matching and the resolver takes
|
|
217
|
+
* over. An effect clearing it on `value` change would be a second mechanism
|
|
218
|
+
* that has to agree with this one.
|
|
219
|
+
*/
|
|
220
|
+
const [uploadedKind, setUploadedKind] = useState(null);
|
|
142
221
|
const hasAlternateSources = showYoutube || showExternal;
|
|
143
222
|
/*
|
|
144
223
|
* PDF mode: derived from the `accept` prop. It forces file-only capture
|
|
@@ -256,6 +335,10 @@ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText
|
|
|
256
335
|
? "image/*,video/*"
|
|
257
336
|
: "image/*";
|
|
258
337
|
const afterUpload = (url, fileType, autoPosterUrl, duration) => {
|
|
338
|
+
// The one authoritative signal there is: the file's real MIME. Recorded
|
|
339
|
+
// before anything else so the preview is correct on the very first render
|
|
340
|
+
// after an upload, including for a video that is about to be trimmed.
|
|
341
|
+
setUploadedKind({ url, kind: previewKindFromMime(fileType) });
|
|
259
342
|
onChangeField?.({
|
|
260
343
|
url,
|
|
261
344
|
type: inferMediaTypeFromMime(fileType, url),
|
|
@@ -281,6 +364,10 @@ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText
|
|
|
281
364
|
onChange(url);
|
|
282
365
|
};
|
|
283
366
|
const handleTrimSave = (trimmedUrl) => {
|
|
367
|
+
// A trim mints a NEW url, so the kind recorded against the original no
|
|
368
|
+
// longer matches. It is a video by construction — the trim modal only opens
|
|
369
|
+
// for one — so carry that across rather than letting the resolver re-guess.
|
|
370
|
+
setUploadedKind({ url: trimmedUrl, kind: "video" });
|
|
284
371
|
setShowTrimModal(false);
|
|
285
372
|
if (enableThumbnail) {
|
|
286
373
|
setPendingVideoUrl(trimmedUrl);
|
|
@@ -411,6 +498,7 @@ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText
|
|
|
411
498
|
}
|
|
412
499
|
onChange("");
|
|
413
500
|
onChangeField?.(null);
|
|
501
|
+
setUploadedKind(null);
|
|
414
502
|
setError(null);
|
|
415
503
|
};
|
|
416
504
|
const handleCameraCapture = async (blob, type) => {
|
|
@@ -445,5 +533,5 @@ kind, accept: acceptProp, maxSizeMB: maxSizeMBProp, disabled = false, helperText
|
|
|
445
533
|
return (_jsxs(Stack, { gap: "sm", children: [_jsx(Label, { className: "block", color: "muted", size: "sm", weight: "medium", children: label }), hasAlternateSources && !disabled && (_jsx(Row, { gap: "none", className: "appkit-media-upload__source-tabs", children: ["upload", ...(showYoutube ? ["youtube"] : []), ...(showExternal ? ["external"] : [])].map((tab) => (_jsx("button", { type: "button", onClick: () => setSourceTab(tab), className: [
|
|
446
534
|
"appkit-media-upload__source-tab",
|
|
447
535
|
sourceTab === tab ? "appkit-media-upload__source-tab--active" : "",
|
|
448
|
-
].filter(Boolean).join(" "), children: tab === "upload" ? "Upload" : tab === "youtube" ? "YouTube" : "External URL" }, tab))) })), hasAlternateSources && sourceTab === "youtube" && !disabled && (_jsx(YoutubeTabPanel, { ytInput: ytInput, setYtInput: setYtInput, ytError: ytError, setYtError: setYtError, onApply: handleYtApply, value: value })), hasAlternateSources && sourceTab === "external" && !disabled && (_jsx(ExternalUrlTabPanel, { extInput: extInput, setExtInput: setExtInput, extError: extError, setExtError: setExtError, onApply: handleExtApply })), (!hasAlternateSources || sourceTab === "upload") && value && !isLoading && (_jsx(MediaPreviewPanel, { value: value, label: label, disabled: disabled, enableTrim: enableTrim, enableThumbnail: enableThumbnail, onEditVideo: handleEditVideo, onRemove: handleRemove, tUpload: tUpload, tMediaEditor: tMediaEditor })), !disabled && !isLoading && (!hasAlternateSources || sourceTab === "upload") && (_jsxs(_Fragment, { children: [captureSource === "both" && isCameraSupported && (_jsxs(Row, { gap: "sm", children: [_jsx(Button, { type: "button", variant: inputMode === "file" ? "primary" : "outline", size: "sm", onClick: () => setInputMode("file"), children: t("switchToUpload") }), _jsx(Button, { type: "button", variant: inputMode === "camera" ? "primary" : "outline", size: "sm", onClick: () => setInputMode("camera"), children: t("switchToCamera") })] })), showCamera && isCameraSupported && (_jsx(CameraCapture, { mode: captureMode, facingMode: "environment", onCapture: handleCameraCapture, onError: (msg) => setError(msg) })), showCamera && !isCameraSupported && (_jsx(Button, { rounded: "xl", type: "button", onClick: () => mobileCaptureRef.current?.click(), variant: "ghost", className: "w-full py-[var(--appkit-space-3)] border-2 border-dashed border-[var(--appkit-color-border)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)]", children: t("switchToCamera") })), showFileInput && (_jsx(Button, { rounded: "xl", type: "button", onClick: () => fileInputRef.current?.click(), variant: "ghost", className: "w-full py-[var(--appkit-space-3)] border-2 border-dashed border-[var(--appkit-color-border)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)]", children: value ? tUpload("replaceFile") : tUpload("chooseFile") }))] })), isLoading && (_jsxs(Row, { gap: "sm", children: [_jsx(Spinner, { size: "sm" }), _jsx(Text, { size: "sm", variant: "secondary", children: tUpload("uploading") })] })), _jsx("input", { ref: fileInputRef, type: "file", accept: accept, multiple: multiple, onChange: handleFileChange, className: "hidden", "aria-hidden": "true", "data-testid": "media-upload-input" }), showCamera && !isCameraSupported && (_jsx("input", { ref: mobileCaptureRef, type: "file", accept: captureModeAccept, capture: "environment", onChange: handleFileChange, className: "hidden", "aria-hidden": "true", "data-testid": "media-upload-capture-input" })), helperText && !error && (_jsx(Text, { variant: "secondary", size: "xs", children: helperText })), error && _jsx(Alert, { variant: "error", children: error }), pendingVideoUrl && (_jsx(VideoTrimModal, { isOpen: showTrimModal, videoUrl: pendingVideoUrl, onClose: handleTrimClose, onSave: handleTrimSave })), pendingVideoUrl && (_jsx(VideoThumbnailSelector, { isOpen: showThumbnailModal, videoUrl: pendingVideoUrl, onClose: handleThumbnailClose, onSelect: handleThumbnailSelect, onUpload: onUpload }))] }));
|
|
536
|
+
].filter(Boolean).join(" "), children: tab === "upload" ? "Upload" : tab === "youtube" ? "YouTube" : "External URL" }, tab))) })), hasAlternateSources && sourceTab === "youtube" && !disabled && (_jsx(YoutubeTabPanel, { ytInput: ytInput, setYtInput: setYtInput, ytError: ytError, setYtError: setYtError, onApply: handleYtApply, value: value })), hasAlternateSources && sourceTab === "external" && !disabled && (_jsx(ExternalUrlTabPanel, { extInput: extInput, setExtInput: setExtInput, extError: extError, setExtError: setExtError, onApply: handleExtApply })), (!hasAlternateSources || sourceTab === "upload") && value && !isLoading && (_jsx(MediaPreviewPanel, { value: value, kind: uploadedKind?.url === value ? uploadedKind.kind : previewKindFor(value), label: label, disabled: disabled, enableTrim: enableTrim, enableThumbnail: enableThumbnail, onEditVideo: handleEditVideo, onRemove: handleRemove, tUpload: tUpload, tMediaEditor: tMediaEditor })), !disabled && !isLoading && (!hasAlternateSources || sourceTab === "upload") && (_jsxs(_Fragment, { children: [captureSource === "both" && isCameraSupported && (_jsxs(Row, { gap: "sm", children: [_jsx(Button, { type: "button", variant: inputMode === "file" ? "primary" : "outline", size: "sm", onClick: () => setInputMode("file"), children: t("switchToUpload") }), _jsx(Button, { type: "button", variant: inputMode === "camera" ? "primary" : "outline", size: "sm", onClick: () => setInputMode("camera"), children: t("switchToCamera") })] })), showCamera && isCameraSupported && (_jsx(CameraCapture, { mode: captureMode, facingMode: "environment", onCapture: handleCameraCapture, onError: (msg) => setError(msg) })), showCamera && !isCameraSupported && (_jsx(Button, { rounded: "xl", type: "button", onClick: () => mobileCaptureRef.current?.click(), variant: "ghost", className: "w-full py-[var(--appkit-space-3)] border-2 border-dashed border-[var(--appkit-color-border)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)]", children: t("switchToCamera") })), showFileInput && (_jsx(Button, { rounded: "xl", type: "button", onClick: () => fileInputRef.current?.click(), variant: "ghost", className: "w-full py-[var(--appkit-space-3)] border-2 border-dashed border-[var(--appkit-color-border)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text-muted)]", children: value ? tUpload("replaceFile") : tUpload("chooseFile") }))] })), isLoading && (_jsxs(Row, { gap: "sm", children: [_jsx(Spinner, { size: "sm" }), _jsx(Text, { size: "sm", variant: "secondary", children: tUpload("uploading") })] })), _jsx("input", { ref: fileInputRef, type: "file", accept: accept, multiple: multiple, onChange: handleFileChange, className: "hidden", "aria-hidden": "true", "data-testid": "media-upload-input" }), showCamera && !isCameraSupported && (_jsx("input", { ref: mobileCaptureRef, type: "file", accept: captureModeAccept, capture: "environment", onChange: handleFileChange, className: "hidden", "aria-hidden": "true", "data-testid": "media-upload-capture-input" })), helperText && !error && (_jsx(Text, { variant: "secondary", size: "xs", children: helperText })), error && _jsx(Alert, { variant: "error", children: error }), pendingVideoUrl && (_jsx(VideoTrimModal, { isOpen: showTrimModal, videoUrl: pendingVideoUrl, onClose: handleTrimClose, onSave: handleTrimSave })), pendingVideoUrl && (_jsx(VideoThumbnailSelector, { isOpen: showThumbnailModal, videoUrl: pendingVideoUrl, onClose: handleThumbnailClose, onSelect: handleThumbnailSelect, onUpload: onUpload }))] }));
|
|
449
537
|
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `OBLIGATION_POLICIES` — the ONE place a lifecycle duration lives.
|
|
3
|
+
*
|
|
4
|
+
* ## What this replaces
|
|
5
|
+
*
|
|
6
|
+
* Durations were scattered across hardcoded constants, dead `siteSettings`
|
|
7
|
+
* fields, and published FAQ prose that disagreed with all of them. The plan's
|
|
8
|
+
* own audit of it found eighteen dead timer settings (D28/D44/D57/D60) and
|
|
9
|
+
* these live contradictions:
|
|
10
|
+
*
|
|
11
|
+
* | fact | code said | the FAQ said | a guide said |
|
|
12
|
+
* |---|---|---|---|
|
|
13
|
+
* | seller return response | nothing enforced | 48 hours | 3 days |
|
|
14
|
+
* | offer checkout deadline | 48h | 24h | — |
|
|
15
|
+
* | return window | `ORDER_RETURN_WINDOW_DAYS` = 7 | 7 days / 2 days / 48h | — |
|
|
16
|
+
*
|
|
17
|
+
* `publishedCopyKey` is the half that fixes that: Phase 9 renders the number
|
|
18
|
+
* from HERE, so published copy **cannot** disagree with the code. A duration
|
|
19
|
+
* changed in this file changes the policy page, the FAQ and the sweep together.
|
|
20
|
+
*
|
|
21
|
+
* ## `deadlinesFor(order)` is a PURE FUNCTION over this registry
|
|
22
|
+
*
|
|
23
|
+
* So a buyer or seller page can render "Dispatch by Thu 3 Nov" with **zero
|
|
24
|
+
* extra reads**. That is strictly better than storing deadline fields on the
|
|
25
|
+
* order, which would need a backfill and would drift from the policy the day
|
|
26
|
+
* somebody edited one — the mirror trap (Root Cause #42).
|
|
27
|
+
*
|
|
28
|
+
* ## 🛑 Every duration here is a DEFAULT, and `settingPath` says which admin
|
|
29
|
+
* field overrides it
|
|
30
|
+
*
|
|
31
|
+
* A duration with no reader is a dead setting; a setting with no duration is a
|
|
32
|
+
* control that silently does nothing. `platformLimits`' own comment states the
|
|
33
|
+
* rule its three deleted siblings were removed under: *"Do not re-add them
|
|
34
|
+
* without a reader: a control that silently does nothing is worse than an
|
|
35
|
+
* absent one."* This registry is the reader.
|
|
36
|
+
*/
|
|
37
|
+
import type { OrderDocument } from "../../orders/schemas/firestore";
|
|
38
|
+
import type { ObligationDueAction, ObligationKind } from "../schemas/firestore";
|
|
39
|
+
export type DurationUnit = "minutes" | "hours" | "days";
|
|
40
|
+
export interface Duration {
|
|
41
|
+
value: number;
|
|
42
|
+
unit: DurationUnit;
|
|
43
|
+
/**
|
|
44
|
+
* `business` counts weekends and the Indian holiday calendar; `calendar` is
|
|
45
|
+
* wall-clock.
|
|
46
|
+
*
|
|
47
|
+
* 🛑 These are NOT interchangeable and the distinction was a live bug:
|
|
48
|
+
* `getBusinessDayCutoff` subtracted CALENDAR days and called them business
|
|
49
|
+
* days, so the "7-business-day" payout hold was really about 5 — four
|
|
50
|
+
* calendar days short of what it promised. A duration that says `business`
|
|
51
|
+
* must go through `addBusinessDays`.
|
|
52
|
+
*/
|
|
53
|
+
calendar: "calendar" | "business";
|
|
54
|
+
}
|
|
55
|
+
export interface ObligationPolicy {
|
|
56
|
+
/** Which fact starts the clock. `null` means the clock is not running yet. */
|
|
57
|
+
anchor: (order: OrderDocument) => Date | null | undefined;
|
|
58
|
+
duration: Duration;
|
|
59
|
+
/** Warn this long BEFORE it fires, so the deadline is visible in advance. */
|
|
60
|
+
warnAt?: readonly Duration[];
|
|
61
|
+
/**
|
|
62
|
+
* Re-checked at FIRE time, against a freshly-read subject.
|
|
63
|
+
*
|
|
64
|
+
* 🛑 This is what makes the collection an index rather than truth. A
|
|
65
|
+
* `seller-accept` obligation whose order has since been paid-and-accepted
|
|
66
|
+
* returns `false` here and closes `superseded` — no action, no case, no
|
|
67
|
+
* noise.
|
|
68
|
+
*/
|
|
69
|
+
precondition: (order: OrderDocument) => boolean;
|
|
70
|
+
/** Kinds that must be closed before this one may fire. */
|
|
71
|
+
blockedBy?: readonly ObligationKind[];
|
|
72
|
+
/** The guardrail tier. See `ObligationDueAction`. */
|
|
73
|
+
onDue: ObligationDueAction;
|
|
74
|
+
/** The `siteSettings` path an admin can override the duration with. */
|
|
75
|
+
settingPath?: string;
|
|
76
|
+
/** The copy key Phase 9 renders this duration into. */
|
|
77
|
+
publishedCopyKey: string;
|
|
78
|
+
/** One line for an operator reading a case opened from this kind. */
|
|
79
|
+
describe: string;
|
|
80
|
+
}
|
|
81
|
+
export declare const OBLIGATION_POLICIES: Record<ObligationKind, ObligationPolicy>;
|
|
82
|
+
/**
|
|
83
|
+
* Minutes a duration represents, for the non-business case.
|
|
84
|
+
*
|
|
85
|
+
* Exported so the sweep and `deadlinesFor` share one conversion rather than
|
|
86
|
+
* two — and `PAYMENT_WINDOW_MINUTES` is imported here purely so the one
|
|
87
|
+
* pre-existing minute-scale window in the system is expressible in the same
|
|
88
|
+
* vocabulary if it is ever folded in.
|
|
89
|
+
*/
|
|
90
|
+
export declare function durationToMinutes(d: Duration): number;
|
|
91
|
+
/** The payment window, in this registry's vocabulary. Not yet an obligation kind. */
|
|
92
|
+
export declare const PAYMENT_WINDOW_AS_DURATION: Duration;
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `OBLIGATION_POLICIES` — the ONE place a lifecycle duration lives.
|
|
3
|
+
*
|
|
4
|
+
* ## What this replaces
|
|
5
|
+
*
|
|
6
|
+
* Durations were scattered across hardcoded constants, dead `siteSettings`
|
|
7
|
+
* fields, and published FAQ prose that disagreed with all of them. The plan's
|
|
8
|
+
* own audit of it found eighteen dead timer settings (D28/D44/D57/D60) and
|
|
9
|
+
* these live contradictions:
|
|
10
|
+
*
|
|
11
|
+
* | fact | code said | the FAQ said | a guide said |
|
|
12
|
+
* |---|---|---|---|
|
|
13
|
+
* | seller return response | nothing enforced | 48 hours | 3 days |
|
|
14
|
+
* | offer checkout deadline | 48h | 24h | — |
|
|
15
|
+
* | return window | `ORDER_RETURN_WINDOW_DAYS` = 7 | 7 days / 2 days / 48h | — |
|
|
16
|
+
*
|
|
17
|
+
* `publishedCopyKey` is the half that fixes that: Phase 9 renders the number
|
|
18
|
+
* from HERE, so published copy **cannot** disagree with the code. A duration
|
|
19
|
+
* changed in this file changes the policy page, the FAQ and the sweep together.
|
|
20
|
+
*
|
|
21
|
+
* ## `deadlinesFor(order)` is a PURE FUNCTION over this registry
|
|
22
|
+
*
|
|
23
|
+
* So a buyer or seller page can render "Dispatch by Thu 3 Nov" with **zero
|
|
24
|
+
* extra reads**. That is strictly better than storing deadline fields on the
|
|
25
|
+
* order, which would need a backfill and would drift from the policy the day
|
|
26
|
+
* somebody edited one — the mirror trap (Root Cause #42).
|
|
27
|
+
*
|
|
28
|
+
* ## 🛑 Every duration here is a DEFAULT, and `settingPath` says which admin
|
|
29
|
+
* field overrides it
|
|
30
|
+
*
|
|
31
|
+
* A duration with no reader is a dead setting; a setting with no duration is a
|
|
32
|
+
* control that silently does nothing. `platformLimits`' own comment states the
|
|
33
|
+
* rule its three deleted siblings were removed under: *"Do not re-add them
|
|
34
|
+
* without a reader: a control that silently does nothing is worse than an
|
|
35
|
+
* absent one."* This registry is the reader.
|
|
36
|
+
*/
|
|
37
|
+
import { ORDER_RETURN_WINDOW_DAYS, ORDER_AUTO_CONFIRM_DAYS, } from "../../../_internal/shared/features/orders/config.js";
|
|
38
|
+
import { PROTECTION_CLAIM_WINDOW_HOURS } from "../../../_internal/shared/features/shipping/protections.js";
|
|
39
|
+
import { PAYMENT_WINDOW_MINUTES } from "../../orders/constants/payment-window.js";
|
|
40
|
+
const isPaid = (o) => o.paymentStatus === "paid";
|
|
41
|
+
const notTerminal = (o) => o.status !== "cancelled" && o.status !== "refunded";
|
|
42
|
+
/**
|
|
43
|
+
* 🛑 `shippingDate != null` IS THE DISCRIMINATOR for before/after dispatch,
|
|
44
|
+
* not the status set — and this is the single highest-value line in the whole
|
|
45
|
+
* obligation design.
|
|
46
|
+
*
|
|
47
|
+
* "Cancel before dispatch" and "cancel after dispatch" are different events
|
|
48
|
+
* with different money: before, the buyer gets everything back; after, the
|
|
49
|
+
* buyer bears outbound shipping and it becomes a return. Today
|
|
50
|
+
* `assertOrderCancellable` just throws *"Only pending or confirmed orders can
|
|
51
|
+
* be cancelled"* and offers the buyer nothing — because it discriminates on
|
|
52
|
+
* STATUS, which cannot express "has the parcel physically left".
|
|
53
|
+
*/
|
|
54
|
+
const hasDispatched = (o) => Boolean(o.shippingDate);
|
|
55
|
+
export const OBLIGATION_POLICIES = {
|
|
56
|
+
/*
|
|
57
|
+
* ── PRE-DISPATCH ─────────────────────────────────────────────────────────
|
|
58
|
+
*/
|
|
59
|
+
/**
|
|
60
|
+
* The seller never accepts. **3 calendar days**, the figure the owner gave.
|
|
61
|
+
*
|
|
62
|
+
* 🛑 D30: there IS no seller-acceptance step today. `customShipOrder`
|
|
63
|
+
* requires `status === "confirmed"`, and **nothing ever sets `confirmed` on a
|
|
64
|
+
* paid order** except an admin PATCH — `onOrderCreate` writes one admin
|
|
65
|
+
* notification and stops. So this obligation is the first thing in the
|
|
66
|
+
* system that notices a seller ignoring an order.
|
|
67
|
+
*
|
|
68
|
+
* `act`: the ceiling is what we captured, the recipient is the payer, and
|
|
69
|
+
* the seller is not debited — they simply forfeit the sale. The platform
|
|
70
|
+
* absorbs the gateway fee, which is the cost of the seller's inaction and
|
|
71
|
+
* not the buyer's problem.
|
|
72
|
+
*/
|
|
73
|
+
"seller-accept": {
|
|
74
|
+
anchor: (o) => o.orderDate ?? o.createdAt,
|
|
75
|
+
duration: { value: 3, unit: "days", calendar: "calendar" },
|
|
76
|
+
warnAt: [{ value: 24, unit: "hours", calendar: "calendar" }],
|
|
77
|
+
precondition: (o) => isPaid(o) && notTerminal(o) && !o.acceptedAt && !hasDispatched(o),
|
|
78
|
+
onDue: "act",
|
|
79
|
+
publishedCopyKey: "obligations.sellerAccept",
|
|
80
|
+
describe: "The seller did not accept this order within the acceptance window.",
|
|
81
|
+
},
|
|
82
|
+
/**
|
|
83
|
+
* Accepted but never dispatched. **2 business days**, because a weekend is
|
|
84
|
+
* not a seller ignoring you.
|
|
85
|
+
*/
|
|
86
|
+
"seller-dispatch": {
|
|
87
|
+
anchor: (o) => o.acceptedAt,
|
|
88
|
+
duration: { value: 2, unit: "days", calendar: "business" },
|
|
89
|
+
warnAt: [{ value: 8, unit: "hours", calendar: "calendar" }],
|
|
90
|
+
precondition: (o) => isPaid(o) && notTerminal(o) && !hasDispatched(o),
|
|
91
|
+
blockedBy: ["seller-accept"],
|
|
92
|
+
onDue: "act",
|
|
93
|
+
publishedCopyKey: "obligations.sellerDispatch",
|
|
94
|
+
describe: "The seller accepted this order but did not dispatch it in time.",
|
|
95
|
+
},
|
|
96
|
+
/**
|
|
97
|
+
* A rejected payment proof the buyer never replaced.
|
|
98
|
+
*
|
|
99
|
+
* 🛑 THIS IS THE D27 FIX. An order in `reupload_requested` was caught by
|
|
100
|
+
* ZERO sweeps — excluded by all three because it has a proof, has an
|
|
101
|
+
* outcome, and has a deadline — so it held stock forever with no terminal
|
|
102
|
+
* state. One policy row replaces the reasoning that failed.
|
|
103
|
+
*/
|
|
104
|
+
"proof-reupload": {
|
|
105
|
+
anchor: (o) => o.paymentReviewedAt,
|
|
106
|
+
duration: { value: 24, unit: "hours", calendar: "calendar" },
|
|
107
|
+
warnAt: [{ value: 4, unit: "hours", calendar: "calendar" }],
|
|
108
|
+
precondition: (o) => o.paymentReviewOutcome === "reupload_requested" && notTerminal(o) && !isPaid(o),
|
|
109
|
+
onDue: "act",
|
|
110
|
+
settingPath: "platformLimits.proofReuploadWindowHours",
|
|
111
|
+
publishedCopyKey: "obligations.proofReupload",
|
|
112
|
+
describe: "The buyer did not re-upload a payment proof after one was rejected.",
|
|
113
|
+
},
|
|
114
|
+
/*
|
|
115
|
+
* ── IN TRANSIT ───────────────────────────────────────────────────────────
|
|
116
|
+
*/
|
|
117
|
+
/**
|
|
118
|
+
* Nobody ever confirms delivery. **14 days** — which is what the dead
|
|
119
|
+
* `ORDER_AUTO_CONFIRM_DAYS` constant was for, and this is its first reader.
|
|
120
|
+
*
|
|
121
|
+
* 🛑 Ordered BEFORE everything post-delivery, because it PRODUCES the anchor
|
|
122
|
+
* those depend on (`deliveryDate`). And `blockedBy` is load-bearing here: a
|
|
123
|
+
* parcel lost in transit must not auto-confirm, open a return window, and
|
|
124
|
+
* start the seller's payout clock. A transit claim blocks it.
|
|
125
|
+
*/
|
|
126
|
+
"delivery-confirm": {
|
|
127
|
+
anchor: (o) => o.shippingDate,
|
|
128
|
+
duration: { value: ORDER_AUTO_CONFIRM_DAYS, unit: "days", calendar: "calendar" },
|
|
129
|
+
precondition: (o) => hasDispatched(o) && !o.deliveryDate && notTerminal(o),
|
|
130
|
+
blockedBy: ["claim-shipping"],
|
|
131
|
+
onDue: "act",
|
|
132
|
+
settingPath: "platformLimits.autoConfirmDeliveryDays",
|
|
133
|
+
publishedCopyKey: "obligations.deliveryConfirm",
|
|
134
|
+
describe: "Delivery was never confirmed, so it is being recorded as delivered.",
|
|
135
|
+
},
|
|
136
|
+
/*
|
|
137
|
+
* ── RETURNS ──────────────────────────────────────────────────────────────
|
|
138
|
+
*/
|
|
139
|
+
/**
|
|
140
|
+
* The seller ignores a return request.
|
|
141
|
+
*
|
|
142
|
+
* 🛑 **48 hours, and this number settles a live contradiction**: the FAQ
|
|
143
|
+
* says the seller has 48 hours, `AdminOrdersGuideView` says 3 days, and
|
|
144
|
+
* nothing enforced either. Picking one and rendering it everywhere via
|
|
145
|
+
* `publishedCopyKey` is the point — 48h is the shorter, so it is the one
|
|
146
|
+
* that cannot make us look like we broke a published promise.
|
|
147
|
+
*
|
|
148
|
+
* `act`: auto-approving a return the seller ignored refunds the BUYER from
|
|
149
|
+
* money we hold. The seller-side consequence is a separate decision and is
|
|
150
|
+
* NOT taken here.
|
|
151
|
+
*/
|
|
152
|
+
"return-respond": {
|
|
153
|
+
anchor: (o) => o.returnRequestedAt,
|
|
154
|
+
duration: { value: 48, unit: "hours", calendar: "calendar" },
|
|
155
|
+
warnAt: [{ value: 12, unit: "hours", calendar: "calendar" }],
|
|
156
|
+
precondition: (o) => o.status === "return_requested" && !o.returnPostedAt,
|
|
157
|
+
onDue: "act",
|
|
158
|
+
settingPath: "returns.sellerResponseHours",
|
|
159
|
+
publishedCopyKey: "obligations.returnRespond",
|
|
160
|
+
describe: "The seller did not respond to this return request in time.",
|
|
161
|
+
},
|
|
162
|
+
/** Return approved, buyer never posts it. No refund; the window does not reopen. */
|
|
163
|
+
"return-post": {
|
|
164
|
+
anchor: (o) => o.returnApprovedAt,
|
|
165
|
+
duration: { value: ORDER_RETURN_WINDOW_DAYS, unit: "days", calendar: "calendar" },
|
|
166
|
+
warnAt: [{ value: 2, unit: "days", calendar: "calendar" }],
|
|
167
|
+
precondition: (o) => Boolean(o.returnApprovedAt) && !o.returnPostedAt,
|
|
168
|
+
blockedBy: ["return-respond"],
|
|
169
|
+
onDue: "act",
|
|
170
|
+
settingPath: "returns.windowDays",
|
|
171
|
+
publishedCopyKey: "obligations.returnPost",
|
|
172
|
+
describe: "The buyer did not post the return within the window.",
|
|
173
|
+
},
|
|
174
|
+
/** Posted but never arrives — a transit problem, so a human looks. */
|
|
175
|
+
"return-receive": {
|
|
176
|
+
anchor: (o) => o.returnPostedAt,
|
|
177
|
+
duration: { value: 10, unit: "days", calendar: "calendar" },
|
|
178
|
+
precondition: (o) => Boolean(o.returnPostedAt) && !o.returnReceivedAt,
|
|
179
|
+
blockedBy: ["return-post"],
|
|
180
|
+
onDue: "escalate",
|
|
181
|
+
publishedCopyKey: "obligations.returnReceive",
|
|
182
|
+
describe: "A posted return never arrived at the seller.",
|
|
183
|
+
},
|
|
184
|
+
/**
|
|
185
|
+
* Received but never inspected. **3 business days, then the buyer wins.**
|
|
186
|
+
*
|
|
187
|
+
* 🛑 `act` on the BUYER side only. The buyer is refunded automatically
|
|
188
|
+
* because the seller had three business days and did nothing; the seller is
|
|
189
|
+
* **charged only by a human**, which is why there is no seller-debit here.
|
|
190
|
+
* Those are two different decisions and the plan's guardrail table keeps
|
|
191
|
+
* them apart deliberately.
|
|
192
|
+
*/
|
|
193
|
+
"return-inspect": {
|
|
194
|
+
anchor: (o) => o.returnReceivedAt,
|
|
195
|
+
duration: { value: 3, unit: "days", calendar: "business" },
|
|
196
|
+
warnAt: [{ value: 1, unit: "days", calendar: "business" }],
|
|
197
|
+
precondition: (o) => Boolean(o.returnReceivedAt) && !o.returnInspection?.inspectedAt,
|
|
198
|
+
blockedBy: ["return-receive"],
|
|
199
|
+
onDue: "act",
|
|
200
|
+
settingPath: "returns.inspectionBusinessDays",
|
|
201
|
+
publishedCopyKey: "obligations.returnInspect",
|
|
202
|
+
describe: "A received return was never inspected, so it resolves in the buyer's favour.",
|
|
203
|
+
},
|
|
204
|
+
/*
|
|
205
|
+
* ── PROTECTION CLAIM WINDOWS ─────────────────────────────────────────────
|
|
206
|
+
*
|
|
207
|
+
* 🛑 These make the add-on REAL for the first time. We charge 2% (min ₹30)
|
|
208
|
+
* shipment protection and 3% (min ₹49) sale protection, the flags are seeded
|
|
209
|
+
* `true`, and `adjudicateClaim()` has existed with zero callers — so the fee
|
|
210
|
+
* was collected against a promise with no mechanism (D11).
|
|
211
|
+
*
|
|
212
|
+
* Closing the window is what makes the premium EARNED, which is also what
|
|
213
|
+
* the payout hold waits for. `act`, because closing a window moves no money.
|
|
214
|
+
*/
|
|
215
|
+
"claim-shipping": {
|
|
216
|
+
anchor: (o) => o.deliveryDate,
|
|
217
|
+
duration: {
|
|
218
|
+
value: PROTECTION_CLAIM_WINDOW_HOURS.shipping,
|
|
219
|
+
unit: "hours",
|
|
220
|
+
calendar: "calendar",
|
|
221
|
+
},
|
|
222
|
+
precondition: (o) => Boolean(o.deliveryDate) && Boolean(o.shipmentProtectionFee),
|
|
223
|
+
onDue: "act",
|
|
224
|
+
publishedCopyKey: "obligations.claimShipping",
|
|
225
|
+
describe: "The shipment-protection claim window has closed.",
|
|
226
|
+
},
|
|
227
|
+
"claim-sale": {
|
|
228
|
+
anchor: (o) => o.deliveryDate,
|
|
229
|
+
duration: {
|
|
230
|
+
value: PROTECTION_CLAIM_WINDOW_HOURS.sale,
|
|
231
|
+
unit: "hours",
|
|
232
|
+
calendar: "calendar",
|
|
233
|
+
},
|
|
234
|
+
precondition: (o) => Boolean(o.deliveryDate) && Boolean(o.saleProtectionFee),
|
|
235
|
+
onDue: "act",
|
|
236
|
+
publishedCopyKey: "obligations.claimSale",
|
|
237
|
+
describe: "The sale-protection claim window has closed.",
|
|
238
|
+
},
|
|
239
|
+
/*
|
|
240
|
+
* ── COMPLETION ───────────────────────────────────────────────────────────
|
|
241
|
+
*/
|
|
242
|
+
/**
|
|
243
|
+
* 7 days after delivery with no issue raised: the order is COMPLETE, points
|
|
244
|
+
* are awarded, and no NEW dispute may be opened.
|
|
245
|
+
*
|
|
246
|
+
* 🛑 Owner's requirement, 2026-10-21. Two things about it need stating
|
|
247
|
+
* because getting either wrong changes the product:
|
|
248
|
+
*
|
|
249
|
+
* **1. "No further disputes entertained" means NO NEW DISPUTE MAY BE RAISED
|
|
250
|
+
* — it cannot mean "everything closes at 7 days".** A return requested on
|
|
251
|
+
* day 6 still has to be approved, posted, received and inspected, which takes
|
|
252
|
+
* weeks; and `claim-sale`'s window is itself 168h = exactly 7 days. Reading
|
|
253
|
+
* it as a hard close would cancel in-flight returns the buyer raised in time,
|
|
254
|
+
* which is the opposite of what a dispute window is for. So this fires a
|
|
255
|
+
* CUTOFF for new claims and leaves open ones running — and `precondition`
|
|
256
|
+
* below is what makes that true, by refusing to fire while anything is still
|
|
257
|
+
* in flight.
|
|
258
|
+
*
|
|
259
|
+
* **2. The award must be idempotent, because completion has two triggers.**
|
|
260
|
+
* The other is the buyer posting a review, which is event-driven and needs no
|
|
261
|
+
* timer. Whichever happens first completes the order. A ledger entry keyed on
|
|
262
|
+
* the order is what stops a review on day 3 and this sweep on day 7 awarding
|
|
263
|
+
* twice — `pointsLedger` is append-only, so an un-keyed second append would
|
|
264
|
+
* be a permanent double-credit with no way to net it off.
|
|
265
|
+
*
|
|
266
|
+
* 🛑 **`act`, but the points award is NOT built yet** — Phase P is step 16.
|
|
267
|
+
* Firing this today closes the dispute window and records the completion;
|
|
268
|
+
* the ledger write is added when Phase P lands, and the sweep's own narrowing
|
|
269
|
+
* (see `performAction`) keeps anything monetary escalating until then.
|
|
270
|
+
*
|
|
271
|
+
* 🛑 **One open question I am NOT guessing at**, recorded in the plan: the
|
|
272
|
+
* requirement says *"if no review no points"* AND *"they are also assigned if
|
|
273
|
+
* the user [raises] no issues… after 7 days"*. Those two cannot both hold —
|
|
274
|
+
* the second awards points to a buyer who never reviewed. Implemented as
|
|
275
|
+
* "review OR 7-day silence", which is what *"they are ALSO assigned"* says,
|
|
276
|
+
* and flagged for confirmation.
|
|
277
|
+
*/
|
|
278
|
+
"order-complete": {
|
|
279
|
+
anchor: (o) => o.deliveryDate,
|
|
280
|
+
duration: { value: 7, unit: "days", calendar: "calendar" },
|
|
281
|
+
warnAt: [{ value: 2, unit: "days", calendar: "calendar" }],
|
|
282
|
+
/*
|
|
283
|
+
* Refuses to fire while ANY issue is live: a return in any state, a refund
|
|
284
|
+
* already posted, or a buyer-raised dispute. That is what makes the cutoff
|
|
285
|
+
* safe — it can only close a window that nothing is currently using.
|
|
286
|
+
*/
|
|
287
|
+
precondition: (o) => Boolean(o.deliveryDate) &&
|
|
288
|
+
/*
|
|
289
|
+
* 🛑 `!o.completedAt` — the BUYER may have completed it already.
|
|
290
|
+
*
|
|
291
|
+
* The owner's fast path (`markOrderCompleteAction`: rate an item, mark
|
|
292
|
+
* complete, immediate disburse) sets `completedAt` and closes this
|
|
293
|
+
* obligation as `met`. Without this clause the sweep would still consider
|
|
294
|
+
* the order due at day 7 and fire a second completion — re-closing a
|
|
295
|
+
* closed window and, once Phase P lands, awarding points a second time
|
|
296
|
+
* against an append-only ledger.
|
|
297
|
+
*
|
|
298
|
+
* Belt and braces with the obligation already being closed, deliberately:
|
|
299
|
+
* the row is an index and the order is the truth, so the truth gets a say.
|
|
300
|
+
*/
|
|
301
|
+
!o.completedAt &&
|
|
302
|
+
notTerminal(o) &&
|
|
303
|
+
o.status !== "return_requested" &&
|
|
304
|
+
o.status !== "returned" &&
|
|
305
|
+
!o.returnRequestedAt &&
|
|
306
|
+
!o.disputeRaised &&
|
|
307
|
+
(o.refunds?.length ?? 0) === 0,
|
|
308
|
+
onDue: "act",
|
|
309
|
+
settingPath: "returns.disputeWindowDays",
|
|
310
|
+
publishedCopyKey: "obligations.orderComplete",
|
|
311
|
+
describe: "7 days passed since delivery with no issue raised — the order is complete and the dispute window has closed.",
|
|
312
|
+
},
|
|
313
|
+
/*
|
|
314
|
+
* ── MONEY OUT ────────────────────────────────────────────────────────────
|
|
315
|
+
*/
|
|
316
|
+
/**
|
|
317
|
+
* When the seller's funds become payable.
|
|
318
|
+
*
|
|
319
|
+
* 🛑 `escalate`, NOT `act`, and the asymmetry is the rule: **withholding a
|
|
320
|
+
* payout is always safe; releasing one never is.** This obligation's firing
|
|
321
|
+
* means "the quiet period has elapsed" — it does not itself pay anybody.
|
|
322
|
+
* The payout job reads the same cutoff and decides.
|
|
323
|
+
*
|
|
324
|
+
* `blockedBy` is the quiet-period invariant in declarative form: an order's
|
|
325
|
+
* funds are payable only when every obligation that could still move money
|
|
326
|
+
* OUT of it is closed.
|
|
327
|
+
*/
|
|
328
|
+
"payout-release": {
|
|
329
|
+
anchor: (o) => o.deliveryDate,
|
|
330
|
+
duration: { value: 7, unit: "days", calendar: "business" },
|
|
331
|
+
precondition: (o) => Boolean(o.deliveryDate) && notTerminal(o),
|
|
332
|
+
/*
|
|
333
|
+
* 🛑 `order-complete` is in this list, and it is the one that makes the
|
|
334
|
+
* quiet period mean what it says. The others close individual windows; the
|
|
335
|
+
* dispute cutoff is the fact that NO NEW claim can arrive. Releasing a
|
|
336
|
+
* payout before it has fired means paying a seller while a buyer can still
|
|
337
|
+
* raise a dispute against that order — which is how a chargeback lands
|
|
338
|
+
* after the money has left.
|
|
339
|
+
*/
|
|
340
|
+
blockedBy: [
|
|
341
|
+
"return-respond",
|
|
342
|
+
"return-post",
|
|
343
|
+
"return-receive",
|
|
344
|
+
"return-inspect",
|
|
345
|
+
"claim-shipping",
|
|
346
|
+
"claim-sale",
|
|
347
|
+
"order-complete",
|
|
348
|
+
],
|
|
349
|
+
onDue: "escalate",
|
|
350
|
+
settingPath: "commissions.payoutHoldDays",
|
|
351
|
+
publishedCopyKey: "obligations.payoutRelease",
|
|
352
|
+
describe: "The payout quiet period has elapsed for this order.",
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
/**
|
|
356
|
+
* Minutes a duration represents, for the non-business case.
|
|
357
|
+
*
|
|
358
|
+
* Exported so the sweep and `deadlinesFor` share one conversion rather than
|
|
359
|
+
* two — and `PAYMENT_WINDOW_MINUTES` is imported here purely so the one
|
|
360
|
+
* pre-existing minute-scale window in the system is expressible in the same
|
|
361
|
+
* vocabulary if it is ever folded in.
|
|
362
|
+
*/
|
|
363
|
+
export function durationToMinutes(d) {
|
|
364
|
+
switch (d.unit) {
|
|
365
|
+
case "minutes":
|
|
366
|
+
return d.value;
|
|
367
|
+
case "hours":
|
|
368
|
+
return d.value * 60;
|
|
369
|
+
case "days":
|
|
370
|
+
return d.value * 24 * 60;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
/** The payment window, in this registry's vocabulary. Not yet an obligation kind. */
|
|
374
|
+
export const PAYMENT_WINDOW_AS_DURATION = {
|
|
375
|
+
value: PAYMENT_WINDOW_MINUTES,
|
|
376
|
+
unit: "minutes",
|
|
377
|
+
calendar: "calendar",
|
|
378
|
+
};
|