@mohasinac/appkit 4.10.0 → 4.11.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/_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/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/grouped/data.d.ts +4 -0
- package/dist/_internal/server/features/grouped/data.js +24 -0
- 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 +2 -2
- package/dist/_internal/server/features/products/data.js +5 -23
- 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/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/features/products/to-product-item.d.ts +25 -0
- package/dist/_internal/shared/features/products/to-product-item.js +45 -0
- package/dist/client.d.ts +21 -6
- package/dist/client.js +13 -3
- package/dist/constants/api-endpoints.d.ts +6 -9
- package/dist/constants/api-endpoints.js +2 -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 +20 -1
- 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 +12 -0
- package/dist/features/analytics/types.d.ts +1 -1
- package/dist/features/analytics/types.js +8 -0
- 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/categories/components/BrandDetailPageView.js +14 -12
- 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/contact/components/ContactForm.js +17 -4
- package/dist/features/contact/email.d.ts +1 -0
- package/dist/features/contact/email.js +3 -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.js +4 -1
- package/dist/features/grouped/repository/grouped-listings.repository.d.ts +4 -0
- package/dist/features/grouped/repository/grouped-listings.repository.js +18 -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/TitleBarLayout.d.ts +1 -1
- package/dist/features/layout/TitleBarLayout.js +3 -3
- package/dist/features/media/MediaImage.js +39 -3
- 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/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 +1 -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/ProductGrid.js +15 -1
- 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 +89 -36
- 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 +404 -15
- package/dist/index.d.ts +15 -3
- package/dist/index.js +22 -5
- package/dist/next/routing/route-map.d.ts +11 -0
- package/dist/next/routing/route-map.js +5 -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/categories-seed-data.js +69 -0
- package/dist/seed/products-auctions-seed-data.js +11 -0
- package/dist/seed/products-standard-seed-data.js +93 -4
- package/dist/seed/site-settings-seed-data.js +13 -1
- package/dist/seed/store-extensions-seed-data.js +1 -1
- package/dist/server-entry.d.ts +2 -1
- package/dist/server-entry.js +20 -2
- package/dist/server.d.ts +7 -2
- package/dist/server.js +8 -3
- package/dist/styles.css +1 -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/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 +3 -1
- package/dist/utils/media-url.js +20 -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,14 +1,56 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useCallback, useEffect, useState } from "react";
|
|
3
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
|
+
import { z } from "zod";
|
|
4
5
|
import { Alert, Badge, Checkbox, Div, FormField, FormGroup, Heading, Label, Row, Stack, Text, Toggle } from "../../../ui";
|
|
5
6
|
import { StackedViewShell } from "../../../ui";
|
|
7
|
+
import { FormShellContext, useFormShellState, applyZodIssues, FormErrorSummary } from "../../../ui/forms";
|
|
6
8
|
import { StepForm } from "../../shell";
|
|
7
9
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
10
|
import { normalizeError } from "../../../errors/normalize";
|
|
9
11
|
const __P = {
|
|
10
12
|
p3: "p-[var(--appkit-space-3)]",
|
|
11
13
|
};
|
|
14
|
+
// No accountNumber format check here on purpose — the UI supports "leave
|
|
15
|
+
// blank to keep the existing saved account number" on re-save (see the
|
|
16
|
+
// accountNumber field's helpText below), and the exact keep-existing
|
|
17
|
+
// semantics live server-side; a client-side format requirement risks
|
|
18
|
+
// blocking that legitimate blank-resave case.
|
|
19
|
+
const payoutSettingsDraftSchema = z.object({
|
|
20
|
+
method: z.enum(["upi", "bank_transfer"]),
|
|
21
|
+
upiId: z.string(),
|
|
22
|
+
accountHolderName: z.string(),
|
|
23
|
+
accountNumber: z.string(),
|
|
24
|
+
ifscCode: z.string(),
|
|
25
|
+
bankName: z.string(),
|
|
26
|
+
accountType: z.enum(["savings", "current"]),
|
|
27
|
+
gstin: z.string(),
|
|
28
|
+
pan: z.string(),
|
|
29
|
+
businessType: z.string(),
|
|
30
|
+
autoPayout: z.boolean(),
|
|
31
|
+
minimumThreshold: z.string(),
|
|
32
|
+
emiEnabled: z.boolean(),
|
|
33
|
+
}).superRefine((v, ctx) => {
|
|
34
|
+
if (v.method === "upi") {
|
|
35
|
+
if (!v.upiId.trim()) {
|
|
36
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["upiId"], message: "UPI ID is required" });
|
|
37
|
+
}
|
|
38
|
+
else if (!/^[\w.\-_]{2,256}@[a-zA-Z]{2,64}$/.test(v.upiId.trim())) {
|
|
39
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["upiId"], message: "Please enter a valid UPI ID" });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
if (!v.accountHolderName.trim()) {
|
|
44
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["accountHolderName"], message: "Account holder name is required" });
|
|
45
|
+
}
|
|
46
|
+
if (v.ifscCode.trim() && !/^[A-Z]{4}0[A-Z0-9]{6}$/.test(v.ifscCode.trim())) {
|
|
47
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["ifscCode"], message: "Invalid IFSC code" });
|
|
48
|
+
}
|
|
49
|
+
if (!v.bankName.trim()) {
|
|
50
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["bankName"], message: "Bank name is required" });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
12
54
|
const DEFAULT_DRAFT = {
|
|
13
55
|
method: "upi",
|
|
14
56
|
upiId: "",
|
|
@@ -32,6 +74,11 @@ export function SellerPayoutSettingsView({ apiBase = SELLER_ENDPOINTS.PAYOUT_SET
|
|
|
32
74
|
const [error, setError] = useState(null);
|
|
33
75
|
const [success, setSuccess] = useState(false);
|
|
34
76
|
const [currentStep, setCurrentStep] = useState(0);
|
|
77
|
+
const { shellCtx, setFieldError, clearErrors, validate } = useFormShellState(payoutSettingsDraftSchema);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
validate(draft);
|
|
80
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
81
|
+
}, [draft, validate]);
|
|
35
82
|
useEffect(() => {
|
|
36
83
|
fetch(apiBase)
|
|
37
84
|
.then((r) => r.json())
|
|
@@ -97,8 +144,14 @@ export function SellerPayoutSettingsView({ apiBase = SELLER_ENDPOINTS.PAYOUT_SET
|
|
|
97
144
|
body: JSON.stringify(body),
|
|
98
145
|
});
|
|
99
146
|
const json = await res.json();
|
|
100
|
-
if (!res.ok)
|
|
101
|
-
|
|
147
|
+
if (!res.ok) {
|
|
148
|
+
if (Array.isArray(json?.issues) && json.issues.length > 0) {
|
|
149
|
+
applyZodIssues(json.issues, setFieldError);
|
|
150
|
+
}
|
|
151
|
+
setError(json?.error ?? "Failed to save");
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
clearErrors();
|
|
102
155
|
setSuccess(true);
|
|
103
156
|
const updated = json?.data?.payoutDetails ?? { method: draft.method, isConfigured: false };
|
|
104
157
|
setCurrent(updated);
|
|
@@ -116,6 +169,7 @@ export function SellerPayoutSettingsView({ apiBase = SELLER_ENDPOINTS.PAYOUT_SET
|
|
|
116
169
|
const steps = [
|
|
117
170
|
{
|
|
118
171
|
label: "Payout Method",
|
|
172
|
+
fields: ["method", "upiId", "accountHolderName", "accountNumber", "ifscCode", "bankName", "accountType"],
|
|
119
173
|
render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Payout Method" }), current && (_jsx(Row, { className: "mb-2", align: "center", gap: "sm", children: _jsx(Badge, { variant: current.isConfigured ? "success" : "warning", children: current.isConfigured ? "Payout configured" : "Not configured" }) })), current?.isConfigured && (_jsx(Alert, { variant: "info", children: current.method === "upi"
|
|
120
174
|
? `Current UPI: ${current.upiId}`
|
|
121
175
|
: `Current bank: ${current.bankAccount?.bankName ?? ""} — ••••${current.bankAccount?.accountNumberMasked?.slice(-4) ?? "••••"} (${current.bankAccount?.accountType})` })), _jsx(Stack, { gap: "sm", children: ([
|
|
@@ -125,14 +179,25 @@ export function SellerPayoutSettingsView({ apiBase = SELLER_ENDPOINTS.PAYOUT_SET
|
|
|
125
179
|
},
|
|
126
180
|
{
|
|
127
181
|
label: "Tax Info",
|
|
182
|
+
fields: ["gstin", "pan", "businessType"],
|
|
128
183
|
render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Tax Information" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Optional \u2014 required only for GST invoice generation and TDS compliance." }), _jsx(FormField, { name: "gstin", label: "GSTIN (optional)", type: "text", value: values.gstin, onChange: (v) => onChange({ gstin: v.toUpperCase() }), placeholder: "e.g. 22AAAAA0000A1Z5", helpText: "15-character Goods and Services Tax Identification Number.", disabled: busy }), _jsx(FormField, { name: "pan", label: "PAN (optional)", type: "text", value: values.pan, onChange: (v) => onChange({ pan: v.toUpperCase() }), placeholder: "e.g. ABCDE1234F", helpText: "Required for payouts above \u20B950,000 per year (TDS threshold).", disabled: busy }), _jsx(FormField, { name: "businessType", label: "Business Type (optional)", type: "text", value: values.businessType, onChange: (v) => onChange({ businessType: v }), placeholder: "e.g. Individual, Sole Proprietor, Private Limited", disabled: busy })] })),
|
|
129
184
|
},
|
|
130
185
|
{
|
|
131
186
|
label: "Preferences",
|
|
187
|
+
fields: ["autoPayout", "minimumThreshold", "emiEnabled"],
|
|
132
188
|
render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Payout Preferences" }), _jsx(Toggle, { checked: values.autoPayout, onChange: (checked) => onChange({ autoPayout: checked }), label: "Enable auto-payout \u2014 automatically transfer earnings on schedule", disabled: busy }), _jsx(FormField, { name: "minimumThreshold", label: "Minimum Payout Threshold (\u20B9)", type: "number", value: values.minimumThreshold, onChange: (v) => onChange({ minimumThreshold: v }), placeholder: "e.g. 500", helpText: "Payouts are held until your balance exceeds this amount. Leave blank for no minimum.", disabled: busy }), _jsxs(Div, { className: `${__P.p3} bg-[var(--appkit-color-surface-alt)] border border-[var(--appkit-color-border)]`, rounded: "lg", children: [_jsx(Text, { className: "mb-1", size: "sm", weight: "medium", children: "Payout Schedule" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Auto-payouts run every Monday for the previous week's settled orders. Manual payouts can be requested from the Payouts page at any time." })] }), _jsx(Toggle, { checked: values.emiEnabled, onChange: (checked) => onChange({ emiEnabled: checked }), label: "Offer EMI financing on eligible orders", disabled: busy }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Only shown to buyers when platform-wide EMI is also enabled and their cart subtotal from your store exceeds the site's minimum order value. Orders on an EMI plan hold your payout until every installment is paid, unless you mark the item \"ship before EMI completes\"." })] })),
|
|
133
189
|
},
|
|
134
190
|
];
|
|
191
|
+
const fieldToStepIndex = useMemo(() => {
|
|
192
|
+
const map = {};
|
|
193
|
+
steps.forEach((step, i) => {
|
|
194
|
+
step.fields?.forEach((field) => { map[field] = i; });
|
|
195
|
+
});
|
|
196
|
+
return map;
|
|
197
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
198
|
+
}, [steps.length]);
|
|
199
|
+
const wizardShellCtx = useMemo(() => ({ ...shellCtx, fieldToStepIndex, goToStep: (n) => setCurrentStep(n) }), [shellCtx, fieldToStepIndex, setCurrentStep]);
|
|
135
200
|
return (_jsx(StackedViewShell, { portal: "seller", title: "Payout Settings", sections: [
|
|
136
|
-
_jsxs(Div, { children: [error && _jsx(Alert, { variant: "error", className: "mb-4", children: error }), success && _jsx(Alert, { variant: "success", className: "mb-4", children: "Payout details saved." }), _jsx(StepForm, { steps: steps, values: draft, onChange: update, onComplete: handleSave, formId: "seller-payout-settings", currentStep: currentStep, onStepChange: setCurrentStep, completeLabel: "Save Payout Details", isLoading: busy })] }, "payout"),
|
|
201
|
+
_jsxs(Div, { children: [error && _jsx(Alert, { variant: "error", className: "mb-4", children: error }), success && _jsx(Alert, { variant: "success", className: "mb-4", children: "Payout details saved." }), _jsxs(FormShellContext.Provider, { value: wizardShellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(StepForm, { steps: steps, values: draft, onChange: update, onComplete: handleSave, formId: "seller-payout-settings", currentStep: currentStep, onStepChange: setCurrentStep, completeLabel: "Save Payout Details", isLoading: busy })] })] }, "payout"),
|
|
137
202
|
] }));
|
|
138
203
|
}
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { ListingLayoutProps } from "../../../ui";
|
|
3
|
+
/**
|
|
4
|
+
* SellerPayoutDetailContent — the payout detail body (status progress,
|
|
5
|
+
* amount breakdown, refund deductions, dates, orders, reminder toggle).
|
|
6
|
+
* Shared by the seller payouts list's SideDrawer and the standalone
|
|
7
|
+
* `/store/payouts/[id]/view` page so the two surfaces can't drift — same
|
|
8
|
+
* pattern as SellerOrderDetailPanel for orders.
|
|
9
|
+
*/
|
|
10
|
+
export declare function SellerPayoutDetailContent({ payout, onToggleReminder, reminderPending, }: {
|
|
11
|
+
payout: Record<string, unknown>;
|
|
12
|
+
onToggleReminder: (next: boolean) => void;
|
|
13
|
+
reminderPending: boolean;
|
|
14
|
+
}): React.JSX.Element;
|
|
3
15
|
export type SellerPayoutsViewProps = ListingLayoutProps;
|
|
4
16
|
export declare function SellerPayoutsView({ children, ...props }: SellerPayoutsViewProps): React.JSX.Element;
|
|
@@ -4,8 +4,10 @@ import { normalizeError } from "../../../errors/normalize";
|
|
|
4
4
|
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit/client";
|
|
5
5
|
import { sortBy } from "@mohasinac/appkit/client";
|
|
6
6
|
import React, { useState } from "react";
|
|
7
|
+
import { useRouter } from "next/navigation";
|
|
7
8
|
import { Badge, Div, FilterChipGroup, ListingLayout, Row, RowActionMenu, SideDrawer, Span, Stack, Text, Toggle, useToast } from "../../../ui";
|
|
8
9
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
|
+
import { ROUTES } from "../../../next/routing/route-map";
|
|
9
11
|
import { ADMIN_PAYOUT_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
10
12
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
13
|
import { SELLER_BULK_ACTIONS, ROW_ACTION_META } from "../../products/constants/action-defs";
|
|
@@ -28,7 +30,25 @@ function StatusProgress({ status }) {
|
|
|
28
30
|
const currentIndex = PAYOUT_STAGES.indexOf(normalized);
|
|
29
31
|
return (_jsx(Row, { gap: "xs", align: "center", children: PAYOUT_STAGES.map((stage, i) => (_jsxs(React.Fragment, { children: [i > 0 && _jsx(Div, { className: `h-0.5 w-6 ${i <= currentIndex ? "bg-primary" : "bg-[var(--appkit-color-border)]"}` }), _jsx(Span, { size: "xs", weight: i === currentIndex ? "bold" : "medium", className: i <= currentIndex ? "text-primary" : "text-[var(--appkit-color-text-muted)]", children: stage[0].toUpperCase() + stage.slice(1) })] }, stage))) }));
|
|
30
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* SellerPayoutDetailContent — the payout detail body (status progress,
|
|
35
|
+
* amount breakdown, refund deductions, dates, orders, reminder toggle).
|
|
36
|
+
* Shared by the seller payouts list's SideDrawer and the standalone
|
|
37
|
+
* `/store/payouts/[id]/view` page so the two surfaces can't drift — same
|
|
38
|
+
* pattern as SellerOrderDetailPanel for orders.
|
|
39
|
+
*/
|
|
40
|
+
export function SellerPayoutDetailContent({ payout, onToggleReminder, reminderPending, }) {
|
|
41
|
+
const expectedByDate = payout.requestedAt
|
|
42
|
+
? new Date(new Date(payout.requestedAt).getTime() + PAYOUT_CYCLE_DAYS * 24 * 60 * 60 * 1000)
|
|
43
|
+
: null;
|
|
44
|
+
const orderIds = Array.isArray(payout.orderIds) ? payout.orderIds : [];
|
|
45
|
+
const refundDeductions = Array.isArray(payout.refundDeductions)
|
|
46
|
+
? payout.refundDeductions
|
|
47
|
+
: [];
|
|
48
|
+
return (_jsxs(Stack, { gap: "lg", children: [_jsx(StatusProgress, { status: toStringValue(payout.status, "pending") }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Amount" }), _jsx(Text, { size: "xl", weight: "bold", children: toCurrency(payout.netAmount ?? payout.amount ?? payout.totalAmount) })] }), (payout.grossAmount != null || payout.platformFee != null) && (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Breakdown" }), _jsxs(Stack, { gap: "none", children: [payout.grossAmount != null && (_jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "xs", color: "muted", children: "Gross amount" }), _jsx(Text, { size: "xs", children: toCurrency(payout.grossAmount) })] })), payout.platformFee != null && (_jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "xs", color: "muted", children: "Platform fee" }), _jsxs(Text, { size: "xs", children: ["-", toCurrency(payout.platformFee)] })] })), refundDeductions.length > 0 && (_jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "xs", color: "muted", children: "Refund deductions" }), _jsxs(Text, { size: "xs", className: "text-error", children: ["-", toCurrency(refundDeductions.reduce((sum, d) => sum + Number(d.deductedAmount ?? 0), 0))] })] }))] })] })), refundDeductions.length > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Refund deductions (", refundDeductions.length, ")"] }), _jsx(Stack, { gap: "xs", children: refundDeductions.map((d, i) => (_jsxs(Div, { rounded: "lg", padding: "inlineSm", surface: "muted", border: "default", children: [_jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "xs", className: "font-mono", children: d.orderId }), _jsxs(Text, { size: "xs", className: "text-error", children: ["-", toCurrency(d.deductedAmount)] })] }), _jsx(Text, { size: "xs", color: "muted", children: d.reason })] }, `${d.orderId}-${i}`))) })] })), payout.transactionId ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Transaction / reference ID" }), _jsx(Text, { size: "sm", className: "font-mono", children: String(payout.transactionId) })] })) : null, _jsxs(Row, { gap: "lg", wrap: true, children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Requested" }), _jsx(Text, { size: "sm", children: formatDate(payout.requestedAt ?? payout.createdAt) })] }), payout.status === "paid" ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Paid on" }), _jsx(Text, { size: "sm", children: formatDate(payout.processedAt) })] })) : (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Expected by" }), _jsx(Text, { size: "sm", children: expectedByDate ? formatDate(expectedByDate.toISOString()) : "—" })] })), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Payment method" }), _jsx(Text, { size: "sm", className: "capitalize", children: toStringValue(payout.paymentMethod, "—").replace("_", " ") })] })] }), orderIds.length > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Orders included (", orderIds.length, ")"] }), _jsx(Div, { className: "max-h-40", overflow: "y-auto", children: _jsx(Stack, { gap: "xs", children: orderIds.map((oid) => (_jsx(Text, { size: "xs", className: "font-mono", color: "muted", children: oid }, oid))) }) })] })), _jsxs(Row, { align: "center", justify: "between", border: "default", rounded: "lg", padding: "inline", children: [_jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", children: "Remind me" }), _jsx(Text, { size: "xs", color: "muted", children: "Flag this payout for a personal follow-up" })] }), _jsx(Toggle, { checked: Boolean(payout.sellerReminderFlag), onChange: onToggleReminder, disabled: reminderPending, size: "md", "aria-label": ACTIONS.STORE["set-payout-reminder"].ariaLabel })] })] }));
|
|
49
|
+
}
|
|
31
50
|
export function SellerPayoutsView({ children, ...props }) {
|
|
51
|
+
const router = useRouter();
|
|
32
52
|
const { showToast } = useToast();
|
|
33
53
|
const [selectedPayout, setSelectedPayout] = useState(null);
|
|
34
54
|
const [reminderPending, setReminderPending] = useState(false);
|
|
@@ -132,6 +152,10 @@ export function SellerPayoutsView({ children, ...props }) {
|
|
|
132
152
|
label: ACTIONS.STORE["view-payout"].label,
|
|
133
153
|
onClick: () => setSelectedPayout(rawByIdRef.current[row.id] ?? { id: row.id }),
|
|
134
154
|
},
|
|
155
|
+
{
|
|
156
|
+
label: "Open full page",
|
|
157
|
+
onClick: () => router.push(String(ROUTES.STORE.PAYOUT_DETAIL(row.id))),
|
|
158
|
+
},
|
|
135
159
|
{
|
|
136
160
|
label: ACTIONS.STORE["export-payout"].label,
|
|
137
161
|
onClick: () => handleExport([rawByIdRef.current[row.id] ?? { id: row.id }]),
|
|
@@ -142,9 +166,5 @@ export function SellerPayoutsView({ children, ...props }) {
|
|
|
142
166
|
{ id: "true", label: "Show all" },
|
|
143
167
|
], value: pendingFilters.showAllPayouts ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, showAllPayouts: id })), allId: "" })] })),
|
|
144
168
|
};
|
|
145
|
-
|
|
146
|
-
? new Date(new Date(selectedPayout.requestedAt).getTime() + PAYOUT_CYCLE_DAYS * 24 * 60 * 60 * 1000)
|
|
147
|
-
: null;
|
|
148
|
-
const orderIds = Array.isArray(selectedPayout?.orderIds) ? selectedPayout.orderIds : [];
|
|
149
|
-
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(SideDrawer, { isOpen: Boolean(selectedPayout), onClose: () => setSelectedPayout(null), title: selectedPayout ? `Payout #${toStringValue(selectedPayout.payoutNumber ?? selectedPayout.id, "")}` : "Payout", mode: "view", children: selectedPayout && (_jsxs(Stack, { gap: "lg", children: [_jsx(StatusProgress, { status: toStringValue(selectedPayout.status, "pending") }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Amount" }), _jsx(Text, { size: "xl", weight: "bold", children: toCurrency(selectedPayout.amount ?? selectedPayout.totalAmount) })] }), selectedPayout.transactionId ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Transaction / reference ID" }), _jsx(Text, { size: "sm", className: "font-mono", children: String(selectedPayout.transactionId) })] })) : null, _jsxs(Row, { gap: "lg", wrap: true, children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Requested" }), _jsx(Text, { size: "sm", children: formatDate(selectedPayout.requestedAt ?? selectedPayout.createdAt) })] }), selectedPayout.status === "paid" ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Paid on" }), _jsx(Text, { size: "sm", children: formatDate(selectedPayout.processedAt) })] })) : (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Expected by" }), _jsx(Text, { size: "sm", children: expectedByDate ? formatDate(expectedByDate.toISOString()) : "—" })] })), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", color: "muted", children: "Payment method" }), _jsx(Text, { size: "sm", className: "capitalize", children: toStringValue(selectedPayout.paymentMethod, "—").replace("_", " ") })] })] }), orderIds.length > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Orders included (", orderIds.length, ")"] }), _jsx(Div, { className: "max-h-40", overflow: "y-auto", children: _jsx(Stack, { gap: "xs", children: orderIds.map((oid) => (_jsx(Text, { size: "xs", className: "font-mono", color: "muted", children: oid }, oid))) }) })] })), _jsxs(Row, { align: "center", justify: "between", border: "default", rounded: "lg", padding: "inline", children: [_jsxs(Stack, { gap: "none", children: [_jsx(Text, { size: "sm", weight: "medium", children: "Remind me" }), _jsx(Text, { size: "xs", color: "muted", children: "Flag this payout for a personal follow-up" })] }), _jsx(Toggle, { checked: Boolean(selectedPayout.sellerReminderFlag), onChange: handleToggleReminder, disabled: reminderPending, size: "md", "aria-label": ACTIONS.STORE["set-payout-reminder"].ariaLabel })] })] })) })] }));
|
|
169
|
+
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(SideDrawer, { isOpen: Boolean(selectedPayout), onClose: () => setSelectedPayout(null), title: selectedPayout ? `Payout #${toStringValue(selectedPayout.payoutNumber ?? selectedPayout.id, "")}` : "Payout", mode: "view", children: selectedPayout && (_jsx(SellerPayoutDetailContent, { payout: selectedPayout, onToggleReminder: handleToggleReminder, reminderPending: reminderPending })) })] }));
|
|
150
170
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import type { MediaField } from "../../media/types";
|
|
2
3
|
export type ProductListingMode = "standard" | "auction" | "pre-order" | "prize-draw" | "bundle" | "classified" | "digital-code" | "live" | "art" | "stickers";
|
|
3
4
|
export interface SellerProductDraft {
|
|
4
5
|
title?: string;
|
|
@@ -10,7 +11,7 @@ export interface SellerProductDraft {
|
|
|
10
11
|
tags?: string[];
|
|
11
12
|
mainImage?: string;
|
|
12
13
|
images?: string[];
|
|
13
|
-
video?:
|
|
14
|
+
video?: MediaField;
|
|
14
15
|
youtubeId?: string;
|
|
15
16
|
price?: number;
|
|
16
17
|
compareAtPrice?: number;
|
|
@@ -70,13 +71,19 @@ export interface SellerProductDraft {
|
|
|
70
71
|
printFinish?: string;
|
|
71
72
|
printEditionSize?: number;
|
|
72
73
|
}
|
|
74
|
+
/** Shape returned by `onSave`/`onPublish` — structurally compatible with appkit's `ActionResult<T>` failure/success arms, without importing the server-only type into this client component. */
|
|
75
|
+
export interface ProductActionResult {
|
|
76
|
+
ok: boolean;
|
|
77
|
+
error?: string;
|
|
78
|
+
issues?: unknown[];
|
|
79
|
+
}
|
|
73
80
|
export interface SellerProductShellProps {
|
|
74
81
|
mode: "create" | "edit";
|
|
75
82
|
listingType?: ProductListingMode;
|
|
76
83
|
initialValues?: SellerProductDraft;
|
|
77
84
|
productId?: string;
|
|
78
|
-
onSave: (draft: SellerProductDraft) =>
|
|
79
|
-
onPublish: (draft: SellerProductDraft) =>
|
|
85
|
+
onSave: (draft: SellerProductDraft) => Promise<ProductActionResult>;
|
|
86
|
+
onPublish: (draft: SellerProductDraft) => Promise<ProductActionResult>;
|
|
80
87
|
onDiscard?: () => void;
|
|
81
88
|
isLoading?: boolean;
|
|
82
89
|
storeSlug?: string;
|
|
@@ -4,7 +4,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
|
4
4
|
import { useRouter } from "next/navigation";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { FormShell, StepForm, StepFormActions, useFormShell } from "../../shell";
|
|
7
|
-
import {
|
|
7
|
+
import { FormShellContext, useFormShellState, applyZodIssues, FormErrorSummary } from "../../../ui/forms";
|
|
8
8
|
import { Alert, Button, Div, FormField, FormGroup, Heading, Section, Stack, TagInput, Text, Toggle, useToast } from "../../../ui";
|
|
9
9
|
import { ImageUpload, MediaUploadField, MediaUploadList, useMediaUpload } from "../../media";
|
|
10
10
|
import { StoreAddressSelectorCreate } from "../../stores/components/StoreAddressSelectorCreate";
|
|
@@ -25,6 +25,18 @@ const BUNDLE_PLUGIN_FALLBACK = {
|
|
|
25
25
|
function pluginForMode(mode) {
|
|
26
26
|
return mode === "bundle" ? BUNDLE_PLUGIN_FALLBACK : pluginFor(mode);
|
|
27
27
|
}
|
|
28
|
+
// Video duration is required for a directly-uploaded file (captured
|
|
29
|
+
// automatically off the video element — see video-poster.ts) but can't be
|
|
30
|
+
// obtained client-side for a YouTube/External-URL-sourced video without a
|
|
31
|
+
// server round-trip, so it's optional for those two sources only.
|
|
32
|
+
const productVideoSchema = z.object({
|
|
33
|
+
url: z.string().min(1),
|
|
34
|
+
type: z.literal("video").optional(),
|
|
35
|
+
thumbnailUrl: z.string().optional(),
|
|
36
|
+
source: z.enum(["upload", "youtube", "external"]).optional(),
|
|
37
|
+
youtubeId: z.string().optional(),
|
|
38
|
+
duration: z.number().positive().max(600).optional(),
|
|
39
|
+
}).refine((v) => v.source === "youtube" || v.source === "external" || (v.duration != null && v.duration > 0), { message: "Video duration could not be detected — try re-uploading the file.", path: ["duration"] });
|
|
28
40
|
const sellerProductSchema = z.object({
|
|
29
41
|
title: z.string().min(1, "Product title is required").max(200),
|
|
30
42
|
description: z.string().max(10000).optional().or(z.literal("")),
|
|
@@ -32,6 +44,8 @@ const sellerProductSchema = z.object({
|
|
|
32
44
|
category: z.string().optional(),
|
|
33
45
|
brand: z.string().optional(),
|
|
34
46
|
isActive: z.boolean().optional(),
|
|
47
|
+
images: z.array(z.string()).max(5, "Up to 5 gallery images allowed").optional(),
|
|
48
|
+
video: productVideoSchema.optional(),
|
|
35
49
|
}).passthrough();
|
|
36
50
|
const CONDITION_OPTIONS = [
|
|
37
51
|
{ value: "new", label: "New" },
|
|
@@ -70,7 +84,7 @@ function StepMedia({ values, onChange, storeSlug = "store", }) {
|
|
|
70
84
|
name: values.title ?? "product",
|
|
71
85
|
store: storeSlug,
|
|
72
86
|
category: values.category ?? "uncategorized",
|
|
73
|
-
}), onChange: (url) => onChange({ mainImage: url }), label: "Main Image", helperText: "Recommended: 800\u00D7800px square (JPG, PNG, WebP \u2014 max 10 MB)", enableAdvancedCrop: true, cropAspectRatio: 1 }), _jsx(MediaUploadList, { label: "Gallery Images (up to
|
|
87
|
+
}), onChange: (url) => onChange({ mainImage: url }), label: "Main Image", helperText: "Recommended: 800\u00D7800px square (JPG, PNG, WebP \u2014 max 10 MB)", enableAdvancedCrop: true, cropAspectRatio: 1 }), _jsx(MediaUploadList, { label: "Gallery Images (up to 5)", value: galleryFields, onChange: (fields) => onChange({ images: fields.map((f) => f.url) }), onUpload: (file) => {
|
|
74
88
|
idxRef.current += 1;
|
|
75
89
|
return upload(file, "products", true, {
|
|
76
90
|
type: "product-image",
|
|
@@ -79,7 +93,11 @@ function StepMedia({ values, onChange, storeSlug = "store", }) {
|
|
|
79
93
|
store: storeSlug,
|
|
80
94
|
category: values.category ?? "uncategorized",
|
|
81
95
|
});
|
|
82
|
-
}, accept: "image/*,video/*", maxItems:
|
|
96
|
+
}, accept: "image/*,video/*", maxItems: 5, maxSizeMB: 10, helperText: "Show multiple angles, grading details, or box contents." }), _jsx(MediaUploadField, { label: "Product Video (optional)", value: values.video?.url ?? "", onChange: (url) => onChange({
|
|
97
|
+
video: url
|
|
98
|
+
? { ...(values.video ?? { type: "video", source: "upload" }), url }
|
|
99
|
+
: undefined,
|
|
100
|
+
}), onChangeField: (media) => onChange({ video: media ?? undefined }), onThumbnailChange: (thumbnailUrl) => onChange({ video: values.video ? { ...values.video, thumbnailUrl } : undefined }), onDurationChange: (duration) => onChange({ video: values.video ? { ...values.video, duration } : undefined }), onUpload: (file) => upload(file, "products", true, {
|
|
83
101
|
type: "product-video",
|
|
84
102
|
name: values.title ?? "product",
|
|
85
103
|
store: storeSlug,
|
|
@@ -223,25 +241,7 @@ export function SellerProductShell({ mode, listingType = "standard", initialValu
|
|
|
223
241
|
const router = useRouter();
|
|
224
242
|
const { showToast } = useToast();
|
|
225
243
|
const { upload: shellUpload } = useMediaUpload();
|
|
226
|
-
|
|
227
|
-
const autoSaveTimer = useRef(null);
|
|
228
|
-
const draftRef = useRef(draft);
|
|
229
|
-
draftRef.current = draft;
|
|
230
|
-
const onSaveRef = useRef(onSave);
|
|
231
|
-
onSaveRef.current = onSave;
|
|
232
|
-
useEffect(() => {
|
|
233
|
-
if (mode !== "create" || !isDirty)
|
|
234
|
-
return;
|
|
235
|
-
if (autoSaveTimer.current)
|
|
236
|
-
clearTimeout(autoSaveTimer.current);
|
|
237
|
-
autoSaveTimer.current = setTimeout(() => {
|
|
238
|
-
const result = onSaveRef.current(draftRef.current);
|
|
239
|
-
if (result && typeof result.catch === "function")
|
|
240
|
-
result.catch(console.error);
|
|
241
|
-
}, 2000);
|
|
242
|
-
return () => { if (autoSaveTimer.current)
|
|
243
|
-
clearTimeout(autoSaveTimer.current); };
|
|
244
|
-
}, [draft, isDirty, mode]);
|
|
244
|
+
const { shellCtx, setFieldError, clearErrors, validate } = useFormShellState(sellerProductSchema);
|
|
245
245
|
const update = useCallback((partial) => {
|
|
246
246
|
setDraft((prev) => {
|
|
247
247
|
const next = { ...prev, ...partial };
|
|
@@ -275,22 +275,35 @@ export function SellerProductShell({ mode, listingType = "standard", initialValu
|
|
|
275
275
|
router.back();
|
|
276
276
|
}, [onDiscard, router]);
|
|
277
277
|
// `silent` suppresses the success toast — used by the debounced auto-save
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
-
//
|
|
278
|
+
// effect above so it doesn't pop a "Saved." toast every couple of seconds
|
|
279
|
+
// while the seller is still typing. The manual "Save Draft"/"Save Changes"
|
|
280
|
+
// button click stays loud (silent=false).
|
|
281
|
+
//
|
|
282
|
+
// `onSave`/`onPublish` never throw in normal operation (backed by
|
|
283
|
+
// `wrapAction`-wrapped server actions, which always resolve to a
|
|
284
|
+
// `{ok, ...}` envelope) — the try/catch here is a defensive fallback for
|
|
285
|
+
// a genuinely unexpected rejection (e.g. a network failure during the RSC
|
|
286
|
+
// round-trip itself), not the primary error path.
|
|
281
287
|
const handleSave = useCallback(async (silent = false) => {
|
|
282
288
|
try {
|
|
283
|
-
await onSave(draft);
|
|
284
|
-
|
|
289
|
+
const result = await onSave(draft);
|
|
290
|
+
if (result.ok) {
|
|
291
|
+
markClean();
|
|
292
|
+
clearErrors();
|
|
293
|
+
if (!silent)
|
|
294
|
+
showToast("Saved.", "success");
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
applyZodIssues(result.issues ?? [], setFieldError);
|
|
285
298
|
if (!silent)
|
|
286
|
-
showToast("
|
|
299
|
+
showToast(result.error || "Fix the highlighted errors and try again.", "error");
|
|
287
300
|
}
|
|
288
301
|
catch (err) {
|
|
289
302
|
void normalizeError(err);
|
|
290
303
|
if (!silent)
|
|
291
304
|
showToast(err instanceof Error ? err.message : "Failed to save.", "error");
|
|
292
305
|
}
|
|
293
|
-
}, [draft, onSave, markClean, showToast]);
|
|
306
|
+
}, [draft, onSave, markClean, clearErrors, setFieldError, showToast]);
|
|
294
307
|
const handleAutoSave = useCallback(async () => {
|
|
295
308
|
try {
|
|
296
309
|
await handleSave(true);
|
|
@@ -299,22 +312,39 @@ export function SellerProductShell({ mode, listingType = "standard", initialValu
|
|
|
299
312
|
void normalizeError(err);
|
|
300
313
|
}
|
|
301
314
|
}, [handleSave]);
|
|
315
|
+
// Auto-save in create mode — debounce 2s on any draft change. Routed
|
|
316
|
+
// through handleAutoSave/handleSave (not a direct onSave call) so a
|
|
317
|
+
// validation failure during auto-save applies field errors + populates
|
|
318
|
+
// FormErrorSummary the same way a manual save does.
|
|
319
|
+
useEffect(() => {
|
|
320
|
+
if (mode !== "create" || !isDirty)
|
|
321
|
+
return;
|
|
322
|
+
const timer = setTimeout(() => { void handleAutoSave(); }, 2000);
|
|
323
|
+
return () => clearTimeout(timer);
|
|
324
|
+
}, [draft, isDirty, mode, handleAutoSave]);
|
|
302
325
|
const handlePublish = useCallback(async () => {
|
|
303
326
|
try {
|
|
304
|
-
await onPublish({ ...draft, status: "published" });
|
|
305
|
-
|
|
306
|
-
|
|
327
|
+
const result = await onPublish({ ...draft, status: "published" });
|
|
328
|
+
if (result.ok) {
|
|
329
|
+
markClean();
|
|
330
|
+
clearErrors();
|
|
331
|
+
showToast("Published.", "success");
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
applyZodIssues(result.issues ?? [], setFieldError);
|
|
335
|
+
showToast(result.error || "Fix the highlighted errors and try again.", "error");
|
|
307
336
|
}
|
|
308
337
|
catch (err) {
|
|
309
338
|
void normalizeError(err);
|
|
310
339
|
showToast(err instanceof Error ? err.message : "Failed to publish.", "error");
|
|
311
340
|
}
|
|
312
|
-
}, [draft, onPublish, markClean, showToast]);
|
|
341
|
+
}, [draft, onPublish, markClean, clearErrors, setFieldError, showToast]);
|
|
313
342
|
const listingTypeLabel = pluginForMode(listingType).typeLabel;
|
|
314
343
|
const typeSpecificStep = TYPE_SPECIFIC_STEPS[listingType] ?? null;
|
|
315
344
|
const steps = [
|
|
316
345
|
{
|
|
317
346
|
label: "Basic",
|
|
347
|
+
fields: ["title", "description", "category", "brand", "condition", "tags", "barcodeId"],
|
|
318
348
|
render: ({ values, onChange }) => (_jsx(StepBasic, { values: values, onChange: onChange, renderCategorySelector: renderCategorySelector, renderBrandSelector: renderBrandSelector, renderTemplateSelector: renderTemplateSelector })),
|
|
319
349
|
validate: (v) => {
|
|
320
350
|
if (!v.title?.trim() || v.title.trim().length < 3)
|
|
@@ -326,12 +356,14 @@ export function SellerProductShell({ mode, listingType = "standard", initialValu
|
|
|
326
356
|
},
|
|
327
357
|
{
|
|
328
358
|
label: "Media",
|
|
359
|
+
fields: ["mainImage", "images", "video", "video.url", "video.duration", "video.thumbnailUrl", "youtubeId", "externalVideoUrl"],
|
|
329
360
|
render: ({ values, onChange }) => (_jsx(StepMedia, { values: values, onChange: onChange, storeSlug: storeSlug })),
|
|
330
361
|
validate: (v) => (!v.mainImage ? "A main image is required" : null),
|
|
331
362
|
},
|
|
332
363
|
...(typeSpecificStep ? [typeSpecificStep] : []),
|
|
333
364
|
{
|
|
334
365
|
label: "Pricing",
|
|
366
|
+
fields: ["price", "compareAtPrice", "stockQuantity", "allowOffers", "minOfferPercent", "gstRate", "hsnCode"],
|
|
335
367
|
render: ({ values, onChange }) => (_jsx(StepPricing, { values: values, onChange: onChange, listingType: listingType })),
|
|
336
368
|
validate: (v) => {
|
|
337
369
|
if (!v.price)
|
|
@@ -345,13 +377,34 @@ export function SellerProductShell({ mode, listingType = "standard", initialValu
|
|
|
345
377
|
},
|
|
346
378
|
{
|
|
347
379
|
label: "Shipping",
|
|
380
|
+
fields: ["shippingPaidBy", "pickupAddressId", "insurance", "insuranceCost"],
|
|
348
381
|
render: ({ values, onChange }) => (_jsx(StepShipping, { values: values, onChange: onChange, renderAddressSelector: renderAddressSelector })),
|
|
349
382
|
},
|
|
350
383
|
{
|
|
351
384
|
label: "Publish",
|
|
385
|
+
fields: ["seoTitle", "seoDescription", "isOnSale", "status"],
|
|
352
386
|
render: ({ values, onChange }) => (_jsx(StepPublish, { values: values, onChange: onChange })),
|
|
353
387
|
},
|
|
354
388
|
];
|
|
389
|
+
// Field-name → owning-step-index map for FormErrorSummary's step
|
|
390
|
+
// attribution/jump-to-step links (full-wizard create mode only — quick
|
|
391
|
+
// and edit modes don't have step navigation).
|
|
392
|
+
const fieldToStepIndex = useMemo(() => {
|
|
393
|
+
const map = {};
|
|
394
|
+
steps.forEach((step, i) => {
|
|
395
|
+
step.fields?.forEach((field) => { map[field] = i; });
|
|
396
|
+
});
|
|
397
|
+
return map;
|
|
398
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
399
|
+
}, [steps.length, typeSpecificStep]);
|
|
400
|
+
// Live validation — re-run on every draft change, not just on submit, so
|
|
401
|
+
// FormErrorSummary and inline field errors both stay current as the
|
|
402
|
+
// seller types (per the "even on-change errors must be shown" requirement).
|
|
403
|
+
useEffect(() => {
|
|
404
|
+
validate(draft);
|
|
405
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
406
|
+
}, [draft, validate]);
|
|
407
|
+
const wizardShellCtx = useMemo(() => ({ ...shellCtx, fieldToStepIndex, goToStep: (n) => setCurrentStep(n) }), [shellCtx, fieldToStepIndex, setCurrentStep]);
|
|
355
408
|
const handleNext = useCallback(async () => {
|
|
356
409
|
const step = steps[currentStep];
|
|
357
410
|
if (step?.validate) {
|
|
@@ -384,7 +437,7 @@ export function SellerProductShell({ mode, listingType = "standard", initialValu
|
|
|
384
437
|
? `New ${listingTypeLabel}`
|
|
385
438
|
: draft.title ?? `Edit ${listingTypeLabel}`;
|
|
386
439
|
if (mode === "create" && formMode === "quick") {
|
|
387
|
-
return (_jsx(
|
|
440
|
+
return (_jsx(FormShellContext.Provider, { value: shellCtx, children: _jsx(FormShell, { isOpen: true, onClose: handleDiscard, title: title, breadcrumb: breadcrumb, isDirty: isDirty, isLoading: isLoading, schema: sellerProductSchema, children: _jsx(QuickProductForm, { values: draft, onChange: update, onPublish: () => void handlePublish(), onSave: () => void handleSave(), onSwitchToFull: () => setFormMode("full"), isLoading: isLoading, renderCategorySelector: renderCategorySelector, storeSlug: storeSlug, onUploadImage: (file) => shellUpload(file, "products", true, {
|
|
388
441
|
type: "product-image",
|
|
389
442
|
index: 1,
|
|
390
443
|
name: draft.title ?? "product",
|
|
@@ -393,12 +446,12 @@ export function SellerProductShell({ mode, listingType = "standard", initialValu
|
|
|
393
446
|
}) }) }) }));
|
|
394
447
|
}
|
|
395
448
|
if (mode === "create") {
|
|
396
|
-
return (_jsx(FormShell, { isOpen: true, onClose: handleDiscard, title: title, breadcrumb: breadcrumb, isDirty: isDirty, isLoading: isLoading, previewSlot: previewSlot, splitPreview: !!previewSlot, renderBottomBar: () => (_jsxs(Div, { className: "flex-shrink-0 border-t border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", children: [_jsx(StepFormActions, { currentStep: currentStep, totalSteps: steps.length, onNext: () => void handleNext(), onPrev: currentStep > 0 ? () => setCurrentStep((c) => c - 1) : undefined, completeLabel: `Publish ${listingTypeLabel}`, isLoading: isLoading && currentStep === steps.length - 1, disabled: isLoading }), currentStep === 0 && (_jsx(Div, { paddingX: "x-5", padding: "b-sm", children: _jsx(Button, { variant: "ghost", size: "sm", onClick: () => void handleSave(), disabled: isLoading || !draft.title?.trim(), children: "Save as draft & finish later" }) })), stepError && (_jsx(Text, { className: "text-[var(--appkit-color-error)] px-[1.25rem] pb-[0.75rem]", size: "sm", children: stepError }))
|
|
449
|
+
return (_jsx(FormShell, { isOpen: true, onClose: handleDiscard, title: title, breadcrumb: breadcrumb, isDirty: isDirty, isLoading: isLoading, previewSlot: previewSlot, splitPreview: !!previewSlot, renderBottomBar: () => (_jsxs(Div, { className: "flex-shrink-0 border-t border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", children: [_jsx(StepFormActions, { currentStep: currentStep, totalSteps: steps.length, onNext: () => void handleNext(), onPrev: currentStep > 0 ? () => setCurrentStep((c) => c - 1) : undefined, completeLabel: `Publish ${listingTypeLabel}`, isLoading: isLoading && currentStep === steps.length - 1, disabled: isLoading }), currentStep === 0 && (_jsx(Div, { paddingX: "x-5", padding: "b-sm", children: _jsx(Button, { variant: "ghost", size: "sm", onClick: () => void handleSave(), disabled: isLoading || !draft.title?.trim(), children: "Save as draft & finish later" }) })), stepError && (_jsx(Text, { className: "text-[var(--appkit-color-error)] px-[1.25rem] pb-[0.75rem]", size: "sm", children: stepError })), _jsx(FormShellContext.Provider, { value: wizardShellCtx, children: _jsx(Div, { paddingX: "x-5", padding: "b-sm", children: _jsx(FormErrorSummary, {}) }) })] })), children: _jsx(FormShellContext.Provider, { value: wizardShellCtx, children: _jsx(StepForm, { steps: steps, values: draft, onChange: update, onComplete: handlePublish, completeLabel: `Publish ${listingTypeLabel}`, currentStep: currentStep, onStepChange: setCurrentStep, isLoading: isLoading, hideActions: true, stepErrors: stepValidationErrors }) }) }));
|
|
397
450
|
}
|
|
398
451
|
// Edit mode — FormShell with section nav + full form
|
|
399
452
|
const editSections = [
|
|
400
453
|
...EDIT_SECTIONS,
|
|
401
454
|
...(typeSpecificStep ? [{ id: typeSpecificStep.id, label: typeSpecificStep.label }] : []),
|
|
402
455
|
];
|
|
403
|
-
return (_jsx(FormShell, { isOpen: true, onClose: handleDiscard, title: title, breadcrumb: breadcrumb, isDirty: isDirty, isLoading: isLoading, sections: editSections, onSaveDraft: handleSave, onPublish: handlePublish, saveLabel: "Save Changes", publishLabel: "Update", previewSlot: previewSlot,
|
|
456
|
+
return (_jsx(FormShell, { isOpen: true, onClose: handleDiscard, title: title, breadcrumb: breadcrumb, isDirty: isDirty, isLoading: isLoading, sections: editSections, onSaveDraft: handleSave, onPublish: handlePublish, saveLabel: "Save Changes", publishLabel: "Update", previewSlot: previewSlot, footerTopSlot: _jsx(FormShellContext.Provider, { value: shellCtx, children: _jsx(FormErrorSummary, {}) }), children: _jsx(FormShellContext.Provider, { value: shellCtx, children: _jsxs(Stack, { gap: "lg", children: [_jsxs(Section, { id: "basic", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Basic Info" }), _jsx(StepBasic, { values: draft, onChange: update, renderCategorySelector: renderCategorySelector, renderBrandSelector: renderBrandSelector, renderTemplateSelector: renderTemplateSelector })] }), _jsxs(Section, { id: "media", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Media" }), _jsx(StepMedia, { values: draft, onChange: update, storeSlug: storeSlug })] }), typeSpecificStep && (_jsxs(Section, { id: typeSpecificStep.id, children: [_jsx(Heading, { level: 3, className: "mb-4", children: typeSpecificStep.sectionHeading ?? typeSpecificStep.label }), typeSpecificStep.render({ values: draft, onChange: update, errors: {} })] })), _jsxs(Section, { id: "pricing", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Pricing" }), _jsx(StepPricing, { values: draft, onChange: update, listingType: listingType })] }), _jsxs(Section, { id: "shipping", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Shipping" }), _jsx(StepShipping, { values: draft, onChange: update, renderAddressSelector: renderAddressSelector })] }), _jsxs(Section, { id: "publish", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Publish" }), _jsx(StepPublish, { values: draft, onChange: update }), onSaveAsTemplate && (_jsxs(Div, { className: "mt-4 border-t border-[var(--appkit-color-border)]", padding: "t-md", children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onSaveAsTemplate(draft), children: "Save as Template" }), _jsx(Text, { className: "mt-1 text-[var(--appkit-color-secondary-text)]", size: "xs", children: "Save these settings as a reusable template for future listings." })] }))] })] }) }) }));
|
|
404
457
|
}
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
export interface
|
|
3
|
-
isOpen: boolean;
|
|
2
|
+
export interface SellerProductsFilterFieldsProps {
|
|
4
3
|
pendingFilters: Record<string, string>;
|
|
5
4
|
statusOptions: ReadonlyArray<{
|
|
6
5
|
id: string;
|
|
7
6
|
label: string;
|
|
8
7
|
}>;
|
|
9
|
-
activeFilterCount: number;
|
|
10
8
|
onChange: (next: Record<string, string>) => void;
|
|
11
|
-
onClear: () => void;
|
|
12
|
-
onApply: () => void;
|
|
13
|
-
onClose: () => void;
|
|
14
9
|
}
|
|
15
|
-
export declare function
|
|
10
|
+
export declare function SellerProductsFilterFields({ pendingFilters, statusOptions, onChange, }: SellerProductsFilterFieldsProps): React.JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Div, FilterChipGroup,
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { Div, FilterChipGroup, Row, Stack, Text } from "../../../ui";
|
|
4
4
|
import { FieldInput, FieldSelect } from "../../../ui/forms";
|
|
5
5
|
import { CategoryInlineSelect } from "./CategoryInlineSelect";
|
|
6
6
|
import { BrandInlineSelect } from "./BrandInlineSelect";
|
|
@@ -12,7 +12,7 @@ const CONDITION_OPTIONS = [
|
|
|
12
12
|
{ value: "refurbished", label: "Refurbished" },
|
|
13
13
|
{ value: "for-parts", label: "For parts" },
|
|
14
14
|
];
|
|
15
|
-
export function
|
|
15
|
+
export function SellerProductsFilterFields({ pendingFilters, statusOptions, onChange, }) {
|
|
16
16
|
const patch = (k, v) => onChange({ ...pendingFilters, [k]: v });
|
|
17
|
-
return (_jsxs(
|
|
17
|
+
return (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: statusOptions, value: pendingFilters.status ?? "", onChange: (id) => patch("status", id) }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", weight: "semibold", color: "muted", transform: "uppercase", children: "Category" }), _jsx(CategoryInlineSelect, { value: pendingFilters.category ?? "", onChange: (v) => patch("category", v), placeholder: "Search categories\u2026" })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", weight: "semibold", color: "muted", transform: "uppercase", children: "Brand" }), _jsx(BrandInlineSelect, { value: pendingFilters.brand ?? "", onChange: (v) => patch("brand", v), placeholder: "Search brands\u2026", allowCreate: false })] }), _jsx(FieldSelect, { name: "condition", label: "Condition", value: pendingFilters.condition ?? "", onChange: (v) => patch("condition", v), options: CONDITION_OPTIONS }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "xs", weight: "semibold", color: "muted", transform: "uppercase", children: "Price (\u20B9 Rupees)" }), _jsxs(Row, { gap: "sm", children: [_jsx(Div, { className: "flex-1", children: _jsx(FieldInput, { name: "minPrice", type: "number", min: 0, value: pendingFilters.minPrice ?? "", onChange: (v) => patch("minPrice", v), placeholder: "min" }) }), _jsx(Div, { className: "flex-1", children: _jsx(FieldInput, { name: "maxPrice", type: "number", min: 0, value: pendingFilters.maxPrice ?? "", onChange: (v) => patch("maxPrice", v), placeholder: "max" }) })] })] }), _jsx(FieldInput, { name: "tags", label: "Tags (comma-separated)", type: "text", value: pendingFilters.tags ?? "", onChange: (v) => patch("tags", v), placeholder: "pokemon, vintage, holo" }), _jsx(FieldInput, { name: "badges", label: "Badges (feature slugs)", type: "text", value: pendingFilters.badges ?? "", onChange: (v) => patch("badges", v), placeholder: "feature-free-shipping, feature-verified" })] }));
|
|
18
18
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
|
|
3
|
-
export interface SellerProductsViewProps extends ListingLayoutProps {
|
|
2
|
+
export interface SellerProductsViewProps {
|
|
4
3
|
onDeleteProduct?: (id: string) => Promise<void>;
|
|
5
|
-
/** S-STORE-2-E — "New Listing" toolbar button. */
|
|
6
4
|
onCreateClick?: () => void;
|
|
7
5
|
}
|
|
8
|
-
export declare function SellerProductsView({ onDeleteProduct, onCreateClick,
|
|
6
|
+
export declare function SellerProductsView({ onDeleteProduct, onCreateClick, }: SellerProductsViewProps): React.JSX.Element;
|