@mohasinac/appkit 4.11.3 → 4.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/server/features/auctions/service.d.ts +7 -1
- package/dist/_internal/server/features/auctions/service.js +13 -5
- package/dist/_internal/server/features/checkout/actions.d.ts +36 -18
- package/dist/_internal/server/features/checkout/actions.js +127 -45
- package/dist/_internal/server/features/orders/actions.js +5 -3
- package/dist/_internal/server/features/orders/adapters.js +13 -0
- package/dist/_internal/server/features/products/list-public.d.ts +28 -0
- package/dist/_internal/server/features/products/list-public.js +92 -0
- package/dist/_internal/server/functions/firestore.d.ts +1 -2
- package/dist/_internal/server/functions/firestore.js +1 -9
- package/dist/_internal/server/jobs/core/auctionSettlement.js +5 -2
- package/dist/_internal/server/jobs/core/index.d.ts +0 -1
- package/dist/_internal/server/jobs/core/index.js +0 -1
- package/dist/_internal/server/jobs/core/offerExpiry.js +33 -1
- package/dist/_internal/server/jobs/handlers/index.d.ts +1 -2
- package/dist/_internal/server/jobs/handlers/index.js +1 -2
- package/dist/_internal/shared/checkout/lanes.d.ts +20 -0
- package/dist/_internal/shared/checkout/lanes.js +28 -0
- package/dist/_internal/shared/features/auctions/config.d.ts +34 -0
- package/dist/_internal/shared/features/auctions/config.js +47 -3
- package/dist/_internal/shared/features/checkout/config.d.ts +4 -0
- package/dist/_internal/shared/features/checkout/config.js +8 -0
- package/dist/_internal/shared/fees/calculator.d.ts +34 -2
- package/dist/_internal/shared/fees/calculator.js +45 -1
- package/dist/_internal/shared/listing-types/auction/config.js +1 -1
- package/dist/_internal/shared/listing-types/digital-code/config.js +1 -1
- package/dist/_internal/shared/listing-types/live/config.js +1 -1
- package/dist/_internal/shared/listing-types/pre-order/config.js +1 -1
- package/dist/client.d.ts +7 -1
- package/dist/client.js +4 -1
- package/dist/constants/api-endpoints.d.ts +6 -0
- package/dist/constants/api-endpoints.js +2 -0
- package/dist/features/account/components/NotificationBell.js +1 -1
- package/dist/features/admin/components/AdminAddressesView.js +18 -10
- package/dist/features/admin/components/AdminArtView.d.ts +9 -0
- package/dist/features/admin/components/AdminArtView.js +11 -35
- package/dist/features/admin/components/AdminClassifiedView.d.ts +9 -0
- package/dist/features/admin/components/AdminClassifiedView.js +11 -35
- package/dist/features/admin/components/AdminDigitalCodesView.d.ts +9 -0
- package/dist/features/admin/components/AdminDigitalCodesView.js +11 -42
- package/dist/features/admin/components/AdminLiveView.d.ts +9 -0
- package/dist/features/admin/components/AdminLiveView.js +11 -41
- package/dist/features/admin/components/AdminNotificationsView.js +4 -1
- package/dist/features/admin/components/AdminOrderEditorView.d.ts +14 -1
- package/dist/features/admin/components/AdminOrderEditorView.js +18 -5
- package/dist/features/admin/components/AdminOrdersView.js +15 -1
- package/dist/features/admin/components/AdminPaymentMethodsView.js +15 -10
- package/dist/features/admin/components/AdminSiteConfigGuideView.js +1 -1
- package/dist/features/admin/components/AdminSiteSettingsView.js +4 -2
- package/dist/features/admin/components/AdminStickersView.d.ts +9 -0
- package/dist/features/admin/components/AdminStickersView.js +11 -35
- package/dist/features/admin/hooks/useAdminListing.js +7 -2
- package/dist/features/admin/schemas/firestore.d.ts +5 -0
- package/dist/features/admin/schemas/firestore.js +1 -0
- package/dist/features/auctions/actions/bid-actions.js +73 -11
- package/dist/features/auctions/components/AuctionDetailPageView.js +8 -2
- package/dist/features/auctions/components/MarketplaceAuctionCard.js +3 -3
- package/dist/features/auctions/components/PlaceBidFormClient.js +89 -14
- package/dist/features/auctions/schemas/bid-input.d.ts +19 -6
- package/dist/features/auctions/schemas/bid-input.js +19 -7
- package/dist/features/auth/repository/user.repository.js +6 -0
- package/dist/features/cart/components/CartDrawer.d.ts +9 -1
- package/dist/features/cart/components/CartDrawer.js +4 -3
- package/dist/features/cart/components/CartPriceBreakdown.d.ts +61 -0
- package/dist/features/cart/components/CartPriceBreakdown.js +25 -0
- package/dist/features/cart/components/StoreAddonsPicker.d.ts +47 -0
- package/dist/features/cart/components/StoreAddonsPicker.js +21 -0
- package/dist/features/cart/components/index.d.ts +4 -0
- package/dist/features/cart/components/index.js +2 -0
- package/dist/features/cart/repository/cart.repository.d.ts +20 -2
- package/dist/features/cart/repository/cart.repository.js +49 -0
- package/dist/features/cart/schemas/firestore.d.ts +26 -0
- package/dist/features/faq/components/FAQHelpfulButtons.js +2 -2
- package/dist/features/filters/FilterFacetSection.js +1 -1
- package/dist/features/homepage/components/WhatsAppCommunitySection.js +1 -1
- package/dist/features/layout/BackToTop.js +1 -1
- package/dist/features/layout/BottomActions.js +38 -5
- package/dist/features/layout/BottomActionsContext.d.ts +12 -0
- package/dist/features/layout/BottomActionsContext.js +5 -0
- package/dist/features/layout/FooterLayout.js +2 -2
- package/dist/features/layout/TitleBarLayout.js +1 -1
- package/dist/features/layout/hooks/useBottomActions.d.ts +6 -0
- package/dist/features/layout/hooks/useBottomActions.js +11 -1
- package/dist/features/orders/components/OrderAddonBadges.d.ts +36 -0
- package/dist/features/orders/components/OrderAddonBadges.js +28 -0
- package/dist/features/orders/components/index.d.ts +1 -0
- package/dist/features/orders/components/index.js +1 -0
- package/dist/features/orders/repository/orders.repository.d.ts +32 -0
- package/dist/features/orders/repository/orders.repository.js +14 -0
- package/dist/features/orders/types/index.d.ts +22 -0
- package/dist/features/pre-orders/components/MarketplacePreorderCard.js +3 -3
- package/dist/features/products/components/ArtStickersListView.js +5 -4
- package/dist/features/products/components/MarketplaceBundleCard.js +2 -2
- package/dist/features/products/components/MarketplacePrizeDrawCard.js +2 -2
- package/dist/features/products/components/PrizeDrawCollage.js +1 -1
- package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
- package/dist/features/products/components/PrizeDrawsListingView.js +18 -33
- package/dist/features/products/components/ProductDetailPageView.js +1 -1
- package/dist/features/products/components/ProductGrid.js +3 -3
- package/dist/features/products/components/ProductsIndexListing.js +87 -11
- package/dist/features/products/components/ProductsIndexPageView.js +14 -2
- package/dist/features/products/config/listing-type-listing-config.d.ts +31 -0
- package/dist/features/products/config/listing-type-listing-config.js +74 -0
- package/dist/features/products/hooks/useProducts.js +14 -0
- package/dist/features/products/repository/products.repository.d.ts +0 -4
- package/dist/features/products/repository/products.repository.js +0 -15
- package/dist/features/products/types/index.d.ts +15 -0
- package/dist/features/promotions/repository/coupons.repository.d.ts +12 -0
- package/dist/features/promotions/repository/coupons.repository.js +12 -0
- package/dist/features/seller/components/SellerOrdersView.js +2 -1
- package/dist/features/seller/components/SellerSidebar.js +1 -1
- package/dist/features/stores/components/StoreArtStickersPageView.d.ts +21 -0
- package/dist/features/stores/components/StoreArtStickersPageView.js +30 -0
- package/dist/features/stores/components/StoreAuctionsPageView.d.ts +4 -1
- package/dist/features/stores/components/StoreAuctionsPageView.js +17 -17
- package/dist/features/stores/components/StoreClassifiedsListing.js +7 -0
- package/dist/features/stores/components/StoreClassifiedsPageView.d.ts +4 -1
- package/dist/features/stores/components/StoreClassifiedsPageView.js +16 -12
- package/dist/features/stores/components/StoreDetailLayoutView.js +4 -3
- package/dist/features/stores/components/StoreDigitalCodesListing.js +5 -0
- package/dist/features/stores/components/StoreDigitalCodesPageView.d.ts +4 -1
- package/dist/features/stores/components/StoreDigitalCodesPageView.js +16 -12
- package/dist/features/stores/components/StoreLiveItemsListing.js +7 -0
- package/dist/features/stores/components/StoreLiveItemsPageView.d.ts +4 -1
- package/dist/features/stores/components/StoreLiveItemsPageView.js +16 -12
- package/dist/features/stores/components/StorePreOrdersPageView.d.ts +4 -1
- package/dist/features/stores/components/StorePreOrdersPageView.js +17 -17
- package/dist/features/stores/components/StorePrizeDrawsPageView.d.ts +4 -8
- package/dist/features/stores/components/StorePrizeDrawsPageView.js +17 -22
- package/dist/features/stores/components/StoreProductsListing.d.ts +7 -1
- package/dist/features/stores/components/StoreProductsListing.js +3 -2
- package/dist/features/stores/components/StoreProductsPageView.d.ts +1 -1
- package/dist/features/stores/components/StoreProductsPageView.js +16 -21
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +875 -0
- package/dist/features/whatsapp-bot/components/WhatsAppChatButton.js +1 -1
- package/dist/jobs.d.ts +1 -1
- package/dist/jobs.js +1 -1
- package/dist/next/routing/route-map.d.ts +2 -0
- package/dist/next/routing/route-map.js +5 -0
- package/dist/seed/homepage-sections-seed-data.js +5 -1
- package/dist/seed/reviews-seed-data.js +52 -0
- package/dist/seed/site-settings-seed-data.js +18 -0
- package/dist/server-entry.d.ts +1 -0
- package/dist/server-entry.js +1 -0
- package/dist/styles.css +66 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/tokens/color-pairs.js +1 -0
- package/dist/tokens/tokens.css +33 -0
- package/dist/ui/components/Button.style.css +21 -0
- package/dist/ui/components/Checkbox.d.ts +10 -1
- package/dist/ui/components/Checkbox.js +14 -2
- package/dist/ui/components/Checkbox.style.css +11 -0
- package/dist/ui/components/ImageLightbox.js +7 -4
- package/dist/ui/components/surface-tokens.d.ts +7 -0
- package/dist/ui/components/surface-tokens.js +7 -0
- package/package.json +1 -1
- package/dist/_internal/server/jobs/core/onBidPlaced.d.ts +0 -15
- package/dist/_internal/server/jobs/core/onBidPlaced.js +0 -63
- package/dist/_internal/server/jobs/handlers/onBidPlaced.d.ts +0 -3
- package/dist/_internal/server/jobs/handlers/onBidPlaced.js +0 -9
package/dist/client.d.ts
CHANGED
|
@@ -207,7 +207,9 @@ export { ProtectedRoute, AuthStatusPanel, ForgotPasswordView, LoginForm, Registe
|
|
|
207
207
|
export type { AuthGuardUser, ForgotPasswordViewProps, LoginFormProps, LoginFormValues, RegisterFormProps, RegisterFormValues, ResetPasswordViewProps, VerifyEmailViewProps, } from "./features/auth/index";
|
|
208
208
|
export { useLogout, useLogin, useGoogleLogin, useLinkGoogleAccount, useRegister, useForgotPassword, useResetPassword, useVerifyEmail, useChangeEmail } from "./features/auth/index";
|
|
209
209
|
export type { LoginCredentials, RegisterData, ForgotPasswordData, ResetPasswordData, VerifyEmailData, ChangeEmailData } from "./features/auth/index";
|
|
210
|
-
export { CartView, CartItemRow, CartSummary, CartDrawer, CheckoutView, CheckoutSuccessView, CheckoutAddressStep, useGuestCart, useCartCount, useAddToCart, useCart, useGuestCartMerge, useCartQuery } from "./features/cart/index";
|
|
210
|
+
export { CartView, CartItemRow, CartSummary, CartPriceBreakdown, CartDrawer, CheckoutView, CheckoutSuccessView, CheckoutAddressStep, useGuestCart, useCartCount, useAddToCart, useCart, useGuestCartMerge, useCartQuery } from "./features/cart/index";
|
|
211
|
+
export { StoreAddonsPicker } from "./features/cart/index";
|
|
212
|
+
export type { CartPriceBreakdownData, CartPriceBreakdownStore, StoreAddonsValue, StoreAddonsRates } from "./features/cart/index";
|
|
211
213
|
export { getCartOps, CART_OPS_CHANGE_EVENT } from "./features/cart/utils/pending-ops";
|
|
212
214
|
export type { CartOp } from "./features/cart/utils/pending-ops";
|
|
213
215
|
export type { CartItem, CartItemMeta, CartData, GuestCartItem } from "./features/cart/index";
|
|
@@ -300,6 +302,10 @@ export type { UserAccountHubViewProps, UserAccountHubViewLabels, UserOrdersViewP
|
|
|
300
302
|
export { useOrders, useOrder, OrdersList } from "./features/orders/index";
|
|
301
303
|
export { OrderStatusTimeline } from "./features/orders/components/OrderStatusTimeline";
|
|
302
304
|
export type { OrderStatusTimelineProps } from "./features/orders/components/OrderStatusTimeline";
|
|
305
|
+
export { CountdownDisplay } from "./ui/components/CountdownDisplay";
|
|
306
|
+
export type { CountdownDisplayProps } from "./ui/components/CountdownDisplay";
|
|
307
|
+
export { OrderAddonBadges } from "./features/orders/components/OrderAddonBadges";
|
|
308
|
+
export type { OrderAddonBadgesOrder, OrderAddonBadgesProps } from "./features/orders/components/OrderAddonBadges";
|
|
303
309
|
export { MANUAL_PAYMENT_METHODS, isManualPaymentMethod, PAYMENT_REVIEW_QUEUE_MODES, isPaymentReviewQueueMode, PAYMENT_WINDOW_MINUTES, } from "./features/orders/constants/payment-window";
|
|
304
310
|
export type { PaymentReviewQueueMode } from "./features/orders/constants/payment-window";
|
|
305
311
|
export { UserOrderTrackView } from "./features/account/components/UserOrderTrackView";
|
package/dist/client.js
CHANGED
|
@@ -238,7 +238,8 @@ export { PAGE_VIEW_ENTITY_TYPES } from "./features/analytics/types";
|
|
|
238
238
|
export { AuctionBidsTable } from "./features/auctions/components/AuctionBidsTable";
|
|
239
239
|
export { ProtectedRoute, AuthStatusPanel, ForgotPasswordView, LoginForm, RegisterForm, ResetPasswordView, VerifyEmailView } from "./features/auth/index";
|
|
240
240
|
export { useLogout, useLogin, useGoogleLogin, useLinkGoogleAccount, useRegister, useForgotPassword, useResetPassword, useVerifyEmail, useChangeEmail } from "./features/auth/index";
|
|
241
|
-
export { CartView, CartItemRow, CartSummary, CartDrawer, CheckoutView, CheckoutSuccessView, CheckoutAddressStep, useGuestCart, useCartCount, useAddToCart, useCart, useGuestCartMerge, useCartQuery } from "./features/cart/index";
|
|
241
|
+
export { CartView, CartItemRow, CartSummary, CartPriceBreakdown, CartDrawer, CheckoutView, CheckoutSuccessView, CheckoutAddressStep, useGuestCart, useCartCount, useAddToCart, useCart, useGuestCartMerge, useCartQuery } from "./features/cart/index";
|
|
242
|
+
export { StoreAddonsPicker } from "./features/cart/index";
|
|
242
243
|
export { getCartOps, CART_OPS_CHANGE_EVENT } from "./features/cart/utils/pending-ops";
|
|
243
244
|
export { useAddresses, useCreateAddress, useUpdateAddress, useDeleteAddress, useSetDefaultAddress, useAddress } from "./features/account/index";
|
|
244
245
|
export { AddressBook, AddressCard, AddressForm } from "./features/account/index";
|
|
@@ -301,6 +302,8 @@ export { SellerPayoutSettingsView, SellerShippingView, SellerReviewsView, Seller
|
|
|
301
302
|
export { UserAccountHubView, UserOrdersView, UserBidsView, OrderDetailView, UserNotificationsView, UserReturnsView, UserSupportView, useNotifications } from "./features/account/index";
|
|
302
303
|
export { useOrders, useOrder, OrdersList } from "./features/orders/index";
|
|
303
304
|
export { OrderStatusTimeline } from "./features/orders/components/OrderStatusTimeline";
|
|
305
|
+
export { CountdownDisplay } from "./ui/components/CountdownDisplay";
|
|
306
|
+
export { OrderAddonBadges } from "./features/orders/components/OrderAddonBadges";
|
|
304
307
|
// Manual-payment (cash / UPI / EMI) shared constants — pure data + predicates,
|
|
305
308
|
// no server deps. Used by the buyer proof-upload page and order-detail CTAs.
|
|
306
309
|
export { MANUAL_PAYMENT_METHODS, isManualPaymentMethod, PAYMENT_REVIEW_QUEUE_MODES, isPaymentReviewQueueMode, PAYMENT_WINDOW_MINUTES, } from "./features/orders/constants/payment-window";
|
|
@@ -234,6 +234,8 @@ export declare const CART_ENDPOINTS: {
|
|
|
234
234
|
readonly BY_ITEM_ID: (itemId: string) => string;
|
|
235
235
|
readonly VALIDATE: "/api/cart/validate";
|
|
236
236
|
readonly SELECTION: "/api/cart/selection";
|
|
237
|
+
/** Per-store paid add-on selections — the source of truth for what's charged. */
|
|
238
|
+
readonly ADDONS: "/api/cart/addons";
|
|
237
239
|
readonly COUPON: "/api/cart/coupon";
|
|
238
240
|
};
|
|
239
241
|
export declare const CATEGORY_ENDPOINTS: {
|
|
@@ -686,6 +688,8 @@ export declare const API_ENDPOINTS: {
|
|
|
686
688
|
readonly BY_ITEM_ID: (itemId: string) => string;
|
|
687
689
|
readonly VALIDATE: "/api/cart/validate";
|
|
688
690
|
readonly SELECTION: "/api/cart/selection";
|
|
691
|
+
/** Per-store paid add-on selections — the source of truth for what's charged. */
|
|
692
|
+
readonly ADDONS: "/api/cart/addons";
|
|
689
693
|
readonly COUPON: "/api/cart/coupon";
|
|
690
694
|
};
|
|
691
695
|
readonly CATEGORIES: {
|
|
@@ -1140,6 +1144,8 @@ export declare const API_ROUTES: {
|
|
|
1140
1144
|
readonly BY_ITEM_ID: (itemId: string) => string;
|
|
1141
1145
|
readonly VALIDATE: "/api/cart/validate";
|
|
1142
1146
|
readonly SELECTION: "/api/cart/selection";
|
|
1147
|
+
/** Per-store paid add-on selections — the source of truth for what's charged. */
|
|
1148
|
+
readonly ADDONS: "/api/cart/addons";
|
|
1143
1149
|
readonly COUPON: "/api/cart/coupon";
|
|
1144
1150
|
};
|
|
1145
1151
|
readonly CATEGORIES: {
|
|
@@ -274,6 +274,8 @@ export const CART_ENDPOINTS = {
|
|
|
274
274
|
BY_ITEM_ID: (itemId) => `/api/cart/${encodeURIComponent(itemId)}`,
|
|
275
275
|
VALIDATE: "/api/cart/validate",
|
|
276
276
|
SELECTION: "/api/cart/selection",
|
|
277
|
+
/** Per-store paid add-on selections — the source of truth for what's charged. */
|
|
278
|
+
ADDONS: "/api/cart/addons",
|
|
277
279
|
COUPON: "/api/cart/coupon",
|
|
278
280
|
};
|
|
279
281
|
// ---------------------------------------------------------------------------
|
|
@@ -10,7 +10,7 @@ const __O = {
|
|
|
10
10
|
hidden: "overflow-hidden",
|
|
11
11
|
yAuto: "overflow-y-auto",
|
|
12
12
|
};
|
|
13
|
-
const CLS_UNREAD_BADGE = "absolute -top-1 -right-1 bg-error-
|
|
13
|
+
const CLS_UNREAD_BADGE = "absolute -top-1 -right-1 bg-error-solid text-error-on-solid min-w-[20px] h-5 px-[var(--appkit-space-1-5)] flex items-center justify-center rounded-full shadow-md";
|
|
14
14
|
const CLS_UNREAD_PILL = "ml-2 bg-error-surface text-error dark:bg-error-surface dark:text-error px-[var(--appkit-space-2)] py-[var(--appkit-space-0-5)] rounded-full";
|
|
15
15
|
const DEFAULT_ICONS = {
|
|
16
16
|
order_placed: "🛍️",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { sortBy } from "@mohasinac/appkit/client";
|
|
4
|
-
import {
|
|
4
|
+
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
5
5
|
import { useQueryClient } from "@tanstack/react-query";
|
|
6
6
|
import { Badge, Div, FilterChipGroup, Span, Stack, Text, useToast } from "../../../ui";
|
|
7
7
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
@@ -47,18 +47,22 @@ const ADDRESS_COLUMNS = [
|
|
|
47
47
|
render: (row) => _jsx(Span, { size: "sm", color: "muted", children: row.updatedAt }),
|
|
48
48
|
},
|
|
49
49
|
];
|
|
50
|
-
function buildAddressConfig(
|
|
50
|
+
function buildAddressConfig(onAction) {
|
|
51
51
|
return {
|
|
52
52
|
portal: "admin",
|
|
53
53
|
title: "Address Management",
|
|
54
54
|
searchPlaceholder: "Search by city or owner",
|
|
55
55
|
emptyLabel: "No addresses found",
|
|
56
|
-
filterKeys
|
|
56
|
+
// URL-synced via filterKeys (2026-08-21). The ban-status chips used to
|
|
57
|
+
// live outside this config in local useState, so the selection was not in
|
|
58
|
+
// the URL, not shareable, not counted by activeFilterCount, and lost on
|
|
59
|
+
// back-navigation. banStatus is a plain query param rather than a Sieve
|
|
60
|
+
// clause, which is exactly what buildExtraParams is for.
|
|
61
|
+
filterKeys: ["banStatus"],
|
|
57
62
|
defaultSort: sortBy("bannedAt", "DESC"),
|
|
58
|
-
queryKey: ["admin", "addresses"
|
|
59
|
-
endpoint:
|
|
60
|
-
|
|
61
|
-
: ADMIN_ENDPOINTS.ADDRESSES,
|
|
63
|
+
queryKey: ["admin", "addresses"],
|
|
64
|
+
endpoint: ADMIN_ENDPOINTS.ADDRESSES,
|
|
65
|
+
buildExtraParams: (state) => state.banStatus ? { banStatus: state.banStatus } : undefined,
|
|
62
66
|
sortOptions: [
|
|
63
67
|
// -bannedAt (newest flagged first) to match defaultSort — the option was
|
|
64
68
|
// "bannedAt" (oldest first), so the dropdown opened with nothing selected.
|
|
@@ -121,7 +125,11 @@ function buildAddressConfig(banStatus, onAction) {
|
|
|
121
125
|
};
|
|
122
126
|
}
|
|
123
127
|
export function AdminAddressesView(_props) {
|
|
124
|
-
|
|
128
|
+
// Independent useUrlTable() against the same URL param DataListingView's own
|
|
129
|
+
// table reads via filterKeys — safe because useUrlTable holds no local state
|
|
130
|
+
// (the documented Root Cause #35 pattern).
|
|
131
|
+
const chipTable = useUrlTable({ defaults: {} });
|
|
132
|
+
const banStatus = chipTable.get("banStatus");
|
|
125
133
|
const queryClient = useQueryClient();
|
|
126
134
|
const { showToast } = useToast();
|
|
127
135
|
const actionMutation = useApiMutation({
|
|
@@ -139,6 +147,6 @@ export function AdminAddressesView(_props) {
|
|
|
139
147
|
const handleAction = (id, action) => {
|
|
140
148
|
actionMutation.mutate({ id, action });
|
|
141
149
|
};
|
|
142
|
-
const config = buildAddressConfig(
|
|
143
|
-
return (_jsxs(Stack, { gap: "md", children: [_jsx(Div, { padding: "inline", border: "default", className: "border-b", children: _jsx(FilterChipGroup, { label: "Status", tabs: BAN_STATUS_TABS, value: banStatus, onChange: (v) =>
|
|
150
|
+
const config = buildAddressConfig(handleAction);
|
|
151
|
+
return (_jsxs(Stack, { gap: "md", children: [_jsx(Div, { padding: "inline", border: "default", className: "border-b", children: _jsx(FilterChipGroup, { label: "Status", tabs: BAN_STATUS_TABS, value: banStatus, onChange: (v) => chipTable.set("banStatus", v) }) }), _jsx(DataListingView, { config: config })] }));
|
|
144
152
|
}
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AdminArtView — admin browse of art listings.
|
|
3
|
+
*
|
|
4
|
+
* A thin wrapper over the shared per-listing-type config. This file (and its
|
|
5
|
+
* four siblings) used to carry a full hand-written ListingViewConfig; all five
|
|
6
|
+
* were the same config with a different listingType, and all five shipped
|
|
7
|
+
* an empty filterKeys array — no filter drawer at all — plus their own copy
|
|
8
|
+
* of a three-option sort array. See listing-type-listing-config.ts.
|
|
9
|
+
*/
|
|
1
10
|
import React from "react";
|
|
2
11
|
import type { ListingLayoutProps } from "../../../ui";
|
|
3
12
|
export type AdminArtViewProps = ListingLayoutProps;
|
|
@@ -1,50 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
/**
|
|
4
|
-
* AdminArtView — admin browse of art
|
|
5
|
-
*
|
|
4
|
+
* AdminArtView — admin browse of art listings.
|
|
5
|
+
*
|
|
6
|
+
* A thin wrapper over the shared per-listing-type config. This file (and its
|
|
7
|
+
* four siblings) used to carry a full hand-written ListingViewConfig; all five
|
|
8
|
+
* were the same config with a different listingType, and all five shipped
|
|
9
|
+
* an empty filterKeys array — no filter drawer at all — plus their own copy
|
|
10
|
+
* of a three-option sort array. See listing-type-listing-config.ts.
|
|
6
11
|
*/
|
|
7
|
-
import { sortBy } from "@mohasinac/appkit/client";
|
|
8
12
|
import React from "react";
|
|
9
13
|
import { ListingLayout } from "../../../ui";
|
|
10
|
-
import {
|
|
11
|
-
import { toRecordArray, toRelativeDate, toCurrency, toStringValue, } from "../hooks/useAdminListingData";
|
|
14
|
+
import { buildListingTypeListingConfig } from "../../products/config/listing-type-listing-config";
|
|
12
15
|
import { DataListingView } from "./DataListingView";
|
|
13
|
-
|
|
14
|
-
const ADMIN_ART_CONFIG = {
|
|
15
|
-
portal: "admin",
|
|
16
|
+
const CONFIG = buildListingTypeListingConfig("art", {
|
|
16
17
|
title: "Art",
|
|
17
18
|
searchPlaceholder: "Search art prints by name or seller",
|
|
18
19
|
emptyLabel: "No art listings",
|
|
19
|
-
|
|
20
|
-
defaultSort: sortBy("createdAt", "DESC"),
|
|
21
|
-
queryKey: ["admin", "art", "listing"],
|
|
22
|
-
endpoint: ADMIN_ENDPOINTS.PRODUCTS,
|
|
23
|
-
sortOptions: [
|
|
24
|
-
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
25
|
-
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
26
|
-
{ value: "title", label: "Title A–Z" },
|
|
27
|
-
],
|
|
28
|
-
mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
|
|
29
|
-
id: toStringValue(item.id, `art-${index}`),
|
|
30
|
-
primary: toStringValue(item.title ?? item.productTitle, "Untitled print"),
|
|
31
|
-
secondary: [
|
|
32
|
-
toStringValue(item.sellerName, "Unknown seller"),
|
|
33
|
-
toCurrency(item.price),
|
|
34
|
-
].join(" · "),
|
|
35
|
-
status: toStringValue(item.status, "draft"),
|
|
36
|
-
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
37
|
-
image: toStringValue(item.mainImage, "") || undefined,
|
|
38
|
-
})),
|
|
39
|
-
getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
|
|
40
|
-
buildFilters: () => "listingType==art",
|
|
41
|
-
// Art listings are products (filtered by listingType) — reuse the real
|
|
42
|
-
// admin product edit page rather than leaving rows non-navigable.
|
|
43
|
-
rowHrefTemplate: String(ROUTES.ADMIN.PRODUCTS_EDIT("{id}")),
|
|
44
|
-
};
|
|
20
|
+
});
|
|
45
21
|
export function AdminArtView({ children, ...props }) {
|
|
46
22
|
if (React.Children.count(children) > 0) {
|
|
47
23
|
return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
|
|
48
24
|
}
|
|
49
|
-
return _jsx(DataListingView, { config:
|
|
25
|
+
return _jsx(DataListingView, { config: CONFIG });
|
|
50
26
|
}
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AdminClassifiedView — admin browse of classified listings.
|
|
3
|
+
*
|
|
4
|
+
* A thin wrapper over the shared per-listing-type config. This file (and its
|
|
5
|
+
* four siblings) used to carry a full hand-written ListingViewConfig; all five
|
|
6
|
+
* were the same config with a different listingType, and all five shipped
|
|
7
|
+
* an empty filterKeys array — no filter drawer at all — plus their own copy
|
|
8
|
+
* of a three-option sort array. See listing-type-listing-config.ts.
|
|
9
|
+
*/
|
|
1
10
|
import React from "react";
|
|
2
11
|
import type { ListingLayoutProps } from "../../../ui";
|
|
3
12
|
export type AdminClassifiedViewProps = ListingLayoutProps;
|
|
@@ -1,50 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
/**
|
|
4
|
-
* AdminClassifiedView — admin browse of classified listings
|
|
5
|
-
*
|
|
4
|
+
* AdminClassifiedView — admin browse of classified listings.
|
|
5
|
+
*
|
|
6
|
+
* A thin wrapper over the shared per-listing-type config. This file (and its
|
|
7
|
+
* four siblings) used to carry a full hand-written ListingViewConfig; all five
|
|
8
|
+
* were the same config with a different listingType, and all five shipped
|
|
9
|
+
* an empty filterKeys array — no filter drawer at all — plus their own copy
|
|
10
|
+
* of a three-option sort array. See listing-type-listing-config.ts.
|
|
6
11
|
*/
|
|
7
|
-
import { sortBy } from "@mohasinac/appkit/client";
|
|
8
12
|
import React from "react";
|
|
9
13
|
import { ListingLayout } from "../../../ui";
|
|
10
|
-
import {
|
|
11
|
-
import { toRecordArray, toRelativeDate, toCurrency, toStringValue, } from "../hooks/useAdminListingData";
|
|
14
|
+
import { buildListingTypeListingConfig } from "../../products/config/listing-type-listing-config";
|
|
12
15
|
import { DataListingView } from "./DataListingView";
|
|
13
|
-
|
|
14
|
-
const ADMIN_CLASSIFIED_CONFIG = {
|
|
15
|
-
portal: "admin",
|
|
16
|
+
const CONFIG = buildListingTypeListingConfig("classified", {
|
|
16
17
|
title: "Classified",
|
|
17
18
|
searchPlaceholder: "Search classified by name or seller",
|
|
18
19
|
emptyLabel: "No classified listings",
|
|
19
|
-
|
|
20
|
-
defaultSort: sortBy("createdAt", "DESC"),
|
|
21
|
-
queryKey: ["admin", "classified", "listing"],
|
|
22
|
-
endpoint: ADMIN_ENDPOINTS.PRODUCTS,
|
|
23
|
-
sortOptions: [
|
|
24
|
-
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
25
|
-
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
26
|
-
{ value: "title", label: "Title A–Z" },
|
|
27
|
-
],
|
|
28
|
-
mapRows: (response) => toRecordArray(response.items).map((item, index) => ({
|
|
29
|
-
id: toStringValue(item.id, `classified-${index}`),
|
|
30
|
-
primary: toStringValue(item.title ?? item.productTitle, "Untitled classified"),
|
|
31
|
-
secondary: [
|
|
32
|
-
toStringValue(item.sellerName, "Unknown seller"),
|
|
33
|
-
toCurrency(item.price),
|
|
34
|
-
].join(" · "),
|
|
35
|
-
status: toStringValue(item.status, "draft"),
|
|
36
|
-
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
37
|
-
image: toStringValue(item.mainImage, "") || undefined,
|
|
38
|
-
})),
|
|
39
|
-
getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
|
|
40
|
-
buildFilters: () => "listingType==classified",
|
|
41
|
-
// Classified listings are products (filtered by listingType) — reuse the
|
|
42
|
-
// real admin product edit page rather than leaving rows non-navigable.
|
|
43
|
-
rowHrefTemplate: String(ROUTES.ADMIN.PRODUCTS_EDIT("{id}")),
|
|
44
|
-
};
|
|
20
|
+
});
|
|
45
21
|
export function AdminClassifiedView({ children, ...props }) {
|
|
46
22
|
if (React.Children.count(children) > 0) {
|
|
47
23
|
return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
|
|
48
24
|
}
|
|
49
|
-
return _jsx(DataListingView, { config:
|
|
25
|
+
return _jsx(DataListingView, { config: CONFIG });
|
|
50
26
|
}
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AdminDigitalCodesView — admin browse of digital codes listings.
|
|
3
|
+
*
|
|
4
|
+
* A thin wrapper over the shared per-listing-type config. This file (and its
|
|
5
|
+
* four siblings) used to carry a full hand-written ListingViewConfig; all five
|
|
6
|
+
* were the same config with a different listingType, and all five shipped
|
|
7
|
+
* an empty filterKeys array — no filter drawer at all — plus their own copy
|
|
8
|
+
* of a three-option sort array. See listing-type-listing-config.ts.
|
|
9
|
+
*/
|
|
1
10
|
import React from "react";
|
|
2
11
|
import type { ListingLayoutProps } from "../../../ui";
|
|
3
12
|
export type AdminDigitalCodesViewProps = ListingLayoutProps;
|
|
@@ -1,57 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
/**
|
|
4
|
-
* AdminDigitalCodesView — admin browse of digital
|
|
4
|
+
* AdminDigitalCodesView — admin browse of digital codes listings.
|
|
5
|
+
*
|
|
6
|
+
* A thin wrapper over the shared per-listing-type config. This file (and its
|
|
7
|
+
* four siblings) used to carry a full hand-written ListingViewConfig; all five
|
|
8
|
+
* were the same config with a different listingType, and all five shipped
|
|
9
|
+
* an empty filterKeys array — no filter drawer at all — plus their own copy
|
|
10
|
+
* of a three-option sort array. See listing-type-listing-config.ts.
|
|
5
11
|
*/
|
|
6
|
-
import { sortBy } from "@mohasinac/appkit/client";
|
|
7
12
|
import React from "react";
|
|
8
13
|
import { ListingLayout } from "../../../ui";
|
|
9
|
-
import {
|
|
10
|
-
import { toRecordArray, toRelativeDate, toCurrency, toStringValue, } from "../hooks/useAdminListingData";
|
|
14
|
+
import { buildListingTypeListingConfig } from "../../products/config/listing-type-listing-config";
|
|
11
15
|
import { DataListingView } from "./DataListingView";
|
|
12
|
-
|
|
13
|
-
const ADMIN_DIGITAL_CODES_CONFIG = {
|
|
14
|
-
portal: "admin",
|
|
16
|
+
const CONFIG = buildListingTypeListingConfig("digital-code", {
|
|
15
17
|
title: "Digital Codes",
|
|
16
18
|
searchPlaceholder: "Search digital code listings",
|
|
17
19
|
emptyLabel: "No digital code listings",
|
|
18
|
-
|
|
19
|
-
defaultSort: sortBy("createdAt", "DESC"),
|
|
20
|
-
queryKey: ["admin", "digital-codes", "listing"],
|
|
21
|
-
endpoint: ADMIN_ENDPOINTS.PRODUCTS,
|
|
22
|
-
sortOptions: [
|
|
23
|
-
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
24
|
-
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
25
|
-
{ value: "title", label: "Title A–Z" },
|
|
26
|
-
],
|
|
27
|
-
mapRows: (response) => toRecordArray(response.items).map((item, index) => {
|
|
28
|
-
const dc = (item.digitalCode ?? {});
|
|
29
|
-
return {
|
|
30
|
-
id: toStringValue(item.id, `dc-${index}`),
|
|
31
|
-
primary: toStringValue(item.title ?? item.productTitle, "Untitled digital code"),
|
|
32
|
-
secondary: [
|
|
33
|
-
toStringValue(item.sellerName, "Unknown seller"),
|
|
34
|
-
toCurrency(item.price),
|
|
35
|
-
typeof dc.codesAvailable === "number"
|
|
36
|
-
? `${dc.codesAvailable} avail`
|
|
37
|
-
: null,
|
|
38
|
-
]
|
|
39
|
-
.filter(Boolean)
|
|
40
|
-
.join(" · "),
|
|
41
|
-
status: toStringValue(item.status, "draft"),
|
|
42
|
-
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
43
|
-
image: toStringValue(item.mainImage, "") || undefined,
|
|
44
|
-
};
|
|
45
|
-
}),
|
|
46
|
-
getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
|
|
47
|
-
buildFilters: () => "listingType==digital-code",
|
|
48
|
-
// Digital-code listings are products (filtered by listingType) — reuse
|
|
49
|
-
// the real admin product edit page rather than leaving rows non-navigable.
|
|
50
|
-
rowHrefTemplate: String(ROUTES.ADMIN.PRODUCTS_EDIT("{id}")),
|
|
51
|
-
};
|
|
20
|
+
});
|
|
52
21
|
export function AdminDigitalCodesView({ children, ...props }) {
|
|
53
22
|
if (React.Children.count(children) > 0) {
|
|
54
23
|
return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
|
|
55
24
|
}
|
|
56
|
-
return _jsx(DataListingView, { config:
|
|
25
|
+
return _jsx(DataListingView, { config: CONFIG });
|
|
57
26
|
}
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AdminLiveView — admin browse of live items listings.
|
|
3
|
+
*
|
|
4
|
+
* A thin wrapper over the shared per-listing-type config. This file (and its
|
|
5
|
+
* four siblings) used to carry a full hand-written ListingViewConfig; all five
|
|
6
|
+
* were the same config with a different listingType, and all five shipped
|
|
7
|
+
* an empty filterKeys array — no filter drawer at all — plus their own copy
|
|
8
|
+
* of a three-option sort array. See listing-type-listing-config.ts.
|
|
9
|
+
*/
|
|
1
10
|
import React from "react";
|
|
2
11
|
import type { ListingLayoutProps } from "../../../ui";
|
|
3
12
|
export type AdminLiveViewProps = ListingLayoutProps;
|
|
@@ -1,56 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
/**
|
|
4
|
-
* AdminLiveView — admin browse of live
|
|
4
|
+
* AdminLiveView — admin browse of live items listings.
|
|
5
|
+
*
|
|
6
|
+
* A thin wrapper over the shared per-listing-type config. This file (and its
|
|
7
|
+
* four siblings) used to carry a full hand-written ListingViewConfig; all five
|
|
8
|
+
* were the same config with a different listingType, and all five shipped
|
|
9
|
+
* an empty filterKeys array — no filter drawer at all — plus their own copy
|
|
10
|
+
* of a three-option sort array. See listing-type-listing-config.ts.
|
|
5
11
|
*/
|
|
6
|
-
import { sortBy } from "@mohasinac/appkit/client";
|
|
7
12
|
import React from "react";
|
|
8
13
|
import { ListingLayout } from "../../../ui";
|
|
9
|
-
import {
|
|
10
|
-
import { toRecordArray, toRelativeDate, toCurrency, toStringValue, } from "../hooks/useAdminListingData";
|
|
14
|
+
import { buildListingTypeListingConfig } from "../../products/config/listing-type-listing-config";
|
|
11
15
|
import { DataListingView } from "./DataListingView";
|
|
12
|
-
|
|
13
|
-
const ADMIN_LIVE_CONFIG = {
|
|
14
|
-
portal: "admin",
|
|
16
|
+
const CONFIG = buildListingTypeListingConfig("live", {
|
|
15
17
|
title: "Live Items",
|
|
16
18
|
searchPlaceholder: "Search live items",
|
|
17
19
|
emptyLabel: "No live item listings",
|
|
18
|
-
|
|
19
|
-
defaultSort: sortBy("createdAt", "DESC"),
|
|
20
|
-
queryKey: ["admin", "live-items", "listing"],
|
|
21
|
-
endpoint: ADMIN_ENDPOINTS.PRODUCTS,
|
|
22
|
-
sortOptions: [
|
|
23
|
-
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
24
|
-
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
25
|
-
{ value: "title", label: "Title A–Z" },
|
|
26
|
-
],
|
|
27
|
-
mapRows: (response) => toRecordArray(response.items).map((item, index) => {
|
|
28
|
-
const live = (item.liveItem ?? {});
|
|
29
|
-
return {
|
|
30
|
-
id: toStringValue(item.id, `live-${index}`),
|
|
31
|
-
primary: toStringValue(item.title ?? item.productTitle, "Untitled live item"),
|
|
32
|
-
secondary: [
|
|
33
|
-
toStringValue(item.sellerName, "Unknown seller"),
|
|
34
|
-
toCurrency(item.price),
|
|
35
|
-
toStringValue(live.species, ""),
|
|
36
|
-
Boolean(live.vendorVerified) ? "verified" : "pending",
|
|
37
|
-
]
|
|
38
|
-
.filter(Boolean)
|
|
39
|
-
.join(" · "),
|
|
40
|
-
status: toStringValue(item.status, "draft"),
|
|
41
|
-
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
42
|
-
image: toStringValue(item.mainImage, "") || undefined,
|
|
43
|
-
};
|
|
44
|
-
}),
|
|
45
|
-
getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
|
|
46
|
-
buildFilters: () => "listingType==live",
|
|
47
|
-
// Live-item listings are products (filtered by listingType) — reuse the
|
|
48
|
-
// real admin product edit page rather than leaving rows non-navigable.
|
|
49
|
-
rowHrefTemplate: String(ROUTES.ADMIN.PRODUCTS_EDIT("{id}")),
|
|
50
|
-
};
|
|
20
|
+
});
|
|
51
21
|
export function AdminLiveView({ children, ...props }) {
|
|
52
22
|
if (React.Children.count(children) > 0) {
|
|
53
23
|
return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
|
|
54
24
|
}
|
|
55
|
-
return _jsx(DataListingView, { config:
|
|
25
|
+
return _jsx(DataListingView, { config: CONFIG });
|
|
56
26
|
}
|
|
@@ -82,7 +82,10 @@ export function AdminNotificationsView({ children, ...props }) {
|
|
|
82
82
|
const config = {
|
|
83
83
|
portal: "admin",
|
|
84
84
|
title: "Notifications",
|
|
85
|
-
|
|
85
|
+
// Title search is not expressible here — `title` is not in the repository's
|
|
86
|
+
// SIEVE_FIELDS and Sieve→Firestore cannot OR across two fields. The route
|
|
87
|
+
// treats `q` as an exact userId lookup, so the placeholder says exactly that.
|
|
88
|
+
searchPlaceholder: "Search by user ID",
|
|
86
89
|
emptyLabel: "No notifications found",
|
|
87
90
|
filterKeys: ["type", "readState"],
|
|
88
91
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type OrderAddonBadgesOrder } from "../../orders/components/OrderAddonBadges";
|
|
1
2
|
import React from "react";
|
|
2
3
|
export interface AdminOrderItemRow {
|
|
3
4
|
productId: string;
|
|
@@ -23,5 +24,17 @@ export interface AdminOrderEditorViewProps {
|
|
|
23
24
|
paymentUpiMismatch?: boolean;
|
|
24
25
|
buyerMarkedPaid?: boolean;
|
|
25
26
|
buyerFraudAgreementAccepted?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Which way a prior review went, if any. Without this the panel cannot tell
|
|
29
|
+
* "awaiting verification" from "already rejected" and re-offers live
|
|
30
|
+
* Verify/Reject buttons on a decided order.
|
|
31
|
+
*/
|
|
32
|
+
paymentReviewOutcome?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Paid add-ons + applied coupon for this order. Operational, not decorative:
|
|
35
|
+
* `whatsappNotifyAddon` is who a status change should message, and the
|
|
36
|
+
* coupon is what an admin needs when reconciling a discounted order.
|
|
37
|
+
*/
|
|
38
|
+
addons?: OrderAddonBadgesOrder;
|
|
26
39
|
}
|
|
27
|
-
export declare function AdminOrderEditorView({ open, onClose, orderId, orderLabel, currentStatus, items, paymentProofUrl, paymentTransactionId, paymentMethod, paymentStatus, displayedUpiId, buyerReportedUpiId, paymentUpiMismatch, buyerMarkedPaid, buyerFraudAgreementAccepted, }: AdminOrderEditorViewProps): React.JSX.Element;
|
|
40
|
+
export declare function AdminOrderEditorView({ open, onClose, orderId, orderLabel, currentStatus, items, paymentProofUrl, paymentTransactionId, paymentMethod, paymentStatus, displayedUpiId, buyerReportedUpiId, paymentUpiMismatch, buyerMarkedPaid, buyerFraudAgreementAccepted, paymentReviewOutcome, addons, }: AdminOrderEditorViewProps): React.JSX.Element;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { OrderAddonBadges } from "../../orders/components/OrderAddonBadges";
|
|
4
|
+
import { isManualPaymentMethod } from "../../orders/constants/payment-window";
|
|
3
5
|
import { useApiMutation } from "@mohasinac/appkit/client";
|
|
4
6
|
import React, { useState } from "react";
|
|
5
7
|
import { normalizeError } from "../../../errors/normalize";
|
|
@@ -29,7 +31,7 @@ const CARRIER_OPTIONS = [
|
|
|
29
31
|
{ label: "Other", value: "Other" },
|
|
30
32
|
];
|
|
31
33
|
// --- Component ---------------------------------------------------------------
|
|
32
|
-
export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, currentStatus, items, paymentProofUrl, paymentTransactionId, paymentMethod, paymentStatus, displayedUpiId, buyerReportedUpiId, paymentUpiMismatch, buyerMarkedPaid, buyerFraudAgreementAccepted, }) {
|
|
34
|
+
export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, currentStatus, items, paymentProofUrl, paymentTransactionId, paymentMethod, paymentStatus, displayedUpiId, buyerReportedUpiId, paymentUpiMismatch, buyerMarkedPaid, buyerFraudAgreementAccepted, paymentReviewOutcome, addons, }) {
|
|
33
35
|
const queryClient = useQueryClient();
|
|
34
36
|
const { showToast } = useToast();
|
|
35
37
|
const [status, setStatus] = React.useState(currentStatus ?? "pending");
|
|
@@ -41,8 +43,19 @@ export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, curre
|
|
|
41
43
|
const [reviewNote, setReviewNote] = React.useState("");
|
|
42
44
|
const [isRequestingReupload, setIsRequestingReupload] = useState(false);
|
|
43
45
|
const [isRejectingFraud, setIsRejectingFraud] = useState(false);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
// This used to inline `paymentMethod === "cash" || === "upi_manual"`, which
|
|
47
|
+
// omitted `emi` — so the whole proof panel never rendered for an EMI order and
|
|
48
|
+
// an admin had no way to verify one, even though the list row said "Awaiting
|
|
49
|
+
// verification". Use the shared predicate, which is the single source of truth
|
|
50
|
+
// for the manual-payment set.
|
|
51
|
+
const isManualPayment = isManualPaymentMethod(paymentMethod ?? "");
|
|
52
|
+
const isVerified = paymentStatus === "paid";
|
|
53
|
+
const isRejected = paymentReviewOutcome === "rejected_fraud";
|
|
54
|
+
const isReuploadRequested = paymentReviewOutcome === "reupload_requested";
|
|
55
|
+
// Only an undecided, unpaid manual order is actionable. Gating on
|
|
56
|
+
// `paymentStatus === "pending"` alone re-offered live Verify/Reject buttons on
|
|
57
|
+
// an order that had already been rejected or sent back for re-upload.
|
|
58
|
+
const needsVerification = isManualPayment && !isVerified && !isRejected && Boolean(paymentProofUrl);
|
|
46
59
|
React.useEffect(() => {
|
|
47
60
|
if (open) {
|
|
48
61
|
setStatus(currentStatus ?? "pending");
|
|
@@ -136,5 +149,5 @@ export function AdminOrderEditorView({ open, onClose, orderId, orderLabel, curre
|
|
|
136
149
|
return (_jsx(SideDrawer, { isOpen: open, onClose: onClose, title: orderLabel ? `Order: ${orderLabel}` : "Update Order", children: _jsxs(Form, { onSubmit: (e) => {
|
|
137
150
|
e.preventDefault();
|
|
138
151
|
saveMutation.mutate();
|
|
139
|
-
}, spacing: "md", padding: "md", children: [items && items.length > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Label, { size: "sm", weight: "medium", color: "primary", children: ["Items (", items.length, ")"] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: items.map((item, i) => (_jsxs(Div, { layout: "flex", align: "center", gap: "3", padding: "sm", children: [_jsx(Div, { className: "h-10 w-10 shrink-0", rounded: "md", overflow: "hidden", children: _jsx(MediaImage, { src: item.image, alt: item.title, size: "thumbnail" }) }), _jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx(Text, { size: "sm", className: "truncate", weight: "medium", children: item.title }), _jsxs(Text, { size: "xs", color: "muted", children: ["Qty: ", item.quantity, " \u00D7 ", toCurrency(item.unitPrice)] })] }), _jsx(Text, { size: "sm", className: "shrink-0", weight: "semibold", children: toCurrency(item.totalPrice) })] }, item.productId || i))) })] })), _jsx(Select, { label: "Order status", options: STATUS_OPTIONS, value: status, onValueChange: setStatus }), _jsx(Input, { label: "Tracking number (optional)", value: trackingNumber, onChange: (e) => setTrackingNumber(e.target.value), placeholder: "e.g. DEL1234567890IN" }), _jsx(Select, { label: "Carrier (optional)", options: CARRIER_OPTIONS, value: carrier, onValueChange: setCarrier }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Internal note (optional)" }), _jsx(Textarea, { value: notes, onChange: (e) => setNotes(e.target.value), rows: 3, placeholder: "Reason for status change, escalation notes\u2026" })] }), (status === "refunded" || status === "return_requested") && (_jsx(Input, { label: "Refund amount \u20B9 (optional)", type: "number", min: "0", step: "0.01", value: refundAmount, onChange: (e) => setRefundAmount(e.target.value), placeholder: "e.g. 499.00" })),
|
|
152
|
+
}, spacing: "md", padding: "md", children: [addons && _jsx(OrderAddonBadges, { order: addons, variant: "detail" }), items && items.length > 0 && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Label, { size: "sm", weight: "medium", color: "primary", children: ["Items (", items.length, ")"] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: items.map((item, i) => (_jsxs(Div, { layout: "flex", align: "center", gap: "3", padding: "sm", children: [_jsx(Div, { className: "h-10 w-10 shrink-0", rounded: "md", overflow: "hidden", children: _jsx(MediaImage, { src: item.image, alt: item.title, size: "thumbnail" }) }), _jsxs(Div, { className: "min-w-0 flex-1", children: [_jsx(Text, { size: "sm", className: "truncate", weight: "medium", children: item.title }), _jsxs(Text, { size: "xs", color: "muted", children: ["Qty: ", item.quantity, " \u00D7 ", toCurrency(item.unitPrice)] })] }), _jsx(Text, { size: "sm", className: "shrink-0", weight: "semibold", children: toCurrency(item.totalPrice) })] }, item.productId || i))) })] })), _jsx(Select, { label: "Order status", options: STATUS_OPTIONS, value: status, onValueChange: setStatus }), _jsx(Input, { label: "Tracking number (optional)", value: trackingNumber, onChange: (e) => setTrackingNumber(e.target.value), placeholder: "e.g. DEL1234567890IN" }), _jsx(Select, { label: "Carrier (optional)", options: CARRIER_OPTIONS, value: carrier, onValueChange: setCarrier }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Internal note (optional)" }), _jsx(Textarea, { value: notes, onChange: (e) => setNotes(e.target.value), rows: 3, placeholder: "Reason for status change, escalation notes\u2026" })] }), (status === "refunded" || status === "return_requested") && (_jsx(Input, { label: "Refund amount \u20B9 (optional)", type: "number", min: "0", step: "0.01", value: refundAmount, onChange: (e) => setRefundAmount(e.target.value), placeholder: "e.g. 499.00" })), isManualPayment && (_jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Payment Proof" }), isRejected && (_jsx(Text, { size: "xs", className: "text-error", weight: "medium", children: "Payment rejected as fraud \u2014 no further action available here." })), isReuploadRequested && !isVerified && (_jsx(Text, { size: "xs", className: "text-warning", weight: "medium", children: "Re-upload requested \u2014 waiting for the buyer to submit a new proof." })), paymentProofUrl ? (_jsxs(Stack, { gap: "xs", children: [_jsx(Div, { border: "default", rounded: "lg", overflow: "hidden", children: _jsx(MediaImage, { src: paymentProofUrl, alt: "Payment screenshot", size: "card" }) }), paymentTransactionId && (_jsxs(Text, { size: "xs", color: "muted", children: ["UTR: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: paymentTransactionId })] })), needsVerification && (_jsxs(Stack, { gap: "xs", children: [(displayedUpiId || buyerReportedUpiId) && (_jsxs(Stack, { gap: "xs", children: [_jsxs(Text, { size: "xs", color: "muted", children: ["Expected UPI: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: displayedUpiId || "—" }), " · ", "Buyer reported: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerReportedUpiId || "—" })] }), paymentUpiMismatch && (_jsx(Div, { rounded: "lg", padding: "inlineSm", className: "border border-error/20", surface: "danger-surface", children: _jsx(Text, { size: "xs", className: "text-error", weight: "semibold", children: "UPI mismatch \u2014 buyer-reported ID doesn't match the one shown for this order." }) }))] })), _jsxs(Text, { size: "xs", color: "muted", children: ["Buyer marked as paid: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerMarkedPaid ? "Yes" : "No" }), " · ", "Fraud agreement accepted: ", _jsx(Text, { as: "span", size: "xs", weight: "medium", children: buyerFraudAgreementAccepted ? "Yes" : "No" })] }), _jsxs(Stack, { gap: "xs", children: [_jsx(Label, { size: "sm", weight: "medium", color: "primary", children: "Review note (required for re-upload / reject)" }), _jsx(Textarea, { value: reviewNote, onChange: (e) => setReviewNote(e.target.value), rows: 2, placeholder: "e.g. Screenshot is blurry \u2014 amount not readable / UPI ID doesn't match order" })] }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["verify-payment"], onClick: handleVerifyPayment, isLoading: isVerifyingPayment, disabled: isVerifyingPayment, variant: "primary", className: "w-full" }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["request-payment-reupload"], onClick: handleRequestReupload, isLoading: isRequestingReupload, disabled: isRequestingReupload || !reviewNote.trim(), variant: "secondary", className: "w-full" }), _jsx(Button, { type: "button", action: ACTIONS.ADMIN["reject-payment-fraud"], onClick: handleRejectFraud, isLoading: isRejectingFraud, disabled: isRejectingFraud || !reviewNote.trim(), variant: "danger", className: "w-full" })] })), isVerified && (_jsx(Div, { rounded: "lg", padding: "inlineSm", className: "border border-success/20", surface: "success-surface", children: _jsx(Text, { size: "xs", className: "text-success", weight: "medium", children: "Payment verified" }) }))] })) : (_jsx(Text, { size: "xs", color: "faint", children: "No proof uploaded yet." }))] })), _jsxs(FormActions, { align: "right", children: [_jsx(Button, { type: "button", variant: "secondary", onClick: onClose, children: "Cancel" }), _jsx(Button, { type: "submit", isLoading: saveMutation.isPending, disabled: !orderId || saveMutation.isPending, children: "Save changes" })] })] }) }));
|
|
140
153
|
}
|
|
@@ -122,6 +122,20 @@ export function AdminOrdersView({ children, ...props }) {
|
|
|
122
122
|
paymentUpiMismatch: Boolean(item.paymentUpiMismatch),
|
|
123
123
|
buyerMarkedPaid: Boolean(item.buyerMarkedPaid),
|
|
124
124
|
buyerFraudAgreementAccepted: Boolean(item.buyerFraudAgreementAccepted),
|
|
125
|
+
paymentReviewOutcome: toStringValue(item.paymentReviewOutcome, "") || undefined,
|
|
126
|
+
// Straight off the order document — the admin list route returns
|
|
127
|
+
// raw docs, so these need no serializer entry (cf. Root Cause #38).
|
|
128
|
+
addons: {
|
|
129
|
+
whatsappNotifyAddon: Boolean(item.whatsappNotifyAddon),
|
|
130
|
+
whatsappNotifyFee: Number(item.whatsappNotifyFee ?? 0) || undefined,
|
|
131
|
+
giftWrapAddon: Boolean(item.giftWrapAddon),
|
|
132
|
+
giftWrapFee: Number(item.giftWrapFee ?? 0) || undefined,
|
|
133
|
+
giftWrapMessage: toStringValue(item.giftWrapMessage) || undefined,
|
|
134
|
+
shipmentProtectionAddon: Boolean(item.shipmentProtectionAddon),
|
|
135
|
+
shipmentProtectionFee: Number(item.shipmentProtectionFee ?? 0) || undefined,
|
|
136
|
+
couponCode: toStringValue(item.couponCode) || undefined,
|
|
137
|
+
couponDiscount: Number(item.couponDiscount ?? 0) || undefined,
|
|
138
|
+
},
|
|
125
139
|
};
|
|
126
140
|
}),
|
|
127
141
|
getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
|
|
@@ -198,5 +212,5 @@ export function AdminOrdersView({ children, ...props }) {
|
|
|
198
212
|
] })),
|
|
199
213
|
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_ORDER_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id, paymentReview: "" })) }), _jsx(FilterChipGroup, { label: "Manual payment", tabs: ADMIN_ORDER_PAYMENT_REVIEW_TABS, value: pendingFilters.paymentReview ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, paymentReview: id, status: "" })) })] })),
|
|
200
214
|
};
|
|
201
|
-
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(AdminOrderEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), orderId: selectedRow?.id, orderLabel: selectedRow?.primary, currentStatus: selectedRow?.status, items: selectedRow?.items, paymentProofUrl: selectedRow?.paymentProofUrl, paymentTransactionId: selectedRow?.paymentTransactionId, paymentMethod: selectedRow?.paymentMethod, paymentStatus: selectedRow?.paymentStatus, displayedUpiId: selectedRow?.displayedUpiId, buyerReportedUpiId: selectedRow?.buyerReportedUpiId, paymentUpiMismatch: selectedRow?.paymentUpiMismatch, buyerMarkedPaid: selectedRow?.buyerMarkedPaid, buyerFraudAgreementAccepted: selectedRow?.buyerFraudAgreementAccepted })] }));
|
|
215
|
+
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(AdminOrderEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), orderId: selectedRow?.id, orderLabel: selectedRow?.primary, currentStatus: selectedRow?.status, items: selectedRow?.items, paymentProofUrl: selectedRow?.paymentProofUrl, paymentTransactionId: selectedRow?.paymentTransactionId, paymentMethod: selectedRow?.paymentMethod, paymentStatus: selectedRow?.paymentStatus, displayedUpiId: selectedRow?.displayedUpiId, buyerReportedUpiId: selectedRow?.buyerReportedUpiId, paymentUpiMismatch: selectedRow?.paymentUpiMismatch, buyerMarkedPaid: selectedRow?.buyerMarkedPaid, buyerFraudAgreementAccepted: selectedRow?.buyerFraudAgreementAccepted, paymentReviewOutcome: selectedRow?.paymentReviewOutcome, addons: selectedRow?.addons })] }));
|
|
202
216
|
}
|