@mohasinac/appkit 4.10.0 → 4.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/layout/DashboardLayoutClient.d.ts +6 -1
- package/dist/_internal/client/features/layout/DashboardLayoutClient.js +4 -1
- package/dist/_internal/client/features/lottery/LotteryPullForm.d.ts +1 -1
- package/dist/_internal/client/features/lottery/LotteryPullForm.js +2 -2
- package/dist/_internal/server/features/admin/daily-digest-actions.d.ts +18 -0
- package/dist/_internal/server/features/admin/daily-digest-actions.js +37 -0
- package/dist/_internal/server/features/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/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/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/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,65 +1,30 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
4
|
-
import { useApiMutation } from "@mohasinac/appkit/client";
|
|
4
|
+
import { useApiMutation, sieveFilter, SIEVE_OP, sortBy } from "@mohasinac/appkit/client";
|
|
5
5
|
import React from "react";
|
|
6
6
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
7
|
-
import { Alert, Button, Checkbox, Div, Input, Label, Row, Select, Stack,
|
|
7
|
+
import { Alert, Button, Checkbox, Div, Input, Label, PaginatedSelect, Row, Select, Stack, Text, TextLink } from "../../../ui";
|
|
8
8
|
import { apiClient } from "../../../http";
|
|
9
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
|
-
import {
|
|
10
|
+
import { DataListingView } from "./DataListingView";
|
|
11
11
|
const __P = {
|
|
12
12
|
p3: "p-[var(--appkit-space-3)]",
|
|
13
13
|
};
|
|
14
14
|
function AdsSettingsPanel({ adsenseClientId, setAdsenseClientId, thirdPartyScriptUrl, setThirdPartyScriptUrl, consentRequired, setConsentRequired, serverCredentialIssues, localCredentialIssues, credentialStatus, providerCredentialsMasked, settingsMutation, hasPendingCredentialInput, currentConsentRequired, settingsMessage, onSave, }) {
|
|
15
15
|
return (_jsxs(Stack, { border: "default", className: __P.p3, gap: "3", rounded: "lg", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Provider and publish settings" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: "Save provider credentials here before publishing AdSense or third-party inventory." }), serverCredentialIssues.length > 0 ? (_jsx(Alert, { variant: "warning", title: "Provider credentials need attention", children: serverCredentialIssues.join("; ") })) : null, _jsxs(Div, { layout: "grid", gap: "3", className: "grid-cols-1 md:grid-cols-2", children: [_jsx(Input, { label: "AdSense client id", value: adsenseClientId, onChange: (event) => setAdsenseClientId(event.target.value), placeholder: providerCredentialsMasked?.adsenseClientId || "ca-pub-XXXXXXXXXX" }), _jsx(Input, { label: "Third-party script URL", value: thirdPartyScriptUrl, onChange: (event) => setThirdPartyScriptUrl(event.target.value), placeholder: providerCredentialsMasked?.thirdPartyScriptUrl || "https://..." })] }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: ["Stored credentials: AdSense ", credentialStatus?.hasAdsenseClientId ? "configured" : "missing", " \u00B7 Third-party ", credentialStatus?.hasThirdPartyScriptUrl ? "configured" : "missing"] }), _jsxs(Row, { align: "center", justify: "between", gap: "3", children: [_jsxs(Label, { layout: "flex", gap: "md", size: "sm", children: [_jsx(Checkbox, { bare: true, checked: consentRequired, onChange: (event) => setConsentRequired(event.target.checked) }), "Require consent globally for ad rendering"] }), _jsx(Button, { size: "sm", variant: "outline", disabled: settingsMutation.isPending || localCredentialIssues.length > 0 || (!hasPendingCredentialInput && consentRequired === Boolean(currentConsentRequired)), onClick: onSave, children: settingsMutation.isPending ? "Saving..." : "Save settings" })] }), localCredentialIssues.length > 0 ? (_jsx(Alert, { variant: "error", title: "Fix settings before saving", children: localCredentialIssues.join("; ") })) : null, settingsMessage ? (_jsx(Alert, { variant: settingsMessage.toLowerCase().includes("failed") ? "error" : "success", title: "Settings", children: settingsMessage })) : null] }));
|
|
16
16
|
}
|
|
17
|
-
function
|
|
18
|
-
return (_jsxs(Div, { layout: "grid", gap: "3", className: "grid-cols-1 md:grid-cols-4", children: [_jsx(Input, { label: "Search", value: q, onChange: (event) => { onPageReset(); setQ(event.target.value); }, placeholder: "Search ads" }), _jsx(Select, { label: "Status", value: status, options: [
|
|
19
|
-
{ label: "All", value: "all" },
|
|
20
|
-
{ label: "Draft", value: "draft" },
|
|
21
|
-
{ label: "Active", value: "active" },
|
|
22
|
-
{ label: "Scheduled", value: "scheduled" },
|
|
23
|
-
{ label: "Paused", value: "paused" },
|
|
24
|
-
], onChange: (event) => { onPageReset(); setStatus(event.target.value); } }), _jsx(Select, { label: "Provider", value: provider, options: [
|
|
25
|
-
{ label: "All", value: "all" },
|
|
26
|
-
{ label: "Manual", value: "manual" },
|
|
27
|
-
{ label: "AdSense", value: "adsense" },
|
|
28
|
-
{ label: "Third Party", value: "thirdParty" },
|
|
29
|
-
], onChange: (event) => { onPageReset(); setProvider(event.target.value); } }), _jsx(Select, { label: "Placement", value: placement, options: [
|
|
30
|
-
{ label: "All", value: "all" },
|
|
31
|
-
...placements.map((item) => ({ label: item.label, value: item.id })),
|
|
32
|
-
], onChange: (event) => { onPageReset(); setPlacement(event.target.value); } })] }));
|
|
33
|
-
}
|
|
34
|
-
export function AdminAdsView({ endpoint = ADMIN_ENDPOINTS.ADS, labels = {}, createHref = "/admin/ads/new", renderEditLink, ...rest }) {
|
|
17
|
+
export function AdminAdsView({ endpoint = ADMIN_ENDPOINTS.ADS, labels = {}, createHref = "/admin/ads/new", renderEditLink, }) {
|
|
35
18
|
const queryClient = useQueryClient();
|
|
36
|
-
const [page, setPage] = React.useState(1);
|
|
37
|
-
const [pageSize] = React.useState(20);
|
|
38
|
-
const [q, setQ] = React.useState("");
|
|
39
|
-
const [status, setStatus] = React.useState("all");
|
|
40
|
-
const [provider, setProvider] = React.useState("all");
|
|
41
|
-
const [placement, setPlacement] = React.useState("all");
|
|
42
19
|
const [consentRequired, setConsentRequired] = React.useState(false);
|
|
43
20
|
const [adsenseClientId, setAdsenseClientId] = React.useState("");
|
|
44
21
|
const [thirdPartyScriptUrl, setThirdPartyScriptUrl] = React.useState("");
|
|
45
22
|
const [settingsMessage, setSettingsMessage] = React.useState(null);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
sp.set("page", String(page));
|
|
52
|
-
sp.set("pageSize", String(pageSize));
|
|
53
|
-
if (q.trim())
|
|
54
|
-
sp.set("q", q.trim());
|
|
55
|
-
if (status !== "all")
|
|
56
|
-
sp.set("status", status);
|
|
57
|
-
if (provider !== "all")
|
|
58
|
-
sp.set("provider", provider);
|
|
59
|
-
if (placement !== "all")
|
|
60
|
-
sp.set("placement", placement);
|
|
61
|
-
return apiClient.get(`${endpoint}?${sp.toString()}`);
|
|
62
|
-
},
|
|
23
|
+
// Lightweight metadata-only fetch (placements, consent, credential status) —
|
|
24
|
+
// decoupled from the paginated listing query DataListingView owns internally.
|
|
25
|
+
const metaQuery = useQuery({
|
|
26
|
+
queryKey: ["admin-ads-meta", endpoint],
|
|
27
|
+
queryFn: () => apiClient.get(`${endpoint}?page=1&pageSize=1`),
|
|
63
28
|
staleTime: 10000,
|
|
64
29
|
});
|
|
65
30
|
const statusMutation = useApiMutation({
|
|
@@ -68,13 +33,14 @@ export function AdminAdsView({ endpoint = ADMIN_ENDPOINTS.ADS, labels = {}, crea
|
|
|
68
33
|
},
|
|
69
34
|
onSuccess: async () => {
|
|
70
35
|
await queryClient.invalidateQueries({ queryKey: ["admin-ads"] });
|
|
36
|
+
await queryClient.invalidateQueries({ queryKey: ["admin-ads-meta"] });
|
|
71
37
|
},
|
|
72
38
|
});
|
|
73
39
|
React.useEffect(() => {
|
|
74
|
-
if (!
|
|
40
|
+
if (!metaQuery.data)
|
|
75
41
|
return;
|
|
76
|
-
setConsentRequired(Boolean(
|
|
77
|
-
}, [
|
|
42
|
+
setConsentRequired(Boolean(metaQuery.data.consentRequired));
|
|
43
|
+
}, [metaQuery.data]);
|
|
78
44
|
const settingsMutation = useApiMutation({
|
|
79
45
|
mutationFn: async (payload) => {
|
|
80
46
|
await apiClient.patch(endpoint, payload);
|
|
@@ -83,46 +49,14 @@ export function AdminAdsView({ endpoint = ADMIN_ENDPOINTS.ADS, labels = {}, crea
|
|
|
83
49
|
setSettingsMessage("Ad settings saved.");
|
|
84
50
|
setAdsenseClientId("");
|
|
85
51
|
setThirdPartyScriptUrl("");
|
|
86
|
-
await queryClient.invalidateQueries({ queryKey: ["admin-ads"] });
|
|
52
|
+
await queryClient.invalidateQueries({ queryKey: ["admin-ads-meta"] });
|
|
87
53
|
},
|
|
88
54
|
onError: (error) => {
|
|
89
55
|
setSettingsMessage(error instanceof Error ? error.message : "Failed to save ad settings");
|
|
90
56
|
},
|
|
91
57
|
});
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
{
|
|
95
|
-
key: "name",
|
|
96
|
-
header: "Ad",
|
|
97
|
-
render: (row) => (_jsxs(Div, { children: [_jsx(Text, { weight: "medium", children: row.name }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: row.id })] })),
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
key: "provider",
|
|
101
|
-
header: "Provider",
|
|
102
|
-
render: (row) => _jsx(Text, { className: "tracking-wide", size: "xs", transform: "uppercase", children: row.provider }),
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
key: "status",
|
|
106
|
-
header: "Status",
|
|
107
|
-
render: (row) => (_jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", size: "xs", transform: "uppercase", children: row.status }), row.publishReady === false ? (_jsx(Text, { className: "text-[11px] text-error", children: "Publish blocked" })) : null] })),
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
key: "placementIds",
|
|
111
|
-
header: "Placements",
|
|
112
|
-
render: (row) => (_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: row.placementIds.join(", ") })),
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
key: "actions",
|
|
116
|
-
header: "Actions",
|
|
117
|
-
render: (row) => {
|
|
118
|
-
const nextStatus = row.status === "active" ? "paused" : "active";
|
|
119
|
-
const cannotPublish = nextStatus === "active" && row.publishReady === false;
|
|
120
|
-
return (_jsxs(Row, { align: "center", gap: "sm", children: [renderEditLink ? renderEditLink(row) : (_jsx(TextLink, { variant: "bare", href: `/admin/ads/${row.id}/edit`, rounded: "md", paddingX: "sm", size: "xs", layout: "inline-flex", align: "center", className: "h-8 border border-neutral-200 border-[var(--appkit-color-border)]", children: "Edit" })), _jsx(Button, { size: "sm", variant: "outline", disabled: statusMutation.isPending || cannotPublish, onClick: () => statusMutation.mutate({ id: row.id, nextStatus }), children: row.status === "active" ? "Pause" : "Publish" })] }));
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
], [renderEditLink, statusMutation]);
|
|
124
|
-
const placements = adsQuery.data?.placements ?? [];
|
|
125
|
-
const credentialStatus = adsQuery.data?.providerCredentialStatus;
|
|
58
|
+
const placements = metaQuery.data?.placements ?? [];
|
|
59
|
+
const credentialStatus = metaQuery.data?.providerCredentialStatus;
|
|
126
60
|
const serverCredentialIssues = credentialStatus?.issues ?? [];
|
|
127
61
|
const localCredentialIssues = React.useMemo(() => {
|
|
128
62
|
const issues = [];
|
|
@@ -155,11 +89,74 @@ export function AdminAdsView({ endpoint = ADMIN_ENDPOINTS.ADS, labels = {}, crea
|
|
|
155
89
|
};
|
|
156
90
|
settingsMutation.mutate(payload);
|
|
157
91
|
};
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
92
|
+
const columns = [
|
|
93
|
+
{
|
|
94
|
+
key: "name",
|
|
95
|
+
header: "Ad",
|
|
96
|
+
render: (row) => (_jsxs(Div, { children: [_jsx(Text, { weight: "medium", children: row.name }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: row.id })] })),
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
key: "provider",
|
|
100
|
+
header: "Provider",
|
|
101
|
+
render: (row) => _jsx(Text, { className: "tracking-wide", size: "xs", transform: "uppercase", children: row.provider }),
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
key: "status",
|
|
105
|
+
header: "Status",
|
|
106
|
+
render: (row) => (_jsxs(Div, { children: [_jsx(Text, { className: "tracking-wide", size: "xs", transform: "uppercase", children: row.status }), row.publishReady === false ? (_jsx(Text, { className: "text-[11px] text-error", children: "Publish blocked" })) : null] })),
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
key: "placementIds",
|
|
110
|
+
header: "Placements",
|
|
111
|
+
render: (row) => (_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: row.placementIds.join(", ") })),
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
key: "actions",
|
|
115
|
+
header: "Actions",
|
|
116
|
+
render: (row) => {
|
|
117
|
+
const nextStatus = row.status === "active" ? "paused" : "active";
|
|
118
|
+
const cannotPublish = nextStatus === "active" && row.publishReady === false;
|
|
119
|
+
return (_jsxs(Row, { align: "center", gap: "sm", children: [renderEditLink ? renderEditLink(row) : (_jsx(TextLink, { variant: "bare", href: `/admin/ads/${row.id}/edit`, rounded: "md", paddingX: "sm", size: "xs", layout: "inline-flex", align: "center", className: "h-8 border border-neutral-200 border-[var(--appkit-color-border)]", children: "Edit" })), _jsx(Button, { size: "sm", variant: "outline", disabled: statusMutation.isPending || cannotPublish, onClick: () => statusMutation.mutate({ id: row.id, nextStatus }), children: row.status === "active" ? "Pause" : "Publish" })] }));
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
const config = {
|
|
124
|
+
portal: "admin",
|
|
125
|
+
title: labels.title ?? "Ad Inventory",
|
|
126
|
+
searchPlaceholder: "Search ads",
|
|
127
|
+
emptyLabel: "No ads found",
|
|
128
|
+
filterKeys: ["status", "provider", "placement"],
|
|
129
|
+
defaultSort: sortBy("updatedAt", "DESC"),
|
|
130
|
+
queryKey: ["admin-ads", endpoint],
|
|
131
|
+
endpoint,
|
|
132
|
+
sortOptions: [{ value: sortBy("updatedAt", "DESC"), label: "Recently updated" }],
|
|
133
|
+
columns,
|
|
134
|
+
mapRows: (response) => response.items,
|
|
135
|
+
getTotal: (response) => response.total,
|
|
136
|
+
buildFilters: (state) => {
|
|
137
|
+
const clauses = [];
|
|
138
|
+
if (state.status)
|
|
139
|
+
clauses.push(sieveFilter("status", SIEVE_OP.EQ, state.status));
|
|
140
|
+
if (state.provider)
|
|
141
|
+
clauses.push(sieveFilter("provider", SIEVE_OP.EQ, state.provider));
|
|
142
|
+
if (state.placement)
|
|
143
|
+
clauses.push(sieveFilter("placement", SIEVE_OP.EQ, state.placement));
|
|
144
|
+
return clauses.length ? clauses.join(",") : undefined;
|
|
145
|
+
},
|
|
146
|
+
toolbarExtra: (_jsx(TextLink, { variant: "bare", href: createHref, rounded: "md", paddingX: "sm", size: "sm", weight: "medium", layout: "inline-flex", align: "center", className: "h-9 bg-[var(--appkit-color-surface)] text-[var(--appkit-color-text)]", children: "New ad" })),
|
|
147
|
+
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Select, { label: "Status", value: pendingFilters.status || "all", options: [
|
|
148
|
+
{ label: "All", value: "all" },
|
|
149
|
+
{ label: "Draft", value: "draft" },
|
|
150
|
+
{ label: "Active", value: "active" },
|
|
151
|
+
{ label: "Scheduled", value: "scheduled" },
|
|
152
|
+
{ label: "Paused", value: "paused" },
|
|
153
|
+
], onChange: (e) => setPendingFilters((p) => ({ ...p, status: e.target.value === "all" ? "" : e.target.value })) }), _jsx(Select, { label: "Provider", value: pendingFilters.provider || "all", options: [
|
|
154
|
+
{ label: "All", value: "all" },
|
|
155
|
+
{ label: "Manual", value: "manual" },
|
|
156
|
+
{ label: "AdSense", value: "adsense" },
|
|
157
|
+
{ label: "Third Party", value: "thirdParty" },
|
|
158
|
+
], onChange: (e) => setPendingFilters((p) => ({ ...p, provider: e.target.value === "all" ? "" : e.target.value })) }), _jsx(Label, { size: "sm", color: "muted", children: "Placement" }), _jsx(PaginatedSelect, { value: pendingFilters.placement || undefined, onChange: (v) => setPendingFilters((p) => ({ ...p, placement: v ?? "" })), options: placements.map((item) => ({ value: item.id, label: item.label })) })] })),
|
|
159
|
+
renderAboveContent: () => (_jsxs(Stack, { gap: "md", className: __P.p3, children: [metaQuery.error ? (_jsx(Alert, { variant: "error", title: "Could not load ads", children: metaQuery.error instanceof Error ? metaQuery.error.message : "Unknown error" })) : null, _jsx(AdsSettingsPanel, { adsenseClientId: adsenseClientId, setAdsenseClientId: setAdsenseClientId, thirdPartyScriptUrl: thirdPartyScriptUrl, setThirdPartyScriptUrl: setThirdPartyScriptUrl, consentRequired: consentRequired, setConsentRequired: setConsentRequired, serverCredentialIssues: serverCredentialIssues, localCredentialIssues: localCredentialIssues, credentialStatus: credentialStatus, providerCredentialsMasked: metaQuery.data?.providerCredentialsMasked, settingsMutation: settingsMutation, hasPendingCredentialInput: hasPendingCredentialInput, currentConsentRequired: Boolean(metaQuery.data?.consentRequired), settingsMessage: settingsMessage, onSave: saveSettings })] })),
|
|
160
|
+
};
|
|
161
|
+
return _jsx(DataListingView, { config: config });
|
|
165
162
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
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 from "react";
|
|
7
7
|
import { useQueryClient } from "@tanstack/react-query";
|
|
8
|
-
import { FilterChipGroup, ListingLayout, RowActionMenu, useToast } from "../../../ui";
|
|
8
|
+
import { FilterChipGroup, ListingLayout, RecordDetailModal, RowActionMenu, useToast } from "../../../ui";
|
|
9
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
10
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
11
|
import { ADMIN_EVENT_ENTRY_STATUS_TABS } from "../constants/filter-tabs";
|
|
@@ -15,6 +15,7 @@ import { apiClient } from "../../../http";
|
|
|
15
15
|
export function AdminAllEventEntriesView({ children, ...props }) {
|
|
16
16
|
const queryClient = useQueryClient();
|
|
17
17
|
const { showToast } = useToast();
|
|
18
|
+
const [selected, setSelected] = React.useState(null);
|
|
18
19
|
const updateMutation = useApiMutation({
|
|
19
20
|
mutationFn: async ({ id, status }) => {
|
|
20
21
|
await apiClient.patch(ADMIN_ENDPOINTS.ADMIN_EVENT_ENTRY_BY_ID(id), { status });
|
|
@@ -52,10 +53,18 @@ export function AdminAllEventEntriesView({ children, ...props }) {
|
|
|
52
53
|
status: toStringValue(item.status ?? item.reviewStatus, "PENDING"),
|
|
53
54
|
updatedAt: toRelativeDate(item.submittedAt ?? item.createdAt),
|
|
54
55
|
eventId: toStringValue(item.eventId, ""),
|
|
56
|
+
_raw: item,
|
|
55
57
|
})),
|
|
56
58
|
getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
|
|
57
59
|
buildFilters: (state) => state.status && state.status !== "All" ? sieveFilter("status", SIEVE_OP.EQ, state.status) : undefined,
|
|
60
|
+
// Confirm/waitlist/cancel used to be the only affordances — an admin
|
|
61
|
+
// decided an entry's fate without ever seeing the entrant's answers.
|
|
62
|
+
onRowClick: (row) => setSelected(row),
|
|
58
63
|
renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
64
|
+
{
|
|
65
|
+
label: "View details",
|
|
66
|
+
onClick: () => setSelected(row),
|
|
67
|
+
},
|
|
59
68
|
{
|
|
60
69
|
label: ACTIONS.ADMIN["confirm-entry"].label,
|
|
61
70
|
onClick: () => updateMutation.mutate({ id: row.id, status: "CONFIRMED" }),
|
|
@@ -72,5 +81,11 @@ export function AdminAllEventEntriesView({ children, ...props }) {
|
|
|
72
81
|
] })),
|
|
73
82
|
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_EVENT_ENTRY_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) })),
|
|
74
83
|
};
|
|
75
|
-
|
|
84
|
+
const raw = selected?._raw ?? {};
|
|
85
|
+
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(RecordDetailModal, { isOpen: Boolean(selected), onClose: () => setSelected(null), title: selected?.primary ?? "Event Entry", badges: [{ label: selected?.status ?? "PENDING", variant: "default" }], fields: [
|
|
86
|
+
{ label: "Event", value: toStringValue(raw.eventId, "—") },
|
|
87
|
+
{ label: "Email", value: toStringValue(raw.userEmail, "—") },
|
|
88
|
+
{ label: "Points", value: String(raw.points ?? "—") },
|
|
89
|
+
{ label: "Submitted", value: selected?.updatedAt ?? "—" },
|
|
90
|
+
], metadata: raw.responses ?? undefined })] }));
|
|
76
91
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AdminAuditLogView — read-only list of admin action audit entries.
|
|
3
|
+
* Entries are written exclusively via `recordAdminAction()` from the
|
|
4
|
+
* instrumented privileged-action call sites (bans, checkout bypass, coupon
|
|
5
|
+
* edits, payout mark-paid, store status changes, role changes) — this view
|
|
6
|
+
* has no create/edit/delete affordance, only "view details".
|
|
7
|
+
*/
|
|
8
|
+
import React from "react";
|
|
9
|
+
import type { ListingLayoutProps } from "../../../ui";
|
|
10
|
+
export type AdminAuditLogViewProps = ListingLayoutProps;
|
|
11
|
+
export declare function AdminAuditLogView({ children, ...props }: AdminAuditLogViewProps): React.JSX.Element;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* AdminAuditLogView — read-only list of admin action audit entries.
|
|
5
|
+
* Entries are written exclusively via `recordAdminAction()` from the
|
|
6
|
+
* instrumented privileged-action call sites (bans, checkout bypass, coupon
|
|
7
|
+
* edits, payout mark-paid, store status changes, role changes) — this view
|
|
8
|
+
* has no create/edit/delete affordance, only "view details".
|
|
9
|
+
*/
|
|
10
|
+
import React, { useState } from "react";
|
|
11
|
+
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit/client";
|
|
12
|
+
import { sortBy } from "@mohasinac/appkit/client";
|
|
13
|
+
import { FilterChipGroup, Input, ListingLayout, RowActionMenu, Stack } from "../../../ui";
|
|
14
|
+
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
15
|
+
import { ADMIN_AUDIT_LOG_ACTION_TABS } from "../constants/filter-tabs";
|
|
16
|
+
import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
|
|
17
|
+
import { DataListingView } from "./DataListingView";
|
|
18
|
+
import { ViewAuditLogEntryModal } from "./ViewAuditLogEntryModal";
|
|
19
|
+
const ACTION_LABELS = {
|
|
20
|
+
user_hard_ban: "User hard-banned",
|
|
21
|
+
user_soft_ban: "User soft-banned",
|
|
22
|
+
user_unban: "User unbanned",
|
|
23
|
+
checkout_bypass: "Admin checkout bypass",
|
|
24
|
+
coupon_update: "Coupon updated",
|
|
25
|
+
payout_mark_paid: "Payout marked paid",
|
|
26
|
+
store_status_change: "Store status changed",
|
|
27
|
+
user_role_change: "User role changed",
|
|
28
|
+
};
|
|
29
|
+
export function AdminAuditLogView({ children, ...props }) {
|
|
30
|
+
const [viewEntry, setViewEntry] = useState(null);
|
|
31
|
+
if (React.Children.count(children) > 0) {
|
|
32
|
+
return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
|
|
33
|
+
}
|
|
34
|
+
const openEntry = (row) => {
|
|
35
|
+
const raw = row._raw;
|
|
36
|
+
setViewEntry({
|
|
37
|
+
id: row.id,
|
|
38
|
+
actorUid: toStringValue(raw.actorUid, ""),
|
|
39
|
+
actorName: toStringValue(raw.actorName, "") || undefined,
|
|
40
|
+
action: toStringValue(raw.action, ""),
|
|
41
|
+
targetType: toStringValue(raw.targetType, ""),
|
|
42
|
+
targetId: toStringValue(raw.targetId, ""),
|
|
43
|
+
targetLabel: toStringValue(raw.targetLabel, "") || undefined,
|
|
44
|
+
reason: toStringValue(raw.reason, "") || undefined,
|
|
45
|
+
metadata: raw.metadata ?? undefined,
|
|
46
|
+
createdAt: toStringValue(raw.createdAt, "") || undefined,
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
const config = {
|
|
50
|
+
portal: "admin",
|
|
51
|
+
title: "Audit Log",
|
|
52
|
+
searchPlaceholder: "Search by actor uid",
|
|
53
|
+
emptyLabel: "No audit log entries found",
|
|
54
|
+
filterKeys: ["action", "actorUid"],
|
|
55
|
+
defaultSort: sortBy("createdAt", "DESC"),
|
|
56
|
+
queryKey: ["admin", "audit-log", "listing"],
|
|
57
|
+
endpoint: ADMIN_ENDPOINTS.AUDIT_LOG,
|
|
58
|
+
sortOptions: [
|
|
59
|
+
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
60
|
+
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
61
|
+
],
|
|
62
|
+
mapRows: (response) => toRecordArray(response.entries).map((item, index) => {
|
|
63
|
+
const action = toStringValue(item.action, "unknown");
|
|
64
|
+
return {
|
|
65
|
+
id: toStringValue(item.id, `audit-${index}`),
|
|
66
|
+
primary: ACTION_LABELS[action] ?? action,
|
|
67
|
+
secondary: [
|
|
68
|
+
toStringValue(item.actorName ?? item.actorUid, "Unknown actor"),
|
|
69
|
+
`${toStringValue(item.targetType, "")}: ${toStringValue(item.targetLabel ?? item.targetId, "")}`,
|
|
70
|
+
].join(" · "),
|
|
71
|
+
status: action,
|
|
72
|
+
updatedAt: toRelativeDate(item.createdAt),
|
|
73
|
+
_raw: item,
|
|
74
|
+
};
|
|
75
|
+
}),
|
|
76
|
+
getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
|
|
77
|
+
onRowClick: (row) => openEntry(row),
|
|
78
|
+
buildFilters: (state) => {
|
|
79
|
+
const parts = [];
|
|
80
|
+
if (state.action && state.action !== "All")
|
|
81
|
+
parts.push(sieveFilter("action", SIEVE_OP.EQ, state.action));
|
|
82
|
+
if (state.actorUid)
|
|
83
|
+
parts.push(sieveFilter("actorUid", SIEVE_OP.EQ, state.actorUid));
|
|
84
|
+
return parts.length > 0 ? parts.join(",") : undefined;
|
|
85
|
+
},
|
|
86
|
+
renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
87
|
+
{
|
|
88
|
+
label: "View details",
|
|
89
|
+
onClick: () => openEntry(row),
|
|
90
|
+
},
|
|
91
|
+
] })),
|
|
92
|
+
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(Stack, { gap: "md", children: [_jsx(FilterChipGroup, { label: "Action", tabs: ADMIN_AUDIT_LOG_ACTION_TABS, value: pendingFilters.action ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, action: id })) }), _jsx(Input, { label: "Actor UID", value: pendingFilters.actorUid ?? "", onChange: (e) => setPendingFilters((p) => ({ ...p, actorUid: e.target.value })), placeholder: "Filter by actor uid" })] })),
|
|
93
|
+
};
|
|
94
|
+
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(ViewAuditLogEntryModal, { entry: viewEntry, isOpen: Boolean(viewEntry), onClose: () => setViewEntry(null) })] }));
|
|
95
|
+
}
|
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
4
4
|
import { useApiMutation } from "@mohasinac/appkit/client";
|
|
5
|
-
import React from "react";
|
|
5
|
+
import React, { useEffect, useMemo } from "react";
|
|
6
|
+
import { z } from "zod";
|
|
6
7
|
import { useQuery } from "@tanstack/react-query";
|
|
7
8
|
import { Button, ConfirmDeleteModal, Div, Heading, Input, RichTextEditor, RichTextRenderer, Row, Select, Span, Stack, StackedViewShell, TagInput, Text, Toggle, useToast } from "../../../ui";
|
|
9
|
+
import { FormShellContext, useFormShellState, applyZodIssues, FormErrorSummary } from "../../../ui/forms";
|
|
8
10
|
import { ImageUpload, MediaImage, useMediaUpload } from "../../media";
|
|
9
11
|
import { apiClient } from "../../../http";
|
|
10
12
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
13
|
+
import { blogPostCategorySchema } from "../../blog/schemas";
|
|
11
14
|
import { StepForm } from "../../shell";
|
|
15
|
+
// Form-input subset — `blogPostSchema` (blog schemas) requires `id` and uses
|
|
16
|
+
// a `MediaField` shape for `coverImage`, neither of which this draft has
|
|
17
|
+
// (coverImage here is a plain uploaded-URL string; id doesn't exist pre-save).
|
|
18
|
+
const blogDraftSchema = z.object({
|
|
19
|
+
title: z.string().min(1, "Title is required"),
|
|
20
|
+
slug: z.string().min(1, "Slug is required").regex(/^blog-/, "Slug must start with 'blog-'"),
|
|
21
|
+
category: blogPostCategorySchema,
|
|
22
|
+
metaDescription: z.string().max(160).optional().or(z.literal("")),
|
|
23
|
+
}).passthrough();
|
|
12
24
|
const __P = {
|
|
13
25
|
p4: "p-[var(--appkit-space-4)]",
|
|
14
26
|
};
|
|
@@ -72,6 +84,11 @@ export function AdminBlogEditorView({ postId, onSaved, onDeleted, embedded, ...r
|
|
|
72
84
|
const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);
|
|
73
85
|
const { showToast } = useToast();
|
|
74
86
|
const { upload } = useMediaUpload();
|
|
87
|
+
const { shellCtx, setFieldError, validate } = useFormShellState(blogDraftSchema);
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
validate(draft);
|
|
90
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
91
|
+
}, [draft, validate]);
|
|
75
92
|
const update = React.useCallback((partial) => {
|
|
76
93
|
setDraft((prev) => ({ ...prev, ...partial }));
|
|
77
94
|
}, []);
|
|
@@ -146,6 +163,10 @@ export function AdminBlogEditorView({ postId, onSaved, onDeleted, embedded, ...r
|
|
|
146
163
|
onSaved(id);
|
|
147
164
|
},
|
|
148
165
|
onError: (err) => {
|
|
166
|
+
const issues = err?.issues;
|
|
167
|
+
if (issues && issues.length > 0) {
|
|
168
|
+
applyZodIssues(issues, setFieldError);
|
|
169
|
+
}
|
|
149
170
|
showToast(err?.message ?? "Failed to save post.", "error");
|
|
150
171
|
},
|
|
151
172
|
});
|
|
@@ -163,6 +184,7 @@ export function AdminBlogEditorView({ postId, onSaved, onDeleted, embedded, ...r
|
|
|
163
184
|
const steps = [
|
|
164
185
|
{
|
|
165
186
|
label: "Content",
|
|
187
|
+
fields: ["title", "slug", "excerpt", "content"],
|
|
166
188
|
validate: (values) => !values.title.trim() ? "Title is required" : null,
|
|
167
189
|
render: ({ values, onChange }) => (_jsxs(Stack, { gap: "5", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Content" }), _jsx(Input, { label: "Title", value: values.title, onChange: (e) => {
|
|
168
190
|
const v = e.target.value;
|
|
@@ -174,6 +196,7 @@ export function AdminBlogEditorView({ postId, onSaved, onDeleted, embedded, ...r
|
|
|
174
196
|
},
|
|
175
197
|
{
|
|
176
198
|
label: "Media",
|
|
199
|
+
fields: ["coverImage", "youtubeId"],
|
|
177
200
|
render: ({ values, onChange }) => (_jsxs(Stack, { gap: "5", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Media" }), _jsx(ImageUpload, { label: "Cover Image", currentImage: values.coverImage, onUpload: (file) => upload(file, "blog", true, {
|
|
178
201
|
type: "blog-cover",
|
|
179
202
|
title: values.title || values.slug,
|
|
@@ -182,6 +205,7 @@ export function AdminBlogEditorView({ postId, onSaved, onDeleted, embedded, ...r
|
|
|
182
205
|
},
|
|
183
206
|
{
|
|
184
207
|
label: "SEO & Tags",
|
|
208
|
+
fields: ["category", "tags", "metaTitle", "metaDescription"],
|
|
185
209
|
render: ({ values, onChange }) => {
|
|
186
210
|
const readTime = Math.max(1, Math.round(values.content.replace(/<[^>]*>/g, "").split(/\s+/).filter(Boolean).length / 200));
|
|
187
211
|
return (_jsxs(Stack, { gap: "5", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "SEO & Tags" }), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(Select, { label: "Category", options: CATEGORY_OPTIONS, value: values.category, onValueChange: (v) => onChange({ category: v }) }), _jsxs(Div, { children: [_jsx(Text, { className: "mb-1.5", size: "sm", weight: "medium", children: "Estimated Read Time" }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: ["~", readTime, " min (auto-calculated from content)"] })] })] }), _jsx(TagInput, { label: "Tags", value: values.tags, onChange: (t) => onChange({ tags: t }), placeholder: "e.g. pokemon, grading, tcg" }), _jsx(Input, { label: "Meta Title (optional)", value: values.metaTitle, onChange: (e) => onChange({ metaTitle: e.target.value }), placeholder: "Defaults to post title" }), _jsx(Input, { label: "Meta Description (optional)", value: values.metaDescription, onChange: (e) => onChange({ metaDescription: e.target.value }), placeholder: "SEO description \u2014 max 160 chars", maxLength: 160 })] }));
|
|
@@ -189,6 +213,7 @@ export function AdminBlogEditorView({ postId, onSaved, onDeleted, embedded, ...r
|
|
|
189
213
|
},
|
|
190
214
|
{
|
|
191
215
|
label: "Publish",
|
|
216
|
+
fields: ["status", "publishedAt", "authorName", "isFeatured"],
|
|
192
217
|
render: ({ values, onChange }) => (_jsxs(Stack, { gap: "5", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Publish Settings" }), _jsxs(Div, { layout: "grid", gap: "4", className: "grid-cols-2", children: [_jsx(Select, { label: "Status", options: STATUS_OPTIONS, value: values.status, onValueChange: (v) => onChange({ status: v }) }), _jsx(Input, { label: "Publish date (optional)", value: values.publishedAt, onChange: (e) => onChange({ publishedAt: e.target.value }), type: "date", helperText: "Auto-set to now when publishing." })] }), _jsx(Input, { label: "Author Name", value: values.authorName, onChange: (e) => onChange({ authorName: e.target.value }), placeholder: "Author display name" }), _jsx(Toggle, { label: "Featured post", checked: values.isFeatured, onChange: (checked) => onChange({ isFeatured: checked }) }), isEdit && (_jsx(Div, { className: "border-t border-[var(--appkit-color-border)]", padding: "t-md", children: _jsx(Button, { type: "button", variant: "danger", size: "sm", onClick: () => setShowDeleteConfirm(true), isLoading: deleteMutation.isPending, children: "Delete post" }) }))] })),
|
|
193
218
|
},
|
|
194
219
|
];
|
|
@@ -196,7 +221,16 @@ export function AdminBlogEditorView({ postId, onSaved, onDeleted, embedded, ...r
|
|
|
196
221
|
setShowDeleteConfirm(false);
|
|
197
222
|
deleteMutation.mutate();
|
|
198
223
|
}, onClose: () => setShowDeleteConfirm(false), title: "Delete post", message: "This will permanently delete the blog post. This cannot be undone." }));
|
|
199
|
-
const
|
|
224
|
+
const fieldToStepIndex = useMemo(() => {
|
|
225
|
+
const map = {};
|
|
226
|
+
steps.forEach((step, i) => {
|
|
227
|
+
step.fields?.forEach((field) => { map[field] = i; });
|
|
228
|
+
});
|
|
229
|
+
return map;
|
|
230
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
231
|
+
}, [steps.length]);
|
|
232
|
+
const wizardShellCtx = useMemo(() => ({ ...shellCtx, fieldToStepIndex, goToStep: (n) => setCurrentStep(n) }), [shellCtx, fieldToStepIndex, setCurrentStep]);
|
|
233
|
+
const formContent = (_jsxs(FormShellContext.Provider, { value: wizardShellCtx, children: [_jsx(FormErrorSummary, {}), _jsx(StepForm, { steps: steps, values: draft, onChange: update, onComplete: () => { saveMutation.mutate(); }, formId: "admin-blog", currentStep: currentStep, onStepChange: setCurrentStep, completeLabel: isEdit ? "Save Changes" : "Create Post", isLoading: isLoading }), deleteModal] }));
|
|
200
234
|
if (embedded) {
|
|
201
235
|
return _jsx(Div, { className: `${__O.yAuto} ${__P.p4}`, children: formContent });
|
|
202
236
|
}
|
|
@@ -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, Toggle, useToast, } from "../../../ui";
|
|
9
|
-
import { FieldInput } from "../../../ui/forms";
|
|
9
|
+
import { FieldInput, FormErrorSummary } from "../../../ui/forms";
|
|
10
10
|
import { ImageUpload } from "../../media/upload/ImageUpload";
|
|
11
11
|
import { useMediaUpload } from "../../media";
|
|
12
12
|
import { apiClient } from "../../../http";
|
|
@@ -16,8 +16,9 @@ const brandFormSchema = z.object({
|
|
|
16
16
|
slug: z.string().regex(/^[a-z0-9-]+$/i).optional().or(z.literal("")),
|
|
17
17
|
description: z.string().max(2000).optional().or(z.literal("")),
|
|
18
18
|
logoURL: z.string().url().optional().or(z.literal("")),
|
|
19
|
-
bannerURL: z.string().url().optional().or(z.literal("")),
|
|
20
19
|
website: z.string().url().optional().or(z.literal("")),
|
|
20
|
+
country: z.string().max(80).optional().or(z.literal("")),
|
|
21
|
+
founded: z.number().int().min(1800).max(new Date().getFullYear()).optional(),
|
|
21
22
|
isActive: z.boolean(),
|
|
22
23
|
displayOrder: z.number().int().min(0).optional(),
|
|
23
24
|
});
|
|
@@ -38,8 +39,9 @@ export function AdminBrandEditorView({ brandId, onSaved, onDeleted, embedded, ..
|
|
|
38
39
|
const [slugManual, setSlugManual] = React.useState(false);
|
|
39
40
|
const [description, setDescription] = React.useState("");
|
|
40
41
|
const [logoURL, setLogoURL] = React.useState("");
|
|
41
|
-
const [bannerURL, setBannerURL] = React.useState("");
|
|
42
42
|
const [website, setWebsite] = React.useState("");
|
|
43
|
+
const [country, setCountry] = React.useState("");
|
|
44
|
+
const [founded, setFounded] = React.useState("");
|
|
43
45
|
const [isActive, setIsActive] = React.useState(true);
|
|
44
46
|
const [displayOrder, setDisplayOrder] = React.useState("");
|
|
45
47
|
const [deleteConfirmOpen, setDeleteConfirmOpen] = React.useState(false);
|
|
@@ -61,8 +63,9 @@ export function AdminBrandEditorView({ brandId, onSaved, onDeleted, embedded, ..
|
|
|
61
63
|
setSlugManual(true);
|
|
62
64
|
setDescription(brand.description ?? "");
|
|
63
65
|
setLogoURL(brand.logoURL ?? "");
|
|
64
|
-
setBannerURL(brand.bannerURL ?? "");
|
|
65
66
|
setWebsite(brand.website ?? "");
|
|
67
|
+
setCountry(brand.country ?? "");
|
|
68
|
+
setFounded(brand.founded !== undefined ? String(brand.founded) : "");
|
|
66
69
|
setIsActive(brand.isActive ?? true);
|
|
67
70
|
setDisplayOrder(brand.displayOrder !== undefined ? String(brand.displayOrder) : "");
|
|
68
71
|
}, [brandQuery.data]);
|
|
@@ -78,8 +81,9 @@ export function AdminBrandEditorView({ brandId, onSaved, onDeleted, embedded, ..
|
|
|
78
81
|
slug: slug || toBrandSlug(name),
|
|
79
82
|
description: description || undefined,
|
|
80
83
|
logoURL: logoURL || undefined,
|
|
81
|
-
bannerURL: bannerURL || undefined,
|
|
82
84
|
website: website || undefined,
|
|
85
|
+
country: country || undefined,
|
|
86
|
+
founded: founded !== "" ? Number(founded) : undefined,
|
|
83
87
|
isActive,
|
|
84
88
|
displayOrder: displayOrder !== "" ? Number(displayOrder) : undefined,
|
|
85
89
|
};
|
|
@@ -112,7 +116,7 @@ export function AdminBrandEditorView({ brandId, onSaved, onDeleted, embedded, ..
|
|
|
112
116
|
const formSection = (_jsxs(_Fragment, { children: [_jsx(Form, { schema: brandFormSchema, 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: "Brand name", value: name, onChange: (v) => handleNameChange(v), required: true, placeholder: "e.g. Hot Wheels" }), _jsx(Input, { label: "Slug", value: slug, onChange: (e) => {
|
|
113
117
|
setSlug(e.target.value);
|
|
114
118
|
setSlugManual(true);
|
|
115
|
-
}, placeholder: "brand-hot-wheels", helperText: "Auto-generated from name. Must start with 'brand-'." })] }), _jsx(Input, { label: "Description", value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Brief description of the brand" }),
|
|
119
|
+
}, placeholder: "brand-hot-wheels", helperText: "Auto-generated from name. Must start with 'brand-'." })] }), _jsx(Input, { label: "Description", value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Brief description of the brand" }), _jsx(ImageUpload, { label: "Cover Image", currentImage: logoURL, onUpload: (file) => upload(file, "brands", true, { type: "brand-logo", brand: name || slug }), onChange: setLogoURL }), _jsxs(Div, { layout: "grid", gap: "4", className: "sm:grid-cols-2", children: [_jsx(Input, { label: "Website", value: website, onChange: (e) => setWebsite(e.target.value), placeholder: "https://brand.com", type: "url" }), _jsx(Input, { label: "Display order", value: displayOrder, onChange: (e) => setDisplayOrder(e.target.value), type: "number", min: 0, placeholder: "0" })] }), _jsxs(Div, { layout: "grid", gap: "4", className: "sm:grid-cols-2", children: [_jsx(Input, { label: "Country of origin", value: country, onChange: (e) => setCountry(e.target.value), placeholder: "e.g. Japan" }), _jsx(Input, { label: "Founded year", value: founded, onChange: (e) => setFounded(e.target.value), type: "number", min: 1800, max: new Date().getFullYear(), placeholder: "e.g. 1999" })] }), _jsx(Toggle, { label: "Active", checked: isActive, onChange: setIsActive }), _jsx(FormErrorSummary, {}), _jsxs(Row, { gap: "3", padding: "t-xs", children: [_jsx(Button, { type: "submit", isLoading: isSubmitting, disabled: !name || isSubmitting, onClick: () => {
|
|
116
120
|
clearErrors();
|
|
117
121
|
if (!name.trim()) {
|
|
118
122
|
setFieldError("name", "Brand name is required");
|