@mohasinac/appkit 4.9.5 → 4.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/layout/DashboardLayoutClient.d.ts +6 -1
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +4 -1
- package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +1 -1
- package/dist/_internal/client/features/lottery/LotteryPullForm.js +2 -2
- package/dist/_internal/server/features/admin/daily-digest-actions.d.ts +18 -0
- package/dist/_internal/server/features/admin/daily-digest-actions.js +37 -0
- package/dist/_internal/server/features/auctions/actions.js +3 -2
- package/dist/_internal/server/features/auctions/service.d.ts +4 -3
- package/dist/_internal/server/features/auctions/service.js +6 -6
- package/dist/_internal/server/features/audit-log/actions.d.ts +25 -0
- package/dist/_internal/server/features/audit-log/actions.js +38 -0
- package/dist/_internal/server/features/brands/actions.js +0 -2
- package/dist/_internal/server/features/brands/og.d.ts +4 -1
- package/dist/_internal/server/features/brands/og.js +1 -1
- package/dist/_internal/server/features/bundles/data.d.ts +9 -0
- package/dist/_internal/server/features/bundles/data.js +14 -0
- package/dist/_internal/server/features/bundles/index.d.ts +1 -1
- package/dist/_internal/server/features/bundles/index.js +1 -1
- package/dist/_internal/server/features/catalogue/og.d.ts +20 -0
- package/dist/_internal/server/features/catalogue/og.js +27 -0
- package/dist/_internal/server/features/grouped/data.d.ts +16 -0
- package/dist/_internal/server/features/grouped/data.js +50 -1
- package/dist/_internal/server/features/grouped/index.d.ts +1 -1
- package/dist/_internal/server/features/grouped/index.js +1 -1
- package/dist/_internal/server/features/orders/actions.js +9 -0
- package/dist/_internal/server/features/orders/adapters.js +25 -0
- package/dist/_internal/server/features/products/data.d.ts +23 -0
- package/dist/_internal/server/features/products/data.js +159 -0
- package/dist/_internal/server/features/products/index.d.ts +1 -1
- package/dist/_internal/server/features/products/index.js +1 -1
- package/dist/_internal/server/functions/scheduled.d.ts +3 -1
- package/dist/_internal/server/functions/scheduled.js +17 -1
- package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +40 -0
- package/dist/_internal/server/jobs/core/dailyStatusDigest.js +164 -0
- package/dist/_internal/server/jobs/core/hardBanCascade.js +11 -0
- package/dist/_internal/server/jobs/core/testerSandboxCleanup.d.ts +1 -0
- package/dist/_internal/server/jobs/core/testerSandboxCleanup.js +4 -1
- package/dist/_internal/server/jobs/core/testerSandboxRefresh.d.ts +12 -0
- package/dist/_internal/server/jobs/core/testerSandboxRefresh.js +85 -0
- package/dist/_internal/server/jobs/core/whatsappNotify.js +16 -0
- package/dist/_internal/server/jobs/handlers/dailyStatusDigest.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/dailyStatusDigest.js +4 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/index.js +3 -0
- package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/testerSandboxRefresh.js +2 -0
- package/dist/_internal/server/notifications/channel-health.d.ts +27 -0
- package/dist/_internal/server/notifications/channel-health.js +101 -0
- package/dist/_internal/shared/features/auctions/config.d.ts +20 -0
- package/dist/_internal/shared/features/auctions/config.js +31 -1
- package/dist/_internal/shared/features/brands/schema.d.ts +0 -6
- package/dist/_internal/shared/features/brands/schema.js +0 -1
- package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +7 -0
- package/dist/_internal/shared/features/categories/bundle-schemas.js +3 -0
- package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
- package/dist/_internal/shared/listing-types/art/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/art/config.js +1 -1
- package/dist/_internal/shared/listing-types/classified/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/classified/config.js +1 -1
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/digital-code/config.js +1 -1
- package/dist/_internal/shared/listing-types/live/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/live/config.js +1 -1
- package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/prize-draw/config.js +1 -1
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +1 -1
- package/dist/_internal/shared/listing-types/stickers/config.js +1 -1
- package/dist/client.d.ts +21 -12
- package/dist/client.js +14 -7
- package/dist/constants/api-endpoints.d.ts +9 -9
- package/dist/constants/api-endpoints.js +3 -3
- package/dist/contracts/client-auth.d.ts +3 -7
- package/dist/contracts/email.d.ts +1 -0
- package/dist/features/account/components/AddressForm.js +24 -2
- package/dist/features/account/components/BecomeSellerView.d.ts +1 -2
- package/dist/features/account/components/BecomeSellerView.js +1 -1
- package/dist/features/account/components/NotificationBell.js +1 -1
- package/dist/features/account/components/UserBidsView.d.ts +1 -0
- package/dist/features/account/components/UserBidsView.js +47 -0
- package/dist/features/account/components/UserOrdersView.d.ts +4 -11
- package/dist/features/account/components/UserOrdersView.js +54 -4
- package/dist/features/account/components/UserReturnsView.d.ts +4 -9
- package/dist/features/account/components/UserReturnsView.js +37 -3
- package/dist/features/account/components/index.d.ts +3 -2
- package/dist/features/account/components/index.js +1 -0
- package/dist/features/admin/actions/admin-actions.js +22 -0
- package/dist/features/admin/actions/admin-coupon-actions.js +10 -0
- package/dist/features/admin/actions/notification-actions.js +4 -3
- package/dist/features/admin/components/AdminAdsView.d.ts +2 -3
- package/dist/features/admin/components/AdminAdsView.js +86 -89
- package/dist/features/admin/components/AdminAllEventEntriesView.js +18 -3
- package/dist/features/admin/components/AdminAuditLogView.d.ts +11 -0
- package/dist/features/admin/components/AdminAuditLogView.js +95 -0
- package/dist/features/admin/components/AdminBlogEditorView.js +37 -3
- package/dist/features/admin/components/AdminBrandEditorView.js +10 -6
- package/dist/features/admin/components/AdminBundleEditorView.js +18 -2
- package/dist/features/admin/components/AdminCarouselEditorView.js +2 -2
- package/dist/features/admin/components/AdminCarouselGroupEditorView.js +2 -2
- package/dist/features/admin/components/AdminCartsView.js +34 -4
- package/dist/features/admin/components/AdminCategoryEditorView.js +5 -4
- package/dist/features/admin/components/AdminCouponEditorView.js +2 -2
- package/dist/features/admin/components/AdminFaqEditorView.js +2 -2
- package/dist/features/admin/components/AdminHistoryView.js +11 -1
- package/dist/features/admin/components/AdminNotificationsView.js +27 -1
- package/dist/features/admin/components/AdminOrderEditorView.d.ts +10 -1
- package/dist/features/admin/components/AdminOrderEditorView.js +4 -3
- package/dist/features/admin/components/AdminOrdersView.js +92 -24
- package/dist/features/admin/components/AdminPayoutMarkPaidModal.d.ts +7 -0
- package/dist/features/admin/components/AdminPayoutMarkPaidModal.js +47 -0
- package/dist/features/admin/components/AdminPayoutsView.js +15 -31
- package/dist/features/admin/components/AdminProductEditorView.js +26 -3
- package/dist/features/admin/components/AdminSiteSettingsView.js +31 -4
- package/dist/features/admin/components/AdminStoreAddressesView.js +22 -2
- package/dist/features/admin/components/AdminStoresView.js +7 -0
- package/dist/features/admin/components/AdminSublistingCategoryEditorView.js +2 -2
- package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +2 -2
- package/dist/features/admin/components/AdminUserEditorView.d.ts +2 -2
- package/dist/features/admin/components/AdminUserEditorView.js +4 -2
- package/dist/features/admin/components/AdminUsersView.js +1 -1
- package/dist/features/admin/components/AdminWishlistsView.js +14 -1
- package/dist/features/admin/components/DataListingView.d.ts +2 -2
- package/dist/features/admin/components/ViewAuditLogEntryModal.d.ts +19 -0
- package/dist/features/admin/components/ViewAuditLogEntryModal.js +22 -0
- package/dist/features/admin/components/ViewNotificationModal.d.ts +21 -0
- package/dist/features/admin/components/ViewNotificationModal.js +15 -0
- package/dist/features/admin/components/analytics/AdminPageViewsReportView.d.ts +4 -7
- package/dist/features/admin/components/analytics/AdminPageViewsReportView.js +56 -36
- package/dist/features/admin/components/index.d.ts +8 -1
- package/dist/features/admin/components/index.js +4 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +49 -0
- package/dist/features/admin/constants/filter-tabs.js +27 -0
- package/dist/features/admin/hooks/useAdminListing.d.ts +6 -0
- package/dist/features/admin/hooks/useAdminListing.js +3 -1
- package/dist/features/admin/hooks/useAdminListingData.d.ts +9 -1
- package/dist/features/admin/hooks/useAdminListingData.js +9 -2
- package/dist/features/admin/repository/site-settings.repository.d.ts +5 -0
- package/dist/features/admin/schemas/firestore.d.ts +21 -0
- package/dist/features/admin/schemas/firestore.js +6 -0
- package/dist/features/analytics/types.d.ts +1 -1
- package/dist/features/analytics/types.js +8 -0
- package/dist/features/auctions/actions/bid-actions.js +4 -1
- package/dist/features/auctions/components/AuctionBottomActions.d.ts +2 -1
- package/dist/features/auctions/components/AuctionBottomActions.js +14 -1
- package/dist/features/auctions/components/AuctionDetailPageView.js +21 -9
- package/dist/features/auctions/components/LiveMinIncrement.d.ts +18 -0
- package/dist/features/auctions/components/LiveMinIncrement.js +17 -0
- package/dist/features/auctions/components/PlaceBidFormClient.d.ts +5 -2
- package/dist/features/auctions/components/PlaceBidFormClient.js +7 -3
- package/dist/features/auctions/schemas/index.d.ts +5 -0
- package/dist/features/audit-log/repository/audit-log.repository.d.ts +45 -0
- package/dist/features/audit-log/repository/audit-log.repository.js +30 -0
- package/dist/features/audit-log/schemas/firestore.d.ts +59 -0
- package/dist/features/audit-log/schemas/firestore.js +35 -0
- package/dist/features/auth/actions/index.d.ts +0 -1
- package/dist/features/auth/actions/index.js +0 -1
- package/dist/features/auth/hooks/useAuth.d.ts +8 -35
- package/dist/features/auth/hooks/useAuth.js +21 -37
- package/dist/features/auth/permissions/constants.d.ts +1 -1
- package/dist/features/auth/permissions/constants.js +2 -0
- package/dist/features/blog/schemas/index.d.ts +32 -0
- package/dist/features/catalogue/components/AdminCatalogueApprovalsView.js +31 -3
- package/dist/features/catalogue/components/CatalogueItemEditorView.js +2 -1
- package/dist/features/catalogue/components/PublicCatalogueItemDetailView.d.ts +11 -0
- package/dist/features/catalogue/components/PublicCatalogueItemDetailView.js +33 -0
- package/dist/features/catalogue/components/PublicCatalogueView.js +2 -1
- package/dist/features/catalogue/index.d.ts +2 -0
- package/dist/features/catalogue/index.js +1 -0
- package/dist/features/categories/components/BrandDetailPageView.js +14 -12
- package/dist/features/categories/components/BundleDetailView.d.ts +3 -1
- package/dist/features/categories/components/BundleDetailView.js +3 -2
- package/dist/features/categories/components/CategoryDetailPageView.js +7 -3
- package/dist/features/categories/components/CategoryHighlightsAndFaqSection.d.ts +14 -0
- package/dist/features/categories/components/CategoryHighlightsAndFaqSection.js +22 -0
- package/dist/features/categories/components/CategoryProductsView.d.ts +1 -5
- package/dist/features/categories/components/CategoryProductsView.js +1 -1
- package/dist/features/categories/components/CategorySelectorCreate.js +16 -2
- package/dist/features/categories/schemas/firestore.d.ts +19 -2
- package/dist/features/categories/schemas/index.d.ts +24 -3
- package/dist/features/categories/schemas/index.js +3 -1
- package/dist/features/categories/types/index.d.ts +7 -0
- package/dist/features/checkout/actions/checkout-value-otp-actions.d.ts +17 -3
- package/dist/features/checkout/actions/checkout-value-otp-actions.js +101 -22
- package/dist/features/classified/components/ClassifiedContactSellerPanel.d.ts +12 -0
- package/dist/features/classified/components/ClassifiedContactSellerPanel.js +49 -0
- package/dist/features/classified/components/ClassifiedDetailPageView.d.ts +7 -0
- package/dist/features/classified/components/ClassifiedDetailPageView.js +69 -0
- package/dist/features/contact/components/ContactForm.js +17 -4
- package/dist/features/contact/components/ContactPageView.js +8 -2
- package/dist/features/contact/email.d.ts +1 -0
- package/dist/features/contact/email.js +3 -0
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.d.ts +19 -0
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +79 -0
- package/dist/features/events/components/AdminEventEditorView.js +54 -2
- package/dist/features/events/components/AdminEventEntriesView.d.ts +2 -16
- package/dist/features/events/components/AdminEventEntriesView.js +56 -89
- package/dist/features/events/components/EventFormDrawer.js +5 -1
- package/dist/features/events/components/EventRaffleEntryForm.d.ts +1 -3
- package/dist/features/events/components/EventRaffleEntryForm.js +1 -1
- package/dist/features/events/schemas/index.d.ts +22 -0
- package/dist/features/grouped/components/GroupedListingsCarousel.d.ts +13 -0
- package/dist/features/grouped/components/GroupedListingsCarousel.js +31 -0
- package/dist/features/grouped/repository/grouped-listings.repository.d.ts +12 -0
- package/dist/features/grouped/repository/grouped-listings.repository.js +34 -0
- package/dist/features/homepage/components/CharacterHotspotForm.js +202 -187
- package/dist/features/homepage/components/SectionCarousel.d.ts +1 -7
- package/dist/features/homepage/components/SectionCarousel.js +1 -1
- package/dist/features/layout/BottomActions.js +4 -3
- package/dist/features/layout/BottomActionsContext.d.ts +10 -0
- package/dist/features/layout/BottomActionsContext.js +5 -1
- package/dist/features/layout/TitleBarLayout.d.ts +1 -1
- package/dist/features/layout/TitleBarLayout.js +3 -3
- package/dist/features/layout/hooks/useBottomActions.d.ts +2 -0
- package/dist/features/layout/hooks/useBottomActions.js +4 -1
- package/dist/features/live/components/LiveItemDetailPageView.d.ts +9 -0
- package/dist/features/live/components/LiveItemDetailPageView.js +74 -0
- package/dist/features/media/MediaImage.js +39 -3
- package/dist/features/media/MediaVideo.js +17 -1
- package/dist/features/media/types/index.d.ts +10 -0
- package/dist/features/media/types/index.js +1 -0
- package/dist/features/media/upload/MediaUploadField.d.ts +7 -1
- package/dist/features/media/upload/MediaUploadField.js +11 -7
- package/dist/features/media/upload/video-poster.d.ts +2 -0
- package/dist/features/media/upload/video-poster.js +16 -1
- package/dist/features/media/upload/video-poster.test.js +1 -1
- package/dist/features/orders/components/OrderStatusTimeline.d.ts +12 -0
- package/dist/features/orders/components/OrderStatusTimeline.js +55 -0
- package/dist/features/orders/components/index.d.ts +2 -0
- package/dist/features/orders/components/index.js +1 -0
- package/dist/features/orders/constants/payment-window.d.ts +25 -0
- package/dist/features/orders/constants/payment-window.js +25 -0
- package/dist/features/orders/repository/orders.repository.d.ts +32 -0
- package/dist/features/orders/repository/orders.repository.js +56 -0
- package/dist/features/orders/schemas/index.d.ts +24 -24
- package/dist/features/orders/types/index.d.ts +33 -0
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +9 -1
- package/dist/features/pre-orders/schemas/index.d.ts +5 -0
- package/dist/features/products/components/BidHistory.js +9 -1
- package/dist/features/products/components/GroupSettingsPanel.js +25 -3
- package/dist/features/products/components/PrizeDrawDetailPageView.js +11 -1
- package/dist/features/products/components/PrizeDrawEntryActions.d.ts +1 -4
- package/dist/features/products/components/PrizeDrawEntryActions.js +1 -1
- package/dist/features/products/components/ProductDetailActions.d.ts +1 -2
- package/dist/features/products/components/ProductDetailActions.js +1 -1
- package/dist/features/products/components/ProductDetailPageView.js +11 -116
- package/dist/features/products/components/ProductGrid.js +15 -1
- package/dist/features/products/components/RelatedItemsSection.d.ts +16 -0
- package/dist/features/products/components/RelatedItemsSection.js +17 -0
- package/dist/features/products/constants/action-defs.d.ts +1 -0
- package/dist/features/products/constants/action-defs.js +1 -0
- package/dist/features/products/schemas/index.d.ts +5 -0
- package/dist/features/promotions/components/CouponsIndexListing.d.ts +1 -3
- package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
- package/dist/features/search/components/SearchView.d.ts +1 -3
- package/dist/features/search/components/SearchView.js +1 -1
- package/dist/features/seller/components/QuickProductForm.js +2 -1
- package/dist/features/seller/components/SellerBidsView.js +66 -107
- package/dist/features/seller/components/SellerGoogleReviewsView.js +17 -1
- package/dist/features/seller/components/SellerOrdersView.d.ts +15 -3
- package/dist/features/seller/components/SellerOrdersView.js +134 -163
- package/dist/features/seller/components/SellerPayoutMethodsView.js +60 -48
- package/dist/features/seller/components/SellerPayoutSettingsView.js +69 -4
- package/dist/features/seller/components/SellerPayoutsView.d.ts +12 -0
- package/dist/features/seller/components/SellerPayoutsView.js +25 -5
- package/dist/features/seller/components/SellerProductShell.d.ts +10 -3
- package/dist/features/seller/components/SellerProductShell.js +90 -37
- package/dist/features/seller/components/SellerProductsFilterDrawer.d.ts +2 -7
- package/dist/features/seller/components/SellerProductsFilterDrawer.js +4 -4
- package/dist/features/seller/components/SellerProductsView.d.ts +2 -4
- package/dist/features/seller/components/SellerProductsView.js +131 -189
- package/dist/features/seller/components/SellerReviewsView.js +95 -133
- package/dist/features/seller/components/SellerShippingConfigsView.js +47 -67
- package/dist/features/seller/components/SellerShippingView.js +41 -4
- package/dist/features/seller/components/SellerStoreCategoriesView.js +51 -59
- package/dist/features/seller/components/SellerStorefrontView.d.ts +6 -1
- package/dist/features/seller/components/SellerStorefrontView.js +42 -5
- package/dist/features/seller/components/index.d.ts +2 -2
- package/dist/features/seller/components/index.js +2 -2
- package/dist/features/shell/FormShell.d.ts +24 -4
- package/dist/features/shell/FormShell.js +12 -6
- package/dist/features/shell/QuickFormDrawer.js +67 -12
- package/dist/features/shell/StepForm.d.ts +30 -1
- package/dist/features/shell/StepForm.js +47 -4
- package/dist/features/shell/index.d.ts +1 -1
- package/dist/features/shell/index.js +1 -1
- package/dist/features/shipments/components/AdminShipmentEditorView.js +2 -2
- package/dist/features/shipments/schemas/validation.d.ts +14 -4
- package/dist/features/tester/components/AdminTesterFeedbackListView.js +23 -2
- package/dist/features/tester/components/TesterChecklistStepRow.js +1 -1
- package/dist/features/tester/components/TesterHubView.js +3 -2
- package/dist/features/tester/seed-data/orders-tester-seed-data.js +9 -1
- package/dist/features/tester/seed-data/products-tester-seed-data.js +43 -32
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +487 -18
- package/dist/index.d.ts +17 -3
- package/dist/index.js +23 -5
- package/dist/next/routing/route-map.d.ts +13 -0
- package/dist/next/routing/route-map.js +12 -0
- package/dist/providers/email-resend/provider.js +1 -0
- package/dist/providers/firebase-client/auth.d.ts +1 -0
- package/dist/providers/firebase-client/auth.js +7 -1
- package/dist/repositories/index.d.ts +1 -0
- package/dist/repositories/index.js +1 -0
- package/dist/schemas/registry.d.ts +61 -25
- package/dist/security/pii-mask.js +1 -1
- package/dist/seed/bids-seed-data.js +48 -3
- package/dist/seed/categories-seed-data.js +69 -0
- package/dist/seed/grouped-listings-seed-data.js +167 -0
- package/dist/seed/payouts-seed-data.js +4 -4
- package/dist/seed/products-art-seed-data.js +188 -5
- package/dist/seed/products-auctions-seed-data.js +226 -6
- package/dist/seed/products-classifieds-seed-data.js +255 -8
- package/dist/seed/products-digital-codes-seed-data.js +258 -19
- package/dist/seed/products-live-items-seed-data.js +143 -10
- package/dist/seed/products-preorders-seed-data.js +213 -8
- package/dist/seed/products-prize-draws-seed-data.js +198 -0
- package/dist/seed/products-standard-seed-data.js +234 -4
- package/dist/seed/products-stickers-seed-data.js +178 -5
- package/dist/seed/site-settings-seed-data.js +23 -30
- package/dist/seed/store-extensions-seed-data.js +2 -2
- package/dist/server-entry.d.ts +6 -2
- package/dist/server-entry.js +24 -3
- package/dist/server.d.ts +10 -3
- package/dist/server.js +11 -4
- package/dist/styles.css +29 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/Form.d.ts +11 -2
- package/dist/ui/components/Form.js +4 -3
- package/dist/ui/components/FormField.d.ts +3 -1
- package/dist/ui/components/FormField.js +22 -4
- package/dist/ui/components/ImageLightbox.js +37 -3
- package/dist/ui/components/ImageLightbox.style.css +28 -0
- package/dist/ui/components/ListingLayout.d.ts +1 -2
- package/dist/ui/components/ListingLayout.js +1 -1
- package/dist/ui/components/RecordDetailModal.d.ts +68 -0
- package/dist/ui/components/RecordDetailModal.js +8 -0
- package/dist/ui/components/SlottedListingView.js +9 -1
- package/dist/ui/forms/FormErrorSummary.d.ts +25 -0
- package/dist/ui/forms/FormErrorSummary.js +37 -0
- package/dist/ui/forms/FormShell.d.ts +37 -2
- package/dist/ui/forms/FormShell.js +37 -7
- package/dist/ui/forms/FormShell.style.css +17 -0
- package/dist/ui/forms/index.d.ts +2 -0
- package/dist/ui/forms/index.js +1 -0
- package/dist/ui/index.d.ts +4 -0
- package/dist/ui/index.js +5 -0
- package/dist/utils/media-url.d.ts +12 -1
- package/dist/utils/media-url.js +59 -1
- package/dist/validation/schemas.d.ts +4 -4
- package/package.json +1 -1
- package/dist/_internal/client/features/classified/ClassifiedDetailView.d.ts +0 -9
- package/dist/_internal/client/features/classified/ClassifiedDetailView.js +0 -55
- package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.d.ts +0 -18
- package/dist/_internal/client/features/digital-code/DigitalCodeDetailView.js +0 -21
- package/dist/_internal/client/features/live/LiveItemDetailView.d.ts +0 -9
- package/dist/_internal/client/features/live/LiveItemDetailView.js +0 -25
- package/dist/_internal/server/jobs/core/prizeRevealClose.d.ts +0 -2
- package/dist/_internal/server/jobs/core/prizeRevealClose.js +0 -28
- package/dist/_internal/server/jobs/core/prizeRevealExpiry.d.ts +0 -2
- package/dist/_internal/server/jobs/core/prizeRevealExpiry.js +0 -61
- package/dist/_internal/server/jobs/core/prizeRevealOpen.d.ts +0 -2
- package/dist/_internal/server/jobs/core/prizeRevealOpen.js +0 -68
- package/dist/_internal/server/jobs/core/prizeRevealReminder.d.ts +0 -2
- package/dist/_internal/server/jobs/core/prizeRevealReminder.js +0 -49
- package/dist/_internal/server/jobs/handlers/prizeRevealClose.d.ts +0 -2
- package/dist/_internal/server/jobs/handlers/prizeRevealClose.js +0 -2
- package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.d.ts +0 -2
- package/dist/_internal/server/jobs/handlers/prizeRevealExpiry.js +0 -2
- package/dist/_internal/server/jobs/handlers/prizeRevealOpen.d.ts +0 -2
- package/dist/_internal/server/jobs/handlers/prizeRevealOpen.js +0 -2
- package/dist/_internal/server/jobs/handlers/prizeRevealReminder.d.ts +0 -2
- package/dist/_internal/server/jobs/handlers/prizeRevealReminder.js +0 -2
- package/dist/features/auth/consent-otp.d.ts +0 -57
- package/dist/features/auth/consent-otp.js +0 -127
- package/dist/features/cart/components/CheckoutOtpModal.d.ts +0 -15
- package/dist/features/cart/components/CheckoutOtpModal.js +0 -12
- package/dist/features/checkout/actions/checkout-actions.d.ts +0 -31
- package/dist/features/checkout/actions/checkout-actions.js +0 -124
- package/dist/features/classified/components/ClassifiedIndexListing.d.ts +0 -5
- package/dist/features/classified/components/ClassifiedIndexListing.js +0 -123
- package/dist/features/classified/components/ClassifiedListView.d.ts +0 -6
- package/dist/features/classified/components/ClassifiedListView.js +0 -44
- package/dist/features/digital-codes/components/DigitalCodesIndexListing.d.ts +0 -5
- package/dist/features/digital-codes/components/DigitalCodesIndexListing.js +0 -114
- package/dist/features/digital-codes/components/DigitalCodesListView.d.ts +0 -6
- package/dist/features/digital-codes/components/DigitalCodesListView.js +0 -38
- package/dist/features/live/components/LiveItemsIndexListing.d.ts +0 -5
- package/dist/features/live/components/LiveItemsIndexListing.js +0 -115
- package/dist/features/live/components/LiveItemsListView.d.ts +0 -6
- package/dist/features/live/components/LiveItemsListView.js +0 -44
- package/dist/features/seller/components/SellerTemplatesView.d.ts +0 -12
- package/dist/features/seller/components/SellerTemplatesView.js +0 -276
- package/dist/react/hooks/useVisibleItems.d.ts +0 -37
- package/dist/react/hooks/useVisibleItems.js +0 -59
- package/dist/ui/components/TabStrip.d.ts +0 -34
- package/dist/ui/components/TabStrip.js +0 -56
- package/dist/ui/components/TabStrip.style.css +0 -97
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { ZodTypeAny } from "zod";
|
|
3
3
|
import { type SurfaceProps } from "./surface-tokens";
|
|
4
|
-
import { type UseFormShellStateResult } from "../forms/FormShell";
|
|
4
|
+
import { type FormShellContextValue, type UseFormShellStateResult } from "../forms/FormShell";
|
|
5
5
|
export type FormSpacing = "none" | "xs" | "sm" | "md" | "lg" | "xl";
|
|
6
6
|
export interface FormProps extends Omit<React.FormHTMLAttributes<HTMLFormElement>, "children">, SurfaceProps {
|
|
7
7
|
/**
|
|
@@ -19,13 +19,22 @@ export interface FormProps extends Omit<React.FormHTMLAttributes<HTMLFormElement
|
|
|
19
19
|
* boilerplate per callsite.
|
|
20
20
|
*/
|
|
21
21
|
schema?: ZodTypeAny;
|
|
22
|
+
/**
|
|
23
|
+
* Use an externally-computed `FormShellContextValue` (e.g. from a parent's
|
|
24
|
+
* own `useFormShellState(schema)` call) instead of `<Form>` creating its
|
|
25
|
+
* own. Needed when other elements outside this `<form>` — a submit button
|
|
26
|
+
* in a separate sidebar, associated via the HTML `form` attribute — must
|
|
27
|
+
* share the exact same validation state; otherwise `<Form>`'s own internal
|
|
28
|
+
* provider would shadow the parent's for everything rendered as `children`.
|
|
29
|
+
*/
|
|
30
|
+
shellCtx?: FormShellContextValue;
|
|
22
31
|
/** Cross-axis alignment of flex/grid children. Replaces raw `items-*` className. */
|
|
23
32
|
align?: "start" | "center" | "end" | "stretch";
|
|
24
33
|
/** Gap between children — use instead of raw `gap-*` className. */
|
|
25
34
|
gap?: GapToken;
|
|
26
35
|
}
|
|
27
36
|
export type GapToken = "none" | "xs" | "sm" | "md" | "lg" | "xl";
|
|
28
|
-
export declare function Form({ children, spacing, schema, align, gap, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, className, ...props }: FormProps): React.JSX.Element;
|
|
37
|
+
export declare function Form({ children, spacing, schema, shellCtx: shellCtxProp, align, gap, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, className, ...props }: FormProps): React.JSX.Element;
|
|
29
38
|
export interface FormGroupProps {
|
|
30
39
|
children: React.ReactNode;
|
|
31
40
|
columns?: 1 | 2 | 3 | 4;
|
|
@@ -43,12 +43,13 @@ const FORM_ALIGN_MAP = {
|
|
|
43
43
|
end: "items-end",
|
|
44
44
|
stretch: "items-stretch",
|
|
45
45
|
};
|
|
46
|
-
export function Form({ children, spacing, schema, align, gap, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, className = "", ...props }) {
|
|
46
|
+
export function Form({ children, spacing, schema, shellCtx: shellCtxProp, align, gap, surface, padding, paddingX, paddingY, rounded, roundedTop, roundedBottom, border, shadow, overflow, className = "", ...props }) {
|
|
47
47
|
const helpers = useFormShellState(schema);
|
|
48
|
+
const effectiveCtx = shellCtxProp ?? helpers.shellCtx;
|
|
48
49
|
const content = typeof children === "function"
|
|
49
|
-
? children(helpers)
|
|
50
|
+
? children({ ...helpers, shellCtx: effectiveCtx })
|
|
50
51
|
: children;
|
|
51
|
-
return (_jsx(FormShellContext.Provider, { value:
|
|
52
|
+
return (_jsx(FormShellContext.Provider, { value: effectiveCtx, children: _jsx("form", { className: [
|
|
52
53
|
"appkit-form",
|
|
53
54
|
spacing ? FORM_SPACING_MAP[spacing] : "",
|
|
54
55
|
align ? FORM_ALIGN_MAP[align] : "",
|
|
@@ -8,7 +8,9 @@ export interface FormFieldProps {
|
|
|
8
8
|
value?: string;
|
|
9
9
|
onChange?: (value: string) => void;
|
|
10
10
|
onBlur?: () => void;
|
|
11
|
+
/** Override error — falls back to FormShell context error for this field */
|
|
11
12
|
error?: string;
|
|
13
|
+
/** Override touched — falls back to FormShell context touched state for this field */
|
|
12
14
|
touched?: boolean;
|
|
13
15
|
placeholder?: string;
|
|
14
16
|
required?: boolean;
|
|
@@ -24,5 +26,5 @@ export interface FormFieldProps {
|
|
|
24
26
|
accept?: string;
|
|
25
27
|
maxSizeMB?: number;
|
|
26
28
|
}
|
|
27
|
-
export declare function FormField({ label, name, card, type, value, onChange, onBlur, error, touched, placeholder, required, disabled, autoComplete, rows, hint, helpText, options, onUpload, captureSource, captureMode, accept, maxSizeMB, }: FormFieldProps): React.JSX.Element;
|
|
29
|
+
export declare function FormField({ label, name, card, type, value, onChange, onBlur, error: errorProp, touched: touchedProp, placeholder, required, disabled, autoComplete, rows, hint, helpText, options, onUpload, captureSource, captureMode, accept, maxSizeMB, }: FormFieldProps): React.JSX.Element;
|
|
28
30
|
export default FormField;
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useContext } from "react";
|
|
3
4
|
import { Input } from "./Input";
|
|
4
5
|
import { Select } from "./Select";
|
|
5
6
|
import { Textarea } from "./Textarea";
|
|
6
7
|
import { Label, Text, Span } from "./Typography";
|
|
7
8
|
import { ImageUpload, MediaUploadField } from "../../features/media";
|
|
9
|
+
import { FormShellContext } from "../forms/FormShell";
|
|
8
10
|
const CARD_CLASS = "appkit-form-field appkit-form-field--card";
|
|
9
11
|
const BASE_CLASS = "appkit-form-field";
|
|
10
|
-
export function FormField({ label, name, card = false, type = "text", value = "", onChange, onBlur, error, touched, placeholder, required = false, disabled = false, autoComplete, rows, hint, helpText, options = [], onUpload, captureSource, captureMode, accept, maxSizeMB, }) {
|
|
12
|
+
export function FormField({ label, name, card = false, type = "text", value = "", onChange, onBlur, error: errorProp, touched: touchedProp, placeholder, required = false, disabled = false, autoComplete, rows, hint, helpText, options = [], onUpload, captureSource, captureMode, accept, maxSizeMB, }) {
|
|
13
|
+
// Falls back to FormShellContext (the same context FieldInput/FieldSelect
|
|
14
|
+
// read) whenever the caller doesn't pass an explicit error/touched prop —
|
|
15
|
+
// an explicit prop always wins, preserving back-compat for the handful of
|
|
16
|
+
// call sites that already manage their own error state manually.
|
|
17
|
+
const ctx = useContext(FormShellContext);
|
|
18
|
+
const error = errorProp ?? ctx?.errors[name];
|
|
19
|
+
const touched = touchedProp ?? ctx?.touched[name];
|
|
11
20
|
const showError = error
|
|
12
21
|
? touched != null
|
|
13
22
|
? touched && !!error
|
|
@@ -16,12 +25,21 @@ export function FormField({ label, name, card = false, type = "text", value = ""
|
|
|
16
25
|
const inputId = `field-${name}`;
|
|
17
26
|
const errorId = `${inputId}-error`;
|
|
18
27
|
const describedBy = showError ? errorId : undefined;
|
|
28
|
+
function handleFieldChange(newValue) {
|
|
29
|
+
onChange?.(newValue);
|
|
30
|
+
if (showError)
|
|
31
|
+
ctx?.clearFieldError(name);
|
|
32
|
+
}
|
|
33
|
+
function handleFieldBlur() {
|
|
34
|
+
ctx?.setFieldTouched(name);
|
|
35
|
+
onBlur?.();
|
|
36
|
+
}
|
|
19
37
|
if (type === "image" && onUpload) {
|
|
20
|
-
return (_jsxs("div", { className: card ? CARD_CLASS : BASE_CLASS, "data-section": "formfield-div-505", children: [_jsx("div", { "aria-disabled": disabled || undefined, className: disabled ? "pointer-events-none opacity-60" : undefined, children: _jsx(ImageUpload, { currentImage: value || undefined, onUpload: onUpload, onChange: (url) =>
|
|
38
|
+
return (_jsxs("div", { className: card ? CARD_CLASS : BASE_CLASS, "data-section": "formfield-div-505", children: [_jsx("div", { "aria-disabled": disabled || undefined, className: disabled ? "pointer-events-none opacity-60" : undefined, children: _jsx(ImageUpload, { currentImage: value || undefined, onUpload: onUpload, onChange: (url) => handleFieldChange(url), label: label ? `${label}${required ? " *" : ""}` : undefined, helperText: hint ?? helpText, captureSource: captureSource ?? "file-only", accept: accept, maxSizeMB: maxSizeMB }) }), showError ? (_jsx(Text, { id: errorId, size: "sm", variant: "error", className: "appkit-form-field__error", role: "alert", children: error })) : null] }));
|
|
21
39
|
}
|
|
22
40
|
if (type === "media" && onUpload) {
|
|
23
|
-
return (_jsxs("div", { className: card ? CARD_CLASS : BASE_CLASS, "data-section": "formfield-div-506", children: [_jsx(MediaUploadField, { label: `${label || name}${required ? " *" : ""}`, value: value, onChange: (url) =>
|
|
41
|
+
return (_jsxs("div", { className: card ? CARD_CLASS : BASE_CLASS, "data-section": "formfield-div-506", children: [_jsx(MediaUploadField, { label: `${label || name}${required ? " *" : ""}`, value: value, onChange: (url) => handleFieldChange(url), onUpload: onUpload, disabled: disabled, helperText: hint ?? helpText, captureSource: captureSource ?? "file-only", captureMode: captureMode ?? "both", accept: accept, maxSizeMB: maxSizeMB }), showError ? (_jsx(Text, { id: errorId, size: "sm", variant: "error", className: "appkit-form-field__error", role: "alert", children: error })) : null] }));
|
|
24
42
|
}
|
|
25
|
-
return (_jsxs("div", { className: card ? CARD_CLASS : BASE_CLASS, "data-section": "formfield-div-507", children: [label ? (_jsxs(Label, { htmlFor: inputId, className: "appkit-form-field__label", children: [label, required ? (_jsx(Span, { className: "appkit-form-field__required", children: "*" })) : null] })) : null, type === "select" ? (_jsx(Select, { id: inputId, name: name, value: value, onChange: (event) =>
|
|
43
|
+
return (_jsxs("div", { className: card ? CARD_CLASS : BASE_CLASS, "data-section": "formfield-div-507", children: [label ? (_jsxs(Label, { htmlFor: inputId, className: "appkit-form-field__label", children: [label, required ? (_jsx(Span, { className: "appkit-form-field__required", children: "*" })) : null] })) : null, type === "select" ? (_jsx(Select, { id: inputId, name: name, value: value, onChange: (event) => handleFieldChange(event.target.value), onBlur: handleFieldBlur, disabled: disabled, options: options, "aria-required": required || undefined, "aria-invalid": showError || undefined, "aria-describedby": describedBy })) : type === "textarea" ? (_jsx(Textarea, { id: inputId, name: name, value: value, onChange: (event) => handleFieldChange(event.target.value), onBlur: handleFieldBlur, placeholder: placeholder, disabled: disabled, rows: rows, "aria-required": required || undefined, "aria-invalid": showError || undefined, "aria-describedby": describedBy })) : (_jsx(Input, { id: inputId, name: name, type: type, value: value, onChange: (event) => handleFieldChange(event.target.value), onBlur: handleFieldBlur, placeholder: placeholder, disabled: disabled, autoComplete: autoComplete, "aria-required": required || undefined, "aria-invalid": showError || undefined, "aria-describedby": describedBy })), !showError && (hint || helpText) ? (_jsx(Text, { size: "sm", variant: "secondary", className: "appkit-form-field__hint", children: hint ?? helpText })) : null, showError ? (_jsx(Text, { id: errorId, size: "sm", variant: "error", className: "appkit-form-field__error", role: "alert", children: error })) : null] }));
|
|
26
44
|
}
|
|
27
45
|
export default FormField;
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
|
-
import { ChevronLeft, ChevronRight, X, ZoomIn, ZoomOut, RotateCw, Maximize2 } from "lucide-react";
|
|
5
|
+
import { ChevronLeft, ChevronRight, X, ZoomIn, ZoomOut, RotateCw, Maximize2, Minimize2 } from "lucide-react";
|
|
6
6
|
import { Button } from "./Button";
|
|
7
7
|
import { Div } from "./Div";
|
|
8
8
|
import { Text, Span } from "./Typography";
|
|
9
9
|
import { MediaImage } from "../../features/media/MediaImage";
|
|
10
|
+
import { getYouTubeVideoId } from "../../utils/media-url";
|
|
10
11
|
const CLS_CLOSE_BTN = "w-10 h-10 p-0 !min-h-0 rounded-full bg-white/15 hover:bg-error-surface text-white flex items-center justify-center";
|
|
11
12
|
const MIN_ZOOM = 10;
|
|
12
13
|
const MAX_ZOOM = 300;
|
|
@@ -15,6 +16,7 @@ export function ImageLightbox({ images, activeIndex, onClose, onNavigate, showTh
|
|
|
15
16
|
const [currentIndex, setCurrentIndex] = useState(activeIndex ?? 0);
|
|
16
17
|
const [zoom, setZoom] = useState(100);
|
|
17
18
|
const [rotation, setRotation] = useState(0);
|
|
19
|
+
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
18
20
|
const overlayRef = useRef(null);
|
|
19
21
|
// Sync external activeIndex and reset transforms
|
|
20
22
|
useEffect(() => {
|
|
@@ -33,6 +35,37 @@ export function ImageLightbox({ images, activeIndex, onClose, onNavigate, showTh
|
|
|
33
35
|
document.body.style.overflow = "hidden";
|
|
34
36
|
return () => { document.body.style.overflow = prev; };
|
|
35
37
|
}, [isOpen]);
|
|
38
|
+
// Track native Fullscreen API state so the toggle button + icon stay in sync
|
|
39
|
+
// even when the user exits via Esc/browser chrome rather than our button.
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const onFsChange = () => {
|
|
42
|
+
const doc = document;
|
|
43
|
+
const fsEl = document.fullscreenElement ?? doc.webkitFullscreenElement ?? null;
|
|
44
|
+
setIsFullscreen(fsEl === overlayRef.current);
|
|
45
|
+
};
|
|
46
|
+
document.addEventListener("fullscreenchange", onFsChange);
|
|
47
|
+
document.addEventListener("webkitfullscreenchange", onFsChange);
|
|
48
|
+
return () => {
|
|
49
|
+
document.removeEventListener("fullscreenchange", onFsChange);
|
|
50
|
+
document.removeEventListener("webkitfullscreenchange", onFsChange);
|
|
51
|
+
};
|
|
52
|
+
}, []);
|
|
53
|
+
// Exit native fullscreen when the lightbox itself closes.
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
const doc = document;
|
|
56
|
+
if (!isOpen && (document.fullscreenElement || doc.webkitFullscreenElement)) {
|
|
57
|
+
(document.exitFullscreen?.() ?? doc.webkitExitFullscreen?.())?.catch?.(() => { });
|
|
58
|
+
}
|
|
59
|
+
}, [isOpen]);
|
|
60
|
+
const toggleFullscreen = useCallback(() => {
|
|
61
|
+
const doc = document;
|
|
62
|
+
if (document.fullscreenElement || doc.webkitFullscreenElement) {
|
|
63
|
+
(document.exitFullscreen?.() ?? doc.webkitExitFullscreen?.())?.catch?.(() => { });
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const el = overlayRef.current;
|
|
67
|
+
(el?.requestFullscreen?.() ?? el?.webkitRequestFullscreen?.())?.catch?.(() => { });
|
|
68
|
+
}, []);
|
|
36
69
|
const navigate = useCallback((dir) => {
|
|
37
70
|
setCurrentIndex((prev) => {
|
|
38
71
|
const next = (prev + dir + images.length) % images.length;
|
|
@@ -89,11 +122,12 @@ export function ImageLightbox({ images, activeIndex, onClose, onNavigate, showTh
|
|
|
89
122
|
return null;
|
|
90
123
|
const image = images[currentIndex];
|
|
91
124
|
const hasMultiple = images.length > 1;
|
|
125
|
+
const youtubeId = image.kind === "video" ? getYouTubeVideoId(image.src) : null;
|
|
92
126
|
const iconBtnClass = "w-10 h-10 p-0 !min-h-0 rounded-full bg-white/15 hover:bg-white/30 text-white flex items-center justify-center";
|
|
93
|
-
return createPortal(_jsxs("div", { ref: overlayRef, tabIndex: -1, className: "appkit-lightbox", role: "dialog", "aria-modal": "true", "aria-label": "Image lightbox", onKeyDown: handleKeyDown, onWheel: handleWheel, children: [_jsxs("div", { className: "absolute top-0 left-0 right-0 z-10 flex items-center justify-between px-4 py-3 bg-gradient-to-b from-black/60 to-transparent", children: [_jsx(Span, { size: "sm", weight: "medium", className: "text-white/70", children: hasMultiple ? `${currentIndex + 1} / ${images.length}` : "" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => adjustZoom(-ZOOM_STEP), className: iconBtnClass, "aria-label": "Zoom out", disabled: zoom <= MIN_ZOOM, children: _jsx(ZoomOut, { className: "w-4 h-4" }) }), _jsxs("button", { type: "button", onClick: () => { setZoom(100); setRotation(0); }, className: "text-white/70 hover:text-white text-xs font-mono min-w-[3rem] text-center", "aria-label": "Reset zoom", children: [zoom, "%"] }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => adjustZoom(ZOOM_STEP), className: iconBtnClass, "aria-label": "Zoom in", disabled: zoom >= MAX_ZOOM, children: _jsx(ZoomIn, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => setRotation((r) => (r + 90) % 360), className: iconBtnClass, "aria-label": "Rotate 90\u00B0", children: _jsx(RotateCw, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick:
|
|
127
|
+
return createPortal(_jsxs("div", { ref: overlayRef, tabIndex: -1, className: "appkit-lightbox", role: "dialog", "aria-modal": "true", "aria-label": "Image lightbox", onKeyDown: handleKeyDown, onWheel: handleWheel, children: [_jsxs("div", { className: "absolute top-0 left-0 right-0 z-10 flex items-center justify-between px-4 py-3 bg-gradient-to-b from-black/60 to-transparent", children: [_jsx(Span, { size: "sm", weight: "medium", className: "text-white/70", children: hasMultiple ? `${currentIndex + 1} / ${images.length}` : "" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => adjustZoom(-ZOOM_STEP), className: iconBtnClass, "aria-label": "Zoom out", disabled: zoom <= MIN_ZOOM, children: _jsx(ZoomOut, { className: "w-4 h-4" }) }), _jsxs("button", { type: "button", onClick: () => { setZoom(100); setRotation(0); }, className: "text-white/70 hover:text-white text-xs font-mono min-w-[3rem] text-center", "aria-label": "Reset zoom", children: [zoom, "%"] }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => adjustZoom(ZOOM_STEP), className: iconBtnClass, "aria-label": "Zoom in", disabled: zoom >= MAX_ZOOM, children: _jsx(ZoomIn, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => setRotation((r) => (r + 90) % 360), className: iconBtnClass, "aria-label": "Rotate 90\u00B0", children: _jsx(RotateCw, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: toggleFullscreen, className: iconBtnClass, "aria-label": isFullscreen ? "Exit fullscreen" : "Enter fullscreen", children: isFullscreen ? _jsx(Minimize2, { className: "w-4 h-4" }) : _jsx(Maximize2, { className: "w-4 h-4" }) }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: onClose, className: CLS_CLOSE_BTN, "aria-label": "Close lightbox", children: _jsx(X, { className: "w-5 h-5" }) })] })] }), hasMultiple && (_jsx(Button, { variant: "ghost", size: "sm", type: "button", onClick: () => navigate(-1), className: "absolute left-4 top-1/2 -translate-y-1/2 w-12 h-12 p-0 !min-h-0 rounded-full bg-white/15 hover:bg-white/30 text-white z-10 flex items-center justify-center", "aria-label": "Previous image", children: _jsx(ChevronLeft, { className: "w-7 h-7" }) })), _jsxs("div", { className: "appkit-lightbox__image-wrap", style: { cursor: zoom > 100 ? "grab" : "default" }, children: [image.kind === "video" ? (youtubeId ? (_jsx("iframe", { src: `https://www.youtube-nocookie.com/embed/${youtubeId}?playsinline=1&rel=0`, title: image.alt ?? "Video", allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share", allowFullScreen: true, className: "appkit-lightbox__img appkit-lightbox__video border-0" })) : (_jsx("video", { src: image.src, poster: image.poster, controls: true, playsInline: true, "aria-label": image.alt ?? "Video", className: "appkit-lightbox__img appkit-lightbox__video", style: {
|
|
94
128
|
transform: `scale(${zoom / 100}) rotate(${rotation}deg)`,
|
|
95
129
|
transition: "transform 0.2s ease",
|
|
96
|
-
} })) : (_jsx(MediaImage, { src: image.src, alt: image.alt ?? "", size: "hero", objectFit: "contain", className: "appkit-lightbox__img", style: {
|
|
130
|
+
} }))) : (_jsx(MediaImage, { src: image.src, alt: image.alt ?? "", size: "hero", objectFit: "contain", className: "appkit-lightbox__img", style: {
|
|
97
131
|
transform: `scale(${zoom / 100}) rotate(${rotation}deg)`,
|
|
98
132
|
transition: "transform 0.2s ease",
|
|
99
133
|
} })), image.badge && (_jsx(Div, { className: "absolute left-3 top-3 rounded bg-black/70 px-2 py-1 text-xs font-semibold text-white pointer-events-none", children: image.badge }))] }), (image.caption || image.sub) && (_jsxs("div", { className: "flex-shrink-0 text-center px-8 pb-2", children: [image.caption && (_jsx(Text, { size: "sm", variant: "secondary", className: "!text-white/80", children: image.caption })), image.sub && (_jsx(Text, { size: "sm", variant: "secondary", className: "!text-white/50", children: image.sub }))] })), showThumbnails && images.length > 1 && (_jsx("div", { className: "flex-shrink-0 flex gap-2 overflow-x-auto px-4 pb-3 justify-center", children: images.map((thumb, i) => (_jsx("button", { type: "button", onClick: () => {
|
|
@@ -37,6 +37,14 @@
|
|
|
37
37
|
position: relative;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
@media (max-width: 640px) {
|
|
41
|
+
.appkit-lightbox__image-wrap {
|
|
42
|
+
/* Tighter than the desktop 4rem so a video's min-width (below) has room
|
|
43
|
+
to fit inside narrow phone viewports without forcing horizontal scroll. */
|
|
44
|
+
padding: 1.5rem 1rem;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
40
48
|
.appkit-lightbox__img {
|
|
41
49
|
max-height: 100%;
|
|
42
50
|
max-width: 100%;
|
|
@@ -44,6 +52,26 @@
|
|
|
44
52
|
user-select: none;
|
|
45
53
|
}
|
|
46
54
|
|
|
55
|
+
/* -- Video / YouTube-embed area ----------------------- */
|
|
56
|
+
/* Applied alongside .appkit-lightbox__img on the theater-mode <video>/<iframe>
|
|
57
|
+
so a video always occupies a real, tappable box — never the browser's tiny
|
|
58
|
+
~300x150 default when a src fails to load or an embed reports no intrinsic
|
|
59
|
+
size (Recurrent Root Cause Pattern #27-adjacent: video needs its own sizing
|
|
60
|
+
contract, it can't just inherit the image one). */
|
|
61
|
+
.appkit-lightbox__video {
|
|
62
|
+
width: 100%;
|
|
63
|
+
height: 100%;
|
|
64
|
+
min-width: 260px;
|
|
65
|
+
min-height: 190px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@media (min-width: 768px) {
|
|
69
|
+
.appkit-lightbox__video {
|
|
70
|
+
min-width: 480px;
|
|
71
|
+
min-height: 320px;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
47
75
|
/* -- Zoom hint --------------------------------------- */
|
|
48
76
|
|
|
49
77
|
.appkit-lightbox__zoom-hint {
|
|
@@ -59,7 +59,6 @@ export interface ListingLayoutProps {
|
|
|
59
59
|
/** Count of filter changes staged but not yet committed. Shows in Apply button. */
|
|
60
60
|
filterPendingCount?: number;
|
|
61
61
|
className?: string;
|
|
62
|
-
loading?: boolean;
|
|
63
62
|
errorSlot?: ReactNode;
|
|
64
63
|
labels?: ListingLayoutLabels;
|
|
65
64
|
/** Portal context — sets `isDashboard` default (admin/seller=true, user/public=false). */
|
|
@@ -69,4 +68,4 @@ export interface ListingLayoutProps {
|
|
|
69
68
|
/** When provided, the entire layout is replaced with this view (detail/edit mode). */
|
|
70
69
|
detailView?: ReactNode;
|
|
71
70
|
}
|
|
72
|
-
export declare function ListingLayout({ headerSlot, statusTabsSlot, filterContent, filterActiveCount, onFilterApply, onFilterClear, filterTitle, activeFiltersSlot, resultCountSlot, searchSlot, sortSlot, viewToggleSlot, actionsSlot, selectedCount, onClearSelection, bulkActionItems, toolbarPaginationSlot, paginationSlot, children, isDashboard, defaultSidebarOpen, filterPendingCount, className,
|
|
71
|
+
export declare function ListingLayout({ headerSlot, statusTabsSlot, filterContent, filterActiveCount, onFilterApply, onFilterClear, filterTitle, activeFiltersSlot, resultCountSlot, searchSlot, sortSlot, viewToggleSlot, actionsSlot, selectedCount, onClearSelection, bulkActionItems, toolbarPaginationSlot, paginationSlot, children, isDashboard, defaultSidebarOpen, filterPendingCount, className, errorSlot, labels, portal, overlays, detailView, }: ListingLayoutProps): import("react").JSX.Element;
|
|
@@ -35,7 +35,7 @@ const DEFAULT_LABELS = {
|
|
|
35
35
|
hideFilters: "Hide filters",
|
|
36
36
|
filterActiveCount: (count) => `${count} active`,
|
|
37
37
|
};
|
|
38
|
-
export function ListingLayout({ headerSlot, statusTabsSlot, filterContent, filterActiveCount = 0, onFilterApply, onFilterClear, filterTitle, activeFiltersSlot, resultCountSlot, searchSlot, sortSlot, viewToggleSlot, actionsSlot, selectedCount = 0, onClearSelection, bulkActionItems, toolbarPaginationSlot, paginationSlot, children, isDashboard, defaultSidebarOpen = false, filterPendingCount, className = "",
|
|
38
|
+
export function ListingLayout({ headerSlot, statusTabsSlot, filterContent, filterActiveCount = 0, onFilterApply, onFilterClear, filterTitle, activeFiltersSlot, resultCountSlot, searchSlot, sortSlot, viewToggleSlot, actionsSlot, selectedCount = 0, onClearSelection, bulkActionItems, toolbarPaginationSlot, paginationSlot, children, isDashboard, defaultSidebarOpen = false, filterPendingCount, className = "", errorSlot, labels, portal, overlays, detailView, }) {
|
|
39
39
|
// useState must run unconditionally on every render — these were
|
|
40
40
|
// previously declared after the detail-view early return below, which
|
|
41
41
|
// skipped both hook calls whenever `detailView` was passed, violating the
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RecordDetailModal — the shared "open one listing row and actually read it"
|
|
3
|
+
* surface.
|
|
4
|
+
*
|
|
5
|
+
* Built 2026-08-21 when `audit-listing-detail-affordance` found 16 of 70
|
|
6
|
+
* dashboard listing views were dead ends: rows the user could see but never
|
|
7
|
+
* open. Two distinct failure shapes, both fixed by this one primitive:
|
|
8
|
+
*
|
|
9
|
+
* 1. Data fetched then discarded — AdminCartsView/AdminWishlistsView/
|
|
10
|
+
* AdminHistoryView all compute `items.length` for the row subtitle and
|
|
11
|
+
* throw the `items[]` array itself away, with no surface to show it
|
|
12
|
+
* (CLAUDE.md Recurrent Root Cause #52).
|
|
13
|
+
* 2. Acting blind — AdminCatalogueApprovalsView offers Approve/Reject, and
|
|
14
|
+
* AdminTesterFeedbackListView offers "Confirm bug", on records whose
|
|
15
|
+
* content (photos, description, the tester's comment/screenshot) was
|
|
16
|
+
* never rendered anywhere.
|
|
17
|
+
*
|
|
18
|
+
* Sixteen bespoke modals would have tripped the Duplication Decision
|
|
19
|
+
* Framework's Rule of Three immediately, so this is deliberately one
|
|
20
|
+
* primitive with three optional content slots — `fields` (definition rows),
|
|
21
|
+
* `items` (a thumbnail list, the Root Cause #52 fix), and `metadata` (a raw
|
|
22
|
+
* JSON payload dump) — rather than a per-feature component each time.
|
|
23
|
+
*
|
|
24
|
+
* Consumers pass plain data, not JSX, so a listing view's detail wiring stays
|
|
25
|
+
* the ~20-line "map the raw row into fields/items" shape at every call site.
|
|
26
|
+
*/
|
|
27
|
+
import type { ReactNode } from "react";
|
|
28
|
+
import type { JsonValue } from "../../schemas/types";
|
|
29
|
+
export interface RecordDetailField {
|
|
30
|
+
label: string;
|
|
31
|
+
/** Rendered as-is when a node; plain strings/numbers get default styling. */
|
|
32
|
+
value: ReactNode;
|
|
33
|
+
}
|
|
34
|
+
export interface RecordDetailItem {
|
|
35
|
+
/** Thumbnail URL — falls back to MediaImage's placeholder when absent. */
|
|
36
|
+
image?: string;
|
|
37
|
+
title: string;
|
|
38
|
+
subtitle?: string;
|
|
39
|
+
/** Right-aligned trailing text (price, quantity, timestamp). */
|
|
40
|
+
trailing?: string;
|
|
41
|
+
/** When set, the title becomes a link to this record's own detail page. */
|
|
42
|
+
href?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface RecordDetailBadge {
|
|
45
|
+
label: string;
|
|
46
|
+
variant?: "default" | "success" | "warning" | "danger" | "info" | "secondary";
|
|
47
|
+
}
|
|
48
|
+
export interface RecordDetailModalProps {
|
|
49
|
+
isOpen: boolean;
|
|
50
|
+
onClose: () => void;
|
|
51
|
+
title: string;
|
|
52
|
+
/** Small status chips under the title (status, read-state, role, etc). */
|
|
53
|
+
badges?: RecordDetailBadge[];
|
|
54
|
+
/** Free-text lead paragraph — a comment body, description, or message. */
|
|
55
|
+
description?: string;
|
|
56
|
+
/** Definition rows: the record's scalar fields. */
|
|
57
|
+
fields?: RecordDetailField[];
|
|
58
|
+
/** A nested collection on the record (cart items, wishlist entries, …). */
|
|
59
|
+
items?: {
|
|
60
|
+
heading: string;
|
|
61
|
+
entries: RecordDetailItem[];
|
|
62
|
+
};
|
|
63
|
+
/** Raw payload dump for records that carry an open-ended object. */
|
|
64
|
+
metadata?: Record<string, JsonValue>;
|
|
65
|
+
/** Action buttons rendered at the bottom (approve/reject/etc). */
|
|
66
|
+
footer?: ReactNode;
|
|
67
|
+
}
|
|
68
|
+
export declare function RecordDetailModal({ isOpen, onClose, title, badges, description, fields, items, metadata, footer, }: RecordDetailModalProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Anchor, Badge, Code, Div, Modal, Row, Stack, Text } from "../index";
|
|
4
|
+
import { MediaImage } from "../../features/media/MediaImage";
|
|
5
|
+
export function RecordDetailModal({ isOpen, onClose, title, badges, description, fields, items, metadata, footer, }) {
|
|
6
|
+
const hasMetadata = Boolean(metadata && Object.keys(metadata).length > 0);
|
|
7
|
+
return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: title, children: _jsxs(Stack, { gap: "md", children: [badges && badges.length > 0 && (_jsx(Row, { gap: "sm", align: "center", wrap: true, children: badges.map((b) => (_jsx(Badge, { variant: b.variant ?? "default", children: b.label }, b.label))) })), description && (_jsx(Div, { rounded: "lg", padding: "sm", surface: "muted", border: "default", children: _jsx(Text, { size: "sm", children: description }) })), fields && fields.length > 0 && (_jsx(Stack, { gap: "xs", children: fields.map((f) => (_jsxs(Row, { justify: "between", gap: "md", children: [_jsx(Text, { size: "sm", color: "muted", children: f.label }), _jsx(Text, { size: "sm", weight: "medium", className: "text-right", children: f.value })] }, f.label))) })), items && items.entries.length > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "xs", weight: "semibold", color: "muted", children: [items.heading, " (", items.entries.length, ")"] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: items.entries.map((entry, i) => (_jsxs(Row, { gap: "sm", align: "center", justify: "between", paddingY: "y-xs-tall", padding: "x-sm", children: [_jsxs(Row, { gap: "sm", align: "center", className: "min-w-0", children: [_jsx(Div, { className: "h-10 w-10 shrink-0", rounded: "md", overflow: "hidden", children: _jsx(MediaImage, { src: entry.image, alt: entry.title, size: "thumbnail" }) }), _jsxs(Stack, { gap: "none", className: "min-w-0", children: [entry.href ? (_jsx(Anchor, { href: entry.href, tone: "brand", size: "sm", weight: "medium", children: entry.title })) : (_jsx(Text, { size: "sm", weight: "medium", className: "truncate", children: entry.title })), entry.subtitle && (_jsx(Text, { size: "xs", color: "muted", children: entry.subtitle }))] })] }), entry.trailing && (_jsx(Text, { size: "sm", weight: "medium", className: "shrink-0", children: entry.trailing }))] }, `${entry.title}-${i}`))) })] })), hasMetadata && (_jsxs(Div, { rounded: "lg", padding: "sm", surface: "muted", border: "default", children: [_jsx(Text, { size: "xs", weight: "semibold", color: "muted", className: "mb-1", children: "Details" }), _jsx(Code, { size: "xs", className: "block whitespace-pre-wrap font-mono", children: JSON.stringify(metadata, null, 2) })] })), footer && _jsx(Row, { justify: "end", gap: "sm", children: footer })] }) }));
|
|
8
|
+
}
|
|
@@ -25,7 +25,15 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
25
25
|
import { useState } from "react";
|
|
26
26
|
import { Div } from "./Div";
|
|
27
27
|
import { Heading } from "./Typography";
|
|
28
|
-
export function SlottedListingView({ title, labels = {}, portal, renderHeader, renderTabs, renderSearch, renderSort, renderFilters, renderActiveFilters, renderBulkActions, renderTable, renderPagination, overlays,
|
|
28
|
+
export function SlottedListingView({ title, labels = {}, portal, renderHeader, renderTabs, renderSearch, renderSort, renderFilters, renderActiveFilters, renderBulkActions, renderTable, renderPagination, overlays,
|
|
29
|
+
// audit-dead-underscore-prop-ok: deliberately unused — see the doc comment
|
|
30
|
+
// on `manageSearch` above; wiring this to gate renderSearch would break
|
|
31
|
+
// real consumers with self-contained closures.
|
|
32
|
+
manageSearch: _manageSearch, manageSelection,
|
|
33
|
+
// audit-dead-underscore-prop-ok: deliberately unused — see the doc comment
|
|
34
|
+
// on `manageSort` above; wiring this to gate renderSort would break real
|
|
35
|
+
// consumers with self-contained closures.
|
|
36
|
+
manageSort: _manageSort = false, inlineToolbar = false, total = 0, isLoading = false, className = "", }) {
|
|
29
37
|
const effectiveManageSelection = manageSelection ?? (portal === "admin" || portal === "seller");
|
|
30
38
|
const [search, setSearch] = useState("");
|
|
31
39
|
const [selectedIds, setSelectedIds] = useState([]);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface FormErrorSummaryProps {
|
|
2
|
+
/** Hide the whole block when there are currently no errors. Default true. */
|
|
3
|
+
hideWhenEmpty?: boolean;
|
|
4
|
+
className?: string;
|
|
5
|
+
/** Heading shown above the error list. */
|
|
6
|
+
title?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Shared, live validation-error summary — supplements (does not replace) the
|
|
10
|
+
* per-field inline errors `FieldInput`/`FieldSelect`/`FormField` already
|
|
11
|
+
* render. Reads `FormShellContext` directly (no required props), so it's
|
|
12
|
+
* placed once beside a form's Submit/Save button and reflects every current
|
|
13
|
+
* error immediately as fields change — not gated by `touched`, unlike the
|
|
14
|
+
* inline per-field errors.
|
|
15
|
+
*
|
|
16
|
+
* For multi-step forms whose step engine populates
|
|
17
|
+
* `FormShellContextValue.fieldToStepIndex`, each entry names its owning step
|
|
18
|
+
* and is clickable to jump there. Falls back to a flat list when no step
|
|
19
|
+
* context exists (single-step `<Form>` usage).
|
|
20
|
+
*
|
|
21
|
+
* Uses the `.appkit-formshell__error-summary` class family already defined
|
|
22
|
+
* in `FormShell.style.css` for this exact purpose.
|
|
23
|
+
*/
|
|
24
|
+
export declare function FormErrorSummary({ hideWhenEmpty, className, title, }: FormErrorSummaryProps): import("react").JSX.Element | null;
|
|
25
|
+
export default FormErrorSummary;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useContext } from "react";
|
|
4
|
+
import { FormShellContext } from "./FormShell";
|
|
5
|
+
import { Span } from "../components/Typography";
|
|
6
|
+
/**
|
|
7
|
+
* Shared, live validation-error summary — supplements (does not replace) the
|
|
8
|
+
* per-field inline errors `FieldInput`/`FieldSelect`/`FormField` already
|
|
9
|
+
* render. Reads `FormShellContext` directly (no required props), so it's
|
|
10
|
+
* placed once beside a form's Submit/Save button and reflects every current
|
|
11
|
+
* error immediately as fields change — not gated by `touched`, unlike the
|
|
12
|
+
* inline per-field errors.
|
|
13
|
+
*
|
|
14
|
+
* For multi-step forms whose step engine populates
|
|
15
|
+
* `FormShellContextValue.fieldToStepIndex`, each entry names its owning step
|
|
16
|
+
* and is clickable to jump there. Falls back to a flat list when no step
|
|
17
|
+
* context exists (single-step `<Form>` usage).
|
|
18
|
+
*
|
|
19
|
+
* Uses the `.appkit-formshell__error-summary` class family already defined
|
|
20
|
+
* in `FormShell.style.css` for this exact purpose.
|
|
21
|
+
*/
|
|
22
|
+
export function FormErrorSummary({ hideWhenEmpty = true, className, title = "Please fix the following:", }) {
|
|
23
|
+
const ctx = useContext(FormShellContext);
|
|
24
|
+
if (!ctx)
|
|
25
|
+
return null;
|
|
26
|
+
const entries = Object.entries(ctx.errors);
|
|
27
|
+
if (entries.length === 0 && hideWhenEmpty)
|
|
28
|
+
return null;
|
|
29
|
+
if (entries.length === 0)
|
|
30
|
+
return null;
|
|
31
|
+
return (_jsxs("div", { role: "alert", "aria-live": "polite", className: ["appkit-formshell__error-summary", className].filter(Boolean).join(" "), children: [_jsx("div", { className: "appkit-formshell__error-summary__title", children: title }), _jsx("ul", { className: "appkit-formshell__error-summary__list", children: entries.map(([key, message]) => {
|
|
32
|
+
const stepIndex = ctx.fieldToStepIndex?.[key];
|
|
33
|
+
const stepLabel = stepIndex != null ? ctx.steps[stepIndex]?.label : undefined;
|
|
34
|
+
return (_jsx("li", { children: stepLabel ? (_jsxs("button", { type: "button", className: "appkit-formshell__error-summary__step-link", onClick: () => ctx.goToStep(stepIndex), children: [_jsxs(Span, { weight: "semibold", children: [stepLabel, ":"] }), " ", message] })) : (_jsx(Span, { children: message })) }, key));
|
|
35
|
+
}) })] }));
|
|
36
|
+
}
|
|
37
|
+
export default FormErrorSummary;
|
|
@@ -30,6 +30,14 @@ export interface FormShellContextValue {
|
|
|
30
30
|
isDirty: boolean;
|
|
31
31
|
isSubmitting: boolean;
|
|
32
32
|
stepErrorCounts: number[];
|
|
33
|
+
/**
|
|
34
|
+
* Maps an error key (same key shape `errors` uses) to the index into
|
|
35
|
+
* `steps` that owns it. Populated only by step-aware producers (the
|
|
36
|
+
* `features/shell` wizard chrome/step engine) — undefined for single-step
|
|
37
|
+
* forms (`<Form>`, `useFormShellState` with no steps). `<FormErrorSummary>`
|
|
38
|
+
* treats undefined as "no step context" and renders a flat list.
|
|
39
|
+
*/
|
|
40
|
+
fieldToStepIndex?: Record<string, number>;
|
|
33
41
|
}
|
|
34
42
|
export declare const FormShellContext: React.Context<FormShellContextValue | null>;
|
|
35
43
|
export declare function useFormShell(): FormShellContextValue;
|
|
@@ -43,12 +51,22 @@ export interface UseFormShellStateResult {
|
|
|
43
51
|
* supplied values, write inline errors via `setFieldError`, and return the
|
|
44
52
|
* parsed value on success or null on failure.
|
|
45
53
|
*/
|
|
46
|
-
validate: <T = unknown>(values:
|
|
54
|
+
validate: <T = unknown>(values: unknown) => T | null;
|
|
47
55
|
}
|
|
48
56
|
/**
|
|
49
57
|
* `applyZodIssues` — pipe every issue from a Zod safeParse failure into the
|
|
50
58
|
* FormShellContext error map. Use after `schema.safeParse(values)` returns
|
|
51
59
|
* `success: false` to surface inline errors on `<FieldInput>` etc.
|
|
60
|
+
*
|
|
61
|
+
* Keys are the full dotted/indexed path (`"video.duration"`, `"images.2"`),
|
|
62
|
+
* not just the top-level segment — a plain `issue.path[0]` key would collapse
|
|
63
|
+
* every nested issue under one object onto the same map entry, silently
|
|
64
|
+
* dropping all but the last. Top-level-only fields (the common case) are
|
|
65
|
+
* unaffected: a single-segment path joins to the same string as before.
|
|
66
|
+
*
|
|
67
|
+
* Root-level `.refine()` issues (empty `path: []`, no owning field) are still
|
|
68
|
+
* skipped — there is no field to attach them to, and no schema in this
|
|
69
|
+
* codebase currently relies on one being surfaced this way.
|
|
52
70
|
*/
|
|
53
71
|
export declare function applyZodIssues(issues: ReadonlyArray<{
|
|
54
72
|
path: ReadonlyArray<PropertyKey>;
|
|
@@ -70,5 +88,22 @@ export interface FormShellProviderProps {
|
|
|
70
88
|
autoSaveDelayMs?: number;
|
|
71
89
|
/** Current form values — passed into onSaveDraft on auto-save */
|
|
72
90
|
values?: FormValues;
|
|
91
|
+
/**
|
|
92
|
+
* Optional Zod schema — when supplied, the provider runs `.safeParse(values)`
|
|
93
|
+
* live (every time `values` changes, not just on submit) and populates its
|
|
94
|
+
* own `errors` map from the issues, so `<FieldInput>`/`<FormField>`/
|
|
95
|
+
* `<FormErrorSummary>` descendants get real validation without the parent
|
|
96
|
+
* component having to reach into the provider's internal state itself.
|
|
97
|
+
*/
|
|
98
|
+
schema?: import("zod").ZodTypeAny;
|
|
99
|
+
/**
|
|
100
|
+
* Maps an error key to the index of the step that owns it — forwarded
|
|
101
|
+
* as-is into the context value for `<FormErrorSummary>` to consume. The
|
|
102
|
+
* provider itself has no concept of "steps"; the caller (typically a
|
|
103
|
+
* step-wizard shell) computes this from its own step definitions.
|
|
104
|
+
*/
|
|
105
|
+
fieldToStepIndex?: Record<string, number>;
|
|
106
|
+
/** Wired into `FormShellContextValue.goToStep` when the caller has real step navigation to drive (e.g. a step-wizard's `setCurrentStep`). */
|
|
107
|
+
onGoToStep?: (n: number) => void;
|
|
73
108
|
}
|
|
74
|
-
export declare function FormShellProvider({ children, onSaveDraft, isDirty, autoSaveDelayMs, values, }: FormShellProviderProps): React.JSX.Element;
|
|
109
|
+
export declare function FormShellProvider({ children, onSaveDraft, isDirty, autoSaveDelayMs, values, schema, fieldToStepIndex, onGoToStep, }: FormShellProviderProps): React.JSX.Element;
|
|
@@ -13,13 +13,23 @@ export function useFormShell() {
|
|
|
13
13
|
* `applyZodIssues` — pipe every issue from a Zod safeParse failure into the
|
|
14
14
|
* FormShellContext error map. Use after `schema.safeParse(values)` returns
|
|
15
15
|
* `success: false` to surface inline errors on `<FieldInput>` etc.
|
|
16
|
+
*
|
|
17
|
+
* Keys are the full dotted/indexed path (`"video.duration"`, `"images.2"`),
|
|
18
|
+
* not just the top-level segment — a plain `issue.path[0]` key would collapse
|
|
19
|
+
* every nested issue under one object onto the same map entry, silently
|
|
20
|
+
* dropping all but the last. Top-level-only fields (the common case) are
|
|
21
|
+
* unaffected: a single-segment path joins to the same string as before.
|
|
22
|
+
*
|
|
23
|
+
* Root-level `.refine()` issues (empty `path: []`, no owning field) are still
|
|
24
|
+
* skipped — there is no field to attach them to, and no schema in this
|
|
25
|
+
* codebase currently relies on one being surfaced this way.
|
|
16
26
|
*/
|
|
17
27
|
export function applyZodIssues(issues, setFieldError) {
|
|
18
28
|
for (const issue of issues) {
|
|
19
|
-
|
|
20
|
-
if (key == null)
|
|
29
|
+
if (!issue.path || issue.path.length === 0)
|
|
21
30
|
continue;
|
|
22
|
-
|
|
31
|
+
const key = issue.path.map(String).join(".");
|
|
32
|
+
setFieldError(key, issue.message);
|
|
23
33
|
}
|
|
24
34
|
}
|
|
25
35
|
/**
|
|
@@ -72,7 +82,7 @@ export function useFormShellState(schema) {
|
|
|
72
82
|
}), [errors, setFieldError]);
|
|
73
83
|
return { shellCtx, setFieldError, clearErrors, hasErrors: Object.keys(errors).length > 0, validate };
|
|
74
84
|
}
|
|
75
|
-
export function FormShellProvider({ children, onSaveDraft, isDirty = false, autoSaveDelayMs = 2000, values = {}, }) {
|
|
85
|
+
export function FormShellProvider({ children, onSaveDraft, isDirty = false, autoSaveDelayMs = 2000, values = {}, schema, fieldToStepIndex, onGoToStep, }) {
|
|
76
86
|
const [errors, setErrors] = useState({});
|
|
77
87
|
const [touched, setTouched] = useState({});
|
|
78
88
|
const autoSaveTimer = useRef(null);
|
|
@@ -89,6 +99,25 @@ export function FormShellProvider({ children, onSaveDraft, isDirty = false, auto
|
|
|
89
99
|
return () => { if (autoSaveTimer.current)
|
|
90
100
|
clearTimeout(autoSaveTimer.current); };
|
|
91
101
|
}, [values, isDirty, onSaveDraft, autoSaveDelayMs]);
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (!schema)
|
|
104
|
+
return;
|
|
105
|
+
// audit-unvalidated-safeparse-ok: bulk-replaces the whole errors map from
|
|
106
|
+
// the full parse result (so a field that just became valid is cleared),
|
|
107
|
+
// which applyZodIssues (incremental setFieldError-per-issue) can't do.
|
|
108
|
+
const parsed = schema.safeParse(values);
|
|
109
|
+
if (parsed.success) {
|
|
110
|
+
setErrors({});
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const next = {};
|
|
114
|
+
for (const issue of parsed.error.issues) {
|
|
115
|
+
if (!issue.path || issue.path.length === 0)
|
|
116
|
+
continue;
|
|
117
|
+
next[issue.path.map(String).join(".")] = issue.message;
|
|
118
|
+
}
|
|
119
|
+
setErrors(next);
|
|
120
|
+
}, [schema, values]);
|
|
92
121
|
const setFieldError = useCallback((name, error) => {
|
|
93
122
|
setErrors((prev) => {
|
|
94
123
|
if (!error) {
|
|
@@ -121,13 +150,14 @@ export function FormShellProvider({ children, onSaveDraft, isDirty = false, auto
|
|
|
121
150
|
clearFieldError,
|
|
122
151
|
steps: [],
|
|
123
152
|
currentStep: 0,
|
|
124
|
-
goToStep: () => { },
|
|
153
|
+
goToStep: onGoToStep ?? (() => { }),
|
|
125
154
|
nextStep: () => { },
|
|
126
155
|
prevStep: () => { },
|
|
127
|
-
isPublishReady:
|
|
156
|
+
isPublishReady: Object.keys(errors).length === 0,
|
|
128
157
|
isDirty,
|
|
129
158
|
isSubmitting: false,
|
|
130
159
|
stepErrorCounts: [],
|
|
131
|
-
|
|
160
|
+
fieldToStepIndex,
|
|
161
|
+
}), [errors, touched, setFieldError, setFieldTouched, clearFieldError, isDirty, onGoToStep, fieldToStepIndex]);
|
|
132
162
|
return _jsx(FormShellContext.Provider, { value: ctx, children: children });
|
|
133
163
|
}
|
|
@@ -142,6 +142,23 @@
|
|
|
142
142
|
gap: var(--appkit-spacing-1, 0.25rem);
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
.appkit-formshell__error-summary__step-link {
|
|
146
|
+
background: none;
|
|
147
|
+
border: none;
|
|
148
|
+
padding: 0;
|
|
149
|
+
margin: 0;
|
|
150
|
+
font: inherit;
|
|
151
|
+
color: inherit;
|
|
152
|
+
text-align: left;
|
|
153
|
+
cursor: pointer;
|
|
154
|
+
text-decoration: underline;
|
|
155
|
+
text-underline-offset: 2px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.appkit-formshell__error-summary__step-link:hover {
|
|
159
|
+
color: var(--appkit-color-error, #dc2626);
|
|
160
|
+
}
|
|
161
|
+
|
|
145
162
|
/* ─── Body + Steps ───────────────────────────────────────────────────────── */
|
|
146
163
|
|
|
147
164
|
.appkit-formshell__body {
|
package/dist/ui/forms/index.d.ts
CHANGED
|
@@ -10,3 +10,5 @@ export type { FieldTextareaProps } from "./FieldTextarea";
|
|
|
10
10
|
export { FieldTextarea } from "./FieldTextarea";
|
|
11
11
|
export type { ColorPickerFieldProps } from "./ColorPickerField";
|
|
12
12
|
export { ColorPickerField } from "./ColorPickerField";
|
|
13
|
+
export type { FormErrorSummaryProps } from "./FormErrorSummary";
|
|
14
|
+
export { FormErrorSummary } from "./FormErrorSummary";
|