@mohasinac/appkit 2.7.59 → 2.8.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/filters/filter-load-options.d.ts +4 -5
- package/dist/_internal/server/features/auctions/og.js +8 -32
- package/dist/_internal/server/features/blog/og.js +10 -49
- package/dist/_internal/server/features/checkout/actions.js +6 -0
- package/dist/_internal/server/features/classified/og.js +9 -51
- package/dist/_internal/server/features/digital-code/actions.d.ts +11 -0
- package/dist/_internal/server/features/digital-code/actions.js +30 -0
- package/dist/_internal/server/features/digital-code/index.d.ts +1 -0
- package/dist/_internal/server/features/digital-code/index.js +1 -0
- package/dist/_internal/server/features/digital-code/og.js +9 -51
- package/dist/_internal/server/features/events/og.js +10 -49
- package/dist/_internal/server/features/item-requests/data.d.ts +17 -0
- package/dist/_internal/server/features/item-requests/data.js +20 -0
- package/dist/_internal/server/features/item-requests/index.d.ts +2 -0
- package/dist/_internal/server/features/item-requests/index.js +2 -0
- package/dist/_internal/server/features/item-requests/og.d.ts +40 -0
- package/dist/_internal/server/features/item-requests/og.js +136 -0
- package/dist/_internal/server/features/live/actions.d.ts +5 -0
- package/dist/_internal/server/features/live/actions.js +24 -0
- package/dist/_internal/server/features/live/index.d.ts +1 -0
- package/dist/_internal/server/features/live/index.js +1 -0
- package/dist/_internal/server/features/live/og.js +9 -51
- package/dist/_internal/server/features/media/contextGuards.js +19 -8
- package/dist/_internal/server/features/pre-orders/og.js +8 -32
- package/dist/_internal/server/features/prize-draws/data.d.ts +17 -0
- package/dist/_internal/server/features/prize-draws/data.js +20 -0
- package/dist/_internal/server/features/prize-draws/index.d.ts +2 -0
- package/dist/_internal/server/features/prize-draws/index.js +2 -0
- package/dist/_internal/server/features/prize-draws/og.d.ts +30 -0
- package/dist/_internal/server/features/prize-draws/og.js +50 -0
- package/dist/_internal/server/features/products/og.js +8 -33
- package/dist/_internal/server/features/seo/og-layout.d.ts +42 -0
- package/dist/_internal/server/features/seo/og-layout.js +69 -0
- package/dist/_internal/server/features/stores/og.js +8 -41
- package/dist/_internal/server/features/sublisting-categories/og.js +11 -42
- package/dist/client.d.ts +10 -6
- package/dist/client.js +7 -3
- package/dist/constants/index.d.ts +4 -0
- package/dist/constants/index.js +2 -0
- package/dist/constants/notification-types.d.ts +20 -0
- package/dist/constants/notification-types.js +37 -0
- package/dist/constants/pagination-presets.d.ts +39 -0
- package/dist/constants/pagination-presets.js +38 -0
- package/dist/errors/messages.d.ts +0 -4
- package/dist/errors/messages.js +0 -4
- package/dist/features/account/components/UserOffersPanel.js +1 -1
- package/dist/features/account/components/UserSupportView.d.ts +1 -2
- package/dist/features/account/components/UserSupportView.js +9 -1
- package/dist/features/admin/actions/admin-actions.js +16 -1
- package/dist/features/admin/components/AdminAllEventEntriesView.d.ts +2 -3
- package/dist/features/admin/components/AdminAllEventEntriesView.js +50 -83
- package/dist/features/admin/components/AdminBidsView.d.ts +2 -3
- package/dist/features/admin/components/AdminBidsView.js +64 -98
- package/dist/features/admin/components/AdminBlogView.d.ts +3 -7
- package/dist/features/admin/components/AdminBlogView.js +53 -83
- package/dist/features/admin/components/AdminBrandsView.d.ts +2 -3
- package/dist/features/admin/components/AdminBrandsView.js +84 -83
- package/dist/features/admin/components/AdminBundlesView.js +82 -125
- package/dist/features/admin/components/AdminCarouselView.d.ts +2 -2
- package/dist/features/admin/components/AdminCarouselView.js +17 -52
- package/dist/features/admin/components/AdminCartsView.d.ts +2 -3
- package/dist/features/admin/components/AdminCartsView.js +39 -88
- package/dist/features/admin/components/AdminCategoriesView.d.ts +3 -3
- package/dist/features/admin/components/AdminCategoriesView.js +98 -92
- package/dist/features/admin/components/AdminCategoryEditorView.js +2 -2
- package/dist/features/admin/components/AdminClassifiedView.d.ts +3 -0
- package/dist/features/admin/components/AdminClassifiedView.js +44 -0
- package/dist/features/admin/components/AdminContactView.d.ts +2 -2
- package/dist/features/admin/components/AdminContactView.js +104 -111
- package/dist/features/admin/components/AdminCouponsView.d.ts +3 -6
- package/dist/features/admin/components/AdminCouponsView.js +66 -86
- package/dist/features/admin/components/AdminDigitalCodesView.d.ts +3 -0
- package/dist/features/admin/components/AdminDigitalCodesView.js +51 -0
- package/dist/features/admin/components/AdminFaqsView.d.ts +4 -4
- package/dist/features/admin/components/AdminFaqsView.js +77 -33
- package/dist/features/admin/components/AdminFeaturesView.d.ts +2 -3
- package/dist/features/admin/components/AdminFeaturesView.js +57 -70
- package/dist/features/admin/components/AdminGroupedListingsView.d.ts +3 -0
- package/dist/features/admin/components/AdminGroupedListingsView.js +43 -0
- package/dist/features/admin/components/AdminHistoryView.d.ts +2 -3
- package/dist/features/admin/components/AdminHistoryView.js +35 -52
- package/dist/features/admin/components/AdminListingScaffold.d.ts +2 -2
- package/dist/features/admin/components/AdminListingScaffold.js +3 -3
- package/dist/features/admin/components/AdminLiveView.d.ts +3 -0
- package/dist/features/admin/components/AdminLiveView.js +50 -0
- package/dist/features/admin/components/AdminNewsletterView.d.ts +2 -2
- package/dist/features/admin/components/AdminNewsletterView.js +67 -97
- package/dist/features/admin/components/AdminNotificationsView.d.ts +2 -3
- package/dist/features/admin/components/AdminNotificationsView.js +67 -93
- package/dist/features/admin/components/AdminOrderEditorView.js +10 -10
- package/dist/features/admin/components/AdminOrdersView.d.ts +2 -3
- package/dist/features/admin/components/AdminOrdersView.js +93 -107
- package/dist/features/admin/components/AdminPayoutsView.d.ts +2 -3
- package/dist/features/admin/components/AdminPayoutsView.js +51 -91
- package/dist/features/admin/components/AdminPrizeDrawsView.d.ts +2 -3
- package/dist/features/admin/components/AdminPrizeDrawsView.js +39 -76
- package/dist/features/admin/components/AdminProductEditorView.js +3 -3
- package/dist/features/admin/components/AdminProductsView.d.ts +4 -4
- package/dist/features/admin/components/AdminProductsView.js +167 -163
- package/dist/features/admin/components/AdminReturnRequestsView.d.ts +1 -1
- package/dist/features/admin/components/AdminReturnRequestsView.js +64 -66
- package/dist/features/admin/components/AdminReviewsView.d.ts +3 -7
- package/dist/features/admin/components/AdminReviewsView.js +94 -110
- package/dist/features/admin/components/AdminScammersView.d.ts +2 -3
- package/dist/features/admin/components/AdminScammersView.js +53 -97
- package/dist/features/admin/components/AdminSectionsView.js +1 -1
- package/dist/features/admin/components/AdminSessionsView.d.ts +2 -3
- package/dist/features/admin/components/AdminSessionsView.js +62 -89
- package/dist/features/admin/components/AdminStoreAddressesView.d.ts +1 -1
- package/dist/features/admin/components/AdminStoreAddressesView.js +39 -53
- package/dist/features/admin/components/AdminStoreEditorView.js +21 -8
- package/dist/features/admin/components/AdminStoresView.d.ts +2 -3
- package/dist/features/admin/components/AdminStoresView.js +67 -99
- package/dist/features/admin/components/AdminSublistingCategoriesView.d.ts +1 -1
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +28 -44
- package/dist/features/admin/components/AdminSupportTicketDetailView.d.ts +10 -1
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +191 -5
- package/dist/features/admin/components/AdminSupportTicketsView.d.ts +2 -3
- package/dist/features/admin/components/AdminSupportTicketsView.js +65 -96
- package/dist/features/admin/components/AdminTeamView.d.ts +2 -2
- package/dist/features/admin/components/AdminTeamView.js +40 -74
- package/dist/features/admin/components/AdminUserEditorView.d.ts +11 -1
- package/dist/features/admin/components/AdminUserEditorView.js +80 -6
- package/dist/features/admin/components/AdminUsersView.d.ts +2 -3
- package/dist/features/admin/components/AdminUsersView.js +108 -100
- package/dist/features/admin/components/AdminViewCards.d.ts +1 -1
- package/dist/features/admin/components/AdminWishlistsView.d.ts +2 -3
- package/dist/features/admin/components/AdminWishlistsView.js +36 -62
- package/dist/features/admin/components/DataListingView.d.ts +122 -0
- package/dist/features/admin/components/DataListingView.js +66 -0
- package/dist/features/admin/components/index.d.ts +10 -4
- package/dist/features/admin/components/index.js +6 -2
- package/dist/features/admin/constants/filter-tabs.d.ts +23 -17
- package/dist/features/admin/constants/filter-tabs.js +21 -17
- package/dist/features/admin/hooks/useAdminListing.d.ts +1 -0
- package/dist/features/admin/hooks/useAdminListing.js +1 -1
- package/dist/features/admin/schemas/firestore.js +6 -7
- package/dist/features/auctions/server.d.ts +0 -1
- package/dist/features/auctions/server.js +0 -1
- package/dist/features/auth/actions/realtime-token-actions.js +1 -1
- package/dist/features/auth/repository/user.repository.js +1 -1
- package/dist/features/cart/repository/cart.repository.d.ts +4 -1
- package/dist/features/cart/repository/cart.repository.js +12 -0
- package/dist/features/checkout/hooks/useCheckoutApi.d.ts +0 -2
- package/dist/features/checkout/index.d.ts +1 -1
- package/dist/features/events/components/AdminEventsView.d.ts +3 -3
- package/dist/features/events/components/AdminEventsView.js +50 -86
- package/dist/features/events/components/EventOfferCard.d.ts +19 -0
- package/dist/features/events/components/EventOfferCard.js +29 -0
- package/dist/features/events/components/EventRaffleEntryForm.d.ts +25 -0
- package/dist/features/events/components/EventRaffleEntryForm.js +37 -0
- package/dist/features/events/components/EventRafflesSection.d.ts +1 -4
- package/dist/features/events/components/EventRafflesSection.js +17 -6
- package/dist/features/events/components/EventSaleBanner.d.ts +21 -0
- package/dist/features/events/components/EventSaleBanner.js +18 -0
- package/dist/features/events/components/index.d.ts +6 -0
- package/dist/features/events/components/index.js +4 -0
- package/dist/features/events/repository/event-entry.repository.d.ts +5 -0
- package/dist/features/events/repository/event-entry.repository.js +7 -0
- package/dist/features/filters/AsyncFacetSection.d.ts +1 -1
- package/dist/features/homepage/components/FAQSection.d.ts +0 -5
- package/dist/features/homepage/lib/live-stats.js +1 -1
- package/dist/features/homepage/schemas/firestore.d.ts +0 -2
- package/dist/features/layout/index.d.ts +0 -2
- package/dist/features/layout/index.js +0 -1
- package/dist/features/pre-orders/server.d.ts +0 -1
- package/dist/features/pre-orders/server.js +0 -1
- package/dist/features/products/components/GroupSettingsPanel.js +2 -2
- package/dist/features/products/components/PrizeDrawItemsEditor.js +2 -2
- package/dist/features/products/components/PrizeDrawsSection.d.ts +1 -4
- package/dist/features/products/components/PrizeDrawsSection.js +17 -6
- package/dist/features/products/components/PrizeRevealModal.js +1 -1
- package/dist/features/products/components/ProductCardMetadataSection.d.ts +11 -0
- package/dist/features/products/components/ProductCardMetadataSection.js +25 -0
- package/dist/features/products/components/ProductDetailPageView.js +3 -1
- package/dist/features/products/components/ProductFeaturesSelector.js +1 -1
- package/dist/features/products/components/ProductGradingTab.d.ts +13 -0
- package/dist/features/products/components/ProductGradingTab.js +25 -0
- package/dist/features/products/components/ProductGrid.js +14 -2
- package/dist/features/products/components/ProductTabsShell.d.ts +5 -1
- package/dist/features/products/components/ProductTabsShell.js +6 -1
- package/dist/features/products/components/index.d.ts +4 -0
- package/dist/features/products/components/index.js +3 -0
- package/dist/features/products/constants/action-defs.d.ts +5 -0
- package/dist/features/products/constants/action-defs.js +52 -32
- package/dist/features/products/constants/sieve.d.ts +24 -4
- package/dist/features/products/constants/sieve.js +20 -11
- package/dist/features/products/schemas/firestore.d.ts +17 -0
- package/dist/features/products/schemas/product-features.validators.d.ts +2 -2
- package/dist/features/products/schemas/product-templates.d.ts +2 -4
- package/dist/features/products/types/index.d.ts +4 -0
- package/dist/features/promotions/server.d.ts +0 -1
- package/dist/features/promotions/server.js +0 -1
- package/dist/features/search/api/route.js +44 -1
- package/dist/features/search/components/Search.js +2 -0
- package/dist/features/search/hooks/useNavSuggestions.d.ts +5 -1
- package/dist/features/search/hooks/useNavSuggestions.js +31 -4
- package/dist/features/seller/components/BrandInlineSelect.js +2 -2
- package/dist/features/seller/components/CategoryInlineSelect.js +2 -2
- package/dist/features/seller/components/SellerAddressesView.js +1 -1
- package/dist/features/seller/components/SellerAuctionsView.d.ts +3 -3
- package/dist/features/seller/components/SellerAuctionsView.js +60 -90
- package/dist/features/seller/components/SellerBidsView.js +1 -1
- package/dist/features/seller/components/SellerBundlesView.js +64 -89
- package/dist/features/seller/components/SellerClassifiedView.js +67 -90
- package/dist/features/seller/components/SellerCouponEditorView.js +1 -1
- package/dist/features/seller/components/SellerCouponsView.d.ts +2 -2
- package/dist/features/seller/components/SellerCouponsView.js +45 -84
- package/dist/features/seller/components/SellerDigitalCodesView.js +63 -88
- package/dist/features/seller/components/SellerGroupedListingsView.js +33 -49
- package/dist/features/seller/components/SellerLiveView.js +63 -84
- package/dist/features/seller/components/SellerOffersPanel.js +1 -1
- package/dist/features/seller/components/SellerOffersView.d.ts +2 -2
- package/dist/features/seller/components/SellerOffersView.js +37 -69
- package/dist/features/seller/components/SellerOrdersView.d.ts +2 -2
- package/dist/features/seller/components/SellerOrdersView.js +4 -4
- package/dist/features/seller/components/SellerPayoutMethodsView.js +1 -1
- package/dist/features/seller/components/SellerPayoutsView.d.ts +2 -2
- package/dist/features/seller/components/SellerPayoutsView.js +34 -68
- package/dist/features/seller/components/SellerPreOrdersView.d.ts +2 -2
- package/dist/features/seller/components/SellerPreOrdersView.js +49 -82
- package/dist/features/seller/components/SellerPrizeDrawsView.d.ts +2 -2
- package/dist/features/seller/components/SellerPrizeDrawsView.js +49 -82
- package/dist/features/seller/components/SellerProductShell.d.ts +1 -1
- package/dist/features/seller/components/SellerProductsView.d.ts +2 -2
- package/dist/features/seller/components/SellerProductsView.js +15 -10
- package/dist/features/seller/components/SellerShippingConfigsView.js +1 -1
- package/dist/features/seller/components/SellerSidebar.d.ts +0 -4
- package/dist/features/seller/components/SellerSidebar.js +0 -2
- package/dist/features/seller/components/SellerStoreCategoriesView.js +1 -1
- package/dist/features/seller/components/SellerTemplatesView.js +2 -2
- package/dist/features/seller/components/SellersListView.d.ts +1 -2
- package/dist/features/seller/components/analytics/SellerAnalyticsStats.d.ts +5 -0
- package/dist/features/seller/components/analytics/SellerAnalyticsStats.js +2 -8
- package/dist/features/seller/components/index.d.ts +0 -3
- package/dist/features/seller/components/index.js +0 -2
- package/dist/features/seller/server.d.ts +0 -1
- package/dist/features/seller/server.js +0 -1
- package/dist/features/shell/FormShell.d.ts +1 -1
- package/dist/features/shell/FormShell.js +4 -1
- package/dist/features/shell/field-groups/ImageFieldGroup.d.ts +18 -0
- package/dist/features/shell/field-groups/ImageFieldGroup.js +15 -0
- package/dist/features/shell/field-groups/SeoFieldGroup.d.ts +22 -0
- package/dist/features/shell/field-groups/SeoFieldGroup.js +16 -0
- package/dist/features/shell/field-groups/StatusFieldGroup.d.ts +35 -0
- package/dist/features/shell/field-groups/StatusFieldGroup.js +16 -0
- package/dist/features/shell/field-groups/TitleDescriptionGroup.d.ts +40 -0
- package/dist/features/shell/field-groups/TitleDescriptionGroup.js +16 -0
- package/dist/features/shell/field-groups/index.d.ts +13 -0
- package/dist/features/shell/field-groups/index.js +9 -0
- package/dist/features/shell/index.d.ts +2 -0
- package/dist/features/shell/index.js +1 -0
- package/dist/features/stores/components/StoreCapabilitiesGuideView.d.ts +1 -2
- package/dist/features/stores/components/StoreFinanceGuideView.d.ts +1 -2
- package/dist/features/stores/components/StoreHeader.js +2 -1
- package/dist/features/stores/components/StoreListingsGuideView.d.ts +1 -2
- package/dist/features/stores/components/StoreOrdersGuideView.d.ts +1 -2
- package/dist/features/stores/components/StoreScopedSearch.d.ts +16 -0
- package/dist/features/stores/components/StoreScopedSearch.js +27 -0
- package/dist/features/stores/components/StoreSettingsGuideView.d.ts +1 -2
- package/dist/features/stores/server.d.ts +0 -1
- package/dist/features/stores/server.js +0 -1
- package/dist/features/support/schemas/firestore.d.ts +22 -1
- package/dist/features/support/schemas/firestore.js +10 -0
- package/dist/features/wishlist/server.d.ts +0 -1
- package/dist/features/wishlist/server.js +0 -1
- package/dist/index.d.ts +9 -27
- package/dist/index.js +14 -50
- package/dist/next/routing/route-map.d.ts +8 -0
- package/dist/next/routing/route-map.js +4 -0
- package/dist/react/hooks/useListingTypeFlags.d.ts +32 -0
- package/dist/react/hooks/useListingTypeFlags.js +19 -0
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +1 -1
- package/dist/security/pii-redact.d.ts +8 -0
- package/dist/security/pii-redact.js +10 -1
- package/dist/seed/grouped-listings-seed-data.d.ts +4 -0
- package/dist/seed/products-preorders-seed-data.d.ts +4 -0
- package/dist/seed/products-prize-draws-seed-data.js +3 -0
- package/dist/seed/site-settings-seed-data.js +5 -5
- package/dist/seed/sub-listings-seed-data.d.ts +4 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +4 -0
- package/dist/styles.css +54 -395
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/BaseListingCard.d.ts +10 -0
- package/dist/ui/components/BaseListingCard.js +17 -1
- package/dist/ui/components/DetailPageGallery.d.ts +32 -0
- package/dist/ui/components/DetailPageGallery.js +47 -0
- package/dist/ui/components/DetailPageHero.d.ts +30 -0
- package/dist/ui/components/DetailPageHero.js +22 -0
- package/dist/ui/components/DetailPageTabs.d.ts +34 -0
- package/dist/ui/components/DetailPageTabs.js +38 -0
- package/dist/ui/components/HorizontalScroller.style.css +15 -2
- package/dist/ui/components/ListingLayout.d.ts +11 -2
- package/dist/ui/components/ListingLayout.js +12 -6
- package/dist/ui/components/PaginatedSelect.d.ts +66 -0
- package/dist/ui/components/PaginatedSelect.js +201 -0
- package/dist/ui/components/PaginatedSelect.style.css +254 -0
- package/dist/ui/components/Semantic.d.ts +0 -4
- package/dist/ui/components/Semantic.js +0 -2
- package/dist/ui/components/SettingsSection.d.ts +47 -0
- package/dist/ui/components/SettingsSection.js +38 -0
- package/dist/ui/components/index.style.css +1 -4
- package/dist/ui/index.d.ts +12 -14
- package/dist/ui/index.js +6 -7
- package/dist/utils/id-generators.d.ts +1 -18
- package/dist/utils/id-generators.js +51 -18
- package/dist/validation/schemas.d.ts +25 -0
- package/dist/validation/schemas.js +28 -0
- package/package.json +1 -1
|
@@ -1,42 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx
|
|
3
|
-
import { useState, useCallback } from "react";
|
|
4
|
-
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
|
-
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
6
|
-
import { BulkActionBar, ListingToolbar, Pagination } from "../../../ui";
|
|
7
|
-
import { useBottomActions } from "../../layout";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
3
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
4
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
10
5
|
import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
|
|
11
|
-
import { toRecordArray, toRelativeDate, toStringValue,
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export function AdminSublistingCategoriesView({ onBulkDelete }) {
|
|
22
|
-
const [view, setView] = useState("table");
|
|
23
|
-
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
24
|
-
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
25
|
-
const commitSearch = useCallback(() => {
|
|
26
|
-
table.set("q", searchInput.trim());
|
|
27
|
-
}, [searchInput, table]);
|
|
28
|
-
const resetAll = useCallback(() => {
|
|
29
|
-
table.setMany({ q: "", sort: "" });
|
|
30
|
-
setSearchInput("");
|
|
31
|
-
}, [table]);
|
|
32
|
-
const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT;
|
|
33
|
-
const { rows, total, isLoading, errorMessage } = useAdminListingData({
|
|
6
|
+
import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
|
|
7
|
+
import { DataListingView } from "./DataListingView";
|
|
8
|
+
export function AdminSublistingCategoriesView({ onBulkDelete, }) {
|
|
9
|
+
const config = {
|
|
10
|
+
portal: "admin",
|
|
11
|
+
title: "Sub-listing Categories",
|
|
12
|
+
searchPlaceholder: "Search sub-listing categories…",
|
|
13
|
+
emptyLabel: "No sub-listing categories found",
|
|
14
|
+
filterKeys: [],
|
|
15
|
+
defaultSort: "name",
|
|
34
16
|
queryKey: ["admin", "sublisting-categories", "listing"],
|
|
35
17
|
endpoint: ADMIN_ENDPOINTS.SUBLISTING_CATEGORIES,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
18
|
+
sortOptions: [
|
|
19
|
+
{ value: "name", label: "Name A–Z" },
|
|
20
|
+
{ value: "-name", label: "Name Z–A" },
|
|
21
|
+
{ value: "-productCount", label: "Most listings" },
|
|
22
|
+
{ value: "-createdAt", label: "Newest" },
|
|
23
|
+
],
|
|
40
24
|
mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
|
|
41
25
|
id: toStringValue(item.id, `sc-${index}`),
|
|
42
26
|
primary: toStringValue(item.name, "Untitled"),
|
|
@@ -50,15 +34,15 @@ export function AdminSublistingCategoriesView({ onBulkDelete }) {
|
|
|
50
34
|
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
51
35
|
})),
|
|
52
36
|
getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
37
|
+
buildFilters: () => undefined,
|
|
38
|
+
buildBulkActions: onBulkDelete
|
|
39
|
+
? (selection) => [
|
|
40
|
+
buildBulkAction(ACTIONS.ADMIN["delete-sublisting-category"], async () => {
|
|
41
|
+
await onBulkDelete(selection.selectedIds);
|
|
42
|
+
selection.clearSelection();
|
|
43
|
+
}),
|
|
44
|
+
]
|
|
45
|
+
: undefined,
|
|
46
|
+
};
|
|
47
|
+
return _jsx(DataListingView, { config: config });
|
|
64
48
|
}
|
|
@@ -5,6 +5,13 @@ interface TicketMessageClient {
|
|
|
5
5
|
body?: string;
|
|
6
6
|
createdAt?: string;
|
|
7
7
|
}
|
|
8
|
+
interface RelatedPartiesClient {
|
|
9
|
+
userId?: string;
|
|
10
|
+
storeId?: string;
|
|
11
|
+
orderId?: string;
|
|
12
|
+
productId?: string;
|
|
13
|
+
bidId?: string;
|
|
14
|
+
}
|
|
8
15
|
export interface AdminSupportTicketDetailViewProps {
|
|
9
16
|
open: boolean;
|
|
10
17
|
onClose: () => void;
|
|
@@ -18,6 +25,8 @@ export interface AdminSupportTicketDetailViewProps {
|
|
|
18
25
|
messages?: TicketMessageClient[];
|
|
19
26
|
internalNotes?: string;
|
|
20
27
|
orderId?: string;
|
|
28
|
+
/** ST-6 — current relatedParties snapshot from the ticket document. */
|
|
29
|
+
relatedParties?: RelatedPartiesClient;
|
|
21
30
|
}
|
|
22
|
-
export declare function AdminSupportTicketDetailView({ open, onClose, ticketId, subject, userDisplayName, category, currentStatus, currentPriority, description, messages, internalNotes, orderId, }: AdminSupportTicketDetailViewProps): import("react/jsx-runtime").JSX.Element;
|
|
31
|
+
export declare function AdminSupportTicketDetailView({ open, onClose, ticketId, subject, userDisplayName, category, currentStatus, currentPriority, description, messages, internalNotes, orderId, relatedParties, }: AdminSupportTicketDetailViewProps): import("react/jsx-runtime").JSX.Element;
|
|
23
32
|
export {};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
4
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
5
|
-
import { Button, Div, FormActions, Select, SideDrawer, Text, useToast, } from "../../../ui";
|
|
5
|
+
import { Button, Div, FormActions, Input, Row, Select, SideDrawer, Text, Toggle, useToast, } from "../../../ui";
|
|
6
6
|
import { apiClient } from "../../../http";
|
|
7
7
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
8
|
const STATUS_OPTIONS = [
|
|
@@ -30,28 +30,46 @@ const ROLE_LABEL = {
|
|
|
30
30
|
support: "Support",
|
|
31
31
|
admin: "Admin",
|
|
32
32
|
};
|
|
33
|
-
export function AdminSupportTicketDetailView({ open, onClose, ticketId, subject, userDisplayName, category, currentStatus, currentPriority, description, messages = [], internalNotes, orderId, }) {
|
|
33
|
+
export function AdminSupportTicketDetailView({ open, onClose, ticketId, subject, userDisplayName, category, currentStatus, currentPriority, description, messages = [], internalNotes, orderId, relatedParties, }) {
|
|
34
34
|
const queryClient = useQueryClient();
|
|
35
35
|
const { showToast } = useToast();
|
|
36
36
|
const [status, setStatus] = React.useState(currentStatus ?? "open");
|
|
37
37
|
const [priority, setPriority] = React.useState(currentPriority ?? "normal");
|
|
38
38
|
const [notes, setNotes] = React.useState(internalNotes ?? "");
|
|
39
39
|
const [replyBody, setReplyBody] = React.useState("");
|
|
40
|
+
// ST-6 — local edits for the linked-parties panel
|
|
41
|
+
const [parties, setParties] = React.useState(relatedParties ?? {});
|
|
40
42
|
React.useEffect(() => {
|
|
41
43
|
if (open) {
|
|
42
44
|
setStatus(currentStatus ?? "open");
|
|
43
45
|
setPriority(currentPriority ?? "normal");
|
|
44
46
|
setNotes(internalNotes ?? "");
|
|
45
47
|
setReplyBody("");
|
|
48
|
+
setParties(relatedParties ?? {});
|
|
46
49
|
}
|
|
47
|
-
}, [open, currentStatus, currentPriority, internalNotes]);
|
|
50
|
+
}, [open, currentStatus, currentPriority, internalNotes, relatedParties]);
|
|
48
51
|
const invalidate = () => queryClient.invalidateQueries({ queryKey: ["admin", "support-tickets"] });
|
|
52
|
+
const cleanedParties = React.useMemo(() => {
|
|
53
|
+
const out = {};
|
|
54
|
+
if (parties.userId?.trim())
|
|
55
|
+
out.userId = parties.userId.trim();
|
|
56
|
+
if (parties.storeId?.trim())
|
|
57
|
+
out.storeId = parties.storeId.trim();
|
|
58
|
+
if (parties.orderId?.trim())
|
|
59
|
+
out.orderId = parties.orderId.trim();
|
|
60
|
+
if (parties.productId?.trim())
|
|
61
|
+
out.productId = parties.productId.trim();
|
|
62
|
+
if (parties.bidId?.trim())
|
|
63
|
+
out.bidId = parties.bidId.trim();
|
|
64
|
+
return out;
|
|
65
|
+
}, [parties]);
|
|
49
66
|
const updateMutation = useMutation({
|
|
50
67
|
mutationFn: async () => {
|
|
51
68
|
await apiClient.patch(ADMIN_ENDPOINTS.SUPPORT_TICKET_BY_ID(ticketId), {
|
|
52
69
|
status,
|
|
53
70
|
priority,
|
|
54
71
|
internalNotes: notes || undefined,
|
|
72
|
+
relatedParties: Object.keys(cleanedParties).length > 0 ? cleanedParties : undefined,
|
|
55
73
|
});
|
|
56
74
|
},
|
|
57
75
|
onSuccess: () => {
|
|
@@ -77,7 +95,175 @@ export function AdminSupportTicketDetailView({ open, onClose, ticketId, subject,
|
|
|
77
95
|
showToast(err?.message ?? "Failed to send reply.", "error");
|
|
78
96
|
},
|
|
79
97
|
});
|
|
98
|
+
// ST-4 — Apply Store Change action (only relevant for store_change_request)
|
|
99
|
+
const [storeStatus, setStoreStatus] = React.useState("active");
|
|
100
|
+
const [storeIsVerified, setStoreIsVerified] = React.useState(false);
|
|
101
|
+
const [storeIsFeatured, setStoreIsFeatured] = React.useState(false);
|
|
102
|
+
const linkedStoreId = parties.storeId?.trim();
|
|
103
|
+
const isStoreChangeRequest = category === "store_change_request";
|
|
104
|
+
const applyStoreChange = useMutation({
|
|
105
|
+
mutationFn: async () => {
|
|
106
|
+
if (!linkedStoreId)
|
|
107
|
+
throw new Error("No linked store on this ticket.");
|
|
108
|
+
await apiClient.patch(ADMIN_ENDPOINTS.STORE_BY_ID(linkedStoreId), {
|
|
109
|
+
storeStatus,
|
|
110
|
+
isVerified: storeIsVerified,
|
|
111
|
+
isFeatured: storeIsFeatured,
|
|
112
|
+
});
|
|
113
|
+
},
|
|
114
|
+
onSuccess: () => {
|
|
115
|
+
showToast("Store change applied.", "success");
|
|
116
|
+
// Append an internal note marking the action; non-fatal if it fails.
|
|
117
|
+
apiClient
|
|
118
|
+
.patch(ADMIN_ENDPOINTS.SUPPORT_TICKET_BY_ID(ticketId), {
|
|
119
|
+
internalNotes: (notes ? notes + "\n" : "") +
|
|
120
|
+
`[${new Date().toISOString()}] Applied store change to ${linkedStoreId}: status=${storeStatus}, verified=${storeIsVerified}, featured=${storeIsFeatured}`,
|
|
121
|
+
})
|
|
122
|
+
.catch(() => { });
|
|
123
|
+
invalidate();
|
|
124
|
+
},
|
|
125
|
+
onError: (err) => {
|
|
126
|
+
showToast(err?.message ?? "Failed to apply store change.", "error");
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
// ST-3 — Modify Order Items panel (fetches current items on demand, lets
|
|
130
|
+
// admin edit quantity per line + remove lines, then PATCHes the order).
|
|
131
|
+
const [orderItemsOpen, setOrderItemsOpen] = React.useState(false);
|
|
132
|
+
const [orderItems, setOrderItems] = React.useState([]);
|
|
133
|
+
const linkedOrderId = parties.orderId?.trim() || orderId;
|
|
134
|
+
const isOrderModificationRequest = category === "order_modification_request" || category === "order_issue";
|
|
135
|
+
const loadOrderItems = useMutation({
|
|
136
|
+
mutationFn: async () => {
|
|
137
|
+
if (!linkedOrderId)
|
|
138
|
+
throw new Error("No linked order on this ticket.");
|
|
139
|
+
const res = await apiClient.get(ADMIN_ENDPOINTS.ORDER_BY_ID(linkedOrderId));
|
|
140
|
+
const items = Array.isArray(res?.items) ? res.items : [];
|
|
141
|
+
setOrderItems(items.map((it) => ({
|
|
142
|
+
productId: String(it.productId ?? ""),
|
|
143
|
+
productTitle: String(it.productTitle ?? ""),
|
|
144
|
+
quantity: Number(it.quantity ?? 0),
|
|
145
|
+
unitPrice: Number(it.unitPrice ?? 0),
|
|
146
|
+
totalPrice: Number(it.totalPrice ?? 0),
|
|
147
|
+
})));
|
|
148
|
+
setOrderItemsOpen(true);
|
|
149
|
+
},
|
|
150
|
+
onError: (err) => {
|
|
151
|
+
showToast(err?.message ?? "Failed to load order.", "error");
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
// ST-5 — Lift Ban actions for unban_request tickets
|
|
155
|
+
const linkedUserId = parties.userId?.trim();
|
|
156
|
+
const isUnbanRequest = category === "unban_request";
|
|
157
|
+
const liftHardBan = useMutation({
|
|
158
|
+
mutationFn: async () => {
|
|
159
|
+
if (!linkedUserId)
|
|
160
|
+
throw new Error("No linked user on this ticket.");
|
|
161
|
+
await apiClient.post(ADMIN_ENDPOINTS.USER_UNBAN(linkedUserId), {});
|
|
162
|
+
},
|
|
163
|
+
onSuccess: () => {
|
|
164
|
+
showToast("Hard ban lifted.", "success");
|
|
165
|
+
apiClient
|
|
166
|
+
.patch(ADMIN_ENDPOINTS.SUPPORT_TICKET_BY_ID(ticketId), {
|
|
167
|
+
internalNotes: (notes ? notes + "\n" : "") +
|
|
168
|
+
`[${new Date().toISOString()}] Lifted hard ban for ${linkedUserId}`,
|
|
169
|
+
status: "resolved",
|
|
170
|
+
})
|
|
171
|
+
.catch(() => { });
|
|
172
|
+
invalidate();
|
|
173
|
+
},
|
|
174
|
+
onError: (err) => {
|
|
175
|
+
showToast(err?.message ?? "Failed to lift hard ban.", "error");
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
const liftSoftBanTickets = useMutation({
|
|
179
|
+
mutationFn: async () => {
|
|
180
|
+
if (!linkedUserId)
|
|
181
|
+
throw new Error("No linked user on this ticket.");
|
|
182
|
+
await apiClient.delete(ADMIN_ENDPOINTS.USER_SOFT_BAN_LIFT(linkedUserId, "create_support_tickets"));
|
|
183
|
+
},
|
|
184
|
+
onSuccess: () => {
|
|
185
|
+
showToast("Ticket-creation soft ban lifted.", "success");
|
|
186
|
+
apiClient
|
|
187
|
+
.patch(ADMIN_ENDPOINTS.SUPPORT_TICKET_BY_ID(ticketId), {
|
|
188
|
+
internalNotes: (notes ? notes + "\n" : "") +
|
|
189
|
+
`[${new Date().toISOString()}] Lifted create_support_tickets soft ban for ${linkedUserId}`,
|
|
190
|
+
status: "resolved",
|
|
191
|
+
})
|
|
192
|
+
.catch(() => { });
|
|
193
|
+
invalidate();
|
|
194
|
+
},
|
|
195
|
+
onError: (err) => {
|
|
196
|
+
showToast(err?.message ?? "Failed to lift soft ban.", "error");
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
const applyOrderItems = useMutation({
|
|
200
|
+
mutationFn: async () => {
|
|
201
|
+
if (!linkedOrderId)
|
|
202
|
+
throw new Error("No linked order on this ticket.");
|
|
203
|
+
// Recalculate totalPrice per line from quantity × unitPrice so qty edits
|
|
204
|
+
// flow through. Lines with quantity 0 are dropped.
|
|
205
|
+
const items = orderItems
|
|
206
|
+
.filter((it) => it.quantity > 0)
|
|
207
|
+
.map((it) => ({
|
|
208
|
+
productId: it.productId,
|
|
209
|
+
productTitle: it.productTitle,
|
|
210
|
+
quantity: it.quantity,
|
|
211
|
+
unitPrice: it.unitPrice,
|
|
212
|
+
totalPrice: it.quantity * it.unitPrice,
|
|
213
|
+
}));
|
|
214
|
+
await apiClient.patch(ADMIN_ENDPOINTS.ORDER_BY_ID(linkedOrderId), {
|
|
215
|
+
items,
|
|
216
|
+
});
|
|
217
|
+
},
|
|
218
|
+
onSuccess: () => {
|
|
219
|
+
showToast("Order items updated.", "success");
|
|
220
|
+
apiClient
|
|
221
|
+
.patch(ADMIN_ENDPOINTS.SUPPORT_TICKET_BY_ID(ticketId), {
|
|
222
|
+
internalNotes: (notes ? notes + "\n" : "") +
|
|
223
|
+
`[${new Date().toISOString()}] Modified order ${linkedOrderId} items: ${orderItems
|
|
224
|
+
.filter((it) => it.quantity > 0)
|
|
225
|
+
.map((it) => `${it.productId}x${it.quantity}`)
|
|
226
|
+
.join(", ")}`,
|
|
227
|
+
})
|
|
228
|
+
.catch(() => { });
|
|
229
|
+
invalidate();
|
|
230
|
+
setOrderItemsOpen(false);
|
|
231
|
+
},
|
|
232
|
+
onError: (err) => {
|
|
233
|
+
showToast(err?.message ?? "Failed to update order items.", "error");
|
|
234
|
+
},
|
|
235
|
+
});
|
|
80
236
|
return (_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: subject ?? "Support Ticket", children: _jsxs(Div, { className: "flex flex-col gap-4 p-4", children: [_jsxs(Div, { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: `inline-flex rounded-full px-2.5 py-1 text-xs font-medium ${STATUS_COLOR[currentStatus ?? "open"] ?? STATUS_COLOR.open}`, children: (currentStatus ?? "open").replace(/_/g, " ") }), _jsx("span", { className: "rounded-full bg-zinc-100 px-2.5 py-1 text-xs text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300", children: category }), orderId && (_jsxs("span", { className: "rounded-full bg-zinc-100 px-2.5 py-1 text-xs text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300", children: ["Order: ", orderId] }))] }), _jsxs(Div, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: ["From: ", _jsx("span", { className: "font-medium text-zinc-700 dark:text-zinc-200", children: userDisplayName })] }), description && (_jsxs(Div, { className: "rounded-lg border border-zinc-200 bg-zinc-50 p-3 dark:border-zinc-700 dark:bg-zinc-900/40", children: [_jsx(Text, { className: "mb-1 text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "Description" }), _jsx(Text, { className: "whitespace-pre-wrap text-sm text-zinc-700 dark:text-zinc-200", children: description })] })), messages.length > 0 && (_jsxs(Div, { className: "space-y-2", children: [_jsxs(Text, { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: ["Messages (", messages.length, ")"] }), _jsx("div", { className: "space-y-2 max-h-64 overflow-y-auto", children: messages.map((msg, i) => (_jsxs("div", { className: `rounded-lg p-3 text-sm ${msg.authorRole === "user"
|
|
81
237
|
? "bg-zinc-50 border border-zinc-200 dark:bg-zinc-900/40 dark:border-zinc-700"
|
|
82
|
-
: "bg-info-surface border border-blue-200 dark:border-blue-800"}`, children: [_jsxs("div", { className: "mb-1 flex items-center gap-2 text-xs text-zinc-400 dark:text-zinc-400", children: [_jsx("span", { className: "font-medium text-zinc-600 dark:text-zinc-300", children: ROLE_LABEL[msg.authorRole ?? "user"] ?? msg.authorRole }), msg.createdAt && (_jsx("span", { children: new Date(msg.createdAt).toLocaleString() }))] }), _jsx(Text, { className: "whitespace-pre-wrap text-zinc-700 dark:text-zinc-200", children: msg.body })] }, msg.id ?? i))) })] })), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "Reply to user" }), _jsx("textarea", { value: replyBody, onChange: (e) => setReplyBody(e.target.value), rows: 3, placeholder: "Type a reply\u2026", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: replyMutation.isPending, disabled: !replyBody.trim() || !ticketId || replyMutation.isPending, onClick: () => replyMutation.mutate(), children: "Send reply" })] }), _jsx("hr", { className: "border-zinc-200 dark:border-zinc-700" }), _jsx(Select, { label: "Status", options: STATUS_OPTIONS, value: status, onValueChange: setStatus }), _jsx(Select, { label: "Priority", options: PRIORITY_OPTIONS, value: priority, onValueChange: setPriority }), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "Internal notes (staff only)" }), _jsx("textarea", { value: notes, onChange: (e) => setNotes(e.target.value), rows: 3, placeholder: "Notes visible only to admins and employees\u2026", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500" })] }), _jsxs(
|
|
238
|
+
: "bg-info-surface border border-blue-200 dark:border-blue-800"}`, children: [_jsxs("div", { className: "mb-1 flex items-center gap-2 text-xs text-zinc-400 dark:text-zinc-400", children: [_jsx("span", { className: "font-medium text-zinc-600 dark:text-zinc-300", children: ROLE_LABEL[msg.authorRole ?? "user"] ?? msg.authorRole }), msg.createdAt && (_jsx("span", { children: new Date(msg.createdAt).toLocaleString() }))] }), _jsx(Text, { className: "whitespace-pre-wrap text-zinc-700 dark:text-zinc-200", children: msg.body })] }, msg.id ?? i))) })] })), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "Reply to user" }), _jsx("textarea", { value: replyBody, onChange: (e) => setReplyBody(e.target.value), rows: 3, placeholder: "Type a reply\u2026", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: replyMutation.isPending, disabled: !replyBody.trim() || !ticketId || replyMutation.isPending, onClick: () => replyMutation.mutate(), children: "Send reply" })] }), _jsx("hr", { className: "border-zinc-200 dark:border-zinc-700" }), _jsx(Select, { label: "Status", options: STATUS_OPTIONS, value: status, onValueChange: setStatus }), _jsx(Select, { label: "Priority", options: PRIORITY_OPTIONS, value: priority, onValueChange: setPriority }), _jsxs(Div, { className: "flex flex-col gap-1", children: [_jsx("label", { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "Internal notes (staff only)" }), _jsx("textarea", { value: notes, onChange: (e) => setNotes(e.target.value), rows: 3, placeholder: "Notes visible only to admins and employees\u2026", className: "w-full rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-2 focus:ring-primary-500" })] }), _jsxs(Div, { padding: "sm", className: "flex flex-col gap-2 rounded-lg border border-zinc-200 dark:border-zinc-700", children: [_jsx(Text, { className: "text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wide", children: "Linked parties" }), _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Tag the buyer / store / order / product / bid this ticket concerns." }), [
|
|
239
|
+
["userId", "User slug"],
|
|
240
|
+
["storeId", "Store slug"],
|
|
241
|
+
["orderId", "Order ID"],
|
|
242
|
+
["productId", "Product slug"],
|
|
243
|
+
["bidId", "Bid ID"],
|
|
244
|
+
].map(([key, label]) => (_jsx(Input, { label: label, type: "text", value: parties[key] ?? "", onChange: (e) => setParties((p) => ({ ...p, [key]: e.target.value })), placeholder: label }, key)))] }), isOrderModificationRequest && (_jsx(OrderItemsPanel, { linkedOrderId: linkedOrderId, orderItemsOpen: orderItemsOpen, setOrderItemsOpen: setOrderItemsOpen, orderItems: orderItems, setOrderItems: setOrderItems, loadOrderItems: loadOrderItems, applyOrderItems: applyOrderItems })), isStoreChangeRequest && (_jsx(StoreChangePanel, { linkedStoreId: linkedStoreId, storeStatus: storeStatus, setStoreStatus: setStoreStatus, storeIsVerified: storeIsVerified, setStoreIsVerified: setStoreIsVerified, storeIsFeatured: storeIsFeatured, setStoreIsFeatured: setStoreIsFeatured, applyStoreChange: applyStoreChange })), isUnbanRequest && (_jsx(UnbanRequestPanel, { linkedUserId: linkedUserId, liftHardBan: liftHardBan, liftSoftBanTickets: liftSoftBanTickets })), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: onClose, children: "Cancel" }), _jsx(Button, { type: "button", isLoading: updateMutation.isPending, disabled: !ticketId || updateMutation.isPending, onClick: () => updateMutation.mutate(), children: "Save changes" })] })] }) }));
|
|
245
|
+
}
|
|
246
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
247
|
+
function OrderItemsPanel(props) {
|
|
248
|
+
const { linkedOrderId, orderItemsOpen, setOrderItemsOpen, orderItems, setOrderItems, loadOrderItems, applyOrderItems, } = props;
|
|
249
|
+
const updateQty = (idx, raw) => {
|
|
250
|
+
const q = Math.max(0, parseInt(raw, 10) || 0);
|
|
251
|
+
setOrderItems((prev) => prev.map((p, i) => (i === idx ? { ...p, quantity: q } : p)));
|
|
252
|
+
};
|
|
253
|
+
return (_jsxs(Div, { padding: "sm", className: "flex flex-col gap-2 rounded-lg border border-info/40 bg-info-surface/40", children: [_jsx(Text, { className: "text-xs font-semibold text-info uppercase tracking-wide", children: "Modify order items" }), linkedOrderId ? (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Editing order: ", _jsx("code", { className: "font-mono", children: linkedOrderId })] }), !orderItemsOpen ? (_jsx(Button, { type: "button", variant: "secondary", size: "sm", isLoading: loadOrderItems.isPending, disabled: loadOrderItems.isPending, onClick: () => loadOrderItems.mutate(), children: "Load current items" })) : (_jsxs(_Fragment, { children: [orderItems.length === 0 ? (_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: "Order has no items." })) : (_jsx(Div, { className: "flex flex-col gap-2", children: orderItems.map((it, idx) => (_jsxs(Div, { className: "flex items-center gap-2 rounded border border-zinc-200 dark:border-zinc-700 px-2 py-1", children: [_jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Text, { className: "text-xs font-medium truncate", children: it.productTitle }), _jsxs(Text, { size: "xs", color: "muted", children: [it.productId, " \u00B7 \u20B9", (it.unitPrice / 100).toFixed(2), "/ea"] })] }), _jsx("input", { type: "number", min: "0", value: it.quantity, onChange: (e) => updateQty(idx, e.target.value), className: "w-16 rounded border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 px-2 py-1 text-xs" })] }, `${it.productId}-${idx}`))) })), _jsxs(Row, { gap: "sm", children: [_jsx(Button, { type: "button", variant: "secondary", size: "sm", onClick: () => setOrderItemsOpen(false), children: "Cancel" }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: applyOrderItems.isPending, disabled: applyOrderItems.isPending, onClick: () => applyOrderItems.mutate(), children: "Apply changes" })] })] }))] })) : (_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Set the", " ", _jsx(Text, { as: "span", weight: "semibold", children: "Order ID" }), " ", "field in the Linked parties panel above to enable this action."] }))] }));
|
|
254
|
+
}
|
|
255
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
256
|
+
function StoreChangePanel(props) {
|
|
257
|
+
const { linkedStoreId, storeStatus, setStoreStatus, storeIsVerified, setStoreIsVerified, storeIsFeatured, setStoreIsFeatured, applyStoreChange, } = props;
|
|
258
|
+
return (_jsxs(Div, { padding: "sm", className: "flex flex-col gap-2 rounded-lg border border-warning/40 bg-warning-surface/40", children: [_jsx(Text, { className: "text-xs font-semibold text-warning uppercase tracking-wide", children: "Apply store change" }), linkedStoreId ? (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Editing store: ", _jsx("code", { className: "font-mono", children: linkedStoreId })] }), _jsx(Select, { label: "Store status", options: [
|
|
259
|
+
{ label: "Active", value: "active" },
|
|
260
|
+
{ label: "Pending", value: "pending" },
|
|
261
|
+
{ label: "Suspended", value: "suspended" },
|
|
262
|
+
{ label: "Rejected", value: "rejected" },
|
|
263
|
+
], value: storeStatus, onValueChange: setStoreStatus }), _jsx(Toggle, { label: "Verified badge", checked: storeIsVerified, onChange: setStoreIsVerified }), _jsx(Toggle, { label: "Featured store", checked: storeIsFeatured, onChange: setStoreIsFeatured }), _jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: applyStoreChange.isPending, disabled: applyStoreChange.isPending, onClick: () => applyStoreChange.mutate(), children: "Apply store change" })] })) : (_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Set the", " ", _jsx(Text, { as: "span", weight: "semibold", children: "Store slug" }), " ", "field in the Linked parties panel above to enable this action."] }))] }));
|
|
264
|
+
}
|
|
265
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
266
|
+
function UnbanRequestPanel(props) {
|
|
267
|
+
const { linkedUserId, liftHardBan, liftSoftBanTickets } = props;
|
|
268
|
+
return (_jsxs(Div, { padding: "sm", className: "flex flex-col gap-2 rounded-lg border border-error/40 bg-error-surface/40", children: [_jsx(Text, { className: "text-xs font-semibold text-error uppercase tracking-wide", children: "Lift account ban" }), linkedUserId ? (_jsxs(_Fragment, { children: [_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Appellant: ", _jsx("code", { className: "font-mono", children: linkedUserId })] }), _jsx(Text, { size: "xs", color: "muted", children: "Resolves the ticket and appends an audit note on success." }), _jsxs(Row, { gap: "sm", children: [_jsx(Button, { type: "button", variant: "primary", size: "sm", isLoading: liftHardBan.isPending, disabled: liftHardBan.isPending, onClick: () => liftHardBan.mutate(), children: "Lift hard ban" }), _jsx(Button, { type: "button", variant: "secondary", size: "sm", isLoading: liftSoftBanTickets.isPending, disabled: liftSoftBanTickets.isPending, onClick: () => liftSoftBanTickets.mutate(), children: "Lift ticket soft ban" })] })] })) : (_jsxs(Text, { className: "text-xs text-zinc-600 dark:text-zinc-300", children: ["Set the", " ", _jsx(Text, { as: "span", weight: "semibold", children: "User slug" }), " ", "field in the Linked parties panel above to enable these actions."] }))] }));
|
|
83
269
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export
|
|
3
|
-
}
|
|
1
|
+
import type { ListingLayoutProps } from "../../../ui";
|
|
2
|
+
export type AdminSupportTicketsViewProps = ListingLayoutProps;
|
|
4
3
|
export declare function AdminSupportTicketsView({ children, ...props }: AdminSupportTicketsViewProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,23 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import React, { useState
|
|
4
|
-
import {
|
|
5
|
-
import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
|
|
6
|
-
import { FilterChipGroup, ListingToolbar, ListingViewShell, Pagination, RowActionMenu, Text, ListingFilterDrawer } from "../../../ui";
|
|
3
|
+
import React, { useState } from "react";
|
|
4
|
+
import { FilterChipGroup, ListingLayout, RowActionMenu, Text } from "../../../ui";
|
|
7
5
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
6
|
import { ROW_ACTION_META, ROW_ACTION_ID } from "../../../features/products/constants/action-defs";
|
|
9
7
|
import { ADMIN_SUPPORT_TICKET_STATUS_TABS, ADMIN_SUPPORT_TICKET_PRIORITY_TABS, } from "../constants/filter-tabs";
|
|
10
|
-
import { toRecordArray, toRelativeDate, toStringValue,
|
|
11
|
-
import {
|
|
8
|
+
import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
|
|
9
|
+
import { DataListingView } from "./DataListingView";
|
|
12
10
|
import { AdminSupportTicketDetailView } from "./AdminSupportTicketDetailView";
|
|
13
|
-
const PAGE_SIZE = 25;
|
|
14
|
-
const FILTER_KEYS = ["status", "priority"];
|
|
15
|
-
const DEFAULT_SORT = "-createdAt";
|
|
16
|
-
const SORT_OPTIONS = [
|
|
17
|
-
{ value: "-createdAt", label: "Newest" },
|
|
18
|
-
{ value: "createdAt", label: "Oldest" },
|
|
19
|
-
{ value: "-updatedAt", label: "Recently updated" },
|
|
20
|
-
];
|
|
21
11
|
const PRIORITY_BADGE = {
|
|
22
12
|
urgent: "bg-error-surface text-error",
|
|
23
13
|
high: "bg-warning-surface text-warning",
|
|
@@ -31,81 +21,49 @@ const STATUS_BADGE = {
|
|
|
31
21
|
resolved: "bg-success-surface text-success",
|
|
32
22
|
closed: "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400",
|
|
33
23
|
};
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return (_jsxs("div", { className: "space-y-1", children: [_jsx(Text, { className: "font-medium text-zinc-900 dark:text-zinc-100", children: row.primary }), row.secondary ? _jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: row.secondary }) : null, _jsx("span", { className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${PRIORITY_BADGE[priority] ?? PRIORITY_BADGE.normal}`, children: priority })] }));
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
key: "status",
|
|
46
|
-
header: "Status",
|
|
47
|
-
className: "w-36",
|
|
48
|
-
render: (row) => (_jsx("span", { className: `inline-flex rounded-full px-2.5 py-1 text-xs font-medium ${STATUS_BADGE[row.status] ?? STATUS_BADGE.open}`, children: row.status.replace(/_/g, " ") })),
|
|
24
|
+
const TICKET_COLUMNS = [
|
|
25
|
+
{
|
|
26
|
+
key: "primary",
|
|
27
|
+
header: "Subject",
|
|
28
|
+
render: (row) => {
|
|
29
|
+
const priority = toStringValue(row._raw?.priority, "normal");
|
|
30
|
+
return (_jsxs("div", { className: "space-y-1", children: [_jsx(Text, { className: "font-medium text-zinc-900 dark:text-zinc-100", children: row.primary }), row.secondary ? (_jsx(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: row.secondary })) : null, _jsx("span", { className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${PRIORITY_BADGE[priority] ?? PRIORITY_BADGE.normal}`, children: priority })] }));
|
|
49
31
|
},
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
},
|
|
56
|
-
|
|
57
|
-
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
key: "status",
|
|
35
|
+
header: "Status",
|
|
36
|
+
className: "w-36",
|
|
37
|
+
render: (row) => (_jsx("span", { className: `inline-flex rounded-full px-2.5 py-1 text-xs font-medium ${STATUS_BADGE[row.status] ?? STATUS_BADGE.open}`, children: row.status.replace(/_/g, " ") })),
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
key: "updatedAt",
|
|
41
|
+
header: "Updated",
|
|
42
|
+
className: "w-32",
|
|
43
|
+
render: (row) => (_jsx("span", { className: "text-sm text-zinc-500 dark:text-zinc-400", children: row.updatedAt })),
|
|
44
|
+
},
|
|
45
|
+
];
|
|
58
46
|
export function AdminSupportTicketsView({ children, ...props }) {
|
|
59
|
-
const hasChildren = React.Children.count(children) > 0;
|
|
60
|
-
const [view, setView] = useState("table");
|
|
61
|
-
const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
|
|
62
|
-
const [searchInput, setSearchInput] = useState(table.get("q") || "");
|
|
63
|
-
const [filterOpen, setFilterOpen] = useState(false);
|
|
64
|
-
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
65
47
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
66
48
|
const [selectedRow, setSelectedRow] = useState(null);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}, [pendingFilters, table]);
|
|
78
|
-
const clearFilters = useCallback(() => {
|
|
79
|
-
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
|
|
80
|
-
}, []);
|
|
81
|
-
const resetAll = useCallback(() => {
|
|
82
|
-
const updates = { q: "", sort: "" };
|
|
83
|
-
for (const k of FILTER_KEYS)
|
|
84
|
-
updates[k] = "";
|
|
85
|
-
table.setMany(updates);
|
|
86
|
-
setSearchInput("");
|
|
87
|
-
}, [table]);
|
|
88
|
-
const commitSearch = useCallback(() => {
|
|
89
|
-
table.set("q", searchInput.trim());
|
|
90
|
-
}, [searchInput, table]);
|
|
91
|
-
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
92
|
-
const hasActiveState = !!table.get("q") || table.get("sort") !== DEFAULT_SORT || activeFilterCount > 0;
|
|
93
|
-
const filterParts = [];
|
|
94
|
-
const statusRaw = table.get("status");
|
|
95
|
-
if (statusRaw && statusRaw !== "All")
|
|
96
|
-
filterParts.push(`status==${statusRaw}`);
|
|
97
|
-
const priorityRaw = table.get("priority");
|
|
98
|
-
if (priorityRaw && priorityRaw !== "All")
|
|
99
|
-
filterParts.push(`priority==${priorityRaw}`);
|
|
100
|
-
const filters = filterParts.join(",") || undefined;
|
|
101
|
-
const { rows, total, isLoading, errorMessage } = useAdminListingData({
|
|
49
|
+
if (React.Children.count(children) > 0) {
|
|
50
|
+
return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
|
|
51
|
+
}
|
|
52
|
+
const config = {
|
|
53
|
+
portal: "admin",
|
|
54
|
+
title: "Support Tickets",
|
|
55
|
+
searchPlaceholder: "Search by subject",
|
|
56
|
+
emptyLabel: "No support tickets found",
|
|
57
|
+
filterKeys: ["status", "priority"],
|
|
58
|
+
defaultSort: "-createdAt",
|
|
102
59
|
queryKey: ["admin", "support-tickets", "listing"],
|
|
103
60
|
endpoint: ADMIN_ENDPOINTS.SUPPORT_TICKETS,
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
61
|
+
sortOptions: [
|
|
62
|
+
{ value: "-createdAt", label: "Newest" },
|
|
63
|
+
{ value: "createdAt", label: "Oldest" },
|
|
64
|
+
{ value: "-updatedAt", label: "Recently updated" },
|
|
65
|
+
],
|
|
66
|
+
columns: TICKET_COLUMNS,
|
|
109
67
|
mapRows: (response) => toRecordArray(response.tickets).map((item, index) => ({
|
|
110
68
|
id: toStringValue(item.id, `ticket-${index}`),
|
|
111
69
|
primary: toStringValue(item.subject, "No subject"),
|
|
@@ -129,18 +87,29 @@ export function AdminSupportTicketsView({ children, ...props }) {
|
|
|
129
87
|
return response.total;
|
|
130
88
|
return mappedRows.length;
|
|
131
89
|
},
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
90
|
+
buildFilters: (f) => {
|
|
91
|
+
const parts = [];
|
|
92
|
+
if (f.status && f.status !== "All")
|
|
93
|
+
parts.push(`status==${f.status}`);
|
|
94
|
+
if (f.priority && f.priority !== "All")
|
|
95
|
+
parts.push(`priority==${f.priority}`);
|
|
96
|
+
return parts.join(",") || undefined;
|
|
97
|
+
},
|
|
98
|
+
renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
99
|
+
{
|
|
100
|
+
label: ROW_ACTION_META[ROW_ACTION_ID.VIEW].label,
|
|
101
|
+
onClick: () => {
|
|
102
|
+
setSelectedRow(row);
|
|
103
|
+
setDrawerOpen(true);
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
] })),
|
|
107
|
+
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_SUPPORT_TICKET_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Priority", tabs: ADMIN_SUPPORT_TICKET_PRIORITY_TABS, value: pendingFilters.priority ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, priority: id })) })] })),
|
|
108
|
+
};
|
|
109
|
+
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(AdminSupportTicketDetailView, { open: drawerOpen, onClose: () => setDrawerOpen(false), ticketId: selectedRow?.id, subject: selectedRow?.primary, userDisplayName: toStringValue(selectedRow?._raw?.userDisplayName, ""), category: toStringValue(selectedRow?._raw?.category, "general"), currentStatus: selectedRow?.status, currentPriority: toStringValue(selectedRow?._raw?.priority, "normal"), description: toStringValue(selectedRow?._raw?.description, ""), messages: Array.isArray(selectedRow?._raw?.messages)
|
|
144
110
|
? selectedRow._raw.messages
|
|
145
|
-
: [], internalNotes: toStringValue(selectedRow?._raw?.internalNotes, "") || undefined, orderId: toStringValue(selectedRow?._raw?.orderId, "") || undefined
|
|
111
|
+
: [], internalNotes: toStringValue(selectedRow?._raw?.internalNotes, "") || undefined, orderId: toStringValue(selectedRow?._raw?.orderId, "") || undefined, relatedParties: selectedRow?._raw?.relatedParties &&
|
|
112
|
+
typeof selectedRow._raw.relatedParties === "object"
|
|
113
|
+
? selectedRow._raw.relatedParties
|
|
114
|
+
: undefined })] }));
|
|
146
115
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export interface AdminTeamViewProps extends
|
|
1
|
+
import type { ListingLayoutProps } from "../../../ui";
|
|
2
|
+
export interface AdminTeamViewProps extends ListingLayoutProps {
|
|
3
3
|
onBulkRemove?: (ids: string[]) => Promise<void>;
|
|
4
4
|
}
|
|
5
5
|
export declare function AdminTeamView({ children, onBulkRemove, ...props }: AdminTeamViewProps): import("react/jsx-runtime").JSX.Element;
|