@mohasinac/appkit 2.8.3 → 2.8.5
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/SidebarCollapseToggle.js +2 -0
- package/dist/_internal/shared/listing-types/action-tracker.js +0 -2
- package/dist/client.d.ts +2 -0
- package/dist/client.js +2 -0
- package/dist/core/hooks/useSiteSettings.d.ts +1 -1
- package/dist/core/unit-of-work.js +0 -1
- package/dist/features/account/components/NotificationBell.js +1 -0
- package/dist/features/account/components/UserOffersPanel.js +1 -0
- package/dist/features/account/components/UserSidebar.js +3 -1
- package/dist/features/account/hooks/useAccount.d.ts +2 -2
- package/dist/features/account/hooks/useAddressSelector.d.ts +1 -1
- package/dist/features/account/hooks/useAddresses.d.ts +2 -2
- package/dist/features/account/hooks/useNotifications.d.ts +1 -1
- package/dist/features/account/hooks/useProfile.d.ts +1 -1
- package/dist/features/account/hooks/usePublicProfile.d.ts +2 -2
- package/dist/features/admin/components/AdminAllEventEntriesView.js +5 -3
- package/dist/features/admin/components/AdminBidsView.js +6 -4
- package/dist/features/admin/components/AdminBlogView.js +6 -4
- package/dist/features/admin/components/AdminBrandsView.js +8 -6
- package/dist/features/admin/components/AdminBundlesView.js +10 -8
- package/dist/features/admin/components/AdminCarouselEditorView.js +3 -1
- package/dist/features/admin/components/AdminCartsView.js +6 -4
- package/dist/features/admin/components/AdminCategoriesView.js +7 -5
- package/dist/features/admin/components/AdminClassifiedView.js +4 -3
- package/dist/features/admin/components/AdminContactView.js +6 -4
- package/dist/features/admin/components/AdminCouponsView.js +6 -4
- package/dist/features/admin/components/AdminDigitalCodesView.js +4 -3
- package/dist/features/admin/components/AdminFaqsView.js +5 -3
- package/dist/features/admin/components/AdminFeaturesView.js +5 -4
- package/dist/features/admin/components/AdminGroupedListingsView.js +4 -3
- package/dist/features/admin/components/AdminHistoryView.js +4 -3
- package/dist/features/admin/components/AdminLiveView.js +4 -3
- package/dist/features/admin/components/AdminNewsletterView.js +5 -3
- package/dist/features/admin/components/AdminNotificationsView.js +6 -4
- package/dist/features/admin/components/AdminOrdersView.js +6 -4
- package/dist/features/admin/components/AdminPayoutsView.js +7 -5
- package/dist/features/admin/components/AdminPrizeDrawsView.js +6 -4
- package/dist/features/admin/components/AdminProductsView.js +7 -5
- package/dist/features/admin/components/AdminReturnRequestsView.js +4 -3
- package/dist/features/admin/components/AdminReviewsView.js +8 -6
- package/dist/features/admin/components/AdminScammersView.js +8 -6
- package/dist/features/admin/components/AdminSessionsView.js +6 -4
- package/dist/features/admin/components/AdminSidebar.js +3 -1
- package/dist/features/admin/components/AdminSiteSettingsView.js +3 -1
- package/dist/features/admin/components/AdminStoreAddressesView.js +2 -1
- package/dist/features/admin/components/AdminStoresView.js +6 -4
- package/dist/features/admin/components/AdminSublistingCategoriesView.js +6 -5
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +0 -3
- package/dist/features/admin/components/AdminSupportTicketsView.js +8 -6
- package/dist/features/admin/components/AdminTeamView.js +6 -4
- package/dist/features/admin/components/AdminUsersView.js +6 -4
- package/dist/features/admin/components/AdminWishlistsView.js +4 -3
- package/dist/features/admin/components/DashboardStats.js +5 -1
- package/dist/features/admin/components/analytics/AdminAnalyticsCharts.js +3 -1
- package/dist/features/admin/components/analytics/AdminStatCard.js +5 -1
- package/dist/features/admin/constants/filter-tabs.d.ts +1 -1
- package/dist/features/admin/constants/filter-tabs.js +0 -11
- package/dist/features/admin/hooks/useAdmin.d.ts +1 -1
- package/dist/features/admin/hooks/useAdminSectionsListing.d.ts +1 -1
- package/dist/features/admin/hooks/useChat.d.ts +1 -1
- package/dist/features/admin/hooks/useChat.js +1 -0
- package/dist/features/admin/types/index.d.ts +1 -1
- package/dist/features/auctions/actions/bid-actions.js +4 -2
- package/dist/features/auctions/components/AuctionBidsTable.js +5 -1
- package/dist/features/auctions/components/AuctionsListView.js +7 -6
- package/dist/features/auctions/hooks/useAuctions.d.ts +8 -8
- package/dist/features/auth/hooks/useAuth.d.ts +2 -2
- package/dist/features/auth/hooks/useAuth.js +1 -0
- package/dist/features/blog/actions/blog-actions.js +3 -2
- package/dist/features/blog/components/BlogFeaturedCard.js +3 -1
- package/dist/features/blog/components/BlogListView.js +5 -1
- package/dist/features/blog/components/BlogPostView.js +3 -1
- package/dist/features/cart/components/CartDrawer.js +3 -1
- package/dist/features/cart/hooks/useCart.d.ts +1 -1
- package/dist/features/cart/hooks/useCartQuery.d.ts +1 -1
- package/dist/features/cart/hooks/useCheckoutReadQueries.d.ts +2 -2
- package/dist/features/cart/hooks/useOrder.d.ts +2 -2
- package/dist/features/categories/components/BrandDetailPageView.js +15 -9
- package/dist/features/categories/components/CategoriesIndexListing.js +1 -1
- package/dist/features/categories/components/CategoriesIndexPageView.js +5 -4
- package/dist/features/categories/components/CategoryBundlesListing.js +5 -4
- package/dist/features/categories/components/CategoryDetailPageView.js +15 -11
- package/dist/features/categories/components/CategoryFilters.d.ts +12 -12
- package/dist/features/categories/components/CategoryFilters.js +13 -12
- package/dist/features/categories/components/CategoryGrid.js +5 -1
- package/dist/features/categories/components/CategoryTableColumns.js +6 -2
- package/dist/features/categories/components/CategoryTree.d.ts +1 -1
- package/dist/features/categories/components/CategoryTree.js +4 -2
- package/dist/features/categories/components/ConcernCard.js +3 -1
- package/dist/features/categories/hooks/useCategories.d.ts +3 -3
- package/dist/features/categories/hooks/useCategorySelector.d.ts +3 -3
- package/dist/features/categories/hooks/useCategoryTree.d.ts +1 -1
- package/dist/features/categories/schemas/firestore.d.ts +2 -2
- package/dist/features/checkout/actions/checkout-actions.d.ts +1 -1
- package/dist/features/checkout/actions/checkout-actions.js +1 -1
- package/dist/features/checkout/hooks/useCheckoutApi.d.ts +2 -2
- package/dist/features/classified/components/ClassifiedListView.js +6 -5
- package/dist/features/collections/components/CollectionCard.js +3 -1
- package/dist/features/collections/hooks/useCollections.d.ts +2 -2
- package/dist/features/cron/jobs/auction-expiry.job.d.ts +2 -2
- package/dist/features/cron/jobs/auction-expiry.job.js +2 -1
- package/dist/features/cron/jobs/preorder-reminder.job.d.ts +1 -1
- package/dist/features/cron/jobs/preorder-reminder.job.js +2 -1
- package/dist/features/digital-codes/components/DigitalCodesListView.js +4 -3
- package/dist/features/events/actions/event-actions.js +6 -4
- package/dist/features/events/components/AdminEventEditorView.js +1 -1
- package/dist/features/events/components/AdminEventsView.js +7 -5
- package/dist/features/events/components/EventCard.js +3 -1
- package/dist/features/events/components/EventFilters.d.ts +4 -4
- package/dist/features/events/components/EventFilters.js +2 -2
- package/dist/features/events/components/EventRafflesSection.js +4 -2
- package/dist/features/events/hooks/useEvent.d.ts +3 -3
- package/dist/features/events/hooks/useEvents.d.ts +1 -1
- package/dist/features/faq/actions/faq-actions.js +4 -2
- package/dist/features/faq/components/FAQPageContent.js +2 -1
- package/dist/features/faq/hooks/useFAQs.d.ts +1 -1
- package/dist/features/faq/hooks/useFaqList.d.ts +15 -15
- package/dist/features/forms/Slider.js +3 -1
- package/dist/features/history/hooks/useHistory.js +1 -0
- package/dist/features/homepage/components/AdSlot.js +6 -2
- package/dist/features/homepage/components/AdvertisementBanner.js +3 -1
- package/dist/features/homepage/components/BeforeAfterCard.js +5 -1
- package/dist/features/homepage/components/CustomCardsSection.js +5 -1
- package/dist/features/homepage/components/HowItWorksSection.js +3 -1
- package/dist/features/homepage/components/MarketplaceHomepageView.js +4 -2
- package/dist/features/homepage/components/NewsletterBanner.js +6 -2
- package/dist/features/homepage/components/SecurityHighlightsSection.js +3 -1
- package/dist/features/homepage/components/StatsCounterSection.js +3 -1
- package/dist/features/homepage/components/TrustFeaturesSection.js +3 -1
- package/dist/features/homepage/hooks/useBlogArticles.d.ts +1 -1
- package/dist/features/homepage/hooks/useFeaturedAuctions.d.ts +1 -1
- package/dist/features/homepage/hooks/useFeaturedPreOrders.d.ts +1 -1
- package/dist/features/homepage/hooks/useFeaturedProducts.d.ts +1 -1
- package/dist/features/homepage/hooks/useFeaturedStores.d.ts +1 -1
- package/dist/features/homepage/hooks/useHeroCarousel.d.ts +1 -1
- package/dist/features/homepage/hooks/useHomepageEvents.d.ts +1 -1
- package/dist/features/homepage/hooks/useHomepageReviews.d.ts +1 -1
- package/dist/features/homepage/hooks/useHomepageSections.d.ts +1 -1
- package/dist/features/homepage/hooks/useTopBrands.d.ts +1 -1
- package/dist/features/homepage/hooks/useTopCategories.d.ts +1 -1
- package/dist/features/layout/AppLayoutShell.js +3 -1
- package/dist/features/layout/BottomActions.js +0 -27
- package/dist/features/layout/BottomActionsContext.d.ts +0 -36
- package/dist/features/layout/BottomActionsContext.js +1 -39
- package/dist/features/layout/BottomNavbar.js +1 -1
- package/dist/features/live/components/LiveItemsListView.js +6 -5
- package/dist/features/loyalty/hooks/useLoyaltyBalance.d.ts +1 -1
- package/dist/features/media/MediaLightbox.js +9 -3
- package/dist/features/media/modals/ImageEditor.js +6 -2
- package/dist/features/media/upload/MediaUploadField.js +1 -1
- package/dist/features/messages/hooks/useConversation.js +2 -0
- package/dist/features/messages/hooks/useConversations.js +1 -0
- package/dist/features/orders/components/OrdersList.js +3 -1
- package/dist/features/orders/hooks/useOrders.d.ts +2 -2
- package/dist/features/payments/hooks/usePayments.d.ts +1 -1
- package/dist/features/pre-orders/components/PreOrderDetailPageView.js +1 -1
- package/dist/features/pre-orders/components/PreOrdersListView.js +5 -4
- package/dist/features/pre-orders/hooks/usePreOrders.d.ts +2 -2
- package/dist/features/products/actions/product-actions.js +17 -15
- package/dist/features/products/columns/index.d.ts +1 -1
- package/dist/features/products/columns/index.js +1 -1
- package/dist/features/products/components/CompareOverlay.js +5 -1
- package/dist/features/products/components/FeatureBadge.js +3 -1
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +1 -1
- package/dist/features/products/components/PrizeDrawCollage.js +0 -14
- package/dist/features/products/components/PrizeDrawsListingView.js +5 -4
- package/dist/features/products/components/PrizeDrawsSection.js +4 -2
- package/dist/features/products/components/ProductGrid.js +3 -1
- package/dist/features/products/constants/sieve.d.ts +2 -2
- package/dist/features/products/constants/sieve.js +2 -2
- package/dist/features/products/hooks/useProductDetail.d.ts +1 -1
- package/dist/features/products/hooks/useRelatedProducts.d.ts +1 -1
- package/dist/features/products/hooks/useRelatedProducts.js +5 -4
- package/dist/features/promotions/hooks/usePromotions.d.ts +2 -2
- package/dist/features/reviews/actions/review-actions.js +2 -1
- package/dist/features/reviews/components/ReviewDetailShell.js +8 -2
- package/dist/features/reviews/components/ReviewModal.js +5 -1
- package/dist/features/reviews/components/ReviewSummary.js +3 -1
- package/dist/features/reviews/components/ReviewsIndexPageView.js +7 -6
- package/dist/features/reviews/components/ReviewsList.js +5 -1
- package/dist/features/reviews/hooks/useReviews.d.ts +2 -2
- package/dist/features/scams/actions/scam-actions.js +3 -2
- package/dist/features/scams/components/ScamRegistryView.js +5 -4
- package/dist/features/search/actions/search-actions.js +7 -6
- package/dist/features/search/components/Search.js +3 -1
- package/dist/features/search/hooks/useSearch.d.ts +2 -2
- package/dist/features/seller/components/CouponInlineSelect.js +2 -1
- package/dist/features/seller/components/PhysicalLocationModal.js +1 -1
- package/dist/features/seller/components/SellerAddressesView.js +2 -1
- package/dist/features/seller/components/SellerAuctionsView.js +7 -5
- package/dist/features/seller/components/SellerBidsView.js +8 -4
- package/dist/features/seller/components/SellerBundlesView.js +7 -6
- package/dist/features/seller/components/SellerClassifiedView.js +6 -5
- package/dist/features/seller/components/SellerCouponEditorView.js +3 -1
- package/dist/features/seller/components/SellerCouponsView.js +6 -4
- package/dist/features/seller/components/SellerDigitalCodesView.js +5 -4
- package/dist/features/seller/components/SellerGroupedListingsView.js +5 -4
- package/dist/features/seller/components/SellerLiveView.js +6 -5
- package/dist/features/seller/components/SellerOffersPanel.js +1 -0
- package/dist/features/seller/components/SellerOffersView.js +6 -4
- package/dist/features/seller/components/SellerOrdersView.js +5 -3
- package/dist/features/seller/components/SellerPayoutMethodsView.js +4 -2
- package/dist/features/seller/components/SellerPayoutsView.js +6 -4
- package/dist/features/seller/components/SellerPreOrdersView.js +6 -4
- package/dist/features/seller/components/SellerPrizeDrawsView.js +6 -4
- package/dist/features/seller/components/SellerProductsView.js +14 -8
- package/dist/features/seller/components/SellerReviewsView.js +1 -0
- package/dist/features/seller/components/SellerShippingConfigsView.js +4 -2
- package/dist/features/seller/components/SellerSidebar.js +12 -4
- package/dist/features/seller/components/SellerStoreCategoriesView.js +5 -3
- package/dist/features/seller/components/SellerTemplatesView.js +5 -3
- package/dist/features/seller/components/SublistingInlineSelect.js +2 -1
- package/dist/features/seller/hooks/useSellerPayouts.d.ts +3 -3
- package/dist/features/seller/hooks/useSellerStore.d.ts +6 -6
- package/dist/features/seller/hooks/useSellerStorefront.d.ts +2 -2
- package/dist/features/shell/FormShell.js +10 -2
- package/dist/features/shell/QuickFormDrawer.js +5 -2
- package/dist/features/stores/actions/store-query-actions.js +3 -2
- package/dist/features/stores/components/StoreAuctionsPageView.js +4 -2
- package/dist/features/stores/components/StoreDetailLayoutView.d.ts +1 -1
- package/dist/features/stores/components/StoreDetailLayoutView.js +7 -6
- package/dist/features/stores/components/StoreHeader.js +5 -1
- package/dist/features/stores/components/StorePreOrdersPageView.js +4 -2
- package/dist/features/stores/components/StorePrizeDrawsPageView.js +4 -2
- package/dist/features/stores/components/StoreProductsPageView.js +2 -1
- package/dist/features/stores/components/StoreProductsView.js +5 -4
- package/dist/features/stores/components/StoresIndexListing.js +4 -3
- package/dist/features/stores/hooks/useStoreAddressSelector.d.ts +1 -1
- package/dist/features/stores/hooks/useStores.d.ts +6 -6
- package/dist/features/wishlist/components/WishlistPage.js +3 -1
- package/dist/features/wishlist/components/WishlistView.js +4 -3
- package/dist/features/wishlist/hooks/useUserWishlist.d.ts +15 -15
- package/dist/features/wishlist/hooks/useWishlist.d.ts +1 -1
- package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
- package/dist/providers/db-firebase/admin.js +0 -1
- package/dist/ui/DataTable.js +6 -2
- package/dist/ui/components/AvatarDisplay.js +3 -1
- package/dist/ui/components/BackgroundRenderer.js +4 -2
- package/dist/ui/components/Card.js +6 -2
- package/dist/ui/components/ImageGallery.js +3 -1
- package/dist/ui/components/ImageLightbox.js +3 -1
- package/dist/ui/components/PasswordStrengthIndicator.js +3 -1
- package/dist/ui/components/Progress.js +6 -2
- package/dist/ui/components/RowActionMenu.js +3 -1
- package/dist/ui/components/SiteLogo.js +7 -1
- package/dist/ui/components/Skeleton.js +3 -1
- package/dist/ui/components/Slider.js +3 -1
- package/dist/ui/components/TabStrip.js +3 -1
- package/package.json +1 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
|
|
4
|
+
import { sortBy } from "@mohasinac/appkit";
|
|
3
5
|
import React, { useState } from "react";
|
|
4
6
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
5
7
|
import { FilterChipGroup, ListingLayout, RowActionMenu, useToast } from "../../../ui";
|
|
@@ -43,12 +45,12 @@ export function AdminStoresView({ children, ...props }) {
|
|
|
43
45
|
searchPlaceholder: "Search stores, slugs, or owner names",
|
|
44
46
|
emptyLabel: "No stores found",
|
|
45
47
|
filterKeys: ["status"],
|
|
46
|
-
defaultSort: "
|
|
48
|
+
defaultSort: sortBy("createdAt", "DESC"),
|
|
47
49
|
queryKey: ["admin", "stores", "listing"],
|
|
48
50
|
endpoint: ADMIN_ENDPOINTS.STORES,
|
|
49
51
|
sortOptions: [
|
|
50
|
-
{ value: "
|
|
51
|
-
{ value: "createdAt", label: "Oldest" },
|
|
52
|
+
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
53
|
+
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
52
54
|
{ value: "storeName", label: "Name A–Z" },
|
|
53
55
|
],
|
|
54
56
|
mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
|
|
@@ -63,7 +65,7 @@ export function AdminStoresView({ children, ...props }) {
|
|
|
63
65
|
_raw: item,
|
|
64
66
|
})),
|
|
65
67
|
getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
|
|
66
|
-
buildFilters: (state) => state.status && state.status !== "All" ?
|
|
68
|
+
buildFilters: (state) => state.status && state.status !== "All" ? sieveFilter("status", SIEVE_OP.EQ, state.status) : undefined,
|
|
67
69
|
onRowClick: (row) => setEditorRow(row),
|
|
68
70
|
buildBulkActions: (selection) => [
|
|
69
71
|
{
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { sortBy } from "@mohasinac/appkit";
|
|
3
4
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
4
5
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
5
6
|
import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
|
|
@@ -12,14 +13,14 @@ export function AdminSublistingCategoriesView({ onBulkDelete, }) {
|
|
|
12
13
|
searchPlaceholder: "Search sub-listing categories…",
|
|
13
14
|
emptyLabel: "No sub-listing categories found",
|
|
14
15
|
filterKeys: [],
|
|
15
|
-
defaultSort: "name",
|
|
16
|
+
defaultSort: sortBy("name", "ASC"),
|
|
16
17
|
queryKey: ["admin", "sublisting-categories", "listing"],
|
|
17
18
|
endpoint: ADMIN_ENDPOINTS.SUBLISTING_CATEGORIES,
|
|
18
19
|
sortOptions: [
|
|
19
|
-
{ value: "name", label: "Name A–Z" },
|
|
20
|
-
{ value: "
|
|
21
|
-
{ value: "
|
|
22
|
-
{ value: "
|
|
20
|
+
{ value: sortBy("name", "ASC"), label: "Name A–Z" },
|
|
21
|
+
{ value: sortBy("name", "DESC"), label: "Name Z–A" },
|
|
22
|
+
{ value: sortBy("productCount", "DESC"), label: "Most listings" },
|
|
23
|
+
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
23
24
|
],
|
|
24
25
|
mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
|
|
25
26
|
id: toStringValue(item.id, `sc-${index}`),
|
|
@@ -250,7 +250,6 @@ export function AdminSupportTicketDetailView({ open, onClose, ticketId, subject,
|
|
|
250
250
|
["bidId", "Bid ID"],
|
|
251
251
|
].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" })] })] }) }));
|
|
252
252
|
}
|
|
253
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
254
253
|
function OrderItemsPanel(props) {
|
|
255
254
|
const { linkedOrderId, orderItemsOpen, setOrderItemsOpen, orderItems, setOrderItems, loadOrderItems, applyOrderItems, } = props;
|
|
256
255
|
const updateQty = (idx, raw) => {
|
|
@@ -259,7 +258,6 @@ function OrderItemsPanel(props) {
|
|
|
259
258
|
};
|
|
260
259
|
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."] }))] }));
|
|
261
260
|
}
|
|
262
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
263
261
|
function StoreChangePanel(props) {
|
|
264
262
|
const { linkedStoreId, storeStatus, setStoreStatus, storeIsVerified, setStoreIsVerified, storeIsFeatured, setStoreIsFeatured, applyStoreChange, } = props;
|
|
265
263
|
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: [
|
|
@@ -269,7 +267,6 @@ function StoreChangePanel(props) {
|
|
|
269
267
|
{ label: "Rejected", value: "rejected" },
|
|
270
268
|
], 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."] }))] }));
|
|
271
269
|
}
|
|
272
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
273
270
|
function UnbanRequestPanel(props) {
|
|
274
271
|
const { linkedUserId, liftHardBan, liftSoftBanTickets } = props;
|
|
275
272
|
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."] }))] }));
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
|
|
4
|
+
import { sortBy } from "@mohasinac/appkit";
|
|
3
5
|
import React, { useState } from "react";
|
|
4
6
|
import { Div, FilterChipGroup, ListingLayout, RowActionMenu, Span, Text } from "../../../ui";
|
|
5
7
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
@@ -55,13 +57,13 @@ export function AdminSupportTicketsView({ children, ...props }) {
|
|
|
55
57
|
searchPlaceholder: "Search by subject",
|
|
56
58
|
emptyLabel: "No support tickets found",
|
|
57
59
|
filterKeys: ["status", "priority"],
|
|
58
|
-
defaultSort: "
|
|
60
|
+
defaultSort: sortBy("createdAt", "DESC"),
|
|
59
61
|
queryKey: ["admin", "support-tickets", "listing"],
|
|
60
62
|
endpoint: ADMIN_ENDPOINTS.SUPPORT_TICKETS,
|
|
61
63
|
sortOptions: [
|
|
62
|
-
{ value: "
|
|
63
|
-
{ value: "createdAt", label: "Oldest" },
|
|
64
|
-
{ value: "
|
|
64
|
+
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
65
|
+
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
66
|
+
{ value: sortBy("updatedAt", "DESC"), label: "Recently updated" },
|
|
65
67
|
],
|
|
66
68
|
columns: TICKET_COLUMNS,
|
|
67
69
|
mapRows: (response) => toRecordArray(response.tickets).map((item, index) => ({
|
|
@@ -90,9 +92,9 @@ export function AdminSupportTicketsView({ children, ...props }) {
|
|
|
90
92
|
buildFilters: (f) => {
|
|
91
93
|
const parts = [];
|
|
92
94
|
if (f.status && f.status !== "All")
|
|
93
|
-
parts.push(
|
|
95
|
+
parts.push(sieveFilter("status", SIEVE_OP.EQ, f.status));
|
|
94
96
|
if (f.priority && f.priority !== "All")
|
|
95
|
-
parts.push(
|
|
97
|
+
parts.push(sieveFilter("priority", SIEVE_OP.EQ, f.priority));
|
|
96
98
|
return parts.join(",") || undefined;
|
|
97
99
|
},
|
|
98
100
|
renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
|
|
4
|
+
import { sortBy } from "@mohasinac/appkit";
|
|
3
5
|
import React, { useState, useCallback } from "react";
|
|
4
6
|
import { UserPlus } from "lucide-react";
|
|
5
7
|
import { Button, FilterChipGroup, ListingLayout, RowActionMenu } from "../../../ui";
|
|
@@ -58,12 +60,12 @@ export function AdminTeamView({ children, onBulkRemove, ...props }) {
|
|
|
58
60
|
searchPlaceholder: "Search by name or email",
|
|
59
61
|
emptyLabel: "No employees found",
|
|
60
62
|
filterKeys: ["group"],
|
|
61
|
-
defaultSort: "
|
|
63
|
+
defaultSort: sortBy("createdAt", "DESC"),
|
|
62
64
|
queryKey: ["admin", "team", "listing"],
|
|
63
65
|
endpoint: ADMIN_ENDPOINTS.TEAM,
|
|
64
66
|
sortOptions: [
|
|
65
|
-
{ value: "
|
|
66
|
-
{ value: "createdAt", label: "Oldest" },
|
|
67
|
+
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
68
|
+
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
67
69
|
{ value: "displayName", label: "Name A–Z" },
|
|
68
70
|
],
|
|
69
71
|
mapRows: (response) => toRecordArray(response.users).map((item, index) => ({
|
|
@@ -90,7 +92,7 @@ export function AdminTeamView({ children, onBulkRemove, ...props }) {
|
|
|
90
92
|
return response.total;
|
|
91
93
|
return mappedRows.length;
|
|
92
94
|
},
|
|
93
|
-
buildFilters: (state) => state.group && state.group !== "All" ?
|
|
95
|
+
buildFilters: (state) => state.group && state.group !== "All" ? sieveFilter("permissionGroup", SIEVE_OP.EQ, state.group) : undefined,
|
|
94
96
|
toolbarExtra: (_jsxs(Button, { type: "button", variant: "primary", onClick: openInvite, className: "flex items-center gap-1.5 whitespace-nowrap", children: [_jsx(UserPlus, { className: "h-4 w-4" }), "Invite Employee"] })),
|
|
95
97
|
buildBulkActions: onBulkRemove
|
|
96
98
|
? (selection) => [
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
|
|
4
|
+
import { sortBy } from "@mohasinac/appkit";
|
|
3
5
|
import React, { useState } from "react";
|
|
4
6
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
5
7
|
import { Button, FilterChipGroup, Form, FormActions, Input, ListingLayout, Modal, RowActionMenu, Text as AppText, useToast, } from "../../../ui";
|
|
@@ -54,12 +56,12 @@ export function AdminUsersView({ children, ...props }) {
|
|
|
54
56
|
searchPlaceholder: "Search users, email, or seller handles",
|
|
55
57
|
emptyLabel: "No users found",
|
|
56
58
|
filterKeys: ["status", "role"],
|
|
57
|
-
defaultSort: "
|
|
59
|
+
defaultSort: sortBy("createdAt", "DESC"),
|
|
58
60
|
queryKey: ["admin", "users", "listing"],
|
|
59
61
|
endpoint: ADMIN_ENDPOINTS.USERS,
|
|
60
62
|
sortOptions: [
|
|
61
|
-
{ value: "
|
|
62
|
-
{ value: "createdAt", label: "Oldest" },
|
|
63
|
+
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
64
|
+
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
63
65
|
{ value: "displayName", label: "Name A–Z" },
|
|
64
66
|
],
|
|
65
67
|
mapRows: (response) => toRecordArray(response.users).map((item, index) => {
|
|
@@ -98,7 +100,7 @@ export function AdminUsersView({ children, ...props }) {
|
|
|
98
100
|
parts.push(f.status === "Active" ? "disabled==false" : "disabled==true");
|
|
99
101
|
}
|
|
100
102
|
if (f.role && f.role !== "All")
|
|
101
|
-
parts.push(
|
|
103
|
+
parts.push(sieveFilter("role", SIEVE_OP.EQ, f.role));
|
|
102
104
|
return parts.join(",") || undefined;
|
|
103
105
|
},
|
|
104
106
|
onRowClick: (row) => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { sortBy } from "@mohasinac/appkit";
|
|
3
4
|
import React from "react";
|
|
4
5
|
import { ListingLayout } from "../../../ui";
|
|
5
6
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
@@ -11,12 +12,12 @@ const ADMIN_WISHLISTS_CONFIG = {
|
|
|
11
12
|
searchPlaceholder: "Search by user ID",
|
|
12
13
|
emptyLabel: "No user wishlists found",
|
|
13
14
|
filterKeys: [],
|
|
14
|
-
defaultSort: "
|
|
15
|
+
defaultSort: sortBy("updatedAt", "DESC"),
|
|
15
16
|
queryKey: ["admin", "wishlists", "listing"],
|
|
16
17
|
endpoint: ADMIN_ENDPOINTS.ADMIN_WISHLISTS,
|
|
17
18
|
sortOptions: [
|
|
18
|
-
{ value: "
|
|
19
|
-
{ value: "
|
|
19
|
+
{ value: sortBy("updatedAt", "DESC"), label: "Recently updated" },
|
|
20
|
+
{ value: sortBy("itemCount", "DESC"), label: "Largest first" },
|
|
20
21
|
],
|
|
21
22
|
mapRows: (response) => toRecordArray(response.items).map((item, index) => {
|
|
22
23
|
const itemCount = typeof item.itemCount === "number" ? item.itemCount : 0;
|
|
@@ -15,7 +15,11 @@ const BRAND_FROM = "var(--appkit-color-primary-700, #1343de)";
|
|
|
15
15
|
const BRAND_MID = "var(--appkit-color-cobalt, #3570fc)";
|
|
16
16
|
const BRAND_TO = "var(--appkit-color-secondary-400, #84e122)";
|
|
17
17
|
function StatCard({ label, value, sub, icon: Icon, gradient, iconColor }) {
|
|
18
|
-
return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${__O.hidden} shadow-sm hover:shadow-md transition-shadow`, children: [_jsx(Div, { className: "absolute top-0 left-0 right-0 h-[3px]",
|
|
18
|
+
return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${__O.hidden} shadow-sm hover:shadow-md transition-shadow`, children: [_jsx(Div, { className: "absolute top-0 left-0 right-0 h-[3px]",
|
|
19
|
+
// audit-inline-style-ok: runtime theme gradient
|
|
20
|
+
style: { background: gradient }, "aria-hidden": "true" }), _jsxs(Div, { className: "px-5 pb-5 pt-6 flex items-start justify-between gap-3", children: [_jsxs(Div, { className: `min-w-0 flex-1 ${__O.hidden}`, children: [_jsx(Text, { className: "text-[11px] font-semibold uppercase tracking-widest text-[var(--appkit-color-text-muted)] truncate", children: label }), _jsx(Text, { className: "mt-2 text-xl font-bold text-[var(--appkit-color-text)] tabular-nums leading-tight break-words", children: value }), sub && (_jsx(Text, { className: "mt-1.5 text-xs text-[var(--appkit-color-text-muted)]", children: sub }))] }), _jsx(Div, { className: "flex-shrink-0 w-10 h-10 rounded-lg flex items-center justify-center",
|
|
21
|
+
// audit-inline-style-ok: runtime theme gradient
|
|
22
|
+
style: { background: gradient, opacity: 0.92 }, children: _jsx(Icon, { className: "w-5 h-5 text-white drop-shadow-sm" }) })] })] }));
|
|
19
23
|
}
|
|
20
24
|
function SkeletonCard() {
|
|
21
25
|
return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${__O.hidden} ${__P.p5} animate-pulse`, children: [_jsx(Div, { className: "absolute top-0 left-0 right-0 h-[3px] bg-[var(--appkit-color-border)]" }), _jsxs(Div, { className: "flex items-start justify-between gap-3 pt-1", children: [_jsxs(Div, { className: "flex-1 space-y-2", children: [_jsx(Div, { className: "h-2.5 w-20 rounded bg-[var(--appkit-color-border)]" }), _jsx(Div, { className: "h-7 w-24 rounded bg-[var(--appkit-color-border)]" })] }), _jsx(Div, { className: "w-10 h-10 rounded-lg bg-[var(--appkit-color-border)]" })] })] }));
|
|
@@ -19,7 +19,9 @@ const CartesianGrid = dynamic(() => import("recharts").then((m) => m.CartesianGr
|
|
|
19
19
|
const Tooltip = dynamic(() => import("recharts").then((m) => m.Tooltip), { ssr: false });
|
|
20
20
|
// Shared chart card wrapper
|
|
21
21
|
function ChartCard({ title, children, className = "", }) {
|
|
22
|
-
return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm ${className}`, children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]",
|
|
22
|
+
return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm ${className}`, children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]",
|
|
23
|
+
// audit-inline-style-ok: dynamic CSS
|
|
24
|
+
style: {
|
|
23
25
|
background: "linear-gradient(to right, var(--appkit-color-primary-700,#1343de) 0%, var(--appkit-color-cobalt,#3570fc) 55%, var(--appkit-color-secondary-400,#84e122) 100%)",
|
|
24
26
|
}, "aria-hidden": "true" }), _jsxs(Div, { className: `${__P.p4} sm:${__P.p6} pt-5 sm:pt-7`, children: [title && (_jsx(Heading, { level: 3, className: "text-sm font-semibold text-[var(--appkit-color-text)] mb-4", children: title })), children] })] }));
|
|
25
27
|
}
|
|
@@ -12,5 +12,9 @@ const GRADIENTS = {
|
|
|
12
12
|
};
|
|
13
13
|
export function AdminStatCard({ label, value, sub, icon, gradient = "brand", className = "", }) {
|
|
14
14
|
const resolvedGradient = GRADIENTS[gradient] ?? gradient;
|
|
15
|
-
return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm hover:shadow-md transition-shadow ${className}`, children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]",
|
|
15
|
+
return (_jsxs(Div, { className: `relative rounded-xl border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] overflow-hidden shadow-sm hover:shadow-md transition-shadow ${className}`, children: [_jsx("div", { className: "absolute top-0 left-0 right-0 h-[3px]",
|
|
16
|
+
// audit-inline-style-ok: dynamic CSS
|
|
17
|
+
style: { background: resolvedGradient }, "aria-hidden": "true" }), _jsxs(Div, { className: "px-5 pb-5 pt-6 flex items-start justify-between gap-3", children: [_jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx(Text, { className: "text-[11px] font-semibold uppercase tracking-widest text-[var(--appkit-color-text-muted)]", children: label }), _jsx(Heading, { level: 3, className: "mt-2 text-2xl font-bold tabular-nums leading-none text-[var(--appkit-color-text)]", children: value }), sub && (_jsx(Text, { className: "mt-1.5 text-xs text-[var(--appkit-color-text-muted)]", children: sub }))] }), icon && (_jsx(Div, { className: "flex-shrink-0 w-10 h-10 rounded-lg flex items-center justify-center text-xl",
|
|
18
|
+
// audit-inline-style-ok: dynamic CSS
|
|
19
|
+
style: { background: resolvedGradient }, children: icon }))] })] }));
|
|
16
20
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* primitive and stay in sync when statuses are added.
|
|
11
11
|
*/
|
|
12
12
|
export interface AdminFilterTab {
|
|
13
|
-
/** Filter value — empty when
|
|
13
|
+
/** Filter value — empty when sieveFilter("id", SIEVE_OP.EQ, "= "All""). */
|
|
14
14
|
id: string;
|
|
15
15
|
/** Display text. */
|
|
16
16
|
label: string;
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Admin filter-chip tab sets (SB10-C completion, S8 2026-05-13).
|
|
3
|
-
*
|
|
4
|
-
* Match current view-filter values exactly so migrating an inline
|
|
5
|
-
* `STATUS_OPTIONS` array to one of these constants is a behaviour-preserving
|
|
6
|
-
* rename. The `ALL_TAB` sentinel collapses to an empty filter string when
|
|
7
|
-
* clicked.
|
|
8
|
-
*
|
|
9
|
-
* Each entry is a typed `{ id, label }` row so views render with one
|
|
10
|
-
* primitive and stay in sync when statuses are added.
|
|
11
|
-
*/
|
|
12
1
|
export const ALL_TAB = { id: "All", label: "All" };
|
|
13
2
|
/** Admin > Products — listing status filter chip set. */
|
|
14
3
|
export const ADMIN_PRODUCT_STATUS_TABS = [
|
|
@@ -23,6 +23,6 @@ export declare function useAdminSectionsListing({ page, pageSize, sorts, filters
|
|
|
23
23
|
hasMore: boolean;
|
|
24
24
|
isLoading: boolean;
|
|
25
25
|
errorMessage: string | undefined;
|
|
26
|
-
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<
|
|
26
|
+
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<AdminSectionsListingResponse, ApiClientError>>;
|
|
27
27
|
};
|
|
28
28
|
export {};
|
|
@@ -16,7 +16,7 @@ export interface UseChatReturn {
|
|
|
16
16
|
export declare function useChat(chatId: string | null, options?: {
|
|
17
17
|
realtimeProvider?: IClientRealtimeProvider;
|
|
18
18
|
}): UseChatReturn;
|
|
19
|
-
export declare function useChatRooms(): import("@tanstack/react-query").UseQueryResult<
|
|
19
|
+
export declare function useChatRooms(): import("@tanstack/react-query").UseQueryResult<unknown[], Error>;
|
|
20
20
|
export declare function useCreateChatRoom(): import("@tanstack/react-query").UseMutationResult<unknown, Error, {
|
|
21
21
|
orderId: string;
|
|
22
22
|
ownerId: string;
|
|
@@ -14,6 +14,7 @@ export function useChat(chatId, options) {
|
|
|
14
14
|
const tokenExpiresAtRef = useRef(0);
|
|
15
15
|
const unsubscribeRef = useRef(null);
|
|
16
16
|
const connectAndSubscribe = useCallback(async () => {
|
|
17
|
+
// toast-intentionally-silent — error stored in setError() for UI inline render
|
|
17
18
|
if (!chatId)
|
|
18
19
|
return;
|
|
19
20
|
setIsLoading(true);
|
|
@@ -4,7 +4,7 @@ export type { AdminProduct, AdminProductStatus, AdminProductDrawerMode, } from "
|
|
|
4
4
|
export { ADMIN_PRODUCT_STATUS_OPTIONS } from "./product.types";
|
|
5
5
|
/**
|
|
6
6
|
* Admin-specific column definition that narrows `render` to `React.ReactNode`.
|
|
7
|
-
* Extends
|
|
7
|
+
* Extends sieveFilter("TableColumn", SIEVE_OP.LT, "T>") from `@mohasinac/contracts` so it is compatible
|
|
8
8
|
* with column arrays built from the base type.
|
|
9
9
|
*/
|
|
10
10
|
export interface AdminTableColumn<T = Record<string, unknown>> extends Omit<TableColumn<T>, "render"> {
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* Business logic for auction bidding. Auth, rate-limiting, and
|
|
5
5
|
* input validation are handled by the calling server action.
|
|
6
6
|
*/
|
|
7
|
+
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
|
|
8
|
+
import { sortBy } from "@mohasinac/appkit";
|
|
7
9
|
import { serverLogger } from "../../../monitoring";
|
|
8
10
|
import { bidRepository } from "../repository/bid.repository";
|
|
9
11
|
import { productRepository } from "../../products/repository/products.repository";
|
|
@@ -215,8 +217,8 @@ export async function buyNowAuction(userId, userName, userEmail, input) {
|
|
|
215
217
|
}
|
|
216
218
|
export async function listBidsByProduct(productId, params) {
|
|
217
219
|
const result = await bidRepository.list({
|
|
218
|
-
filters:
|
|
219
|
-
sorts: "
|
|
220
|
+
filters: sieveFilter("productId", SIEVE_OP.EQ, productId),
|
|
221
|
+
sorts: sortBy("bidDate", "DESC"),
|
|
220
222
|
page: params?.page ?? 1,
|
|
221
223
|
pageSize: params?.pageSize ?? 20,
|
|
222
224
|
});
|
|
@@ -54,7 +54,11 @@ function AuctionRow({ auction, portal, }) {
|
|
|
54
54
|
const sorted = useMemo(() => [...auction.bids].sort((a, b) => b.bidAmount - a.bidAmount), [auction.bids]);
|
|
55
55
|
const highest = sorted[0]?.bidAmount ?? 0;
|
|
56
56
|
const isWinning = auction.bids.some((b) => b.isWinning);
|
|
57
|
-
return (_jsxs(Div, { className: `border border-[var(--appkit-color-border)] rounded-xl ${__O.hidden} bg-[var(--appkit-color-surface)] shadow-sm`, children: [_jsxs("button", { type: "button", onClick: () => setExpanded((v) => !v), className: "w-full flex items-center gap-3 px-4 py-3 hover:bg-[var(--appkit-color-border-subtle)] transition-colors text-left", "aria-expanded": expanded, children: [_jsx(ChevronRight, { className: `shrink-0 text-[var(--appkit-color-text-muted)] transition-transform ${expanded ? "rotate-90" : ""}`, size: 16 }), _jsx(Div, { className: "flex-1 min-w-0", children: _jsx(Text, { className: "text-sm font-semibold text-[var(--appkit-color-text)] line-clamp-1", children: auction.productTitle }) }), _jsxs(Row, { gap: "sm", className: "shrink-0", children: [isWinning && (_jsx(Badge, { variant: "active", children: "Winning" })), _jsxs(Text, { variant: "secondary", className: "text-xs", children: [auction.bids.length, " bid", auction.bids.length !== 1 ? "s" : ""] }), _jsx(Text, { className: "text-sm font-semibold text-[var(--appkit-color-text)]", children: paise(highest) })] })] }), expanded && (_jsxs(Div, { className: "border-t border-[var(--appkit-color-border)]", children: [_jsxs("div", { className: "grid text-xs font-medium text-[var(--appkit-color-text-muted)] uppercase tracking-wide px-4 py-2 border-b border-[var(--appkit-color-border-subtle)]",
|
|
57
|
+
return (_jsxs(Div, { className: `border border-[var(--appkit-color-border)] rounded-xl ${__O.hidden} bg-[var(--appkit-color-surface)] shadow-sm`, children: [_jsxs("button", { type: "button", onClick: () => setExpanded((v) => !v), className: "w-full flex items-center gap-3 px-4 py-3 hover:bg-[var(--appkit-color-border-subtle)] transition-colors text-left", "aria-expanded": expanded, children: [_jsx(ChevronRight, { className: `shrink-0 text-[var(--appkit-color-text-muted)] transition-transform ${expanded ? "rotate-90" : ""}`, size: 16 }), _jsx(Div, { className: "flex-1 min-w-0", children: _jsx(Text, { className: "text-sm font-semibold text-[var(--appkit-color-text)] line-clamp-1", children: auction.productTitle }) }), _jsxs(Row, { gap: "sm", className: "shrink-0", children: [isWinning && (_jsx(Badge, { variant: "active", children: "Winning" })), _jsxs(Text, { variant: "secondary", className: "text-xs", children: [auction.bids.length, " bid", auction.bids.length !== 1 ? "s" : ""] }), _jsx(Text, { className: "text-sm font-semibold text-[var(--appkit-color-text)]", children: paise(highest) })] })] }), expanded && (_jsxs(Div, { className: "border-t border-[var(--appkit-color-border)]", children: [_jsxs("div", { className: "grid text-xs font-medium text-[var(--appkit-color-text-muted)] uppercase tracking-wide px-4 py-2 border-b border-[var(--appkit-color-border-subtle)]",
|
|
58
|
+
// audit-inline-style-ok: runtime grid template
|
|
59
|
+
style: { gridTemplateColumns: portal === "buyer" ? "1fr auto auto" : "1fr 1fr auto auto" }, children: [_jsx("span", { children: portal === "buyer" ? "Amount" : "Bidder" }), portal !== "buyer" && _jsx("span", { children: "Amount" }), _jsx("span", { children: "Status" }), _jsx("span", { className: "text-right", children: "Time" })] }), sorted.map((bid) => (_jsxs("div", { className: "grid items-center px-4 py-2.5 border-b border-[var(--appkit-color-border-subtle)] last:border-0 hover:bg-[var(--appkit-color-border-subtle)] transition-colors",
|
|
60
|
+
// audit-inline-style-ok: runtime grid template
|
|
61
|
+
style: { gridTemplateColumns: portal === "buyer" ? "1fr auto auto" : "1fr 1fr auto auto" }, children: [portal !== "buyer" && (_jsx(Text, { className: "text-sm text-[var(--appkit-color-text)] truncate pr-3", children: bid.userName || bid.userId })), _jsx(Text, { className: "text-sm font-medium text-[var(--appkit-color-text)]", children: paise(bid.bidAmount) }), _jsx(Badge, { variant: STATUS_VARIANT[bid.status] ?? "pending", className: "capitalize", children: bid.status }), _jsx(Text, { variant: "secondary", className: "text-xs text-right", children: relDate(bid.bidDate) })] }, bid.id)))] }))] }));
|
|
58
62
|
}
|
|
59
63
|
export function AuctionBidsTable({ bids, portal = "buyer", emptyLabel = "No bids found.", }) {
|
|
60
64
|
const auctions = useMemo(() => groupByAuction(bids), [bids]);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
|
|
2
3
|
import { productRepository } from "../../../repositories";
|
|
3
4
|
import { Container, Heading, Main, Section } from "../../../ui";
|
|
4
5
|
import { AdSlot } from "../../homepage/components/AdSlot";
|
|
@@ -16,23 +17,23 @@ function buildAuctionFilters(params) {
|
|
|
16
17
|
const minBid = sp(params, "minBid");
|
|
17
18
|
const maxBid = sp(params, "maxBid");
|
|
18
19
|
if (minBid)
|
|
19
|
-
parts.push(
|
|
20
|
+
parts.push(sieveFilter("currentBid", SIEVE_OP.GTE, minBid));
|
|
20
21
|
if (maxBid)
|
|
21
|
-
parts.push(
|
|
22
|
+
parts.push(sieveFilter("currentBid", SIEVE_OP.LTE, maxBid));
|
|
22
23
|
const store = sp(params, "store");
|
|
23
24
|
if (store) {
|
|
24
25
|
const values = store.split("|").filter(Boolean);
|
|
25
26
|
if (values.length === 1)
|
|
26
|
-
parts.push(
|
|
27
|
+
parts.push(sieveFilter("storeId", SIEVE_OP.EQ, values[0]));
|
|
27
28
|
else if (values.length > 1)
|
|
28
|
-
parts.push(
|
|
29
|
+
parts.push(sieveFilter("storeId", SIEVE_OP.EQ, values.join("|")));
|
|
29
30
|
}
|
|
30
31
|
const dateFrom = sp(params, "dateFrom");
|
|
31
32
|
const dateTo = sp(params, "dateTo");
|
|
32
33
|
if (dateFrom)
|
|
33
|
-
parts.push(
|
|
34
|
+
parts.push(sieveFilter("auctionEndDate", SIEVE_OP.GTE, dateFrom));
|
|
34
35
|
if (dateTo)
|
|
35
|
-
parts.push(
|
|
36
|
+
parts.push(sieveFilter("auctionEndDate", SIEVE_OP.LTE, dateTo));
|
|
36
37
|
return parts.join(",");
|
|
37
38
|
}
|
|
38
39
|
export async function AuctionsListView({ searchParams = {} }) {
|
|
@@ -17,22 +17,22 @@ interface UseAuctionDetailOptions {
|
|
|
17
17
|
bidsQueryKeyPrefix?: string;
|
|
18
18
|
}
|
|
19
19
|
export declare function useAuctions(params?: AuctionListQuery, opts?: UseAuctionsOptions): {
|
|
20
|
-
data:
|
|
20
|
+
data: AuctionListResponse | undefined;
|
|
21
21
|
auctions: AuctionItem[];
|
|
22
22
|
total: number;
|
|
23
23
|
totalPages: number;
|
|
24
24
|
hasMore: boolean;
|
|
25
25
|
isLoading: boolean;
|
|
26
26
|
error: string | null;
|
|
27
|
-
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<
|
|
27
|
+
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<AuctionListResponse, Error>>;
|
|
28
28
|
};
|
|
29
29
|
export declare function useAuction(slug: string, opts?: {
|
|
30
30
|
enabled?: boolean;
|
|
31
31
|
}): {
|
|
32
|
-
auction:
|
|
32
|
+
auction: AuctionItem | null;
|
|
33
33
|
isLoading: boolean;
|
|
34
34
|
error: string | null;
|
|
35
|
-
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<
|
|
35
|
+
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<AuctionItem | null, Error>>;
|
|
36
36
|
};
|
|
37
37
|
export declare function useAuctionBids(auctionSlug: string, limit?: number, opts?: {
|
|
38
38
|
enabled?: boolean;
|
|
@@ -43,9 +43,9 @@ export declare function useAuctionBids(auctionSlug: string, limit?: number, opts
|
|
|
43
43
|
error: string | null;
|
|
44
44
|
};
|
|
45
45
|
export declare function useAuctionDetail(id: string, opts?: UseAuctionDetailOptions): {
|
|
46
|
-
productQuery: import("@tanstack/react-query").UseQueryResult<
|
|
47
|
-
product:
|
|
48
|
-
bidsQuery: import("@tanstack/react-query").UseQueryResult<
|
|
49
|
-
bids:
|
|
46
|
+
productQuery: import("@tanstack/react-query").UseQueryResult<ProductItem | null, Error>;
|
|
47
|
+
product: ProductItem | null;
|
|
48
|
+
bidsQuery: import("@tanstack/react-query").UseQueryResult<PublicBid[], Error>;
|
|
49
|
+
bids: PublicBid[];
|
|
50
50
|
};
|
|
51
51
|
export {};
|
|
@@ -4,11 +4,11 @@ interface UseCurrentUserOptions {
|
|
|
4
4
|
enabled?: boolean;
|
|
5
5
|
}
|
|
6
6
|
export declare function useCurrentUser(opts?: UseCurrentUserOptions): {
|
|
7
|
-
user:
|
|
7
|
+
user: AuthUser | null;
|
|
8
8
|
isLoading: boolean;
|
|
9
9
|
isAuthenticated: boolean;
|
|
10
10
|
error: Error | null;
|
|
11
|
-
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<
|
|
11
|
+
refetch: (options?: import("@tanstack/query-core").RefetchOptions) => Promise<import("@tanstack/query-core").QueryObserverResult<AuthUser | null, Error>>;
|
|
12
12
|
};
|
|
13
13
|
export interface LoginCredentials {
|
|
14
14
|
email: string;
|
|
@@ -119,6 +119,7 @@ export function useGoogleLogin(options) {
|
|
|
119
119
|
return () => window.removeEventListener("message", handleMessage);
|
|
120
120
|
}, []); // mount once — uses refs, no stale closure risk
|
|
121
121
|
const mutate = useCallback(async () => {
|
|
122
|
+
// toast-intentionally-silent — errors surfaced via onErrorRef.current() callback
|
|
122
123
|
calledRef.current = false; // reset for each new auth flow
|
|
123
124
|
const popup = window.open(`${window.location.origin}/auth.html`, "oauth_google", "width=500,height=660,left=400,top=100");
|
|
124
125
|
if (!popup) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sortBy } from "@mohasinac/appkit";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { serverLogger } from "../../../monitoring";
|
|
3
4
|
import { blogRepository } from "../repository/blog.repository";
|
|
@@ -104,11 +105,11 @@ export async function listBlogPosts(params) {
|
|
|
104
105
|
return blogRepository.listPublished({ category: params?.category }, sieve);
|
|
105
106
|
}
|
|
106
107
|
export async function getFeaturedBlogPosts(count = 3) {
|
|
107
|
-
const result = await blogRepository.listPublished({ featuredOnly: true }, { sorts: "
|
|
108
|
+
const result = await blogRepository.listPublished({ featuredOnly: true }, { sorts: sortBy("publishedAt", "DESC"), page: 1, pageSize: count });
|
|
108
109
|
return result.items;
|
|
109
110
|
}
|
|
110
111
|
export async function getLatestBlogPosts(count = 5) {
|
|
111
|
-
const result = await blogRepository.listPublished({}, { sorts: "
|
|
112
|
+
const result = await blogRepository.listPublished({}, { sorts: sortBy("publishedAt", "DESC"), page: 1, pageSize: count });
|
|
112
113
|
return result.items;
|
|
113
114
|
}
|
|
114
115
|
export async function getBlogPostById(id) {
|
|
@@ -31,5 +31,7 @@ export function BlogFeaturedCard({ post, href, labels = {}, className = "", sele
|
|
|
31
31
|
day: "numeric",
|
|
32
32
|
})
|
|
33
33
|
: "";
|
|
34
|
-
return (_jsxs(Article, { className: `group relative flex h-full flex-col overflow-hidden rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm hover:shadow-md transition-shadow duration-200 ${isSelected ? "ring-2 ring-primary outline outline-2 outline-primary" : ""} ${className}`, onMouseDown: onSelect && !isSelected ? longPress.onMouseDown : undefined, onMouseUp: onSelect && !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: onSelect && !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: onSelect && !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: onSelect && !isSelected ? longPress.onTouchEnd : undefined, children: [onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onSelect(post.id, !isSelected); }, label: isSelected ? "Deselect post" : "Select post", position: "top-2 left-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsxs(TextLink, { href: href, className: "flex h-full flex-col", children: [_jsx(Div, { className: `aspect-video ${__O.hidden} flex-shrink-0`, children: coverImageUrl ? (_jsx(Div, { role: "img", "aria-label": safeTitle, className: "h-full w-full bg-cover bg-center transition-transform duration-300 group-hover:scale-105",
|
|
34
|
+
return (_jsxs(Article, { className: `group relative flex h-full flex-col overflow-hidden rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm hover:shadow-md transition-shadow duration-200 ${isSelected ? "ring-2 ring-primary outline outline-2 outline-primary" : ""} ${className}`, onMouseDown: onSelect && !isSelected ? longPress.onMouseDown : undefined, onMouseUp: onSelect && !isSelected ? longPress.onMouseUp : undefined, onMouseLeave: onSelect && !isSelected ? longPress.onMouseLeave : undefined, onTouchStart: onSelect && !isSelected ? longPress.onTouchStart : undefined, onTouchEnd: onSelect && !isSelected ? longPress.onTouchEnd : undefined, children: [onSelect && (_jsx(BaseListingCard.Checkbox, { selected: isSelected, onSelect: (e) => { e.preventDefault(); onSelect(post.id, !isSelected); }, label: isSelected ? "Deselect post" : "Select post", position: "top-2 left-2", className: selectable || isSelected ? "opacity-100" : "opacity-0 group-hover:opacity-100 transition-opacity" })), _jsxs(TextLink, { href: href, className: "flex h-full flex-col", children: [_jsx(Div, { className: `aspect-video ${__O.hidden} flex-shrink-0`, children: coverImageUrl ? (_jsx(Div, { role: "img", "aria-label": safeTitle, className: "h-full w-full bg-cover bg-center transition-transform duration-300 group-hover:scale-105",
|
|
35
|
+
// audit-inline-style-ok: dynamic image URL
|
|
36
|
+
style: { backgroundImage: `url(${coverImageUrl})` } })) : (_jsx(Div, { role: "img", "aria-label": safeTitle, className: "h-full w-full bg-gradient-to-br from-zinc-200 via-zinc-100 to-zinc-300 dark:from-slate-800 dark:via-slate-700 dark:to-slate-900" })) }), _jsxs(Div, { className: `flex flex-1 flex-col ${__P.p4}`, children: [_jsxs(Row, { className: "gap-1.5 mb-2 flex-wrap", children: [_jsx(Span, { size: "xs", weight: "medium", className: `inline-block px-2 py-0.5 rounded-full capitalize ${CATEGORY_BADGE[post.category] ?? "bg-zinc-100 text-zinc-700 dark:bg-zinc-800 dark:text-zinc-300"}`, children: post.category }), post.isFeatured && (_jsx(Span, { size: "xs", weight: "medium", className: CLS_FEATURED_BADGE, children: labels.featuredBadge ?? "Featured" }))] }), _jsx(Heading, { level: 3, className: `font-semibold text-neutral-900 dark:text-zinc-100 text-base leading-snug mb-1 ${THEME_CONSTANTS.utilities.textClamp2}`, children: safeTitle }), post.excerpt && (_jsx(Text, { className: `text-neutral-500 dark:text-zinc-400 text-sm ${THEME_CONSTANTS.utilities.textClamp2} mb-3`, children: post.excerpt })), _jsxs(Row, { className: "mt-auto gap-3 text-xs text-zinc-400 dark:text-zinc-400 flex-wrap", children: [post.authorName && (_jsx(Span, { children: safeDisplayName(post.authorName, "Author") })), post.readTimeMinutes != null && (_jsxs(Span, { children: [post.readTimeMinutes, " ", labels.readTime ?? "min read"] })), date && _jsx(Span, { children: date })] })] })] })] }));
|
|
35
37
|
}
|
|
@@ -26,7 +26,11 @@ export function BlogCard({ post, href, onClick, className = "" }) {
|
|
|
26
26
|
const isInteractive = !!(href || onClick);
|
|
27
27
|
const card = (_jsxs(Article, { role: onClick && !href ? "button" : undefined, tabIndex: onClick && !href ? 0 : undefined, onKeyDown: onClick && !href
|
|
28
28
|
? (e) => (e.key === "Enter" || e.key === " ") && onClick(post)
|
|
29
|
-
: undefined, onClick: onClick && !href ? () => onClick(post) : undefined, className: `group flex flex-col h-full overflow-hidden rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm transition hover:shadow-md ${isInteractive ? "cursor-pointer" : ""} ${className}`, children: [_jsx(Div, { className: `aspect-video w-full ${__O.hidden} bg-neutral-100 dark:bg-slate-800 flex-shrink-0`, children: coverImageUrl ? (_jsx(Div, { role: "img", "aria-label": post.title, className: "h-full w-full bg-center bg-cover transition-transform duration-300 group-hover:scale-105",
|
|
29
|
+
: undefined, onClick: onClick && !href ? () => onClick(post) : undefined, className: `group flex flex-col h-full overflow-hidden rounded-xl border border-neutral-200 dark:border-slate-700 bg-white dark:bg-slate-900 shadow-sm transition hover:shadow-md ${isInteractive ? "cursor-pointer" : ""} ${className}`, children: [_jsx(Div, { className: `aspect-video w-full ${__O.hidden} bg-neutral-100 dark:bg-slate-800 flex-shrink-0`, children: coverImageUrl ? (_jsx(Div, { role: "img", "aria-label": post.title, className: "h-full w-full bg-center bg-cover transition-transform duration-300 group-hover:scale-105",
|
|
30
|
+
// audit-inline-style-ok: dynamic image URL
|
|
31
|
+
style: { backgroundImage: `url(${coverImageUrl})` } })) : (_jsx(Div, { className: "h-full w-full bg-gradient-to-br from-zinc-200 via-zinc-100 to-zinc-300 dark:from-slate-800 dark:via-slate-700 dark:to-slate-900 flex items-center justify-center", children: _jsx(Span, { className: "text-4xl opacity-30", "aria-hidden": "true", children: "\u270D\uFE0F" }) })) }), _jsxs(Div, { className: `flex flex-1 flex-col ${__P.p5}`, children: [_jsxs(Row, { className: "mb-2 gap-2 flex-wrap", children: [_jsx(Span, { size: "xs", weight: "medium", className: "rounded-full bg-primary/10 px-2 py-0.5 capitalize text-primary", children: post.category }), post.isFeatured && (_jsx(Span, { size: "xs", weight: "medium", className: CLS_FEATURED_BADGE, children: "Featured" })), post.readTimeMinutes && (_jsxs(Span, { size: "xs", className: "text-zinc-400 dark:text-zinc-400", children: [post.readTimeMinutes, " min read"] }))] }), _jsx(Heading, { level: 3, className: `${THEME_CONSTANTS.utilities.textClamp2} text-base font-semibold text-neutral-900 dark:text-zinc-100 group-hover:text-primary`, children: post.title }), post.excerpt && (_jsx(Text, { className: `mt-2 ${THEME_CONSTANTS.utilities.textClamp3} flex-1 text-sm text-neutral-500 dark:text-zinc-400`, children: post.excerpt })), _jsxs(Row, { className: "mt-auto pt-3 gap-3", children: [post.authorAvatar ? (_jsx(Div, { role: "img", "aria-label": post.authorName ?? "author", className: "h-7 w-7 flex-shrink-0 rounded-full bg-center bg-cover",
|
|
32
|
+
// audit-inline-style-ok: dynamic image URL
|
|
33
|
+
style: { backgroundImage: `url(${post.authorAvatar})` } })) : post.authorName ? (_jsx(Div, { className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-primary/10 text-xs font-bold text-primary", children: post.authorName.charAt(0).toUpperCase() })) : null, _jsxs(Text, { className: "text-xs text-neutral-500 dark:text-zinc-400 min-w-0", children: [post.authorName && (_jsx(Span, { weight: "medium", className: "text-neutral-700 dark:text-zinc-300", children: safeDisplayName(post.authorName, "Author") })), date && _jsxs(Span, { className: "ml-1 text-neutral-400", children: ["\u00B7 ", date] })] })] })] })] }));
|
|
30
34
|
if (href) {
|
|
31
35
|
return (_jsx(Link, { href: href, className: "block h-full", children: card }));
|
|
32
36
|
}
|
|
@@ -53,5 +53,7 @@ export function BlogPostView({ slug, initialData, labels = {}, renderImage, rend
|
|
|
53
53
|
})
|
|
54
54
|
: "";
|
|
55
55
|
const coverImageUrl = getMediaUrl(post.coverImage);
|
|
56
|
-
return (_jsxs(Div, { className: `min-h-screen ${className}`, children: [coverImageUrl && (_jsxs(Div, { className: `relative h-72 md:h-96 ${__O.hidden}`, children: [renderImage ? (renderImage(post)) : (_jsx(Div, { role: "img", "aria-label": post.title, className: "h-full w-full bg-cover bg-center",
|
|
56
|
+
return (_jsxs(Div, { className: `min-h-screen ${className}`, children: [coverImageUrl && (_jsxs(Div, { className: `relative h-72 md:h-96 ${__O.hidden}`, children: [renderImage ? (renderImage(post)) : (_jsx(Div, { role: "img", "aria-label": post.title, className: "h-full w-full bg-cover bg-center",
|
|
57
|
+
// audit-inline-style-ok: dynamic image URL
|
|
58
|
+
style: { backgroundImage: `url(${coverImageUrl})` } })), _jsx(Div, { className: "absolute inset-0 bg-gradient-to-t from-black/50 to-transparent" })] })), _jsxs(Div, { className: "max-w-3xl mx-auto px-4 py-12", children: [renderBlogPostHeader(post, date, labels), post.tags && post.tags.length > 0 && (_jsx(Row, { wrap: true, gap: "sm", className: "mb-8", children: post.tags.map((tag) => (_jsxs(Link, { href: `${String(ROUTES.PUBLIC.BLOG)}?tags=${encodeURIComponent(tag)}`, className: "inline-block px-3 py-1 rounded-full bg-neutral-100 dark:bg-zinc-800 hover:bg-primary/10 text-zinc-600 dark:text-zinc-400 hover:text-primary text-xs font-medium transition-colors", children: ["#", tag] }, tag))) })), renderAuthorBio && _jsx(Div, { className: "mb-6", children: renderAuthorBio(post) }), _jsx(Div, { surface: "card", padding: "xl", className: "mb-12", children: renderContent ? renderContent(post) : (_jsx(RichText, { html: normalizeRichTextHtml(post.content ?? ""), proseClass: "prose max-w-none dark:prose-invert" })) }), renderBlogPostRelated(related, labels, renderRelatedCard), renderBackButton && _jsx(Div, { className: "mt-10 pt-8 border-t border-zinc-200 dark:border-zinc-700", children: renderBackButton() })] })] }));
|
|
57
59
|
}
|