@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
|
@@ -4,9 +4,11 @@ import { useApiMutation } from "@mohasinac/appkit/client";
|
|
|
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 { useQueryClient } from "@tanstack/react-query";
|
|
8
9
|
import { FilterChipGroup, ListingLayout, RowActionMenu, useToast } from "../../../ui";
|
|
9
10
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
11
|
+
import { ROUTES } from "../../../next/routing/route-map";
|
|
10
12
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
13
|
import { ADMIN_BULK_ACTIONS, ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
|
|
12
14
|
import { ADMIN_STORE_STATUS_TABS } from "../constants/filter-tabs";
|
|
@@ -18,6 +20,7 @@ export function AdminStoresView({ children, ...props }) {
|
|
|
18
20
|
const [editorRow, setEditorRow] = useState(null);
|
|
19
21
|
const toast = useToast();
|
|
20
22
|
const queryClient = useQueryClient();
|
|
23
|
+
const router = useRouter();
|
|
21
24
|
const verifyStore = useApiMutation({
|
|
22
25
|
mutationFn: (storeId) => apiClient.patch(ADMIN_ENDPOINTS.STORE_BY_ID(storeId), { isVerified: true }),
|
|
23
26
|
onSuccess: () => {
|
|
@@ -96,6 +99,10 @@ export function AdminStoresView({ children, ...props }) {
|
|
|
96
99
|
label: ACTIONS.ADMIN["manage-store"].label,
|
|
97
100
|
onClick: () => setEditorRow(row),
|
|
98
101
|
},
|
|
102
|
+
{
|
|
103
|
+
label: "Open full page",
|
|
104
|
+
onClick: () => router.push(String(ROUTES.ADMIN.STORE_DETAIL(row.id))),
|
|
105
|
+
},
|
|
99
106
|
{
|
|
100
107
|
label: ACTIONS.ADMIN["verify-store"].label,
|
|
101
108
|
onClick: () => verifyStore.mutate(row.id),
|
|
@@ -6,7 +6,7 @@ import React from "react";
|
|
|
6
6
|
import { useQuery } from "@tanstack/react-query";
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { Button, ConfirmDeleteModal, Div, Form, Input, StackedViewShell, useToast, } from "../../../ui";
|
|
9
|
-
import { FieldInput } from "../../../ui/forms";
|
|
9
|
+
import { FieldInput, FormErrorSummary } from "../../../ui/forms";
|
|
10
10
|
const sublistingCategoryFormSchema = z.object({
|
|
11
11
|
name: z.string().min(1, "Name is required").max(120),
|
|
12
12
|
slug: z.string().regex(/^[a-z0-9-]+$/, "Lowercase letters, digits and hyphens only").optional().or(z.literal("")),
|
|
@@ -81,7 +81,7 @@ export function AdminSublistingCategoryEditorView({ categoryId, onSaved, onDelet
|
|
|
81
81
|
_jsx(Form, { schema: sublistingCategoryFormSchema, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsxs(Div, { layout: "grid", gap: "4", className: "sm:grid-cols-2", children: [_jsx(FieldInput, { name: "name", label: "Category name", value: name, onChange: (v) => setName(v), required: true, placeholder: "e.g. Base Set Charizard 108/120" }), _jsx(Input, { label: "Item code", value: itemCode, onChange: (e) => setItemCode(e.target.value), placeholder: "e.g. PSA 10, 108/120, STH", helperText: "Grade, card number, or series code. Optional." })] }), _jsx(Input, { label: "Description", value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Brief description shown on the public category page" }), _jsx(ImageUpload, { label: "Cover image", currentImage: coverImage, onUpload: (file) => upload(file, "sublisting-categories", true, {
|
|
82
82
|
type: "category-image",
|
|
83
83
|
name: name || "sublisting",
|
|
84
|
-
}), onChange: setCoverImage }), _jsxs(Row, { gap: "3", padding: "t-xs", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !name || isSubmitting, onClick: () => {
|
|
84
|
+
}), onChange: setCoverImage }), _jsx(FormErrorSummary, {}), _jsxs(Row, { gap: "3", padding: "t-xs", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !name || isSubmitting, onClick: () => {
|
|
85
85
|
clearErrors();
|
|
86
86
|
if (!name.trim()) {
|
|
87
87
|
setFieldError("name", "Category name is required");
|
|
@@ -5,7 +5,7 @@ import React from "react";
|
|
|
5
5
|
import { useQuery } from "@tanstack/react-query";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { Button, ConfirmDeleteModal, Div, Form, Input, Row, Stack, StackedViewShell, Text, Toggle, useToast, } from "../../../ui";
|
|
8
|
-
import { FieldInput, FieldTextarea } from "../../../ui/forms";
|
|
8
|
+
import { FieldInput, FieldTextarea, FormErrorSummary } from "../../../ui/forms";
|
|
9
9
|
import { apiClient } from "../../../http";
|
|
10
10
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
11
11
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
@@ -119,7 +119,7 @@ export function AdminTesterChecklistItemEditorView({ itemId, onSaved, onDeleted,
|
|
|
119
119
|
});
|
|
120
120
|
const isSubmitting = saveMutation.isPending || itemQuery.isLoading;
|
|
121
121
|
const canSave = Boolean(groupLabel.trim()) && Boolean(pageLabel.trim()) && Boolean(label.trim());
|
|
122
|
-
const formSection = (_jsxs(_Fragment, { children: [_jsx(Form, { schema: checklistItemFormSchema, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(FieldInput, { name: "groupLabel", label: "Group", value: groupLabel, onChange: handleGroupLabelChange, required: true, placeholder: "e.g. Buying", hint: "Top-level accordion section on the Tester Hub." }), _jsx(Input, { label: "Group key", value: groupKey, onChange: (e) => setGroupKey(e.target.value), placeholder: "buying", helperText: "Auto-generated from the group name." })] }), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(FieldInput, { name: "pageLabel", label: "Page", value: pageLabel, onChange: handlePageLabelChange, required: true, placeholder: "e.g. Checkout", hint: "Sub-accordion within the group." }), _jsx(Input, { label: "Page key", value: pageKey, onChange: (e) => setPageKey(e.target.value), placeholder: "checkout", helperText: "Auto-generated from the page name." })] }), _jsx(FieldInput, { name: "label", label: "Test case (Yes/No question)", value: label, onChange: setLabel, required: true, placeholder: "e.g. Coupon discount is correctly reflected in the order total" }), _jsx(FieldTextarea, { name: "description", label: "Description (optional)", value: description, onChange: setDescription, placeholder: "Extra context for the tester." }), _jsx(FieldInput, { name: "href", label: "Deep link (optional)", value: href, onChange: setHref, placeholder: "/cart", hint: "Jumps the tester straight to the feature being tested." }), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(Input, { label: "Display order", value: String(order), onChange: (e) => setOrder(parseInt(e.target.value, 10) || 0), type: "number", min: 0, helperText: "Lower = shown first within the page." }), _jsx(Input, { label: "Phase", value: String(phase), onChange: (e) => setPhase(Math.max(1, parseInt(e.target.value, 10) || 1)), type: "number", min: 1, helperText: "Test batch this case belongs to \u2014 testers work through one phase at a time." })] }), _jsxs(Stack, { className: `${__P.p4}`, gap: "3", rounded: "lg", border: "default", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Visibility" }), _jsx(Toggle, { label: "Active (visible to testers)", checked: isActive, onChange: setIsActive }), _jsx(Toggle, { label: "Admin-only (requires canTestAdmin)", checked: adminOnly, onChange: setAdminOnly })] }), _jsxs(Row, { gap: "3", padding: "t-xs", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !canSave || isSubmitting, onClick: () => {
|
|
122
|
+
const formSection = (_jsxs(_Fragment, { children: [_jsx(Form, { schema: checklistItemFormSchema, onSubmit: (e) => e.preventDefault(), spacing: "md", children: ({ setFieldError, clearErrors }) => (_jsxs(_Fragment, { children: [_jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(FieldInput, { name: "groupLabel", label: "Group", value: groupLabel, onChange: handleGroupLabelChange, required: true, placeholder: "e.g. Buying", hint: "Top-level accordion section on the Tester Hub." }), _jsx(Input, { label: "Group key", value: groupKey, onChange: (e) => setGroupKey(e.target.value), placeholder: "buying", helperText: "Auto-generated from the group name." })] }), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(FieldInput, { name: "pageLabel", label: "Page", value: pageLabel, onChange: handlePageLabelChange, required: true, placeholder: "e.g. Checkout", hint: "Sub-accordion within the group." }), _jsx(Input, { label: "Page key", value: pageKey, onChange: (e) => setPageKey(e.target.value), placeholder: "checkout", helperText: "Auto-generated from the page name." })] }), _jsx(FieldInput, { name: "label", label: "Test case (Yes/No question)", value: label, onChange: setLabel, required: true, placeholder: "e.g. Coupon discount is correctly reflected in the order total" }), _jsx(FieldTextarea, { name: "description", label: "Description (optional)", value: description, onChange: setDescription, placeholder: "Extra context for the tester." }), _jsx(FieldInput, { name: "href", label: "Deep link (optional)", value: href, onChange: setHref, placeholder: "/cart", hint: "Jumps the tester straight to the feature being tested." }), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(Input, { label: "Display order", value: String(order), onChange: (e) => setOrder(parseInt(e.target.value, 10) || 0), type: "number", min: 0, helperText: "Lower = shown first within the page." }), _jsx(Input, { label: "Phase", value: String(phase), onChange: (e) => setPhase(Math.max(1, parseInt(e.target.value, 10) || 1)), type: "number", min: 1, helperText: "Test batch this case belongs to \u2014 testers work through one phase at a time." })] }), _jsxs(Stack, { className: `${__P.p4}`, gap: "3", rounded: "lg", border: "default", children: [_jsx(Text, { size: "sm", weight: "medium", color: "muted", children: "Visibility" }), _jsx(Toggle, { label: "Active (visible to testers)", checked: isActive, onChange: setIsActive }), _jsx(Toggle, { label: "Admin-only (requires canTestAdmin)", checked: adminOnly, onChange: setAdminOnly })] }), _jsx(FormErrorSummary, {}), _jsxs(Row, { gap: "3", padding: "t-xs", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !canSave || isSubmitting, onClick: () => {
|
|
123
123
|
clearErrors();
|
|
124
124
|
if (!label.trim()) {
|
|
125
125
|
setFieldError("label", "Test case label is required");
|
|
@@ -11,8 +11,8 @@ export interface AdminUserEditorViewProps {
|
|
|
11
11
|
onClose: () => void;
|
|
12
12
|
userId?: string;
|
|
13
13
|
displayName?: string;
|
|
14
|
+
photoURL?: string | null;
|
|
14
15
|
currentRole?: string;
|
|
15
|
-
currentIsDisabled?: boolean;
|
|
16
16
|
currentEmailVerified?: boolean;
|
|
17
17
|
/** Tester program flag — orthogonal to role. Grants access to the Tester Hub and auto-approves the user's store. */
|
|
18
18
|
currentIsTester?: boolean;
|
|
@@ -37,5 +37,5 @@ export interface AdminUserEditorViewProps {
|
|
|
37
37
|
linkedin?: string;
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
-
export declare function AdminUserEditorView({ open, onClose, userId, displayName,
|
|
40
|
+
export declare function AdminUserEditorView({ open, onClose, userId, displayName, photoURL, currentRole, currentEmailVerified, currentIsTester, currentCanTestAdmin, ownedStoreId, ownedStoreName, currentSoftBans, currentIsHardBanned, currentHardBanReason, currentPhoneNumber, currentBio, currentLocation, currentWebsite, currentSocialLinks, }: AdminUserEditorViewProps): React.JSX.Element;
|
|
41
41
|
export {};
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { Code, useApiMutation } from "@mohasinac/appkit/client";
|
|
4
4
|
import React from "react";
|
|
5
5
|
import { useQueryClient } from "@tanstack/react-query";
|
|
6
|
-
import { Button, ConfirmDeleteModal, Div, Form, FormActions, Heading, Input, Row, Select, SideDrawer, Span, Stack, StackedViewShell, Text, Textarea, Toggle, useToast } from "../../../ui";
|
|
6
|
+
import { Avatar, Button, ConfirmDeleteModal, Div, Form, FormActions, Heading, Input, Row, Select, SideDrawer, Span, Stack, StackedViewShell, Text, Textarea, Toggle, useToast } from "../../../ui";
|
|
7
7
|
import { apiClient } from "../../../http";
|
|
8
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
9
|
const ROLE_OPTIONS = [
|
|
@@ -41,7 +41,7 @@ function SoftBanPanel({ userId, softBans, showAddSoftBan, setShowAddSoftBan, sof
|
|
|
41
41
|
return (_jsxs(Div, { surface: "muted", padding: "sm", rounded: "lg", border: "default", children: [_jsxs(Row, { justify: "between", gap: "sm", className: "mb-2", children: [_jsxs(Span, { size: "sm", weight: "medium", color: "muted", children: ["Soft bans", softBans.length > 0 ? ` (${softBans.length})` : ""] }), !showAddSoftBan && (_jsx(Button, { type: "button", variant: "secondary", size: "sm", disabled: !userId, onClick: () => setShowAddSoftBan(true), children: "Add soft ban" }))] }), softBans.length > 0 && (_jsx(Stack, { as: "ul", gap: "xs", className: "mb-3", children: softBans.map((ban) => (_jsxs(Row, { as: "li", align: "start", justify: "between", gap: "xs", surface: "default", padding: "inlineSm", rounded: "md", border: "default", children: [_jsxs(Stack, { gap: "none", className: "min-w-0 flex-1", children: [_jsx(Span, { size: "xs", weight: "semibold", children: formatBanAction(ban.action) }), _jsx(Span, { size: "xs", color: "muted", children: ban.reason }), _jsx(Span, { size: "xs", color: "muted", children: formatExpiry(ban.expiresAt) })] }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", isLoading: liftPending, disabled: liftPending, onClick: () => onLiftSoftBan(ban.action), children: "Lift" })] }, ban.action))) })), showAddSoftBan && (_jsxs(Stack, { gap: "xs", children: [_jsx(Select, { label: "Action to restrict", options: BANNED_ACTION_OPTIONS, value: softBanAction, onValueChange: setSoftBanAction }), _jsx(Textarea, { label: "Reason (required)", value: softBanReason, onChange: (e) => setSoftBanReason(e.target.value), rows: 2, placeholder: "e.g. Suspicious bid activity\u2026" }), _jsx(Input, { label: "Expires at (optional \u2014 leave blank for permanent)", type: "datetime-local", value: softBanExpiry, onChange: (e) => setSoftBanExpiry(e.target.value) }), _jsxs(Row, { gap: "xs", children: [_jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: softBanPending, disabled: !softBanReason.trim() || softBanPending, onClick: () => onAddSoftBan({ action: softBanAction, reason: softBanReason.trim(), ...(softBanExpiry ? { expiresAt: new Date(softBanExpiry).toISOString() } : {}) }), children: "Apply soft ban" }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => { setShowAddSoftBan(false); setSoftBanReason(""); setSoftBanExpiry(""); }, children: "Cancel" })] })] })), softBans.length === 0 && !showAddSoftBan && (_jsx(Text, { size: "xs", color: "muted", children: "No active soft bans." }))] }));
|
|
42
42
|
}
|
|
43
43
|
// --- Component ---------------------------------------------------------------
|
|
44
|
-
export function AdminUserEditorView({ open, onClose, userId, displayName,
|
|
44
|
+
export function AdminUserEditorView({ open, onClose, userId, displayName, photoURL, currentRole, currentEmailVerified, currentIsTester, currentCanTestAdmin, ownedStoreId, ownedStoreName, currentSoftBans, currentIsHardBanned, currentHardBanReason, currentPhoneNumber, currentBio, currentLocation, currentWebsite, currentSocialLinks, }) {
|
|
45
45
|
const queryClient = useQueryClient();
|
|
46
46
|
const { showToast } = useToast();
|
|
47
47
|
// --- General fields -------------------------------------------------------
|
|
@@ -224,6 +224,7 @@ export function AdminUserEditorView({ open, onClose, userId, displayName, curren
|
|
|
224
224
|
});
|
|
225
225
|
const isHardBanned = currentIsHardBanned ?? false;
|
|
226
226
|
const softBans = currentSoftBans ?? [];
|
|
227
|
+
const renderAvatarHeader = () => userId ? (_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(Avatar, { src: photoURL ?? null, name: displayName ?? userId, size: "md" }), _jsx(Text, { size: "sm", weight: "semibold", children: displayName ?? userId })] })) : null;
|
|
227
228
|
const renderInfoCard = () => userId ? (_jsx(Div, { textSize: "xs", surface: "muted", rounded: "lg", border: "default", padding: "inlineSm", children: _jsxs(Stack, { color: "primary", gap: "xs", children: [_jsxs(Text, { size: "xs", children: [_jsx(Span, { weight: "semibold", children: "Owner ID (Firebase UID):" }), " ", _jsx(Code, { className: "select-all font-mono", children: userId })] }), ownedStoreId && (_jsxs(Text, { size: "xs", children: [_jsx(Span, { weight: "semibold", children: "Owns store:" }), " ", _jsx(Code, { className: "select-all font-mono", children: ownedStoreId }), ownedStoreName ? ` — ${ownedStoreName}` : ""] }))] }) })) : null;
|
|
228
229
|
const renderRoleSection = () => (_jsx(Select, { label: "Role", options: ROLE_OPTIONS, value: role, onValueChange: setRole }));
|
|
229
230
|
const renderEmailVerifiedSection = () => (_jsx(Toggle, { label: "Email verified", checked: emailVerified, onChange: setEmailVerified }));
|
|
@@ -240,6 +241,7 @@ export function AdminUserEditorView({ open, onClose, userId, displayName, curren
|
|
|
240
241
|
e.preventDefault();
|
|
241
242
|
saveMutation.mutate();
|
|
242
243
|
}, padding: "md", children: _jsx(StackedViewShell, { portal: "admin", className: "space-y-4", sections: [
|
|
244
|
+
renderAvatarHeader,
|
|
243
245
|
renderInfoCard,
|
|
244
246
|
renderRoleSection,
|
|
245
247
|
renderEmailVerifiedSection,
|
|
@@ -215,7 +215,7 @@ export function AdminUsersView({ children, ...props }) {
|
|
|
215
215
|
},
|
|
216
216
|
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_USER_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Role", tabs: ADMIN_USER_ROLE_TABS, value: pendingFilters.role ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, role: id })) })] })),
|
|
217
217
|
};
|
|
218
|
-
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(AdminUserEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), userId: selectedRow?.id, displayName: selectedRow?.primary, currentRole: toStringValue(selectedRow?._raw?.role, "user"), currentEmailVerified: Boolean(selectedRow?._raw?.emailVerified), currentIsTester: Boolean(selectedRow?._raw?.isTester), currentCanTestAdmin: Boolean(selectedRow?._raw?.canTestAdmin), ownedStoreId: toStringValue(selectedRow?._raw?.storeId, "") || undefined, ownedStoreName: toStringValue(selectedRow?._raw?.storeName, "") || undefined, currentIsHardBanned: Boolean((selectedRow?._raw?.isDisabled ?? selectedRow?._raw?.disabled) &&
|
|
218
|
+
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(AdminUserEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), userId: selectedRow?.id, displayName: selectedRow?.primary, photoURL: toStringValue(selectedRow?._raw?.photoURL, "") || undefined, currentRole: toStringValue(selectedRow?._raw?.role, "user"), currentEmailVerified: Boolean(selectedRow?._raw?.emailVerified), currentIsTester: Boolean(selectedRow?._raw?.isTester), currentCanTestAdmin: Boolean(selectedRow?._raw?.canTestAdmin), ownedStoreId: toStringValue(selectedRow?._raw?.storeId, "") || undefined, ownedStoreName: toStringValue(selectedRow?._raw?.storeName, "") || undefined, currentIsHardBanned: Boolean((selectedRow?._raw?.isDisabled ?? selectedRow?._raw?.disabled) &&
|
|
219
219
|
selectedRow?._raw?.hardBanReason), currentHardBanReason: toStringValue(selectedRow?._raw?.hardBanReason, "") || undefined, currentSoftBans: Array.isArray(selectedRow?._raw?.softBans)
|
|
220
220
|
? selectedRow._raw.softBans.map((b) => ({
|
|
221
221
|
action: toStringValue(b.action, ""),
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { sortBy } from "@mohasinac/appkit/client";
|
|
4
4
|
import React from "react";
|
|
5
|
+
import { useRouter } from "next/navigation";
|
|
5
6
|
import { ListingLayout } from "../../../ui";
|
|
6
7
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
|
+
import { ROUTES } from "../../../next/routing/route-map";
|
|
7
9
|
import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
|
|
8
10
|
import { DataListingView } from "./DataListingView";
|
|
9
11
|
const ADMIN_WISHLISTS_CONFIG = {
|
|
@@ -29,14 +31,25 @@ const ADMIN_WISHLISTS_CONFIG = {
|
|
|
29
31
|
secondary: `${itemCount} item${itemCount === 1 ? "" : "s"} of ${limit}`,
|
|
30
32
|
status: isFull ? "Full" : itemCount >= limit - 2 ? "Near cap" : "OK",
|
|
31
33
|
updatedAt: toRelativeDate(item.updatedAt),
|
|
34
|
+
userId: toStringValue(item.userId, ""),
|
|
32
35
|
};
|
|
33
36
|
}),
|
|
34
37
|
getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
|
|
35
38
|
buildFilters: () => undefined,
|
|
36
39
|
};
|
|
37
40
|
export function AdminWishlistsView({ children, ...props }) {
|
|
41
|
+
const router = useRouter();
|
|
38
42
|
if (React.Children.count(children) > 0) {
|
|
39
43
|
return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
|
|
40
44
|
}
|
|
41
|
-
|
|
45
|
+
// The list API (`wishlistRepository.findAllSummaries()`) deliberately
|
|
46
|
+
// returns only {userId, itemCount, isFull, updatedAt} — never the items
|
|
47
|
+
// themselves, since pulling every user's ≤20 items into one payload is
|
|
48
|
+
// exactly the unbounded-response shape Rule #6 forbids. So the row already
|
|
49
|
+
// shows its whole record, and the genuinely useful destination is the
|
|
50
|
+
// owning user's admin page, which renders their real detail.
|
|
51
|
+
return (_jsx(DataListingView, { config: {
|
|
52
|
+
...ADMIN_WISHLISTS_CONFIG,
|
|
53
|
+
onRowClick: (row) => row.userId && router.push(String(ROUTES.ADMIN.USER_DETAIL(row.userId))),
|
|
54
|
+
} }));
|
|
42
55
|
}
|
|
@@ -74,8 +74,8 @@ export interface ListingSelectionContext<TRow extends {
|
|
|
74
74
|
export interface ListingViewConfig<TResponse, TRow extends {
|
|
75
75
|
id: string;
|
|
76
76
|
}> extends AdminListingConfig<TResponse, TRow> {
|
|
77
|
-
/** Portal context —
|
|
78
|
-
portal: "admin" | "seller";
|
|
77
|
+
/** Portal context — admin/seller/user all share the same chrome. */
|
|
78
|
+
portal: "admin" | "seller" | "user";
|
|
79
79
|
title: string;
|
|
80
80
|
subtitle?: string;
|
|
81
81
|
searchPlaceholder: string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { JsonValue } from "../../../schemas/types";
|
|
2
|
+
export interface AuditLogEntryDetail {
|
|
3
|
+
id: string;
|
|
4
|
+
actorUid: string;
|
|
5
|
+
actorName?: string;
|
|
6
|
+
action: string;
|
|
7
|
+
targetType: string;
|
|
8
|
+
targetId: string;
|
|
9
|
+
targetLabel?: string;
|
|
10
|
+
reason?: string;
|
|
11
|
+
metadata?: Record<string, JsonValue>;
|
|
12
|
+
createdAt?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ViewAuditLogEntryModalProps {
|
|
15
|
+
entry: AuditLogEntryDetail | null;
|
|
16
|
+
isOpen: boolean;
|
|
17
|
+
onClose: () => void;
|
|
18
|
+
}
|
|
19
|
+
export declare function ViewAuditLogEntryModal({ entry, isOpen, onClose }: ViewAuditLogEntryModalProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* ViewAuditLogEntryModal — full audit-log entry details in a Modal overlay.
|
|
5
|
+
* Mirrors ViewReviewModal's shape (appkit/src/features/reviews/components/ReviewModal.tsx).
|
|
6
|
+
*/
|
|
7
|
+
import { Badge, Code, Div, Modal, Row, Stack, Text } from "../../../ui";
|
|
8
|
+
const ACTION_LABELS = {
|
|
9
|
+
user_hard_ban: "User hard-banned",
|
|
10
|
+
user_soft_ban: "User soft-banned",
|
|
11
|
+
user_unban: "User unbanned",
|
|
12
|
+
checkout_bypass: "Admin checkout bypass",
|
|
13
|
+
coupon_update: "Coupon updated",
|
|
14
|
+
payout_mark_paid: "Payout marked paid",
|
|
15
|
+
store_status_change: "Store status changed",
|
|
16
|
+
user_role_change: "User role changed",
|
|
17
|
+
};
|
|
18
|
+
export function ViewAuditLogEntryModal({ entry, isOpen, onClose }) {
|
|
19
|
+
if (!entry)
|
|
20
|
+
return null;
|
|
21
|
+
return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Audit Log Entry", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsx(Badge, { variant: "default", children: ACTION_LABELS[entry.action] ?? entry.action }), entry.createdAt && (_jsx(Text, { size: "xs", color: "muted", children: new Date(entry.createdAt).toLocaleString() }))] }), _jsxs(Stack, { gap: "xs", children: [_jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "sm", color: "muted", children: "Actor" }), _jsx(Text, { size: "sm", weight: "medium", children: entry.actorName ?? entry.actorUid })] }), _jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "sm", color: "muted", children: "Target" }), _jsxs(Text, { size: "sm", weight: "medium", children: [entry.targetType, ": ", entry.targetLabel ?? entry.targetId] })] })] }), entry.reason && (_jsxs(Div, { rounded: "lg", padding: "sm", surface: "muted", border: "default", children: [_jsx(Text, { size: "xs", weight: "semibold", color: "muted", children: "Reason" }), _jsx(Text, { size: "sm", children: entry.reason })] })), entry.metadata && Object.keys(entry.metadata).length > 0 && (_jsxs(Div, { rounded: "lg", padding: "sm", surface: "muted", border: "default", children: [_jsx(Text, { size: "xs", weight: "semibold", color: "muted", className: "mb-1", children: "Metadata" }), _jsx(Code, { size: "xs", className: "block whitespace-pre-wrap font-mono", children: JSON.stringify(entry.metadata, null, 2) })] }))] }) }));
|
|
22
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface NotificationEntryDetail {
|
|
2
|
+
id: string;
|
|
3
|
+
userId: string;
|
|
4
|
+
type: string;
|
|
5
|
+
priority?: string;
|
|
6
|
+
title: string;
|
|
7
|
+
message: string;
|
|
8
|
+
imageUrl?: string;
|
|
9
|
+
actionUrl?: string;
|
|
10
|
+
actionLabel?: string;
|
|
11
|
+
isRead: boolean;
|
|
12
|
+
relatedId?: string;
|
|
13
|
+
relatedType?: string;
|
|
14
|
+
createdAt?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ViewNotificationModalProps {
|
|
17
|
+
notification: NotificationEntryDetail | null;
|
|
18
|
+
isOpen: boolean;
|
|
19
|
+
onClose: () => void;
|
|
20
|
+
}
|
|
21
|
+
export declare function ViewNotificationModal({ notification, isOpen, onClose }: ViewNotificationModalProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* ViewNotificationModal — full notification details in a Modal overlay.
|
|
5
|
+
* Mirrors ViewReviewModal's shape (appkit/src/features/reviews/components/ReviewModal.tsx).
|
|
6
|
+
* AdminNotificationsView was previously list-only — no way to see a
|
|
7
|
+
* notification's full body/payload/link, only Resend/Delete row actions.
|
|
8
|
+
*/
|
|
9
|
+
import { Anchor, Badge, Div, Modal, Row, Stack, Text } from "../../../ui";
|
|
10
|
+
import { MediaImage } from "../../media/MediaImage";
|
|
11
|
+
export function ViewNotificationModal({ notification, isOpen, onClose }) {
|
|
12
|
+
if (!notification)
|
|
13
|
+
return null;
|
|
14
|
+
return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Notification Details", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsxs(Row, { gap: "sm", align: "center", children: [_jsx(Badge, { variant: "default", children: notification.type }), _jsx(Badge, { variant: notification.isRead ? "default" : "info", children: notification.isRead ? "Read" : "Unread" })] }), notification.createdAt && (_jsx(Text, { size: "xs", color: "muted", children: new Date(notification.createdAt).toLocaleString() }))] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Text, { size: "lg", weight: "semibold", children: notification.title }), _jsx(Text, { size: "sm", color: "muted", children: notification.message })] }), notification.imageUrl && (_jsx(Div, { className: "h-32 w-full", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: notification.imageUrl, alt: notification.title, size: "card" }) })), _jsxs(Stack, { gap: "xs", children: [_jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "sm", color: "muted", children: "Recipient" }), _jsx(Text, { size: "sm", weight: "medium", children: notification.userId })] }), notification.relatedType && (_jsxs(Row, { justify: "between", children: [_jsx(Text, { size: "sm", color: "muted", children: "Related to" }), _jsxs(Text, { size: "sm", weight: "medium", children: [notification.relatedType, ": ", notification.relatedId] })] }))] }), notification.actionUrl && (_jsx(Row, { justify: "end", children: _jsxs(Anchor, { href: notification.actionUrl, tone: "brand", weight: "semibold", size: "sm", children: [notification.actionLabel ?? "Open link", " \u2192"] }) }))] }) }));
|
|
15
|
+
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
export interface AdminPageViewsReportViewProps {
|
|
2
|
-
startDate: string;
|
|
3
|
-
endDate: string;
|
|
4
|
-
}
|
|
5
1
|
/** Admin analytics tab — page views for the requested date range, grouped by
|
|
6
|
-
* entity (
|
|
7
|
-
* by
|
|
8
|
-
|
|
2
|
+
* entity (see PAGE_VIEW_ENTITY_TYPES for the full list), searchable by
|
|
3
|
+
* entity/URL, filterable by entity type, sortable by view count, and
|
|
4
|
+
* paginated. Backed by the day-bucketed pageViews collection. */
|
|
5
|
+
export declare function AdminPageViewsReportView(): import("react").JSX.Element;
|
|
@@ -1,41 +1,61 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { SIEVE_OP, sieveFilter, sortBy } from "@mohasinac/appkit/client";
|
|
5
|
+
import { Label, Input, PaginatedSelect } from "../../../../ui";
|
|
6
|
+
import { DataListingView } from "../DataListingView";
|
|
7
7
|
import { ADMIN_ENDPOINTS } from "../../../../constants/api-endpoints";
|
|
8
|
+
import { PAGE_VIEW_ENTITY_TYPES } from "../../../analytics/types";
|
|
9
|
+
const ENTITY_TYPE_OPTIONS = PAGE_VIEW_ENTITY_TYPES.map((t) => ({ value: t, label: t }));
|
|
10
|
+
function today() {
|
|
11
|
+
// eslint-disable-next-line lir/no-raw-date
|
|
12
|
+
return new Date().toISOString().slice(0, 10);
|
|
13
|
+
}
|
|
14
|
+
function daysAgo(n) {
|
|
15
|
+
// eslint-disable-next-line lir/no-raw-date
|
|
16
|
+
const d = new Date();
|
|
17
|
+
d.setDate(d.getDate() - n);
|
|
18
|
+
return d.toISOString().slice(0, 10);
|
|
19
|
+
}
|
|
20
|
+
const COLUMNS = [
|
|
21
|
+
{ key: "entityType", header: "Type" },
|
|
22
|
+
{ key: "entityId", header: "Entity" },
|
|
23
|
+
{ key: "url", header: "URL" },
|
|
24
|
+
{ key: "count", header: "Views", sortable: true },
|
|
25
|
+
];
|
|
8
26
|
/** Admin analytics tab — page views for the requested date range, grouped by
|
|
9
|
-
* entity (
|
|
10
|
-
* by
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
27
|
+
* entity (see PAGE_VIEW_ENTITY_TYPES for the full list), searchable by
|
|
28
|
+
* entity/URL, filterable by entity type, sortable by view count, and
|
|
29
|
+
* paginated. Backed by the day-bucketed pageViews collection. */
|
|
30
|
+
export function AdminPageViewsReportView() {
|
|
31
|
+
const [startDate, setStartDate] = useState(daysAgo(30));
|
|
32
|
+
const [endDate, setEndDate] = useState(today());
|
|
33
|
+
const config = {
|
|
34
|
+
portal: "admin",
|
|
35
|
+
title: "Page Views",
|
|
36
|
+
searchPlaceholder: "Search by entity or URL",
|
|
37
|
+
emptyLabel: "No page views recorded for this range.",
|
|
38
|
+
filterKeys: ["entityType"],
|
|
39
|
+
defaultSort: sortBy("count", "DESC"),
|
|
40
|
+
queryKey: ["admin", "analytics", "pageviews", "listing", startDate, endDate],
|
|
41
|
+
endpoint: `${ADMIN_ENDPOINTS.ANALYTICS_PAGE_VIEWS}?startDate=${startDate}&endDate=${endDate}`,
|
|
42
|
+
sortOptions: [
|
|
43
|
+
{ value: sortBy("count", "DESC"), label: "Most views" },
|
|
44
|
+
{ value: sortBy("count", "ASC"), label: "Fewest views" },
|
|
45
|
+
],
|
|
46
|
+
columns: COLUMNS,
|
|
47
|
+
mapRows: (response) => response.items.map((item) => ({
|
|
48
|
+
id: `${item.entityType}:${item.entityId}`,
|
|
49
|
+
entityType: item.entityType,
|
|
50
|
+
entityId: item.entityId,
|
|
51
|
+
url: item.url,
|
|
52
|
+
count: item.count,
|
|
53
|
+
})),
|
|
54
|
+
getTotal: (response) => response.total,
|
|
55
|
+
buildFilters: (state) => state.entityType ? sieveFilter("entityType", SIEVE_OP.EQ, state.entityType) : undefined,
|
|
56
|
+
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(PaginatedSelect, { value: pendingFilters.entityType || undefined, onChange: (v) => setPendingFilters((p) => ({ ...p, entityType: v ?? "" })), options: ENTITY_TYPE_OPTIONS })),
|
|
57
|
+
rowHrefTemplate: (row) => row.url,
|
|
58
|
+
renderAboveContent: () => (_jsxs(Label, { layout: "flex", gap: "md", color: "muted", size: "sm", className: "px-[var(--appkit-space-3)] pb-[var(--appkit-space-2)]", children: [_jsx(Input, { type: "date", value: startDate, max: endDate, onChange: (e) => setStartDate(e.target.value), className: "rounded-lg border px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-sm)] border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text)]" }), "to", _jsx(Input, { type: "date", value: endDate, min: startDate, max: today(), onChange: (e) => setEndDate(e.target.value), className: "rounded-lg border px-[var(--appkit-space-2-5)] py-[var(--appkit-space-1-5)] text-[length:var(--appkit-text-sm)] border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-elevated)] text-[var(--appkit-color-text)]" })] })),
|
|
59
|
+
};
|
|
60
|
+
return _jsx(DataListingView, { config: config });
|
|
41
61
|
}
|
|
@@ -97,6 +97,14 @@ export { AdminHistoryView } from "./AdminHistoryView";
|
|
|
97
97
|
export type { AdminHistoryViewProps } from "./AdminHistoryView";
|
|
98
98
|
export { AdminPayoutsView } from "./AdminPayoutsView";
|
|
99
99
|
export type { AdminPayoutsViewProps } from "./AdminPayoutsView";
|
|
100
|
+
export { AdminPayoutMarkPaidModal } from "./AdminPayoutMarkPaidModal";
|
|
101
|
+
export type { AdminPayoutMarkPaidModalProps } from "./AdminPayoutMarkPaidModal";
|
|
102
|
+
export { AdminAuditLogView } from "./AdminAuditLogView";
|
|
103
|
+
export type { AdminAuditLogViewProps } from "./AdminAuditLogView";
|
|
104
|
+
export { ViewAuditLogEntryModal } from "./ViewAuditLogEntryModal";
|
|
105
|
+
export type { AuditLogEntryDetail, ViewAuditLogEntryModalProps } from "./ViewAuditLogEntryModal";
|
|
106
|
+
export { ViewNotificationModal } from "./ViewNotificationModal";
|
|
107
|
+
export type { NotificationEntryDetail, ViewNotificationModalProps } from "./ViewNotificationModal";
|
|
100
108
|
export { AdminReviewsView } from "./AdminReviewsView";
|
|
101
109
|
export type { AdminReviewsViewProps } from "./AdminReviewsView";
|
|
102
110
|
export { AdminSectionsView } from "./AdminSectionsView";
|
|
@@ -134,7 +142,6 @@ export type { AdminRevenueChartProps, AdminOrdersChartProps, } from "./analytics
|
|
|
134
142
|
export { AdminTopProductsTable } from "./analytics/AdminTopProductsTable";
|
|
135
143
|
export type { AdminTopProductsTableProps, AdminTopProductsTableLabels, } from "./analytics/AdminTopProductsTable";
|
|
136
144
|
export { AdminPageViewsReportView } from "./analytics/AdminPageViewsReportView";
|
|
137
|
-
export type { AdminPageViewsReportViewProps } from "./analytics/AdminPageViewsReportView";
|
|
138
145
|
export { AdminFeatureFlagsView } from "./AdminFeatureFlagsView";
|
|
139
146
|
export type { AdminFeatureFlagsViewProps } from "./AdminFeatureFlagsView";
|
|
140
147
|
export { AdminNewsletterView } from "./AdminNewsletterView";
|
|
@@ -53,6 +53,10 @@ export { AdminCartsView } from "./AdminCartsView";
|
|
|
53
53
|
export { AdminWishlistsView } from "./AdminWishlistsView";
|
|
54
54
|
export { AdminHistoryView } from "./AdminHistoryView";
|
|
55
55
|
export { AdminPayoutsView } from "./AdminPayoutsView";
|
|
56
|
+
export { AdminPayoutMarkPaidModal } from "./AdminPayoutMarkPaidModal";
|
|
57
|
+
export { AdminAuditLogView } from "./AdminAuditLogView";
|
|
58
|
+
export { ViewAuditLogEntryModal } from "./ViewAuditLogEntryModal";
|
|
59
|
+
export { ViewNotificationModal } from "./ViewNotificationModal";
|
|
56
60
|
export { AdminReviewsView } from "./AdminReviewsView";
|
|
57
61
|
export { AdminSectionsView } from "./AdminSectionsView";
|
|
58
62
|
export { AdminSessionsManager } from "./AdminSessionsManager";
|
|
@@ -148,6 +148,36 @@ export declare const ADMIN_PAYOUT_STATUS_TABS: readonly [{
|
|
|
148
148
|
readonly id: "failed";
|
|
149
149
|
readonly label: "Failed";
|
|
150
150
|
}];
|
|
151
|
+
/** Admin > Audit Log — action-type filter chip set. IDs match
|
|
152
|
+
* `AdminAuditActionValues` (`appkit/src/features/audit-log/schemas/firestore.ts`) exactly. */
|
|
153
|
+
export declare const ADMIN_AUDIT_LOG_ACTION_TABS: readonly [{
|
|
154
|
+
readonly id: "All";
|
|
155
|
+
readonly label: "All";
|
|
156
|
+
}, {
|
|
157
|
+
readonly id: "user_hard_ban";
|
|
158
|
+
readonly label: "Hard ban";
|
|
159
|
+
}, {
|
|
160
|
+
readonly id: "user_soft_ban";
|
|
161
|
+
readonly label: "Soft ban";
|
|
162
|
+
}, {
|
|
163
|
+
readonly id: "user_unban";
|
|
164
|
+
readonly label: "Unban";
|
|
165
|
+
}, {
|
|
166
|
+
readonly id: "checkout_bypass";
|
|
167
|
+
readonly label: "Checkout bypass";
|
|
168
|
+
}, {
|
|
169
|
+
readonly id: "coupon_update";
|
|
170
|
+
readonly label: "Coupon update";
|
|
171
|
+
}, {
|
|
172
|
+
readonly id: "payout_mark_paid";
|
|
173
|
+
readonly label: "Payout paid";
|
|
174
|
+
}, {
|
|
175
|
+
readonly id: "store_status_change";
|
|
176
|
+
readonly label: "Store status";
|
|
177
|
+
}, {
|
|
178
|
+
readonly id: "user_role_change";
|
|
179
|
+
readonly label: "Role change";
|
|
180
|
+
}];
|
|
151
181
|
/** Admin > Orders — order-state filter chip set. Lowercase IDs match
|
|
152
182
|
* `OrderStatusValues` and the actual Firestore document `status` field. */
|
|
153
183
|
export declare const ADMIN_ORDER_STATUS_TABS: readonly [{
|
|
@@ -175,6 +205,25 @@ export declare const ADMIN_ORDER_STATUS_TABS: readonly [{
|
|
|
175
205
|
readonly id: "return_requested";
|
|
176
206
|
readonly label: "Return Requested";
|
|
177
207
|
}];
|
|
208
|
+
/**
|
|
209
|
+
* Admin > Orders — manual-payment (cash / UPI / EMI) review queue.
|
|
210
|
+
*
|
|
211
|
+
* Deliberately NOT a `status` chip set: these two states are *derived* from
|
|
212
|
+
* `paymentProofUrl` + `paymentReviewOutcome`, both of which are absent-by-
|
|
213
|
+
* default fields that no Firestore equality filter can select on. The IDs are
|
|
214
|
+
* `PaymentReviewQueueMode` values and are sent as the `paymentReview` query
|
|
215
|
+
* param (not a Sieve filter) — see `orderRepository.listPaymentReviewQueue`.
|
|
216
|
+
*/
|
|
217
|
+
export declare const ADMIN_ORDER_PAYMENT_REVIEW_TABS: readonly [{
|
|
218
|
+
readonly id: "All";
|
|
219
|
+
readonly label: "All";
|
|
220
|
+
}, {
|
|
221
|
+
readonly id: "awaiting_proof";
|
|
222
|
+
readonly label: "Awaiting payment";
|
|
223
|
+
}, {
|
|
224
|
+
readonly id: "awaiting_verification";
|
|
225
|
+
readonly label: "Awaiting verification";
|
|
226
|
+
}];
|
|
178
227
|
/** Admin > Reviews — moderation state filter chip set. */
|
|
179
228
|
export declare const ADMIN_REVIEW_STATUS_TABS: readonly [{
|
|
180
229
|
readonly id: "All";
|
|
@@ -74,6 +74,19 @@ export const ADMIN_PAYOUT_STATUS_TABS = [
|
|
|
74
74
|
{ id: "paid", label: "Paid" },
|
|
75
75
|
{ id: "failed", label: "Failed" },
|
|
76
76
|
];
|
|
77
|
+
/** Admin > Audit Log — action-type filter chip set. IDs match
|
|
78
|
+
* `AdminAuditActionValues` (`appkit/src/features/audit-log/schemas/firestore.ts`) exactly. */
|
|
79
|
+
export const ADMIN_AUDIT_LOG_ACTION_TABS = [
|
|
80
|
+
ALL_TAB,
|
|
81
|
+
{ id: "user_hard_ban", label: "Hard ban" },
|
|
82
|
+
{ id: "user_soft_ban", label: "Soft ban" },
|
|
83
|
+
{ id: "user_unban", label: "Unban" },
|
|
84
|
+
{ id: "checkout_bypass", label: "Checkout bypass" },
|
|
85
|
+
{ id: "coupon_update", label: "Coupon update" },
|
|
86
|
+
{ id: "payout_mark_paid", label: "Payout paid" },
|
|
87
|
+
{ id: "store_status_change", label: "Store status" },
|
|
88
|
+
{ id: "user_role_change", label: "Role change" },
|
|
89
|
+
];
|
|
77
90
|
/** Admin > Orders — order-state filter chip set. Lowercase IDs match
|
|
78
91
|
* `OrderStatusValues` and the actual Firestore document `status` field. */
|
|
79
92
|
export const ADMIN_ORDER_STATUS_TABS = [
|
|
@@ -86,6 +99,20 @@ export const ADMIN_ORDER_STATUS_TABS = [
|
|
|
86
99
|
{ id: "refunded", label: "Refunded" },
|
|
87
100
|
{ id: "return_requested", label: "Return Requested" },
|
|
88
101
|
];
|
|
102
|
+
/**
|
|
103
|
+
* Admin > Orders — manual-payment (cash / UPI / EMI) review queue.
|
|
104
|
+
*
|
|
105
|
+
* Deliberately NOT a `status` chip set: these two states are *derived* from
|
|
106
|
+
* `paymentProofUrl` + `paymentReviewOutcome`, both of which are absent-by-
|
|
107
|
+
* default fields that no Firestore equality filter can select on. The IDs are
|
|
108
|
+
* `PaymentReviewQueueMode` values and are sent as the `paymentReview` query
|
|
109
|
+
* param (not a Sieve filter) — see `orderRepository.listPaymentReviewQueue`.
|
|
110
|
+
*/
|
|
111
|
+
export const ADMIN_ORDER_PAYMENT_REVIEW_TABS = [
|
|
112
|
+
ALL_TAB,
|
|
113
|
+
{ id: "awaiting_proof", label: "Awaiting payment" },
|
|
114
|
+
{ id: "awaiting_verification", label: "Awaiting verification" },
|
|
115
|
+
];
|
|
89
116
|
/** Admin > Reviews — moderation state filter chip set. */
|
|
90
117
|
export const ADMIN_REVIEW_STATUS_TABS = [
|
|
91
118
|
ALL_TAB,
|
|
@@ -9,6 +9,12 @@ export interface AdminListingConfig<TResponse, TRow extends {
|
|
|
9
9
|
mapRows: (response: TResponse) => TRow[];
|
|
10
10
|
getTotal?: (response: TResponse, rows: TRow[]) => number;
|
|
11
11
|
buildFilters: (filterState: Record<string, string>) => string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Extra query params derived from the same filter state, for endpoints whose
|
|
14
|
+
* filter can't be expressed as a Sieve string. Use `buildFilters` for
|
|
15
|
+
* anything Sieve *can* express — this is the escape hatch, not the default.
|
|
16
|
+
*/
|
|
17
|
+
buildExtraParams?: (filterState: Record<string, string>) => Record<string, string | undefined> | undefined;
|
|
12
18
|
initialView?: "grid" | "list" | "table";
|
|
13
19
|
/** Values used for filterKeys absent from the URL — lets a view default to
|
|
14
20
|
* a non-"All" filter state (e.g. hide inactive rows) on first load, while
|
|
@@ -6,7 +6,7 @@ import { usePanelUrlSync } from "../../../react/hooks/use-panel-url-sync";
|
|
|
6
6
|
import { useDataViewMode } from "../../account/hooks/useDataViewMode";
|
|
7
7
|
import { useAdminListingData } from "./useAdminListingData";
|
|
8
8
|
export function useAdminListing(config) {
|
|
9
|
-
const { filterKeys, defaultSort, pageSize: defaultPageSize = 25, queryKey, endpoint, mapRows, getTotal, buildFilters, filterDefaults = {}, } = config;
|
|
9
|
+
const { filterKeys, defaultSort, pageSize: defaultPageSize = 25, queryKey, endpoint, mapRows, getTotal, buildFilters, buildExtraParams, filterDefaults = {}, } = config;
|
|
10
10
|
// Persisted, viewport-aware view-mode: below 768px defaults to "list"
|
|
11
11
|
// (AdminViewCards' one-full-width-card-per-row layout) unless the caller
|
|
12
12
|
// explicitly pins hideTableView (grid-only views like coupons); the user's
|
|
@@ -52,6 +52,7 @@ export function useAdminListing(config) {
|
|
|
52
52
|
const hasActiveState = !!table.get("q") || table.get("sort") !== defaultSort || activeFilterCount > 0;
|
|
53
53
|
const currentFilterState = Object.fromEntries(filterKeys.map((k) => [k, table.get(k)]));
|
|
54
54
|
const filters = buildFilters(currentFilterState);
|
|
55
|
+
const extraParams = buildExtraParams?.(currentFilterState);
|
|
55
56
|
const { rows, total, isLoading, errorMessage, refetch } = useAdminListingData({
|
|
56
57
|
queryKey,
|
|
57
58
|
endpoint,
|
|
@@ -60,6 +61,7 @@ export function useAdminListing(config) {
|
|
|
60
61
|
sorts: table.get("sort") || defaultSort,
|
|
61
62
|
filters,
|
|
62
63
|
q: table.get("q") || undefined,
|
|
64
|
+
extraParams,
|
|
63
65
|
mapRows,
|
|
64
66
|
getTotal,
|
|
65
67
|
});
|