@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
|
@@ -13,6 +13,9 @@ import { ADMIN_USER_STATUS_TABS, ADMIN_USER_ROLE_TABS } from "../constants/filte
|
|
|
13
13
|
import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData.js";
|
|
14
14
|
import { DataListingView } from "./DataListingView.js";
|
|
15
15
|
import { apiClient } from "../../../http/index.js";
|
|
16
|
+
import { QuickFormDrawer } from "../../shell/QuickFormDrawer.js";
|
|
17
|
+
import { pointsAdjustFormSchema } from "../../points/schemas/adjust-form.js";
|
|
18
|
+
import { POINTS_ADJUSTMENT_MAX, POINTS_ADJUSTMENT_REASON_MIN, } from "../../points/schemas/firestore.js";
|
|
16
19
|
import { AdminUserEditorView } from "./AdminUserEditorView.js";
|
|
17
20
|
import { useBulkAction } from "../../../react/index.js";
|
|
18
21
|
import { useBulkEvent } from "../../events/hooks/useBulkEvent.js";
|
|
@@ -28,6 +31,7 @@ export function AdminUsersView({ children, ...props }) {
|
|
|
28
31
|
const [banModalOpen, setBanModalOpen] = useState(false);
|
|
29
32
|
const [banTargetId, setBanTargetId] = useState(null);
|
|
30
33
|
const [banReason, setBanReason] = useState("");
|
|
34
|
+
const [pointsTargetId, setPointsTargetId] = useState(null);
|
|
31
35
|
const [bulkConfirmTarget, setBulkConfirmTarget] = useState(null);
|
|
32
36
|
// Hard-ban runs as a Firebase Function job (cascade across address/payment
|
|
33
37
|
// clusters can exceed the Vercel Hobby 10s ceiling) — the route only
|
|
@@ -80,6 +84,25 @@ export function AdminUsersView({ children, ...props }) {
|
|
|
80
84
|
void queryClient.invalidateQueries({ queryKey: ["admin", "users", "listing"] });
|
|
81
85
|
},
|
|
82
86
|
});
|
|
87
|
+
/*
|
|
88
|
+
* Points adjustment (D48). `useApiMutation` is the house idiom here — it owns
|
|
89
|
+
* the loading toast and the error toast, so this only has to say what success
|
|
90
|
+
* means and what to invalidate.
|
|
91
|
+
*
|
|
92
|
+
* 🛑 Invalidates the users listing because `pointsBalance` lives on the user
|
|
93
|
+
* document, so the row an admin is looking at is stale the moment this
|
|
94
|
+
* succeeds. Without it the operator applies a credit and sees the old number,
|
|
95
|
+
* which reads as the adjustment having failed.
|
|
96
|
+
*/
|
|
97
|
+
const adjustPoints = useApiMutation({
|
|
98
|
+
errorMessage: "Failed to adjust points.",
|
|
99
|
+
mutationFn: (payload) => apiClient.post(ADMIN_ENDPOINTS.POINTS, payload),
|
|
100
|
+
onSuccess: (data) => {
|
|
101
|
+
toast.showToast(`Points adjusted. New balance: ${data.balance}.`, "success");
|
|
102
|
+
setPointsTargetId(null);
|
|
103
|
+
void queryClient.invalidateQueries({ queryKey: ["admin", "users", "listing"] });
|
|
104
|
+
},
|
|
105
|
+
});
|
|
83
106
|
const handleManageBulkClick = (selection) => {
|
|
84
107
|
const rowId = selection.selectedIds[0];
|
|
85
108
|
const row = selection.rows.find((r) => r.id === rowId) ?? null;
|
|
@@ -221,6 +244,10 @@ export function AdminUsersView({ children, ...props }) {
|
|
|
221
244
|
onClick: () => unbanUser.mutate(row.id),
|
|
222
245
|
disabled: !isBanned || unbanUser.isPending,
|
|
223
246
|
},
|
|
247
|
+
{
|
|
248
|
+
label: ACTIONS.ADMIN["adjust-user-points"].label,
|
|
249
|
+
onClick: () => setPointsTargetId(row.id),
|
|
250
|
+
},
|
|
224
251
|
] }));
|
|
225
252
|
},
|
|
226
253
|
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_USER_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Role", tabs: ADMIN_USER_ROLE_TABS, value: pendingFilters.role ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, role: id })) })] })),
|
|
@@ -282,5 +309,49 @@ export function AdminUsersView({ children, ...props }) {
|
|
|
282
309
|
.finally(() => setBulkConfirmTarget(null));
|
|
283
310
|
}, children: bulkConfirmTarget?.action === "suspend"
|
|
284
311
|
? ACTIONS.ADMIN["bulk-suspend-users"].confirmation.confirmLabel
|
|
285
|
-
: ACTIONS.ADMIN["bulk-delete-users"].confirmation.confirmLabel })] })] })] }
|
|
312
|
+
: ACTIONS.ADMIN["bulk-delete-users"].confirmation.confirmLabel })] })] }), _jsx(QuickFormDrawer, { isOpen: Boolean(pointsTargetId), onClose: () => setPointsTargetId(null), title: ACTIONS.ADMIN["adjust-user-points"].label, schema: pointsAdjustFormSchema, submitLabel: "Apply adjustment", defaultValues: { userId: pointsTargetId ?? "" }, fields: [
|
|
313
|
+
/*
|
|
314
|
+
* `userId` is carried as a hidden-in-practice text field rather than
|
|
315
|
+
* injected at submit time, so the schema validates the SAME object
|
|
316
|
+
* the route will receive. Pre-filled from the row and not meant to be
|
|
317
|
+
* retyped.
|
|
318
|
+
*/
|
|
319
|
+
{
|
|
320
|
+
name: "userId",
|
|
321
|
+
label: "User ID",
|
|
322
|
+
type: "text",
|
|
323
|
+
required: true,
|
|
324
|
+
helperText: "Pre-filled from the row you opened.",
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
name: "amount",
|
|
328
|
+
label: "Points (negative to debit)",
|
|
329
|
+
type: "number",
|
|
330
|
+
required: true,
|
|
331
|
+
helperText: `1 point = ₹1 against a future order. Whole numbers, up to ${POINTS_ADJUSTMENT_MAX} per adjustment.`,
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
name: "description",
|
|
335
|
+
label: "What the buyer sees",
|
|
336
|
+
type: "text",
|
|
337
|
+
required: true,
|
|
338
|
+
placeholder: "Goodwill credit for the delayed BX-53 order",
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
name: "adminReason",
|
|
342
|
+
label: "Internal reason",
|
|
343
|
+
type: "textarea",
|
|
344
|
+
required: true,
|
|
345
|
+
helperText: `At least ${POINTS_ADJUSTMENT_REASON_MIN} characters. Never shown to the buyer — the reader is the next admin.`,
|
|
346
|
+
},
|
|
347
|
+
], isLoading: adjustPoints.isPending, onSubmit: (values) => {
|
|
348
|
+
/*
|
|
349
|
+
* 🛑 The cast is to the SCHEMA'S OWN inferred type, and it is safe
|
|
350
|
+
* only because `schema={pointsAdjustFormSchema}` is passed above —
|
|
351
|
+
* the drawer will not call this until that parse succeeds, so the
|
|
352
|
+
* shape is already proven. `FormValues` is an index-signature bag,
|
|
353
|
+
* which is why a cast is needed at all rather than a widening.
|
|
354
|
+
*/
|
|
355
|
+
adjustPoints.mutate(values);
|
|
356
|
+
} })] }));
|
|
286
357
|
}
|
|
@@ -5,6 +5,8 @@ interface AdminViewCardsProps {
|
|
|
5
5
|
view: "grid" | "list";
|
|
6
6
|
isLoading?: boolean;
|
|
7
7
|
emptyLabel?: string;
|
|
8
|
+
/** Rendered beneath `emptyLabel` when there are no rows — see DataTable's note. */
|
|
9
|
+
emptyAction?: React.ReactNode;
|
|
8
10
|
onRowClick?: (row: AdminListingScaffoldRow) => void;
|
|
9
11
|
selectedIdSet?: Set<string>;
|
|
10
12
|
onToggleSelect?: (id: string) => void;
|
|
@@ -13,5 +15,5 @@ interface AdminViewCardsProps {
|
|
|
13
15
|
/** Resource-type emoji fallback for rows without their own `image` (e.g. 👤 for users, 🏪 for stores). */
|
|
14
16
|
resourceIcon?: string;
|
|
15
17
|
}
|
|
16
|
-
export declare function AdminViewCards({ rows, view, isLoading, emptyLabel, onRowClick, selectedIdSet, onToggleSelect, renderRowActions, resourceIcon, }: AdminViewCardsProps): React.JSX.Element;
|
|
18
|
+
export declare function AdminViewCards({ rows, view, isLoading, emptyLabel, emptyAction, onRowClick, selectedIdSet, onToggleSelect, renderRowActions, resourceIcon, }: AdminViewCardsProps): React.JSX.Element;
|
|
17
19
|
export {};
|
|
@@ -58,7 +58,7 @@ function AdminCardItem({ row, view, selected, onToggleSelect, onRowClick, render
|
|
|
58
58
|
e.stopPropagation();
|
|
59
59
|
if (e.target === e.currentTarget)
|
|
60
60
|
onToggleSelect(row.id);
|
|
61
|
-
}, children: _jsx(Checkbox, { bare: true, checked: selected, onChange: () => onToggleSelect(row.id), className: "h-4 w-4 rounded border-zinc-300 text-primary accent-primary cursor-pointer", "aria-label": `Select ${row.primary}` }) })), _jsx(RowAvatar, { image: row.image, alt: row.primary, icon: resourceIcon }), _jsxs(Stack, { gap: "none", className: "flex-1 min-w-0", children: [_jsx(Text, { size: "sm", weight: "semibold",
|
|
61
|
+
}, children: _jsx(Checkbox, { bare: true, checked: selected, onChange: () => onToggleSelect(row.id), className: "h-4 w-4 rounded border-zinc-300 text-primary accent-primary cursor-pointer", "aria-label": `Select ${row.primary}` }) })), _jsx(RowAvatar, { image: row.image, alt: row.primary, icon: resourceIcon }), _jsxs(Stack, { gap: "none", className: "flex-1 min-w-0", children: [_jsx(Text, { size: "sm", weight: "semibold", truncate: 2, color: "primary", children: row.primary }), _jsx(Text, { size: "xs", color: "muted", className: "truncate", children: row.secondary }), (row.barcodeId || row.boxCode) && (_jsx(Text, { size: "xs", color: "faint", className: "truncate font-mono", children: [row.barcodeId, row.boxCode].filter(Boolean).join(" · ") })), _jsx(Div, { className: "sm:hidden mt-1", children: _jsx(StatusBadge, { status: row.status }) })] }), flags.length > 0 && (_jsx(Row, { gap: "xs", className: "hidden sm:flex shrink-0", children: flags.map(({ key, label, tone }) => (_jsx(Span, { padding: "pill-2xs", weight: "medium", className: `inline-flex text-[10px] ${STATUS_TONE_CLASSES[tone]}`, rounded: "full", children: label }, key))) })), _jsx(Div, { className: "hidden sm:block shrink-0", children: _jsx(StatusBadge, { status: row.status }) }), _jsx(Span, { size: "xs", color: "muted", className: "hidden sm:block shrink-0 w-24", align: "end", children: row.updatedAt }), renderRowActions && (_jsx(Div, { "data-no-row-click": true, className: "shrink-0", onClick: (e) => e.stopPropagation(), children: renderRowActions(row) }))] }));
|
|
62
62
|
}
|
|
63
63
|
return (_jsxs(Div, { rounded: "xl", className: [
|
|
64
64
|
"border overflow-hidden cursor-pointer transition-all hover:shadow-md hover:-translate-y-0.5",
|
|
@@ -71,9 +71,9 @@ function AdminCardItem({ row, view, selected, onToggleSelect, onRowClick, render
|
|
|
71
71
|
e.stopPropagation();
|
|
72
72
|
if (e.target === e.currentTarget)
|
|
73
73
|
onToggleSelect(row.id);
|
|
74
|
-
}, children: [_jsx(Checkbox, { bare: true, checked: selected, onChange: () => onToggleSelect(row.id), className: "h-4 w-4 rounded border-zinc-300 text-primary accent-primary cursor-pointer", "aria-label": `Select ${row.primary}` }), flags.length > 0 && (_jsx(Row, { gap: "xs", wrap: true, children: flags.map(({ key, label, tone }) => (_jsx(Span, { padding: "pill-2xs", weight: "medium", className: `inline-flex text-[10px] ${STATUS_TONE_CLASSES[tone]}`, rounded: "full", children: label }, key))) }))] })), _jsxs(Stack, { gap: "xs", className: `${__P.p3}.5`, children: [_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(RowAvatar, { image: row.image, alt: row.primary, icon: resourceIcon, size: "12" }), _jsxs(Stack, { gap: "none", className: "min-w-0 flex-1", children: [_jsx(Text, { size: "sm", weight: "semibold", className: "line-clamp-2 leading-snug", color: "primary", children: row.primary }), _jsx(Text, { size: "xs", color: "muted", className: "truncate", children: row.secondary })] })] }), row.barcodeId && (_jsx(Text, { size: "xs", color: "faint", className: "truncate font-mono", children: row.barcodeId })), _jsxs(Row, { justify: "between", gap: "xs", children: [_jsx(StatusBadge, { status: row.status }), _jsx(Span, { color: "muted", className: "text-[11px] shrink-0", children: row.updatedAt })] }), renderRowActions && (_jsx(Row, { "data-no-row-click": true, justify: "end", gap: "xs", className: "border-t border-[var(--appkit-color-border)] -mx-[var(--appkit-space-3)] -mb-[var(--appkit-space-3)] mt-[var(--appkit-space-1)] px-[var(--appkit-space-3)] py-[var(--appkit-space-2)]", onClick: (e) => e.stopPropagation(), children: renderRowActions(row) }))] })] }));
|
|
74
|
+
}, children: [_jsx(Checkbox, { bare: true, checked: selected, onChange: () => onToggleSelect(row.id), className: "h-4 w-4 rounded border-zinc-300 text-primary accent-primary cursor-pointer", "aria-label": `Select ${row.primary}` }), flags.length > 0 && (_jsx(Row, { gap: "xs", wrap: true, children: flags.map(({ key, label, tone }) => (_jsx(Span, { padding: "pill-2xs", weight: "medium", className: `inline-flex text-[10px] ${STATUS_TONE_CLASSES[tone]}`, rounded: "full", children: label }, key))) }))] })), _jsxs(Stack, { gap: "xs", className: `${__P.p3}.5`, children: [_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(RowAvatar, { image: row.image, alt: row.primary, icon: resourceIcon, size: "12" }), _jsxs(Stack, { gap: "none", className: "min-w-0 flex-1", children: [_jsx(Text, { size: "sm", weight: "semibold", className: "line-clamp-2 leading-snug", color: "primary", children: row.primary }), _jsx(Text, { size: "xs", color: "muted", className: "truncate", children: row.secondary })] })] }), (row.barcodeId || row.boxCode) && (_jsx(Text, { size: "xs", color: "faint", className: "truncate font-mono", children: [row.barcodeId, row.boxCode].filter(Boolean).join(" · ") })), _jsxs(Row, { justify: "between", gap: "xs", children: [_jsx(StatusBadge, { status: row.status }), _jsx(Span, { color: "muted", className: "text-[11px] shrink-0", children: row.updatedAt })] }), renderRowActions && (_jsx(Row, { "data-no-row-click": true, justify: "end", gap: "xs", className: "border-t border-[var(--appkit-color-border)] -mx-[var(--appkit-space-3)] -mb-[var(--appkit-space-3)] mt-[var(--appkit-space-1)] px-[var(--appkit-space-3)] py-[var(--appkit-space-2)]", onClick: (e) => e.stopPropagation(), children: renderRowActions(row) }))] })] }));
|
|
75
75
|
}
|
|
76
|
-
export function AdminViewCards({ rows, view, isLoading, emptyLabel = "No items found", onRowClick, selectedIdSet, onToggleSelect, renderRowActions, resourceIcon, }) {
|
|
76
|
+
export function AdminViewCards({ rows, view, isLoading, emptyLabel = "No items found", emptyAction, onRowClick, selectedIdSet, onToggleSelect, renderRowActions, resourceIcon, }) {
|
|
77
77
|
if (isLoading) {
|
|
78
78
|
const count = view === "grid" ? 12 : 8;
|
|
79
79
|
if (view === "list") {
|
|
@@ -82,7 +82,7 @@ export function AdminViewCards({ rows, view, isLoading, emptyLabel = "No items f
|
|
|
82
82
|
return (_jsx(Grid, { cols: "cards", gap: "md", children: Array.from({ length: count }).map((_, i) => _jsx(SkeletonCard, { view: "grid" }, i)) }));
|
|
83
83
|
}
|
|
84
84
|
if (rows.length === 0) {
|
|
85
|
-
return (
|
|
85
|
+
return (_jsxs(Stack, { paddingY: "y-3xl", gap: "md", align: "center", children: [_jsx(Text, { size: "sm", color: "muted", align: "center", children: emptyLabel }), emptyAction] }));
|
|
86
86
|
}
|
|
87
87
|
if (view === "list") {
|
|
88
88
|
return (_jsx(Div, { rounded: "xl", border: "subtle", className: `${__O.hidden} divide-y divide-zinc-100 divide-[var(--appkit-color-border)]`, children: rows.map((row) => (_jsx(AdminCardItem, { row: row, view: "list", selected: selectedIdSet?.has(row.id) ?? false, onToggleSelect: onToggleSelect, onRowClick: onRowClick, renderRowActions: renderRowActions, resourceIcon: resourceIcon }, row.id))) }));
|
|
@@ -39,6 +39,26 @@ export interface AdminListingScaffoldRow {
|
|
|
39
39
|
isOnSale?: boolean;
|
|
40
40
|
isSold?: boolean;
|
|
41
41
|
barcodeId?: string;
|
|
42
|
+
/**
|
|
43
|
+
* The storage box this row's item is in — `AC60-03`.
|
|
44
|
+
*
|
|
45
|
+
* 🛑 OPTIONAL, AND ONLY A ROW MAPPER THAT ACTUALLY HAS IT MAY SET IT. A
|
|
46
|
+
* placement lives in `inventoryPlacements`, keyed `{boxId}__{productId}`, so
|
|
47
|
+
* a listing query does not carry it for free: a mapper that wants this
|
|
48
|
+
* column has to join, and most should not — one placements read per row is
|
|
49
|
+
* an N+1 against Rule #6's ~3-round-trip budget.
|
|
50
|
+
*
|
|
51
|
+
* It is declared here rather than left to a bespoke column because "where
|
|
52
|
+
* is this thing" is the question a warehouse screen exists to answer, and
|
|
53
|
+
* the alternative was the free-text `physicalLocation` that was retired for
|
|
54
|
+
* being three uncontrolled inputs nothing could group.
|
|
55
|
+
*
|
|
56
|
+
* 🛑 A row with no placement renders NOTHING here, never "—" and never
|
|
57
|
+
* "Unknown". An item that has not been put in a box yet and an item whose
|
|
58
|
+
* placement we failed to read are different facts, and a dash asserts the
|
|
59
|
+
* first about both.
|
|
60
|
+
*/
|
|
61
|
+
boxCode?: string;
|
|
42
62
|
/** Row photo/thumbnail (avatar, product image, store logo, cover image, etc). Falls back to the table's resource icon when omitted. */
|
|
43
63
|
image?: string;
|
|
44
64
|
}
|
|
@@ -78,6 +98,14 @@ export interface ListingViewConfig<TResponse, TRow extends {
|
|
|
78
98
|
portal: "admin" | "seller" | "user";
|
|
79
99
|
title: string;
|
|
80
100
|
subtitle?: string;
|
|
101
|
+
/**
|
|
102
|
+
* Suppress the `<h1>` this view renders from `title`.
|
|
103
|
+
*
|
|
104
|
+
* For the handful of pages that already author their own heading — a second
|
|
105
|
+
* `<h1>` is a worse outline than none, which is the defect this header was
|
|
106
|
+
* added to fix. Everything else leaves it unset and gets the heading.
|
|
107
|
+
*/
|
|
108
|
+
hideHeader?: boolean;
|
|
81
109
|
/**
|
|
82
110
|
* Search box copy. OPTIONAL — declaring it is what makes the box exist.
|
|
83
111
|
*
|
|
@@ -1,7 +1,30 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* DataListingView — config-driven listing scaffold for admin + seller dashboards.
|
|
5
|
+
*
|
|
6
|
+
* Replaces ~30 hand-wired admin/seller view components (AdminBrandsView, AdminCategoriesView,
|
|
7
|
+
* SellerCouponsView, etc.) by accepting a single `config` object describing:
|
|
8
|
+
* - data layer (endpoint, queryKey, mapRows, buildFilters, sortOptions)
|
|
9
|
+
* - presentation (title, subtitle, columns, searchPlaceholder, emptyLabel)
|
|
10
|
+
* - actions (bulkActions, primaryAction, renderRowActions)
|
|
11
|
+
* - filter panel (renderFilterPanel)
|
|
12
|
+
* - editor side-drawer (renderEditor)
|
|
13
|
+
*
|
|
14
|
+
* Composes: useAdminListing (data + state) + ListingToolbar + BulkActionBar + Pagination
|
|
15
|
+
* + DataTable | AdminViewCards + ListingFilterDrawer + SideDrawer
|
|
16
|
+
* + useBottomActions (mobile bulk bar)
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* const ADMIN_BRANDS_CONFIG: ListingViewConfig<BrandsResponse, BrandRow> = { ... };
|
|
20
|
+
* <DataListingView config={ADMIN_BRANDS_CONFIG} />
|
|
21
|
+
*
|
|
22
|
+
* Created 2026-05-23 as part of W6-12 (plan: read-this-files-and-modular-storm.md).
|
|
23
|
+
*/
|
|
24
|
+
import React from "react";
|
|
3
25
|
import { Plus } from "lucide-react";
|
|
4
|
-
import { BulkActionBar, Button, Div, ListingFilterDrawer, ListingToolbar, Pagination, Row, SideDrawer, StickyToolbar, TextLink } from "../../../ui/index.js";
|
|
26
|
+
import { BulkActionBar, Button, Div, Heading, Icon, IconButton, ListingFilterDrawer, ListingToolbar, Pagination, Row, SideDrawer, Stack, StickyToolbar, Text, TextLink } from "../../../ui/index.js";
|
|
27
|
+
import { ROW_ACTION_ID, ROW_ACTION_META } from "../../products/constants/action-defs.js";
|
|
5
28
|
import { useBottomActions } from "../../layout/index.js";
|
|
6
29
|
import { useAdminListing } from "../hooks/useAdminListing.js";
|
|
7
30
|
import { getResourceIcon } from "../../../ui/columns/column-renderers.js";
|
|
@@ -44,6 +67,55 @@ export function DataListingView({ config, }) {
|
|
|
44
67
|
openEditPanel: panel.openEditPanel,
|
|
45
68
|
};
|
|
46
69
|
const resourceIcon = getResourceIcon(typeof config.queryKey[1] === "string" ? config.queryKey[1] : undefined);
|
|
70
|
+
/**
|
|
71
|
+
* ONE renderer for `config.primaryAction`, used by the toolbar AND by the
|
|
72
|
+
* empty state.
|
|
73
|
+
*
|
|
74
|
+
* 🛑 WHY THIS EXISTS: the action was rendered in the toolbar and nowhere
|
|
75
|
+
* else, so an admin landing on an EMPTY list was told "No X found" with no
|
|
76
|
+
* way forward — while the button that creates the first record sat in a
|
|
77
|
+
* toolbar above an empty table. 27 of ~84 listing configs declare a
|
|
78
|
+
* `primaryAction`; all 27 had that hole.
|
|
79
|
+
*
|
|
80
|
+
* 🛑 And it is a FUNCTION rather than two copies, because the alternative is
|
|
81
|
+
* the shape Root Cause #75 is about: a rule spelled out twice, where the
|
|
82
|
+
* copy made first learns nothing when the other gains a branch. The `href`
|
|
83
|
+
* vs `onClick` split and the `asChild` anchor are exactly the sort of detail
|
|
84
|
+
* that would drift.
|
|
85
|
+
*
|
|
86
|
+
* `size` is the only difference between the two placements — a toolbar
|
|
87
|
+
* button is `sm`, and an empty state is the one thing on screen, so it gets
|
|
88
|
+
* the default.
|
|
89
|
+
*/
|
|
90
|
+
/*
|
|
91
|
+
* 🛑 TWO DIFFERENT EMPTY STATES, and offering the wrong action is worse than
|
|
92
|
+
* offering none.
|
|
93
|
+
*
|
|
94
|
+
* "No products found" with a New Product button is right on a fresh install
|
|
95
|
+
* and actively misleading when the admin has just filtered to
|
|
96
|
+
* `status=archived` — there may be a thousand products, and the useful move
|
|
97
|
+
* is to clear the filter, not to create a thousand-and-first. `resetAll`
|
|
98
|
+
* and `hasActiveState` already exist for the toolbar's own Reset control,
|
|
99
|
+
* so this needs no new state and cannot disagree with it.
|
|
100
|
+
*/
|
|
101
|
+
const emptyIsFiltered = hasActiveState;
|
|
102
|
+
const emptyLabel = config.emptyLabel ?? `No ${config.title.toLowerCase()} found`;
|
|
103
|
+
const resolvedEmptyLabel = emptyIsFiltered
|
|
104
|
+
? `No ${config.title.toLowerCase()} match these filters`
|
|
105
|
+
: emptyLabel;
|
|
106
|
+
const renderPrimaryAction = (size) => {
|
|
107
|
+
const action = config.primaryAction;
|
|
108
|
+
if (!action)
|
|
109
|
+
return null;
|
|
110
|
+
const icon = action.icon ?? _jsx(Plus, { className: "h-4 w-4" });
|
|
111
|
+
return action.href ? (
|
|
112
|
+
// `asChild` so a create PAGE is a real anchor — middle-click and
|
|
113
|
+
// copy-link work, and it is not a button that happens to navigate (a
|
|
114
|
+
// Known-TS-Pattern this repo bans outright).
|
|
115
|
+
_jsx(Button, { gap: "sm", size: size, asChild: true, children: _jsxs(TextLink, { variant: "inherit", href: action.href, children: [icon, action.label] }) })) : (_jsxs(Button, { gap: "sm", size: size, onClick: () => action.onClick?.({ openCreatePanel: panel.openCreatePanel }), children: [icon, action.label] }));
|
|
116
|
+
};
|
|
117
|
+
/** What an empty list offers: clear the filters, or create the first record. */
|
|
118
|
+
const renderEmptyAction = () => emptyIsFiltered ? (_jsx(Button, { variant: "secondary", onClick: resetAll, children: "Clear filters" })) : (renderPrimaryAction());
|
|
47
119
|
const bulkActionItems = config.buildBulkActions?.(selectionContext);
|
|
48
120
|
// Mobile bulk bar
|
|
49
121
|
useBottomActions(selection.selectedCount > 0 && bulkActionItems
|
|
@@ -85,20 +157,109 @@ export function DataListingView({ config, }) {
|
|
|
85
157
|
window.location.href = href;
|
|
86
158
|
}
|
|
87
159
|
: undefined;
|
|
160
|
+
/*
|
|
161
|
+
* ── A DIRECT EDIT AFFORDANCE ON EVERY ROW ─────────────────────────────────
|
|
162
|
+
*
|
|
163
|
+
* Measured across 16 admin and store listings: ZERO edit links in rows and
|
|
164
|
+
* zero pencils on admin. Editing meant opening a `⋯` menu first — two
|
|
165
|
+
* clicks and a hidden menu for the single most common action an admin
|
|
166
|
+
* performs. `/store/products` had 15 pencil-labelled buttons and admin had
|
|
167
|
+
* none, so the two halves of the product did not even agree.
|
|
168
|
+
*
|
|
169
|
+
* Composed here rather than added to each config because ~105 listing
|
|
170
|
+
* screens render through this one component, and both row renderers
|
|
171
|
+
* (`DataTable` and `AdminViewCards`) already drop `renderRowActions` into a
|
|
172
|
+
* click-stopped container — so one wrapper reaches every surface.
|
|
173
|
+
*
|
|
174
|
+
* 🛑 ONLY WHEN AN EDIT TARGET ACTUALLY RESOLVES. This file already carries a
|
|
175
|
+
* long note about `AdminViewCards` once wiring every card to
|
|
176
|
+
* `openEditPanel` whether or not the config had anywhere to go, so cards
|
|
177
|
+
* looked clickable and silently did nothing. A pencil that opens nothing is
|
|
178
|
+
* that same lie with a sharper point on it: the two real targets are a
|
|
179
|
+
* drawer (`renderEditor`) and a route (`rowHrefTemplate`), and a config with
|
|
180
|
+
* neither keeps exactly the actions it declared.
|
|
181
|
+
*
|
|
182
|
+
* 🛑 `onRowClick` is deliberately NOT a target. It is an arbitrary callback
|
|
183
|
+
* — several configs use it to open a *view* modal, toggle a selection, or
|
|
184
|
+
* navigate somewhere that is not an editor — so labelling it "Edit" would
|
|
185
|
+
* promise one thing and do another.
|
|
186
|
+
*
|
|
187
|
+
* Label and icon come from `ROW_ACTION_META[ROW_ACTION_ID.EDIT]` per Rule
|
|
188
|
+
* #7, so this shares one definition with every `⋯` menu's own Edit entry
|
|
189
|
+
* rather than hardcoding a second "Edit" string.
|
|
190
|
+
*/
|
|
191
|
+
const rowEditHref = React.useCallback((row) => {
|
|
192
|
+
const t = config.rowHrefTemplate;
|
|
193
|
+
if (!t)
|
|
194
|
+
return null;
|
|
195
|
+
return typeof t === "function"
|
|
196
|
+
? t(row)
|
|
197
|
+
: t.replace("{id}", encodeURIComponent(row.id));
|
|
198
|
+
}, [config.rowHrefTemplate]);
|
|
199
|
+
const hasEditTarget = Boolean(config.renderEditor ?? config.rowHrefTemplate);
|
|
200
|
+
/*
|
|
201
|
+
* The row's own name for the accessible label, falling back to its id.
|
|
202
|
+
*
|
|
203
|
+
* 🛑 A RUNTIME CHECK, not a cast. `TRow` is only `{ id: string }` here, but
|
|
204
|
+
* every config maps its rows through `mapRows` into `AdminDataRow`, so
|
|
205
|
+
* `primary` is present in practice — which is exactly the situation where
|
|
206
|
+
* `(row as AdminDataRow).primary` would compile and then read `undefined`
|
|
207
|
+
* on the one config that is shaped differently. `typeof` proves it instead.
|
|
208
|
+
*
|
|
209
|
+
* It matters because the alternative is ~50 buttons on a page all announcing
|
|
210
|
+
* "Edit product": a screen-reader user hears the control and not the record,
|
|
211
|
+
* which is the same as having no label at all.
|
|
212
|
+
*/
|
|
213
|
+
const rowEditLabel = React.useCallback((row) => {
|
|
214
|
+
const primary = row.primary;
|
|
215
|
+
const name = typeof primary === "string" && primary.trim() ? primary.trim() : row.id;
|
|
216
|
+
return `${ROW_ACTION_META[ROW_ACTION_ID.EDIT].label} ${name}`;
|
|
217
|
+
}, []);
|
|
218
|
+
/*
|
|
219
|
+
* Hoisted so the memo below depends on plain identifiers.
|
|
220
|
+
* `react-hooks/exhaustive-deps` cannot track a property read inside the
|
|
221
|
+
* nested function the memo RETURNS, so reading `config.x` there asks for
|
|
222
|
+
* `config` itself as a dependency — and `config` is a fresh object on every
|
|
223
|
+
* render for every inline config, which would make the memo recompute each
|
|
224
|
+
* time and hand `DataTable` a new prop identity on every render.
|
|
225
|
+
*/
|
|
226
|
+
const { renderEditor, renderRowActions } = config;
|
|
227
|
+
const { openEditPanel } = panel;
|
|
228
|
+
const renderRowActionsWithEdit = React.useMemo(() => {
|
|
229
|
+
if (!hasEditTarget)
|
|
230
|
+
return renderRowActions;
|
|
231
|
+
return (row) => {
|
|
232
|
+
const href = rowEditHref(row);
|
|
233
|
+
return (_jsxs(Row, { align: "center", justify: "end", gap: "xs", children: [renderEditor ? (_jsx(IconButton, { "aria-label": rowEditLabel(row), size: "sm", variant: "ghost", icon: _jsx(Icon, { name: "edit" }), onClick: () => openEditPanel(row.id) })) : href ? (
|
|
234
|
+
/*
|
|
235
|
+
* A real link, not a click handler calling `window.location`.
|
|
236
|
+
* The row's own navigation still does that — changing it is the
|
|
237
|
+
* Phase 3 chrome work — but a NEW affordance has no reason to
|
|
238
|
+
* inherit a full page load, and a link is also middle-clickable,
|
|
239
|
+
* which is how an admin opens three records to compare them.
|
|
240
|
+
*
|
|
241
|
+
* Conditional rather than falling back to `href="#"`: the branch
|
|
242
|
+
* already guarantees a template exists, so a null here would mean
|
|
243
|
+
* one returned nothing — and a pencil linking to the current page
|
|
244
|
+
* is worse than no pencil.
|
|
245
|
+
*/
|
|
246
|
+
_jsx(TextLink, { href: href, variant: "bare", "aria-label": rowEditLabel(row), children: _jsx(Icon, { name: "edit", size: "sm" }) })) : null, renderRowActions?.(row)] }));
|
|
247
|
+
};
|
|
248
|
+
}, [
|
|
249
|
+
hasEditTarget,
|
|
250
|
+
renderRowActions,
|
|
251
|
+
renderEditor,
|
|
252
|
+
rowEditHref,
|
|
253
|
+
rowEditLabel,
|
|
254
|
+
openEditPanel,
|
|
255
|
+
]);
|
|
88
256
|
// A box exists only when the config asks for one.
|
|
89
257
|
const searchEnabled = Boolean(config.search ?? config.searchPlaceholder);
|
|
90
|
-
return (_jsxs(Div, { className: config.className ?? "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: config.search?.placeholder ?? config.searchPlaceholder,
|
|
258
|
+
return (_jsxs(Div, { className: config.className ?? "min-h-screen", children: [!config.hideHeader && (_jsxs(Stack, { gap: "xs", className: "mb-[var(--appkit-space-4)]", children: [_jsx(Heading, { level: 1, size: "2xl", weight: "bold", children: config.title }), config.subtitle && (_jsx(Text, { size: "sm", color: "muted", children: config.subtitle }))] })), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: config.search?.placeholder ?? config.searchPlaceholder,
|
|
91
259
|
// Passing this is what renders the box — ListingToolbar gates on it.
|
|
92
260
|
// Undefined when the config declares no search, so a view whose
|
|
93
261
|
// endpoint cannot search no longer shows an input that does nothing.
|
|
94
|
-
onSearchChange: searchEnabled ? setSearchInput : undefined, onSearchCommit: searchEnabled ? commitSearch : undefined, sortValue: table.get("sort") || config.defaultSort, sortOptions: config.sortOptions, onSortChange: (v) => table.set("sort", v), showTableView: !config.hideTableView, toggles: config.toggles, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: config.primaryAction || config.toolbarExtra ? (_jsxs(Row, { align: "center", gap: "sm", children: [config.toolbarExtra, config.
|
|
95
|
-
(config.primaryAction.href ? (
|
|
96
|
-
// `asChild` so the create PAGE is a real anchor — middle-click
|
|
97
|
-
// and copy-link work, and it is not a button that happens to
|
|
98
|
-
// navigate (a Known-TS-Pattern this repo bans outright).
|
|
99
|
-
_jsx(Button, { gap: "sm", size: "sm", asChild: true, children: _jsxs(TextLink, { variant: "inherit", href: config.primaryAction.href, children: [config.primaryAction.icon ?? _jsx(Plus, { className: "h-4 w-4" }), config.primaryAction.label] }) })) : (_jsxs(Button, { gap: "sm", size: "sm", onClick: () => config.primaryAction.onClick?.({
|
|
100
|
-
openCreatePanel: panel.openCreatePanel,
|
|
101
|
-
}), children: [config.primaryAction.icon ?? _jsx(Plus, { className: "h-4 w-4" }), config.primaryAction.label] })))] })) : undefined }), config.renderAboveContent?.(), bulkActionItems && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActionItems })), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p), pageSize: pageSize, onPageSizeChange: setPageSize, paginationConfig: { showPageSizeSelector: true, pageSizeOptions: [10, 25, 50, 100] } }) }) })), _jsxs(Div, { paddingX: "x-sm-md", padding: "y-md", children: [errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), view === "table" && !config.hideTableView && !isMobile ? (_jsx(DataTable, { columns: config.columns, rows: rows, resourceIcon: resourceIcon, isLoading: isLoading, emptyLabel: config.emptyLabel ?? `No ${config.title.toLowerCase()} found`, selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next
|
|
262
|
+
onSearchChange: searchEnabled ? setSearchInput : undefined, onSearchCommit: searchEnabled ? commitSearch : undefined, sortValue: table.get("sort") || config.defaultSort, sortOptions: config.sortOptions, onSortChange: (v) => table.set("sort", v), showTableView: !config.hideTableView, toggles: config.toggles, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: config.primaryAction || config.toolbarExtra ? (_jsxs(Row, { align: "center", gap: "sm", children: [config.toolbarExtra, renderPrimaryAction("sm")] })) : undefined }), config.renderAboveContent?.(), bulkActionItems && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: bulkActionItems })), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p), pageSize: pageSize, onPageSizeChange: setPageSize, paginationConfig: { showPageSizeSelector: true, pageSizeOptions: [10, 25, 50, 100] } }) }) })), _jsxs(Div, { paddingX: "x-sm-md", padding: "y-md", children: [errorMessage && (_jsx(Div, { textSize: "sm", className: "mb-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "xl", children: errorMessage })), view === "table" && !config.hideTableView && !isMobile ? (_jsx(DataTable, { columns: config.columns, rows: rows, resourceIcon: resourceIcon, isLoading: isLoading, emptyLabel: resolvedEmptyLabel, emptyAction: renderEmptyAction(), selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next
|
|
102
263
|
? selection.setSelectedIds(rows.map((r) => r.id))
|
|
103
264
|
: selection.clearSelection(), rowHrefTemplate: config.rowHrefTemplate, onRowClick: config.onRowClick
|
|
104
265
|
? (row) => config.onRowClick(row, {
|
|
@@ -106,14 +267,14 @@ export function DataListingView({ config, }) {
|
|
|
106
267
|
})
|
|
107
268
|
: config.renderEditor
|
|
108
269
|
? (row) => panel.openEditPanel(row.id)
|
|
109
|
-
: undefined, renderRowActions:
|
|
270
|
+
: undefined, renderRowActions: renderRowActionsWithEdit })) : config.renderCards ? (
|
|
110
271
|
// The persisted view-mode preference is global, so a "table"
|
|
111
272
|
// value can still reach here on a hideTableView-only view (see
|
|
112
273
|
// the DataTable branch's guard above) — fall back to "grid".
|
|
113
|
-
config.renderCards(rows, view === "table" ? "grid" : view, selectionContext, isLoading)) : (_jsx(AdminViewCards, { rows: rows, view: view === "table" ? "grid" : view, resourceIcon: resourceIcon, isLoading: isLoading, emptyLabel:
|
|
274
|
+
config.renderCards(rows, view === "table" ? "grid" : view, selectionContext, isLoading)) : (_jsx(AdminViewCards, { rows: rows, view: view === "table" ? "grid" : view, resourceIcon: resourceIcon, isLoading: isLoading, emptyLabel: resolvedEmptyLabel, emptyAction: renderEmptyAction(), onRowClick: resolvedRowClick
|
|
114
275
|
? (row) => resolvedRowClick(row)
|
|
115
|
-
: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle, renderRowActions:
|
|
116
|
-
? (row) =>
|
|
276
|
+
: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle, renderRowActions: renderRowActionsWithEdit
|
|
277
|
+
? (row) => renderRowActionsWithEdit(row)
|
|
117
278
|
: undefined }))] }), config.renderFilterPanel && (_jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: config.renderFilterPanel({
|
|
118
279
|
pendingFilters,
|
|
119
280
|
setPendingFilters,
|
|
@@ -24,6 +24,17 @@ interface DataTableProps<T extends {
|
|
|
24
24
|
currentPage?: number;
|
|
25
25
|
onPageChange?: (page: number) => void;
|
|
26
26
|
emptyLabel?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Rendered beneath `emptyLabel` when there are no rows — normally the
|
|
29
|
+
* listing's own create button.
|
|
30
|
+
*
|
|
31
|
+
* 🛑 A ReactNode rather than `{label, href}`: the caller already owns a
|
|
32
|
+
* renderer for its primary action, and a second description of the same
|
|
33
|
+
* button here would be two things to keep in step. Optional, because a
|
|
34
|
+
* listing with nothing to offer should say so and stop — an empty state
|
|
35
|
+
* with a fabricated action is worse than a quiet one.
|
|
36
|
+
*/
|
|
37
|
+
emptyAction?: React.ReactNode;
|
|
27
38
|
/** When provided, renders a leading checkbox column + long-press to toggle. */
|
|
28
39
|
selectedIds?: Set<string>;
|
|
29
40
|
onToggleSelect?: (id: string, selected: boolean) => void;
|
|
@@ -33,5 +44,5 @@ interface DataTableProps<T extends {
|
|
|
33
44
|
}
|
|
34
45
|
export declare function DataTable<T extends {
|
|
35
46
|
id: string;
|
|
36
|
-
}>({ columns: columnsProp, rows, isLoading, sortKey, sortDir, onSort, totalPages, currentPage, onPageChange, emptyLabel, rowHrefTemplate, onRowClick, renderRowActions, selectedIds, onToggleSelect, onToggleSelectAll, resourceIcon, }: DataTableProps<T>): React.JSX.Element;
|
|
47
|
+
}>({ columns: columnsProp, rows, isLoading, sortKey, sortDir, onSort, totalPages, currentPage, onPageChange, emptyLabel, emptyAction, rowHrefTemplate, onRowClick, renderRowActions, selectedIds, onToggleSelect, onToggleSelectAll, resourceIcon, }: DataTableProps<T>): React.JSX.Element;
|
|
37
48
|
export {};
|
|
@@ -48,11 +48,11 @@ function SelectableRow({ row, columns, isSelected, onToggle, renderRowActions, o
|
|
|
48
48
|
? col.render(row)
|
|
49
49
|
: String(row[col.key] ?? "") }, col.key))), renderRowActions && (_jsx(Td, { padding: "xs-3", onClick: (e) => e.stopPropagation(), children: renderRowActions(row) }))] }));
|
|
50
50
|
}
|
|
51
|
-
export function DataTable({ columns: columnsProp, rows, isLoading, sortKey, sortDir, onSort, totalPages = 1, currentPage = 1, onPageChange, emptyLabel = "No records found", rowHrefTemplate, onRowClick, renderRowActions, selectedIds, onToggleSelect, onToggleSelectAll, resourceIcon, }) {
|
|
51
|
+
export function DataTable({ columns: columnsProp, rows, isLoading, sortKey, sortDir, onSort, totalPages = 1, currentPage = 1, onPageChange, emptyLabel = "No records found", emptyAction, rowHrefTemplate, onRowClick, renderRowActions, selectedIds, onToggleSelect, onToggleSelectAll, resourceIcon, }) {
|
|
52
52
|
const columns = (columnsProp ?? buildDefaultColumns(resourceIcon));
|
|
53
53
|
const selectionEnabled = Boolean(onToggleSelect);
|
|
54
54
|
const allRowsSelected = selectionEnabled && rows.length > 0 && rows.every((r) => selectedIds?.has(r.id));
|
|
55
|
-
return (_jsxs(Div, { surface: "card", className: `${__O.hidden}`, children: [_jsx(Div, { className: `${__O.xAuto}`, children: _jsxs(Table, { "data-testid": "data-table", size: "sm", children: [_jsx(Thead, { children: _jsxs(Tr, { className: "bg-[var(--appkit-color-bg)] dark:bg-[var(--appkit-color-surface-elevated)]", border: "default", children: [selectionEnabled && (_jsx(Th, { padding: "xs-3", scope: "col", className: "w-10", children: onToggleSelectAll && (_jsx(Checkbox, { bare: true, "data-testid": "select-all-checkbox", "aria-label": allRowsSelected ? "Deselect all" : "Select all", checked: allRowsSelected, onChange: () => onToggleSelectAll(!allRowsSelected), className: "h-4 w-4 rounded border-[var(--appkit-color-border)] accent-zinc-900 dark:accent-zinc-100" })) })), columns.map((col) => (_jsxs(Th, { scope: "col", onClick: col.sortable && onSort ? () => onSort(col.key) : undefined, className: `text-left text-[var(--appkit-color-text)] ${col.sortable && onSort ? "cursor-pointer select-none hover:text-primary" : ""} ${COLUMN_PRIORITY_CLASS[resolveColumnPriority(col)]} ${col.className ?? ""}`, padding: "md", weight: "semibold", children: [col.header, col.sortable && sortKey === col.key && (_jsx(Span, { className: "ml-1", children: sortDir === "asc" ? "↑" : "↓" }))] }, col.key))), renderRowActions && _jsx(Th, { padding: "xs-3", scope: "col", className: "w-12" })] }) }), _jsx(Tbody, { children: isLoading ? (Array.from({ length: 5 }).map((_, i) => (_jsxs(Tr, { border: "default", children: [selectionEnabled && _jsx(Td, { padding: "xs-3", className: "w-10" }), columns.map((col) => (_jsx(Td, { padding: "md", className: COLUMN_PRIORITY_CLASS[resolveColumnPriority(col)], children: _jsx(Div, { className: "h-4 w-full animate-pulse bg-neutral-200", rounded: "default" }) }, col.key)))] }, i)))) : rows.length === 0 ? (_jsx(Tr, { children: _jsx(Td, { colSpan: columns.length + (selectionEnabled ? 1 : 0) + (renderRowActions ? 1 : 0), align: "center", color: "faint", paddingX: "md", paddingY: "md", children: emptyLabel }) })) : (rows.map((row) => (_jsx(SelectableRow, { row: row, columns: columns, isSelected: selectedIds?.has(row.id) ?? false, onToggle: onToggleSelect, renderRowActions: renderRowActions, onRowClick: onRowClick, rowHref: typeof rowHrefTemplate === "function"
|
|
55
|
+
return (_jsxs(Div, { surface: "card", className: `${__O.hidden}`, children: [_jsx(Div, { className: `${__O.xAuto}`, children: _jsxs(Table, { "data-testid": "data-table", size: "sm", children: [_jsx(Thead, { children: _jsxs(Tr, { className: "bg-[var(--appkit-color-bg)] dark:bg-[var(--appkit-color-surface-elevated)]", border: "default", children: [selectionEnabled && (_jsx(Th, { padding: "xs-3", scope: "col", className: "w-10", children: onToggleSelectAll && (_jsx(Checkbox, { bare: true, "data-testid": "select-all-checkbox", "aria-label": allRowsSelected ? "Deselect all" : "Select all", checked: allRowsSelected, onChange: () => onToggleSelectAll(!allRowsSelected), className: "h-4 w-4 rounded border-[var(--appkit-color-border)] accent-zinc-900 dark:accent-zinc-100" })) })), columns.map((col) => (_jsxs(Th, { scope: "col", onClick: col.sortable && onSort ? () => onSort(col.key) : undefined, className: `text-left text-[var(--appkit-color-text)] ${col.sortable && onSort ? "cursor-pointer select-none hover:text-primary" : ""} ${COLUMN_PRIORITY_CLASS[resolveColumnPriority(col)]} ${col.className ?? ""}`, padding: "md", weight: "semibold", children: [col.header, col.sortable && sortKey === col.key && (_jsx(Span, { className: "ml-1", children: sortDir === "asc" ? "↑" : "↓" }))] }, col.key))), renderRowActions && _jsx(Th, { padding: "xs-3", scope: "col", className: "w-12" })] }) }), _jsx(Tbody, { children: isLoading ? (Array.from({ length: 5 }).map((_, i) => (_jsxs(Tr, { border: "default", children: [selectionEnabled && _jsx(Td, { padding: "xs-3", className: "w-10" }), columns.map((col) => (_jsx(Td, { padding: "md", className: COLUMN_PRIORITY_CLASS[resolveColumnPriority(col)], children: _jsx(Div, { className: "h-4 w-full animate-pulse bg-neutral-200", rounded: "default" }) }, col.key)))] }, i)))) : rows.length === 0 ? (_jsx(Tr, { children: _jsx(Td, { colSpan: columns.length + (selectionEnabled ? 1 : 0) + (renderRowActions ? 1 : 0), align: "center", color: "faint", paddingX: "md", paddingY: "md", children: _jsxs(Stack, { paddingY: "y-xl", gap: "md", align: "center", children: [_jsx(Text, { size: "sm", color: "muted", children: emptyLabel }), emptyAction] }) }) })) : (rows.map((row) => (_jsx(SelectableRow, { row: row, columns: columns, isSelected: selectedIds?.has(row.id) ?? false, onToggle: onToggleSelect, renderRowActions: renderRowActions, onRowClick: onRowClick, rowHref: typeof rowHrefTemplate === "function"
|
|
56
56
|
? rowHrefTemplate(row)
|
|
57
|
-
: rowHrefTemplate?.replace("{id}", encodeURIComponent(row.id)), selectionEnabled: selectionEnabled }, row.id)))) })] }) }), totalPages > 1 && onPageChange && (_jsx(Row, { className: "border-t border-neutral-200", padding: "inline", align: "center", justify: "end", gap: "sm", children: Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (_jsx(Button, { onClick: () => onPageChange(p), variant: p === currentPage ? "primary" : "ghost", size: "sm", className: `h-8 w-8 rounded text-[length:var(--appkit-text-xs)] font-medium transition ${p === currentPage ? "bg-neutral-900 text-white" : "text-[var(--appkit-color-text-muted)]
|
|
57
|
+
: rowHrefTemplate?.replace("{id}", encodeURIComponent(row.id)), selectionEnabled: selectionEnabled }, row.id)))) })] }) }), totalPages > 1 && onPageChange && (_jsx(Row, { className: "border-t border-neutral-200", padding: "inline", align: "center", justify: "end", gap: "sm", children: Array.from({ length: totalPages }, (_, i) => i + 1).map((p) => (_jsx(Button, { onClick: () => onPageChange(p), variant: p === currentPage ? "primary" : "ghost", size: "sm", className: `h-8 w-8 rounded text-[length:var(--appkit-text-xs)] font-medium transition ${p === currentPage ? "bg-neutral-900 text-white" : "text-[var(--appkit-color-text-muted)] hover:bg-surface-hover"}`, children: p }, p))) }))] }));
|
|
58
58
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Which registry bucket supplies the labels and the confirm copy. */
|
|
2
|
-
export type OwnerQuickActionsResource = "PRODUCT" | "CATEGORY" | "BRAND" | "FRANCHISE" | "PART" | "BEY" | "BUNDLE" | "GROUP" | "SUBLISTING" | "EVENT" | "BLOG" | "REVIEW" | "STORE" | "USER" | "CAROUSEL" | "SECTION";
|
|
2
|
+
export type OwnerQuickActionsResource = "PRODUCT" | "CATEGORY" | "BRAND" | "FRANCHISE" | "PART" | "BEY" | "FAQ" | "BUNDLE" | "GROUP" | "SUBLISTING" | "EVENT" | "BLOG" | "REVIEW" | "STORE" | "USER" | "CAROUSEL" | "SECTION";
|
|
3
3
|
export interface OwnerQuickActionsProps {
|
|
4
4
|
resource: OwnerQuickActionsResource;
|
|
5
5
|
/** Where the pencil goes — that record's dashboard editor. */
|
|
@@ -66,6 +66,12 @@ const RESOURCE_LEAVES = {
|
|
|
66
66
|
*/
|
|
67
67
|
PART: { bucket: "PART", edit: "edit", remove: "delete" },
|
|
68
68
|
BEY: { bucket: "BEY", edit: "edit", remove: "delete" },
|
|
69
|
+
/*
|
|
70
|
+
* No `duplicate`: two identical questions in a help centre is a defect, not
|
|
71
|
+
* a shortcut — and unlike a product there is no per-copy field (price,
|
|
72
|
+
* stock, barcode) that makes a near-duplicate useful.
|
|
73
|
+
*/
|
|
74
|
+
FAQ: { bucket: "FAQ", edit: "edit", remove: "delete" },
|
|
69
75
|
BRAND: { bucket: "BRAND", edit: "edit", remove: "delete" },
|
|
70
76
|
BUNDLE: { bucket: "BUNDLE", edit: "edit", remove: "delete" },
|
|
71
77
|
GROUP: { bucket: "GROUP", edit: "edit", remove: "delete" },
|
|
@@ -183,7 +189,18 @@ export function OwnerQuickActions({ resource, editHref, onDelete, onClone, owner
|
|
|
183
189
|
* therefore rendered near-black on near-black and read as "admins get no
|
|
184
190
|
* edit button at all". See `onMedia` above and Root Cause #67.
|
|
185
191
|
*/
|
|
186
|
-
|
|
192
|
+
/*
|
|
193
|
+
* 🛑 `onMedia` ALONE now — `placement === "card"` no longer implies a dark
|
|
194
|
+
* backdrop and including it would be actively wrong.
|
|
195
|
+
*
|
|
196
|
+
* It used to, because every card pinned this toolbar over its hero
|
|
197
|
+
* PHOTOGRAPH, so "card" and "on media" were the same thing. The card
|
|
198
|
+
* toolbars now sit in normal flow at the foot of the card, on the card's own
|
|
199
|
+
* surface — where a dark scrim with white icons is a black rectangle on a
|
|
200
|
+
* white card. A caller that genuinely still renders over artwork passes
|
|
201
|
+
* `onMedia` explicitly, which is what that prop is for.
|
|
202
|
+
*/
|
|
203
|
+
const onDark = onMedia;
|
|
187
204
|
const neutralVariant = onDark ? "scrim" : "ghost";
|
|
188
205
|
/*
|
|
189
206
|
* Danger ink is dropped on a dark backdrop for the same reason: `danger`
|
|
@@ -192,7 +209,48 @@ export function OwnerQuickActions({ resource, editHref, onDelete, onClone, owner
|
|
|
192
209
|
* actually communicated — colour was never the safeguard.
|
|
193
210
|
*/
|
|
194
211
|
const dangerVariant = onDark ? "scrim" : "danger";
|
|
195
|
-
|
|
212
|
+
/*
|
|
213
|
+
* ── THE BOX ───────────────────────────────────────────────────────────────
|
|
214
|
+
*
|
|
215
|
+
* 🛑 THE TOOLBAR NOW CARRIES ITS OWN CONTAINER, AND THAT IS THE FIX FOR
|
|
216
|
+
* "they appear at random places".
|
|
217
|
+
*
|
|
218
|
+
* This component used to render a BARE `<Row>` and leave every decision
|
|
219
|
+
* about where it sat — and whether it read as a group at all — to each
|
|
220
|
+
* caller's `className`. Measured, the callers disagreed completely: the
|
|
221
|
+
* product grid pinned it `absolute right-2 top-14`, i.e. ON the product
|
|
222
|
+
* photo; the pre-order card landed it over its own "Reserve now" CTA; the
|
|
223
|
+
* category hero put it ABOVE the `<h1>`, overlapping the title. Three
|
|
224
|
+
* surfaces, three positions, none of them legible as a toolbar.
|
|
225
|
+
*
|
|
226
|
+
* A shared control cluster needs a shared container, or the next surface
|
|
227
|
+
* invents a fourth position. So the box is here, once:
|
|
228
|
+
*
|
|
229
|
+
* - a real surface and border, so it reads as a discrete group of
|
|
230
|
+
* controls rather than three icons floating on artwork;
|
|
231
|
+
* - `rounded` + padding, so the icons have breathing room and the hit
|
|
232
|
+
* targets do not touch;
|
|
233
|
+
* - a shadow, which is what separates it from whatever it sits on when
|
|
234
|
+
* the backdrop happens to be a similar tone.
|
|
235
|
+
*
|
|
236
|
+
* 🛑 `inline-flex`, never `w-full`. In normal flow this must shrink to its
|
|
237
|
+
* own content — a full-width bar would read as a section divider and would
|
|
238
|
+
* stretch across a card body it is only a corner of.
|
|
239
|
+
*/
|
|
240
|
+
const boxClass = onDark
|
|
241
|
+
? /*
|
|
242
|
+
* On media: a translucent dark scrim rather than a theme surface. The
|
|
243
|
+
* `frost` reasoning from Root Cause #67 — a theme-relative surface is a
|
|
244
|
+
* LIGHT background in every light theme, so a themed box on a dark hero
|
|
245
|
+
* photo is a white rectangle over the artwork.
|
|
246
|
+
*/
|
|
247
|
+
"inline-flex rounded-[var(--appkit-radius-lg)] bg-black/55 backdrop-blur-sm p-[var(--appkit-space-1)] shadow-md"
|
|
248
|
+
: /*
|
|
249
|
+
* Off media: the real surface tokens, so it inverts with the theme
|
|
250
|
+
* instead of being a fixed near-white that disappears in dark mode.
|
|
251
|
+
*/
|
|
252
|
+
"inline-flex rounded-[var(--appkit-radius-lg)] bg-[var(--appkit-color-surface-elevated)] border border-[var(--appkit-color-border)] p-[var(--appkit-space-1)] shadow-sm";
|
|
253
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Row, { gap: "xs", align: "center", className: `${boxClass} ${className}`.trim(),
|
|
196
254
|
/*
|
|
197
255
|
* Stops the click reaching an ancestor `<Link>` or the card's own
|
|
198
256
|
* `onClick`. Without it, pressing Edit on a card both navigates to the
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* leaf there still renders no bin — the resource owns that decision, which is
|
|
9
9
|
* the point of gating it in two places.
|
|
10
10
|
*/
|
|
11
|
-
export type RecordQuickActionResource = "CATEGORY" | "BRAND" | "BUNDLE" | "GROUP" | "EVENT" | "BLOG" | "FRANCHISE" | "SUBLISTING" | "PART" | "BEY";
|
|
11
|
+
export type RecordQuickActionResource = "CATEGORY" | "BRAND" | "BUNDLE" | "GROUP" | "EVENT" | "BLOG" | "FRANCHISE" | "SUBLISTING" | "PART" | "BEY" | "FAQ" | "PRODUCT";
|
|
12
12
|
export interface RecordOwnerQuickActionsProps {
|
|
13
13
|
resource: RecordQuickActionResource;
|
|
14
14
|
/** The record's document id — what the endpoint and the editor are keyed on. */
|