@mohasinac/appkit 4.59.0 → 4.60.1
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.d.ts +20 -2
- package/dist/_internal/server/features/orders/adapters.js +44 -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 +10 -4
- package/dist/client.js +29 -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 +43 -0
- package/dist/server.js +98 -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
|
@@ -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. */
|
|
@@ -12,9 +12,9 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
12
12
|
*
|
|
13
13
|
* Event handlers cannot be passed to Client Component props.
|
|
14
14
|
*
|
|
15
|
-
* at render.
|
|
16
|
-
* down every `/products/[slug]` page in production, with an
|
|
17
|
-
* handler.
|
|
15
|
+
* at render. The now-deleted product-specific wrapper recorded that this exact
|
|
16
|
+
* mistake took down every `/products/[slug]` page in production, with an
|
|
17
|
+
* opaque digest per handler.
|
|
18
18
|
*
|
|
19
19
|
* That is why ten detail views were passing `editHref` and nothing else: the
|
|
20
20
|
* bin and the clone are rendered behind `{showDelete && …}` / `{showClone && …}`,
|
|
@@ -23,20 +23,26 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
23
23
|
*
|
|
24
24
|
* ## 🛑 Why ONE wrapper and not one per resource
|
|
25
25
|
*
|
|
26
|
-
*
|
|
27
|
-
* collection a SELLER can also own, so
|
|
28
|
-
* `SELLER_ENDPOINTS
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
26
|
+
* There used to be TWO wrappers — this one and `ProductOwnerQuickActions` —
|
|
27
|
+
* because a product is the only collection a SELLER can also own, so it needs
|
|
28
|
+
* `SELLER_ENDPOINTS`, a clone that lands on the copy's editor, and a
|
|
29
|
+
* post-delete target that is not an admin list.
|
|
30
|
+
*
|
|
31
|
+
* 🛑 THOSE THREE DIFFERENCES ARE NOW ROWS IN THE TABLE, NOT A SECOND FILE.
|
|
32
|
+
* Every one of them is real and every one survives — but none needed a
|
|
33
|
+
* separate component to express, and having two meant two places for the box,
|
|
34
|
+
* the placement and the visibility rule to drift. That drift is exactly what
|
|
35
|
+
* shipped: five card types pinned the toolbar to four different corners, one
|
|
36
|
+
* of them on top of its own CTA, and the category hero rendered it over the
|
|
37
|
+
* `<h1>`. One table, one component, one position everywhere.
|
|
32
38
|
*
|
|
33
39
|
* ## Why the redirect lands on the ADMIN list
|
|
34
40
|
*
|
|
35
41
|
* You deleted the record from its own public page, so that page is now a 404 —
|
|
36
42
|
* staying is not an option and the public index tells you nothing about whether
|
|
37
43
|
* the delete worked. The admin list is where the absence is verifiable.
|
|
38
|
-
*
|
|
39
|
-
* product and has no admin list to land on.
|
|
44
|
+
* PRODUCT differs deliberately, through its own `afterDelete`: a seller may
|
|
45
|
+
* delete a product and has no admin list to land on.
|
|
40
46
|
*
|
|
41
47
|
* @tag domain:admin
|
|
42
48
|
* @tag layer:components
|
|
@@ -44,7 +50,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
44
50
|
*/
|
|
45
51
|
import { useCallback } from "react";
|
|
46
52
|
import { OwnerQuickActions } from "./OwnerQuickActions.js";
|
|
47
|
-
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints.js";
|
|
53
|
+
import { ADMIN_ENDPOINTS, SELLER_ENDPOINTS } from "../../../constants/api-endpoints.js";
|
|
48
54
|
import { ROUTES } from "../../../next/routing/route-map.js";
|
|
49
55
|
const WIRING = {
|
|
50
56
|
CATEGORY: {
|
|
@@ -97,6 +103,18 @@ const WIRING = {
|
|
|
97
103
|
editHref: (id) => String(ROUTES.ADMIN.CATEGORIES_EDIT(id)),
|
|
98
104
|
afterDelete: String(ROUTES.ADMIN.CATEGORIES),
|
|
99
105
|
},
|
|
106
|
+
PRODUCT: {
|
|
107
|
+
endpoint: SELLER_ENDPOINTS.PRODUCT_BY_ID,
|
|
108
|
+
editHref: (id) => String(ROUTES.STORE.PRODUCTS_EDIT(id)),
|
|
109
|
+
afterDelete: String(ROUTES.STORE.PRODUCTS),
|
|
110
|
+
clone: SELLER_ENDPOINTS.PRODUCT_DUPLICATE,
|
|
111
|
+
afterClone: (newId) => String(ROUTES.STORE.PRODUCTS_EDIT(newId)),
|
|
112
|
+
},
|
|
113
|
+
FAQ: {
|
|
114
|
+
endpoint: ADMIN_ENDPOINTS.FAQ_BY_ID,
|
|
115
|
+
editHref: (id) => String(ROUTES.ADMIN.FAQS_EDIT(id)),
|
|
116
|
+
afterDelete: String(ROUTES.ADMIN.FAQS),
|
|
117
|
+
},
|
|
100
118
|
SUBLISTING: {
|
|
101
119
|
endpoint: ADMIN_ENDPOINTS.SUBLISTING_CATEGORY_BY_ID,
|
|
102
120
|
editHref: (id) => String(ROUTES.ADMIN.SUBLISTING_CATEGORIES_EDIT(id)),
|
|
@@ -112,7 +130,7 @@ export function RecordOwnerQuickActions({ resource, recordId, recordLabel, owner
|
|
|
112
130
|
* navigates away — so a swallowed failure is indistinguishable from "it
|
|
113
131
|
* worked but the page did not move", and the obvious user response is to
|
|
114
132
|
* press the button again. Returning early on `!res.ok` is the bug
|
|
115
|
-
*
|
|
133
|
+
* the deleted product wrapper documented having had.
|
|
116
134
|
*/
|
|
117
135
|
const handleDelete = useCallback(async () => {
|
|
118
136
|
const res = await fetch(wiring.endpoint(recordId), { method: "DELETE" });
|
|
@@ -124,5 +142,34 @@ export function RecordOwnerQuickActions({ resource, recordId, recordLabel, owner
|
|
|
124
142
|
window.location.assign(wiring.afterDelete);
|
|
125
143
|
}
|
|
126
144
|
}, [wiring, recordId]);
|
|
127
|
-
|
|
145
|
+
/*
|
|
146
|
+
* Clone, for the resources whose table entry declares one.
|
|
147
|
+
*
|
|
148
|
+
* 🛑 Built UNCONDITIONALLY — the hook cannot be called behind an `if` — but
|
|
149
|
+
* passed to `OwnerQuickActions` only when `wiring.clone` exists, which is
|
|
150
|
+
* what makes the button absent rather than present-and-broken for the
|
|
151
|
+
* resources that cannot be copied.
|
|
152
|
+
*
|
|
153
|
+
* 🛑 THROWS on failure, like `handleDelete` above and for the same reason:
|
|
154
|
+
* `OwnerQuickActions` catches and toasts, so returning quietly is
|
|
155
|
+
* indistinguishable from "it worked but the page did not move", and the
|
|
156
|
+
* obvious user response is to press it again. The previous product-specific
|
|
157
|
+
* implementation returned early after showing its own toast, which is the
|
|
158
|
+
* double-toast path this avoids.
|
|
159
|
+
*/
|
|
160
|
+
const handleClone = useCallback(async () => {
|
|
161
|
+
if (!wiring.clone)
|
|
162
|
+
return;
|
|
163
|
+
const res = await fetch(wiring.clone(recordId), { method: "POST" });
|
|
164
|
+
if (!res.ok) {
|
|
165
|
+
const body = (await res.json().catch(() => null));
|
|
166
|
+
throw Object.assign(new Error("Clone failed"), { code: body?.code });
|
|
167
|
+
}
|
|
168
|
+
const json = (await res.json().catch(() => null));
|
|
169
|
+
const newId = json?.data?.id;
|
|
170
|
+
if (newId && wiring.afterClone && typeof window !== "undefined") {
|
|
171
|
+
window.location.assign(wiring.afterClone(newId));
|
|
172
|
+
}
|
|
173
|
+
}, [wiring, recordId]);
|
|
174
|
+
return (_jsx(OwnerQuickActions, { resource: resource, placement: placement, onMedia: onMedia, className: className, recordLabel: recordLabel, ownerId: ownerId, storeId: storeId, editHref: wiring.editHref(recordId), onDelete: handleDelete, onClone: wiring.clone ? handleClone : undefined }));
|
|
128
175
|
}
|
|
@@ -9,6 +9,40 @@ export interface AdminStatCardProps {
|
|
|
9
9
|
/** Named preset or raw CSS gradient string */
|
|
10
10
|
gradient?: keyof typeof GRADIENTS | string;
|
|
11
11
|
className?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Change against the previous comparable period, as a SIGNED percentage.
|
|
14
|
+
* `12.4` renders "▲ 12.4%", `-3` renders "▼ 3%", `0` renders "no change".
|
|
15
|
+
*
|
|
16
|
+
* 🛑 OMIT IT WHEN THERE IS NO COMPARISON, and never pass `0` to mean "we
|
|
17
|
+
* did not measure". A number here is a factual claim about a previous
|
|
18
|
+
* period; `0` claims the figure held steady, which is a different and
|
|
19
|
+
* stronger statement than silence. The whole reason a trend is worth adding
|
|
20
|
+
* is that it is trusted, and a fabricated flat line is the cheapest way to
|
|
21
|
+
* lose that.
|
|
22
|
+
*/
|
|
23
|
+
trendPercent?: number;
|
|
24
|
+
/**
|
|
25
|
+
* What `trendPercent` is measured against — "vs last week", "vs yesterday".
|
|
26
|
+
*
|
|
27
|
+
* 🛑 REQUIRED WHENEVER `trendPercent` IS PASSED, enforced by the discriminated
|
|
28
|
+
* shape below rather than by a comment. "▲ 12.4%" alone is unreadable: up
|
|
29
|
+
* on what? The same number is excellent against last month and alarming
|
|
30
|
+
* against last hour.
|
|
31
|
+
*/
|
|
32
|
+
trendLabel?: string;
|
|
33
|
+
/**
|
|
34
|
+
* 🛑 DOES A HIGHER NUMBER MEAN BETTER? Defaults to `true`, and the exception
|
|
35
|
+
* is the reason this prop exists: refunds, cancellations, failed payments
|
|
36
|
+
* and error counts all go UP when things go WRONG. Colouring those green
|
|
37
|
+
* because the arrow points up is a dashboard actively misinforming the
|
|
38
|
+
* person reading it.
|
|
39
|
+
*/
|
|
40
|
+
higherIsBetter?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Recent values, oldest first, for the sparkline. Needs at least two points
|
|
43
|
+
* to draw a line; fewer renders nothing rather than a dot or a flat stub.
|
|
44
|
+
*/
|
|
45
|
+
series?: readonly number[];
|
|
12
46
|
}
|
|
13
|
-
export declare function AdminStatCard({ label, value, sub, icon, gradient, className, }: AdminStatCardProps): React.JSX.Element;
|
|
47
|
+
export declare function AdminStatCard({ label, value, sub, icon, gradient, className, trendPercent, trendLabel, higherIsBetter, series, }: AdminStatCardProps): React.JSX.Element;
|
|
14
48
|
export {};
|