@mohasinac/appkit 4.1.1 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/client/features/maintenance/index.d.ts +2 -0
- package/dist/_internal/client/features/maintenance/index.js +1 -0
- package/dist/_internal/client/features/maintenance/views/CloudLogsListView.d.ts +9 -0
- package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +55 -0
- package/dist/_internal/server/features/auctions/index.d.ts +1 -1
- package/dist/_internal/server/features/auctions/index.js +1 -1
- package/dist/_internal/server/features/auctions/service.js +2 -2
- package/dist/_internal/server/features/checkout/actions.d.ts +15 -0
- package/dist/_internal/server/features/checkout/actions.js +245 -182
- package/dist/_internal/server/features/maintenance/cloud-logs-data.d.ts +50 -0
- package/dist/_internal/server/features/maintenance/cloud-logs-data.js +152 -0
- package/dist/_internal/server/functions/scheduled.d.ts +2 -1
- package/dist/_internal/server/functions/scheduled.js +9 -1
- package/dist/_internal/server/jobs/core/jobRunners.js +10 -0
- package/dist/_internal/server/jobs/core/newsletterExport.d.ts +15 -0
- package/dist/_internal/server/jobs/core/newsletterExport.js +43 -0
- package/dist/_internal/server/jobs/core/onOrderCreate.d.ts +4 -12
- package/dist/_internal/server/jobs/core/onOrderCreate.js +3 -3
- package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +2 -0
- package/dist/_internal/server/jobs/core/onOrderStatusChange.js +1 -0
- package/dist/_internal/server/jobs/core/onProductWrite.js +14 -2
- package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +1 -0
- package/dist/_internal/server/jobs/core/pendingOrderTimeout.js +1 -0
- package/dist/_internal/server/jobs/core/revenueRollup.d.ts +13 -0
- package/dist/_internal/server/jobs/core/revenueRollup.js +22 -0
- package/dist/_internal/server/jobs/core/whatsappNotify.d.ts +35 -0
- package/dist/_internal/server/jobs/core/whatsappNotify.js +82 -0
- package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
- package/dist/_internal/server/jobs/handlers/index.js +1 -0
- package/dist/_internal/server/jobs/handlers/revenueRollup.d.ts +2 -0
- package/dist/_internal/server/jobs/handlers/revenueRollup.js +2 -0
- package/dist/_internal/shared/features/auctions/config.d.ts +1 -1
- package/dist/_internal/shared/features/auctions/config.js +1 -1
- package/dist/_internal/shared/features/auctions/schema.js +1 -1
- package/dist/_internal/shared/features/cart/schema.js +2 -2
- package/dist/_internal/shared/features/checkout/config.d.ts +7 -0
- package/dist/_internal/shared/features/checkout/config.js +7 -0
- package/dist/_internal/shared/features/events/schema.js +1 -1
- package/dist/_internal/shared/features/orders/schema.js +1 -1
- package/dist/_internal/shared/features/products/schema.js +3 -3
- package/dist/_internal/shared/features/promotions/schema.js +3 -3
- package/dist/_internal/shared/fees/calculator.d.ts +32 -7
- package/dist/_internal/shared/fees/calculator.js +25 -0
- package/dist/client.d.ts +4 -4
- package/dist/client.js +4 -2
- package/dist/constants/api-endpoints.d.ts +9 -6
- package/dist/constants/api-endpoints.js +3 -2
- package/dist/contracts/client-payment-gateway.d.ts +1 -2
- package/dist/features/about/components/AboutView.d.ts +6 -16
- package/dist/features/about/components/AboutView.js +3 -3
- package/dist/features/about/components/FeesView.js +18 -0
- package/dist/features/about/index.d.ts +2 -1
- package/dist/features/about/schemas/firestore.d.ts +47 -0
- package/dist/features/about/schemas/firestore.js +6 -0
- package/dist/features/about/schemas/index.d.ts +210 -0
- package/dist/features/about/schemas/index.js +45 -0
- package/dist/features/account/hooks/useProfile.js +3 -0
- package/dist/features/admin/actions/notification-actions.d.ts +4 -1
- package/dist/features/admin/actions/notification-actions.js +56 -10
- package/dist/features/admin/components/AdminAdsView.js +1 -1
- package/dist/features/admin/components/AdminCarouselGroupEditorView.d.ts +6 -0
- package/dist/features/admin/components/AdminCarouselGroupEditorView.js +50 -0
- package/dist/features/admin/components/AdminCartsView.js +16 -3
- package/dist/features/admin/components/AdminCouponEditorView.js +2 -2
- package/dist/features/admin/components/AdminCouponsView.js +14 -4
- package/dist/features/admin/components/AdminEmployeeEditorView.js +1 -42
- package/dist/features/admin/components/AdminMediaView.js +1 -1
- package/dist/features/admin/components/AdminNewsletterView.js +42 -11
- package/dist/features/admin/components/AdminNotificationsView.js +31 -19
- package/dist/features/admin/components/AdminOrdersView.js +7 -0
- package/dist/features/admin/components/AdminPayoutsView.js +18 -3
- package/dist/features/admin/components/AdminProductsView.js +17 -2
- package/dist/features/admin/components/AdminReviewsView.js +16 -2
- package/dist/features/admin/components/AdminScammersView.js +7 -0
- package/dist/features/admin/components/AdminSectionsView.js +4 -0
- package/dist/features/admin/components/AdminSiteConfigGuideView.js +1 -1
- package/dist/features/admin/components/AdminSiteSettingsView.js +127 -8
- package/dist/features/admin/components/AdminStoreEditorView.d.ts +2 -1
- package/dist/features/admin/components/AdminStoreEditorView.js +4 -4
- package/dist/features/admin/components/AdminStoresView.js +1 -1
- package/dist/features/admin/components/AdminSupportTicketDetailView.js +23 -5
- package/dist/features/admin/components/AdminSupportTicketsView.js +20 -2
- package/dist/features/admin/components/AdminTesterChecklistItemEditorView.js +4 -1
- package/dist/features/admin/components/AdminTesterChecklistView.js +8 -0
- package/dist/features/admin/components/index.d.ts +2 -0
- package/dist/features/admin/components/index.js +1 -0
- package/dist/features/admin/constants/filter-tabs.d.ts +55 -41
- package/dist/features/admin/constants/filter-tabs.js +55 -27
- package/dist/features/admin/repository/analytics-rollup.repository.d.ts +25 -0
- package/dist/features/admin/repository/analytics-rollup.repository.js +31 -0
- package/dist/features/admin/repository/site-settings.repository.js +8 -1
- package/dist/features/admin/schemas/firestore.d.ts +46 -0
- package/dist/features/admin/schemas/firestore.js +62 -24
- package/dist/features/auctions/actions/bid-actions.js +1 -1
- package/dist/features/auctions/components/AuctionDetailPageView.js +2 -2
- package/dist/features/auctions/components/AuctionsListView.js +8 -1
- package/dist/features/auth/components/LoginForm.js +2 -2
- package/dist/features/auth/components/RegisterForm.js +2 -2
- package/dist/features/auth/permissions/constants.d.ts +16 -0
- package/dist/features/auth/permissions/constants.js +53 -0
- package/dist/features/auth/schemas/firestore.js +1 -0
- package/dist/features/blog/components/BlogFilters.js +1 -1
- package/dist/features/cart/schemas/firestore.d.ts +1 -1
- package/dist/features/categories/schemas/firestore.d.ts +1 -1
- package/dist/features/consultation/components/ConsultationForm.js +1 -1
- package/dist/features/events/components/EventFilters.js +2 -0
- package/dist/features/homepage/components/WelcomeSection.js +2 -2
- package/dist/features/layout/BackToTop.d.ts +9 -5
- package/dist/features/layout/BackToTop.js +17 -21
- package/dist/features/layout/TitleBarLayout.d.ts +2 -2
- package/dist/features/layout/TitleBarLayout.js +4 -4
- package/dist/features/media/AvatarUpload.js +7 -3
- package/dist/features/media/MediaVideo.js +1 -1
- package/dist/features/media/finalize.d.ts +9 -13
- package/dist/features/media/finalize.js +23 -74
- package/dist/features/media/server.d.ts +1 -1
- package/dist/features/media/server.js +1 -1
- package/dist/features/orders/actions/refund-actions.js +1 -0
- package/dist/features/orders/hooks/useOrders.js +16 -4
- package/dist/features/orders/schemas/firestore.d.ts +19 -5
- package/dist/features/payments/schemas/firestore.d.ts +2 -2
- package/dist/features/pre-orders/components/PreOrdersListView.js +7 -0
- package/dist/features/products/components/ArtStickersListView.js +5 -0
- package/dist/features/products/components/ProductDetailPageView.d.ts +7 -1
- package/dist/features/products/components/ProductForm.js +14 -1
- package/dist/features/products/components/ProductsIndexPageView.js +7 -0
- package/dist/features/products/constants/action-defs.d.ts +29 -3
- package/dist/features/products/constants/action-defs.js +77 -1
- package/dist/features/products/schemas/firestore.d.ts +1 -1
- package/dist/features/products/schemas/product-templates.d.ts +1 -1
- package/dist/features/products/types/index.d.ts +4 -0
- package/dist/features/scams/schemas/index.js +2 -2
- package/dist/features/search/components/Search.js +3 -3
- package/dist/features/seller/components/SellerAuctionsView.js +27 -2
- package/dist/features/seller/components/SellerOrdersView.js +2 -2
- package/dist/features/seller/components/SellerPayoutsView.js +24 -10
- package/dist/features/seller/components/SellerPreOrdersView.js +12 -4
- package/dist/features/seller/components/SellerPrizeDrawsView.js +23 -5
- package/dist/features/seller/components/SellerProductShell.d.ts +2 -0
- package/dist/features/seller/components/SellerProductShell.js +1 -1
- package/dist/features/seller/components/SellerProductsView.js +8 -6
- package/dist/features/seller/components/SellerReviewsView.js +27 -6
- package/dist/features/seller/components/index.d.ts +0 -2
- package/dist/features/seller/components/index.js +0 -1
- package/dist/features/stores/components/StoreListingsGuideView.js +2 -1
- package/dist/features/stores/components/StoreProductsView.js +1 -1
- package/dist/features/stores/schemas/firestore.d.ts +11 -0
- package/dist/features/stores/schemas/firestore.js +5 -0
- package/dist/features/stores/schemas/index.d.ts +2 -2
- package/dist/features/tester/actions/checklist-item-actions.d.ts +6 -0
- package/dist/features/tester/actions/checklist-item-actions.js +2 -0
- package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +13 -2
- package/dist/features/tester/components/AdminTesterFeedbackListView.js +2 -1
- package/dist/features/tester/components/AdminTesterFeedbackReportView.js +9 -5
- package/dist/features/tester/components/TesterHubView.js +34 -21
- package/dist/features/tester/repository/tester-checklist-item.repository.js +1 -0
- package/dist/features/tester/repository/tester-checklist-response.repository.d.ts +18 -4
- package/dist/features/tester/repository/tester-checklist-response.repository.js +43 -8
- package/dist/features/tester/schemas/firestore.d.ts +8 -3
- package/dist/features/tester/schemas/firestore.js +6 -0
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +21 -9
- package/dist/features/tester/utils/phases.d.ts +31 -0
- package/dist/features/tester/utils/phases.js +46 -0
- package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +1 -1
- package/dist/features/whatsapp-bot/helpers/whatsapp.d.ts +10 -1
- package/dist/features/whatsapp-bot/helpers/whatsapp.js +47 -0
- package/dist/features/whatsapp-bot/types/index.d.ts +20 -1
- package/dist/features/wishlist/types/index.d.ts +3 -0
- package/dist/index.d.ts +15 -6
- package/dist/index.js +19 -9
- package/dist/next/components/NotFoundView.js +2 -1
- package/dist/next/routing/route-map.d.ts +6 -12
- package/dist/next/routing/route-map.js +3 -4
- package/dist/providers/db-firebase/admin.d.ts +1 -0
- package/dist/providers/db-firebase/admin.js +1 -1
- package/dist/repositories/index.d.ts +2 -0
- package/dist/repositories/index.js +1 -0
- package/dist/schemas/registry.d.ts +2 -2
- package/dist/seed/addresses-seed-data.js +18 -18
- package/dist/seed/bids-seed-data.js +4 -4
- package/dist/seed/blog-posts-seed-data.js +82 -82
- package/dist/seed/carousel-slides-seed-data.js +14 -10
- package/dist/seed/carousels-seed-data.js +3 -3
- package/dist/seed/cart-seed-data.js +92 -92
- package/dist/seed/claimed-coupons-seed-data.js +10 -10
- package/dist/seed/conversations-seed-data.js +75 -75
- package/dist/seed/coupon-usage-seed-data.js +12 -12
- package/dist/seed/coupons-seed-data.js +41 -41
- package/dist/seed/events-seed-data.js +105 -105
- package/dist/seed/grouped-listings-seed-data.d.ts +2 -137
- package/dist/seed/grouped-listings-seed-data.js +79 -143
- package/dist/seed/history-seed-data.js +38 -38
- package/dist/seed/homepage-sections-seed-data.js +9 -9
- package/dist/seed/notifications-seed-data.js +56 -56
- package/dist/seed/offers-seed-data.js +91 -91
- package/dist/seed/orders-seed-data.js +121 -106
- package/dist/seed/payouts-seed-data.js +42 -42
- package/dist/seed/products-auctions-seed-data.js +10 -2
- package/dist/seed/products-standard-seed-data.js +40 -8
- package/dist/seed/reviews-seed-data.js +42 -23
- package/dist/seed/scammers-seed-data.js +22 -22
- package/dist/seed/sessions-seed-data.js +7 -7
- package/dist/seed/shipments-seed-data.js +10 -10
- package/dist/seed/site-settings-seed-data.js +89 -0
- package/dist/seed/store-addresses-seed-data.js +2 -2
- package/dist/seed/store-extensions-seed-data.js +93 -93
- package/dist/seed/stores-seed-data.js +2 -0
- package/dist/seed/support-tickets-seed-data.js +47 -47
- package/dist/seed/users-seed-data.js +55 -89
- package/dist/seed/wishlists-seed-data.js +22 -22
- package/dist/server.d.ts +4 -3
- package/dist/server.js +7 -7
- package/dist/styles.css +1 -1
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/PageLoader.js +8 -3
- package/dist/ui/components/Select.d.ts +8 -1
- package/dist/ui/components/Select.js +2 -2
- package/dist/ui/components/SiteMark.d.ts +23 -0
- package/dist/ui/components/SiteMark.js +76 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.js +1 -0
- package/dist/validation/schemas.d.ts +0 -2
- package/dist/validation/schemas.js +0 -2
- package/package.json +1 -1
- package/scripts/seed-cli.mjs +17 -15
|
@@ -330,7 +330,7 @@ export interface PrizeDrawItem {
|
|
|
330
330
|
};
|
|
331
331
|
condition: string;
|
|
332
332
|
estimatedValue?: number;
|
|
333
|
-
/** Per-slot price in
|
|
333
|
+
/** Per-slot price in decimal rupees for lottery mode. Lower price = higher weight = higher chance. */
|
|
334
334
|
prizeSlotPrice?: number;
|
|
335
335
|
isWon: boolean;
|
|
336
336
|
}
|
|
@@ -14,7 +14,7 @@ export interface ProductTemplateDocument {
|
|
|
14
14
|
brand?: string;
|
|
15
15
|
condition?: string;
|
|
16
16
|
tags?: string[];
|
|
17
|
-
/** Price hint in
|
|
17
|
+
/** Price hint in decimal rupees */
|
|
18
18
|
price?: number;
|
|
19
19
|
currency?: string;
|
|
20
20
|
shippingPaidBy?: "buyer" | "seller";
|
|
@@ -40,6 +40,10 @@ export interface ProductItem {
|
|
|
40
40
|
featured?: boolean;
|
|
41
41
|
isPromoted?: boolean;
|
|
42
42
|
allowShipBeforeEmiComplete?: boolean;
|
|
43
|
+
/** P-8 GST — buyer-facing tax rate on this product (%). Unset/0 = exempt. */
|
|
44
|
+
gstRate?: 0 | 5 | 12 | 18 | 28;
|
|
45
|
+
/** P-8 GST — Harmonized System of Nomenclature code for GST-compliant invoices. */
|
|
46
|
+
hsnCode?: string;
|
|
43
47
|
printMeta?: ProductPrintMeta;
|
|
44
48
|
currentBid?: number;
|
|
45
49
|
availableQuantity?: number;
|
|
@@ -66,7 +66,7 @@ export const scammerFirestoreSchema = z.object({
|
|
|
66
66
|
scamType: scamTypeSchema,
|
|
67
67
|
scamPlatform: scamPlatformSchema,
|
|
68
68
|
description: z.string(),
|
|
69
|
-
amountLost: z.number().
|
|
69
|
+
amountLost: z.number().nonnegative().optional(),
|
|
70
70
|
itemInvolved: z.string().optional(),
|
|
71
71
|
evidence: z.array(z.string()),
|
|
72
72
|
reportedBy: z.string(),
|
|
@@ -93,7 +93,7 @@ export const scammerIncidentFirestoreSchema = z.object({
|
|
|
93
93
|
scamType: scamTypeSchema,
|
|
94
94
|
scamPlatform: scamPlatformSchema,
|
|
95
95
|
description: z.string(),
|
|
96
|
-
amountLost: z.number().
|
|
96
|
+
amountLost: z.number().nonnegative().optional(),
|
|
97
97
|
itemInvolved: z.string().optional(),
|
|
98
98
|
evidence: z.array(z.string()),
|
|
99
99
|
matchedPhones: z.array(z.string()).optional(),
|
|
@@ -208,7 +208,7 @@ export function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, pla
|
|
|
208
208
|
setIsInlineOpen(false);
|
|
209
209
|
}
|
|
210
210
|
};
|
|
211
|
-
return (_jsxs(Row, { className: `relative ${className ?? ""}`, gap: "sm", children: [_jsxs(Row, { className: "relative flex-1", children: [_jsx("svg", { className: "absolute left-3 w-4 h-4 text-zinc-400 pointer-events-none", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }), _jsx(Input, { ref: inputRef, bare: true, type: "search", value: query, onChange: (event) => handleInlineChange(event.target.value), onKeyDown: handleInlineKeyDown, onFocus: () => {
|
|
211
|
+
return (_jsxs(Row, { className: `relative ${className ?? ""}`, gap: "sm", children: [_jsxs(Row, { className: "relative flex-1 min-w-0", children: [_jsx("svg", { className: "absolute left-3 w-4 h-4 text-zinc-400 pointer-events-none", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }), _jsx(Input, { ref: inputRef, bare: true, type: "search", value: query, onChange: (event) => handleInlineChange(event.target.value), onKeyDown: handleInlineKeyDown, onFocus: () => {
|
|
212
212
|
if (inlineBlurRef.current)
|
|
213
213
|
clearTimeout(inlineBlurRef.current);
|
|
214
214
|
setIsInlineOpen(true);
|
|
@@ -216,7 +216,7 @@ export function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, pla
|
|
|
216
216
|
if (inlineBlurRef.current)
|
|
217
217
|
clearTimeout(inlineBlurRef.current);
|
|
218
218
|
inlineBlurRef.current = setTimeout(() => setIsInlineOpen(false), 120);
|
|
219
|
-
}, placeholder: placeholder ?? labels.placeholder, className: "w-full rounded-lg border border-zinc-300 bg-[var(--appkit-color-surface)] pl-9 text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] dark:text-white dark:placeholder:text-slate-500" }), query && (_jsx(Button, { rounded: "full", textColor: "faint", type: "button", variant: "ghost", onClick: handleClear, className: "absolute right-3 p-[0.125rem] hover:text-[var(--appkit-color-text-muted)] dark:hover:text-[var(--appkit-color-text)] transition-colors", "aria-label": labels.clearAriaLabel, children: _jsx("svg", { className: "w-4 h-4", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }))] }), resourceTypes && resourceTypes.length > 0 && (_jsx(Select, { options: resourceTypes, value: selectedType, onValueChange: handleTypeChange, "aria-label": labels.resourceTypeLabel ?? "Search in",
|
|
219
|
+
}, placeholder: placeholder ?? labels.placeholder, className: "w-full rounded-lg border border-zinc-300 bg-[var(--appkit-color-surface)] pl-9 text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] dark:text-white dark:placeholder:text-slate-500" }), query && (_jsx(Button, { rounded: "full", textColor: "faint", type: "button", variant: "ghost", onClick: handleClear, className: "absolute right-3 p-[0.125rem] hover:text-[var(--appkit-color-text-muted)] dark:hover:text-[var(--appkit-color-text)] transition-colors", "aria-label": labels.clearAriaLabel, children: _jsx("svg", { className: "w-4 h-4", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) }))] }), resourceTypes && resourceTypes.length > 0 && (_jsx(Select, { options: resourceTypes, value: selectedType, onValueChange: handleTypeChange, "aria-label": labels.resourceTypeLabel ?? "Search in", wrapperClassName: "flex-shrink-0" })), deferred && (_jsx(Button, { rounded: "lg", paddingX: "sm", paddingY: "sm", type: "button", variant: "primary", onClick: handleDeferredSubmit, "aria-label": labels.ariaLabel, className: "flex-shrink-0", children: _jsx("svg", { className: "w-4 h-4", "aria-hidden": "true", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }) })), isInlineOpen && (filteredQuickLinks.length > 0 || query) && (_jsxs(Div, { rounded: "xl", border: "default", shadow: "lg", className: "absolute top-full left-0 right-0 mt-2 overflow-hidden bg-[var(--appkit-color-surface)] [z-index:var(--appkit-z-dropdown)]", onMouseDown: (event) => event.preventDefault(), children: [filteredQuickLinks.length > 0 && (_jsx(Ul, { children: filteredQuickLinks.map((link, index) => renderInlineQuickLink(link, index)) })), query && suggestionsLoading && (_jsx(Div, { padding: "inline", children: _jsx(Text, { variant: "secondary", size: "sm", children: labels.searching }) })), query &&
|
|
220
220
|
suggestions.slice(0, 5).map((suggestion, suggestionIndex) => {
|
|
221
221
|
const itemIndex = inlineQuickLinkItems.length + suggestionIndex;
|
|
222
222
|
const isActive = activeIndex === itemIndex;
|
|
@@ -292,7 +292,7 @@ export function Search({ isOpen, onClose, onSearch, onOpen, value, onChange, pla
|
|
|
292
292
|
};
|
|
293
293
|
if (!isOpen)
|
|
294
294
|
return null;
|
|
295
|
-
return (_jsx(Div, { border: "bottom-subtle", className: "animate-[fadeIn_150ms_ease-out]", surface: "default", shadow: "md", children: _jsxs(Div, { className: "container mx-auto md:py-[1rem] relative", padding: "inline", children: [_jsxs(Row, { gap: "sm", className: "md:gap-[0.75rem]", children: [_jsx(Div, { className: "flex-1 relative", children: _jsx(Input, { ref: inputRef, bare: true, type: "search", placeholder: placeholder ?? labels.placeholder, value: query, onChange: (event) => setQuery(event.target.value), onKeyDown: handleOverlayKeyDown, className: "w-full rounded-lg border border-zinc-300 bg-[var(--appkit-color-surface)] text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] dark:text-white dark:placeholder:text-slate-500" }) }), resourceTypes && resourceTypes.length > 0 && (_jsx(Select, { options: resourceTypes, value: selectedType, onValueChange: handleTypeChange, "aria-label": labels.resourceTypeLabel ?? "Search in",
|
|
295
|
+
return (_jsx(Div, { border: "bottom-subtle", className: "animate-[fadeIn_150ms_ease-out]", surface: "default", shadow: "md", children: _jsxs(Div, { className: "container mx-auto md:py-[1rem] relative", padding: "inline", children: [_jsxs(Row, { gap: "sm", className: "md:gap-[0.75rem]", children: [_jsx(Div, { className: "flex-1 relative min-w-0", children: _jsx(Input, { ref: inputRef, bare: true, type: "search", placeholder: placeholder ?? labels.placeholder, value: query, onChange: (event) => setQuery(event.target.value), onKeyDown: handleOverlayKeyDown, className: "w-full rounded-lg border border-zinc-300 bg-[var(--appkit-color-surface)] text-zinc-900 placeholder:text-zinc-400 focus:border-primary focus:ring-2 focus:ring-primary/20 border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] dark:text-white dark:placeholder:text-slate-500" }) }), resourceTypes && resourceTypes.length > 0 && (_jsx(Select, { options: resourceTypes, value: selectedType, onValueChange: handleTypeChange, "aria-label": labels.resourceTypeLabel ?? "Search in", wrapperClassName: "flex-shrink-0" })), _jsxs(Button, { gap: "md", onClick: handleOverlaySearch, variant: "primary", size: "md", className: "hidden sm:", children: [_jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: SVG_PATH_SEARCH }) }), labels.title] }), _jsx(Button, { rounded: "xl", variant: "ghost", onClick: () => onClose?.(), className: "p-[var(--appkit-space-2-5)] md:p-[var(--appkit-space-3)] transition-colors text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface)] hover:text-zinc-700 text-[var(--appkit-color-text-muted)] hover:bg-[var(--appkit-color-surface-elevated)] hover:text-[var(--appkit-color-text-muted)] flex-shrink-0", "aria-label": labels.closeAriaLabel, children: _jsx("svg", { className: "w-6 h-6 md:w-7 md:h-7", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), (filteredSiteLinks.length > 0 || query) && (_jsxs(Stack, { className: "absolute top-full left-0 right-0 z-50 pt-[0.5rem] pb-[1rem]", padding: "x-md", gap: "sm", children: [filteredSiteLinks.length > 0 && !suggestionsLoading && (_jsxs(Div, { className: `${__O.hidden}`, rounded: "xl", shadow: "lg", surface: "default", border: "default", children: [_jsx(Div, { border: "default", className: "border-b", paddingY: "y-xs", paddingX: "x-md", children: _jsx(Text, { variant: "secondary", size: "xs", className: "uppercase tracking-wider", weight: "semibold", children: labels.quickLinks }) }), _jsx(Ul, { children: filteredSiteLinks.map((link) => {
|
|
296
296
|
const Icon = link.icon;
|
|
297
297
|
const itemIndex = quickLinkItems.findIndex((item) => item.link.href === link.href);
|
|
298
298
|
const isActive = activeIndex === itemIndex;
|
|
@@ -4,8 +4,10 @@ import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
|
|
|
4
4
|
import { sortBy } from "@mohasinac/appkit";
|
|
5
5
|
import React, { useState, useCallback } from "react";
|
|
6
6
|
import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
|
|
7
|
+
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
8
|
import { ConfirmDeleteModal, FilterChipGroup, ListingLayout, RowActionMenu } from "../../../ui";
|
|
8
9
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
|
+
import { TABLE_KEYS } from "../../../constants/table-keys";
|
|
9
11
|
import { SELLER_AUCTION_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
10
12
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
13
|
import { ROUTES } from "../../../constants";
|
|
@@ -13,6 +15,12 @@ import { toRecordArray, toRelativeDate, toCurrency, toStringValue, } from "../..
|
|
|
13
15
|
import { DataListingView } from "../../admin/components/DataListingView";
|
|
14
16
|
import { SELLER_BULK_ACTIONS, ROW_ACTION_META } from "../../products/constants/action-defs";
|
|
15
17
|
export function SellerAuctionsView({ children, onEditClick, onDelete, onBulkDelete, ...props }) {
|
|
18
|
+
// Independent of DataListingView's own internal useUrlTable() — both read
|
|
19
|
+
// and write the same URL, useUrlTable has no local state, so they stay in
|
|
20
|
+
// sync (same pattern SellerProductsView already uses for its "Show sold"
|
|
21
|
+
// toggle).
|
|
22
|
+
const toggleTable = useUrlTable({});
|
|
23
|
+
const showEnded = toggleTable.get(TABLE_KEYS.SHOW_ENDED) === "true";
|
|
16
24
|
const [deleteTargetId, setDeleteTargetId] = useState(null);
|
|
17
25
|
const { deletingId, handleDelete: performDelete } = useEntityDelete({
|
|
18
26
|
endpoint: SELLER_ENDPOINTS.PRODUCT_BY_ID,
|
|
@@ -38,7 +46,7 @@ export function SellerAuctionsView({ children, onEditClick, onDelete, onBulkDele
|
|
|
38
46
|
title: "Auctions",
|
|
39
47
|
searchPlaceholder: "Search auctions by product name",
|
|
40
48
|
emptyLabel: "No auctions found",
|
|
41
|
-
filterKeys: ["status"],
|
|
49
|
+
filterKeys: ["status", TABLE_KEYS.SHOW_ENDED],
|
|
42
50
|
defaultSort: sortBy("auctionEndDate", "ASC"),
|
|
43
51
|
queryKey: ["seller", "auctions", "listing"],
|
|
44
52
|
endpoint: SELLER_ENDPOINTS.AUCTIONS,
|
|
@@ -61,7 +69,24 @@ export function SellerAuctionsView({ children, onEditClick, onDelete, onBulkDele
|
|
|
61
69
|
getTotal: (response, mappedRows) => typeof response.meta?.total === "number"
|
|
62
70
|
? response.meta.total
|
|
63
71
|
: mappedRows.length,
|
|
64
|
-
buildFilters: (state) =>
|
|
72
|
+
buildFilters: (state) => {
|
|
73
|
+
// Previously missing entirely — the seller Auctions tab silently
|
|
74
|
+
// listed the entire seller catalog, not just auctions.
|
|
75
|
+
const parts = ["listingType==auction"];
|
|
76
|
+
if (state.status && state.status !== "All")
|
|
77
|
+
parts.push(sieveFilter("status", SIEVE_OP.EQ, state.status));
|
|
78
|
+
if (state[TABLE_KEYS.SHOW_ENDED] !== "true") {
|
|
79
|
+
parts.push(sieveFilter("auctionEndDate", SIEVE_OP.GTE, new Date().toISOString()));
|
|
80
|
+
}
|
|
81
|
+
return parts.join(",");
|
|
82
|
+
},
|
|
83
|
+
toggles: [
|
|
84
|
+
{
|
|
85
|
+
label: "Show ended",
|
|
86
|
+
active: showEnded,
|
|
87
|
+
onChange: (next) => toggleTable.set(TABLE_KEYS.SHOW_ENDED, next ? "true" : ""),
|
|
88
|
+
},
|
|
89
|
+
],
|
|
65
90
|
// Rule #7: bulk-action array sourced from the SELLER_BULK_ACTIONS preset.
|
|
66
91
|
buildBulkActions: onBulkDelete
|
|
67
92
|
? (selection) => SELLER_BULK_ACTIONS.auctions.map((id) => ({
|
|
@@ -151,7 +151,7 @@ function OrderDetailDrawer({ orderId, apiBase, onClose, }) {
|
|
|
151
151
|
};
|
|
152
152
|
const addr = order?.shippingAddress ?? {};
|
|
153
153
|
const addrLine = [addr.addressLine1, addr.city, addr.state, addr.pincode].filter(Boolean).join(", ");
|
|
154
|
-
return (_jsxs(SideDrawer, { isOpen: true, title: `Order ${order?.id ?? orderId}`, onClose: onClose, children: [loading && (_jsx(Row, { align: "center", justify: "center", padding: "y-4xl", children: _jsx(Div, { className: "h-6 w-6 animate-spin border-2 border-[var(--appkit-color-primary)] border-t-transparent", rounded: "full" }) })), fetchError && (_jsx(Div, { textSize: "sm", className: "mx-4 mt-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "lg", children: fetchError })), order && !loading && (_jsxs(Stack, { gap: "none", children: [_jsxs(Stack, { className: `flex-1 ${__O.yAuto}`, gap: "5", padding: "md", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsx(Badge, { variant: STATUS_BADGE_VARIANT[order.status?.toUpperCase()] ?? "default", children: order.status ?? "Unknown" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-secondary)]", children: toRelativeDate(order.createdAt) })] }), (order.items ?? []).length > 0 && (_jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-primary)] mb-2", weight: "semibold", children: "Items" }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: (order.items ?? []).map((item, i) => (_jsxs(Row, { paddingY: "y-xs-tall", padding: "x-sm", align: "center", justify: "between", gap: "3", children: [_jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { size: "sm", className: "truncate", weight: "medium", children: item.title ?? item.productId ?? "Item" }), _jsxs(Text, { size: "xs", className: "text-[var(--appkit-color-text-secondary)]", children: ["Qty: ", item.quantity ?? 1] })] }), _jsx(Text, { size: "sm", className: "shrink-0", weight: "medium", children: toCurrency(item.price ?? 0) })] }, i))) })] })), _jsxs(Row, { surface: "muted", padding: "inline", align: "center", justify: "between", rounded: "lg", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Total" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-primary)]", weight: "bold", children: toCurrency(order.totalAmount ?? 0) })] }), addrLine && (_jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "mb-1", weight: "semibold", children: "Shipping address" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-secondary)]", children: [String(addr.fullName ?? ""), addrLine].filter(Boolean).join(" · ") })] })), order.paymentMethod && (_jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "mb-1", weight: "semibold", children: "Payment" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-secondary)]", transform: "capitalize", children: order.paymentMethod })] })), order.emiEnabled && (_jsxs(Div, { className: "border-t border-[var(--appkit-color-border)]", padding: "t-md", children: [_jsxs(Row, { align: "center", justify: "between", className: "mb-2", children: [_jsxs(Text, { size: "sm", weight: "semibold", children: ["EMI plan ", order.emiTenureMonths ? `· ${order.emiTenureMonths} months` : ""] }), _jsx(Badge, { variant: order.emiComplete ? "success" : "warning", children: order.emiComplete ? "Fully paid" : "In progress" })] }), _jsxs(Row, { align: "center", justify: "between", className: "mb-2", children: [_jsx(Text, { size: "xs", color: "muted", children: "Token collected" }), _jsx(Text, { size: "xs", weight: "medium", children: toCurrency(order.emiTokenAmount ?? 0) })] }), _jsxs(Row, { align: "center", justify: "between", className: "mb-3", children: [_jsx(Text, { size: "xs", color: "muted", children: "Remaining balance" }), _jsx(Text, { size: "xs", weight: "medium", children: toCurrency(order.emiRemainingBalance ?? 0) })] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: (order.emiInstallments ?? []).map((inst) => (_jsxs(Row, { paddingY: "y-xs-tall", padding: "x-sm", align: "center", justify: "between", gap: "3", children: [_jsxs(Div, { className: "min-w-0", children: [_jsxs(Text, { size: "sm", weight: "medium", children: ["Installment ", inst.index, " \u00B7 ", toCurrency(inst.amount)] }), _jsx(Text, { size: "xs", color: "muted", children: inst.status === "paid" && inst.paidAt
|
|
154
|
+
return (_jsxs(SideDrawer, { isOpen: true, title: `Order ${order?.id ?? orderId}`, onClose: onClose, children: [loading && (_jsx(Row, { align: "center", justify: "center", padding: "y-4xl", children: _jsx(Div, { className: "h-6 w-6 animate-spin border-2 border-[var(--appkit-color-primary)] border-t-transparent", rounded: "full" }) })), fetchError && (_jsx(Div, { textSize: "sm", className: "mx-4 mt-4 border border-error/20", color: "error", surface: "danger-surface", padding: "inline", rounded: "lg", children: fetchError })), order && !loading && (_jsxs(Stack, { gap: "none", children: [_jsxs(Stack, { className: `flex-1 ${__O.yAuto}`, gap: "5", padding: "md", children: [_jsxs(Row, { align: "center", justify: "between", children: [_jsx(Badge, { variant: STATUS_BADGE_VARIANT[order.status?.toUpperCase()] ?? "default", children: order.status ?? "Unknown" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-secondary)]", children: toRelativeDate(order.createdAt) })] }), (order.items ?? []).length > 0 && (_jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-primary)] mb-2", weight: "semibold", children: "Items" }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: (order.items ?? []).map((item, i) => (_jsxs(Row, { paddingY: "y-xs-tall", padding: "x-sm", align: "center", justify: "between", gap: "3", children: [_jsxs(Div, { className: "min-w-0", children: [_jsx(Text, { size: "sm", className: "truncate", weight: "medium", children: item.title ?? item.productId ?? "Item" }), _jsxs(Text, { size: "xs", className: "text-[var(--appkit-color-text-secondary)]", children: ["Qty: ", item.quantity ?? 1] })] }), _jsx(Text, { size: "sm", className: "shrink-0", weight: "medium", children: toCurrency(item.price ?? 0) })] }, i))) })] })), order.giftWrapAddon && (_jsxs(Div, { className: "border border-[var(--appkit-color-primary-200)] dark:border-[var(--appkit-color-primary-800)]", surface: "subtle", padding: "inline", rounded: "lg", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "\uD83C\uDF81 Gift wrap requested" }), order.giftWrapMessage && (_jsxs(Text, { size: "sm", className: "mt-1 text-[var(--appkit-color-text-secondary)]", children: ["\u201C", order.giftWrapMessage, "\u201D"] }))] })), _jsxs(Row, { surface: "muted", padding: "inline", align: "center", justify: "between", rounded: "lg", children: [_jsx(Text, { size: "sm", weight: "semibold", children: "Total" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-primary)]", weight: "bold", children: toCurrency(order.totalAmount ?? 0) })] }), addrLine && (_jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "mb-1", weight: "semibold", children: "Shipping address" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-secondary)]", children: [String(addr.fullName ?? ""), addrLine].filter(Boolean).join(" · ") })] })), order.paymentMethod && (_jsxs(Div, { children: [_jsx(Text, { size: "sm", className: "mb-1", weight: "semibold", children: "Payment" }), _jsx(Text, { size: "sm", className: "text-[var(--appkit-color-text-secondary)]", transform: "capitalize", children: order.paymentMethod })] })), order.emiEnabled && (_jsxs(Div, { className: "border-t border-[var(--appkit-color-border)]", padding: "t-md", children: [_jsxs(Row, { align: "center", justify: "between", className: "mb-2", children: [_jsxs(Text, { size: "sm", weight: "semibold", children: ["EMI plan ", order.emiTenureMonths ? `· ${order.emiTenureMonths} months` : ""] }), _jsx(Badge, { variant: order.emiComplete ? "success" : "warning", children: order.emiComplete ? "Fully paid" : "In progress" })] }), _jsxs(Row, { align: "center", justify: "between", className: "mb-2", children: [_jsx(Text, { size: "xs", color: "muted", children: "Token collected" }), _jsx(Text, { size: "xs", weight: "medium", children: toCurrency(order.emiTokenAmount ?? 0) })] }), _jsxs(Row, { align: "center", justify: "between", className: "mb-3", children: [_jsx(Text, { size: "xs", color: "muted", children: "Remaining balance" }), _jsx(Text, { size: "xs", weight: "medium", children: toCurrency(order.emiRemainingBalance ?? 0) })] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)] border border-[var(--appkit-color-border)]", rounded: "lg", children: (order.emiInstallments ?? []).map((inst) => (_jsxs(Row, { paddingY: "y-xs-tall", padding: "x-sm", align: "center", justify: "between", gap: "3", children: [_jsxs(Div, { className: "min-w-0", children: [_jsxs(Text, { size: "sm", weight: "medium", children: ["Installment ", inst.index, " \u00B7 ", toCurrency(inst.amount)] }), _jsx(Text, { size: "xs", color: "muted", children: inst.status === "paid" && inst.paidAt
|
|
155
155
|
? `Paid ${toRelativeDate(inst.paidAt)}`
|
|
156
156
|
: inst.dueDate
|
|
157
157
|
? `Due ${toRelativeDate(inst.dueDate)}`
|
|
@@ -309,7 +309,7 @@ export function SellerOrdersView({ orderDetailApiBase = SELLER_ENDPOINTS.ORDERS,
|
|
|
309
309
|
const ids = selection.selectedIds.join(",");
|
|
310
310
|
void dispatch({
|
|
311
311
|
type: "NAVIGATE",
|
|
312
|
-
href: `${String(ROUTES.STORE.
|
|
312
|
+
href: `${String(ROUTES.STORE.PRINT_CENTER)}?type=order&ids=${ids}&autoprint=1`,
|
|
313
313
|
});
|
|
314
314
|
}, [selection.selectedIds, dispatch]);
|
|
315
315
|
const handleSetLocation = useCallback(async (loc) => {
|
|
@@ -8,7 +8,7 @@ import { Badge, Div, FilterChipGroup, ListingLayout, Row, RowActionMenu, SideDra
|
|
|
8
8
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
9
9
|
import { ADMIN_PAYOUT_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
10
10
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
|
-
import {
|
|
11
|
+
import { SELLER_BULK_ACTIONS, ROW_ACTION_META } from "../../products/constants/action-defs";
|
|
12
12
|
import { apiClient } from "../../../http";
|
|
13
13
|
import { toRecordArray, toRelativeDate, toCurrency, toStringValue, } from "../../admin/hooks/useAdminListingData";
|
|
14
14
|
import { DataListingView } from "../../admin/components/DataListingView";
|
|
@@ -77,7 +77,7 @@ export function SellerPayoutsView({ children, ...props }) {
|
|
|
77
77
|
title: "Payouts",
|
|
78
78
|
searchPlaceholder: "Search payouts by payout # or amount",
|
|
79
79
|
emptyLabel: "No payouts found",
|
|
80
|
-
filterKeys: ["status"],
|
|
80
|
+
filterKeys: ["status", "showAllPayouts"],
|
|
81
81
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
82
82
|
queryKey: ["seller", "payouts", "listing"],
|
|
83
83
|
endpoint: SELLER_ENDPOINTS.PAYOUTS,
|
|
@@ -102,17 +102,28 @@ export function SellerPayoutsView({ children, ...props }) {
|
|
|
102
102
|
getTotal: (response, mappedRows) => typeof response.meta?.total === "number"
|
|
103
103
|
? response.meta.total
|
|
104
104
|
: mappedRows.length,
|
|
105
|
-
buildFilters: (state) =>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
buildFilters: (state) => {
|
|
106
|
+
// An explicit status chip always wins. Otherwise, hide already-paid
|
|
107
|
+
// payouts by default — mirrors the isSold-hide-by-default precedent.
|
|
108
|
+
// Pipe-joined EQ (not !=) — see AdminPayoutsView.tsx for why.
|
|
109
|
+
if (state.status && state.status !== "All")
|
|
110
|
+
return sieveFilter("status", SIEVE_OP.EQ, state.status);
|
|
111
|
+
return state.showAllPayouts !== "true"
|
|
112
|
+
? sieveFilter("status", SIEVE_OP.EQ, "pending|processing|failed|cancelled")
|
|
113
|
+
: undefined;
|
|
114
|
+
},
|
|
115
|
+
// Rule #7: bulk-action array sourced from the SELLER_BULK_ACTIONS preset.
|
|
116
|
+
buildBulkActions: (selection) => SELLER_BULK_ACTIONS.payouts.map((id) => ({
|
|
117
|
+
id,
|
|
118
|
+
label: ROW_ACTION_META[id].label,
|
|
119
|
+
onClick: () => {
|
|
109
120
|
const rows = selection.selectedIds
|
|
110
|
-
.map((
|
|
121
|
+
.map((rowId) => rawByIdRef.current[rowId])
|
|
111
122
|
.filter((r) => Boolean(r));
|
|
112
123
|
handleExport(rows);
|
|
113
124
|
selection.clearSelection();
|
|
114
|
-
}
|
|
115
|
-
|
|
125
|
+
},
|
|
126
|
+
})),
|
|
116
127
|
renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
117
128
|
{
|
|
118
129
|
label: ACTIONS.STORE["view-payout"].label,
|
|
@@ -123,7 +134,10 @@ export function SellerPayoutsView({ children, ...props }) {
|
|
|
123
134
|
onClick: () => handleExport([rawByIdRef.current[row.id] ?? { id: row.id }]),
|
|
124
135
|
},
|
|
125
136
|
] })),
|
|
126
|
-
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_PAYOUT_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) })
|
|
137
|
+
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_PAYOUT_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Paid payouts", tabs: [
|
|
138
|
+
{ id: "", label: "Hide paid" },
|
|
139
|
+
{ id: "true", label: "Show all" },
|
|
140
|
+
], value: pendingFilters.showAllPayouts ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, showAllPayouts: id })), allId: "" })] })),
|
|
127
141
|
};
|
|
128
142
|
const expectedByDate = selectedPayout?.requestedAt
|
|
129
143
|
? new Date(new Date(selectedPayout.requestedAt).getTime() + PAYOUT_CYCLE_DAYS * 24 * 60 * 60 * 1000)
|
|
@@ -36,13 +36,13 @@ const PRE_ORDER_COLUMNS = [
|
|
|
36
36
|
header: "Status",
|
|
37
37
|
className: "w-28",
|
|
38
38
|
render: (row) => {
|
|
39
|
-
|
|
39
|
+
// Real ProductStatus is draft|published|in_review|archived — "active"
|
|
40
|
+
// fixed to "published"; "cancelled" is not a stored status value.
|
|
41
|
+
const variant = row.status === "published"
|
|
40
42
|
? "success"
|
|
41
43
|
: row.status === "draft"
|
|
42
44
|
? "default"
|
|
43
|
-
:
|
|
44
|
-
? "danger"
|
|
45
|
-
: "warning";
|
|
45
|
+
: "warning";
|
|
46
46
|
return _jsx(Badge, { variant: variant, children: row.status });
|
|
47
47
|
},
|
|
48
48
|
},
|
|
@@ -107,6 +107,14 @@ export function SellerPreOrdersView({ children, onDelete, ...props }) {
|
|
|
107
107
|
};
|
|
108
108
|
}),
|
|
109
109
|
getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
|
|
110
|
+
// No "Show closed" toggle here (unlike Auctions/Prize Draws): the public
|
|
111
|
+
// PreOrdersIndexListing equivalent filters on stockQuantity>0, a range
|
|
112
|
+
// filter, which cannot be combined with this view's title/
|
|
113
|
+
// preOrderDeliveryDate/createdAt sort options without violating
|
|
114
|
+
// Firestore's orderBy-must-match-the-range-field rule (confirmed by
|
|
115
|
+
// appkit/scripts/audit-listing-indices.mjs QUERY_UNSATISFIABLE). Fixing
|
|
116
|
+
// this properly would mean constraining sort choices per toggle state,
|
|
117
|
+
// out of scope for this pass — left as a real gap, not silently broken.
|
|
110
118
|
buildFilters: (state) => {
|
|
111
119
|
const status = state.status && state.status !== "All" ? sieveFilter("status", SIEVE_OP.EQ, state.status) : null;
|
|
112
120
|
return ["listingType==pre-order", status].filter(Boolean).join(",");
|
|
@@ -4,20 +4,24 @@ import { Row, SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit";
|
|
|
4
4
|
import { sortBy } from "@mohasinac/appkit";
|
|
5
5
|
import React, { useState, useCallback } from "react";
|
|
6
6
|
import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
|
|
7
|
+
import { useUrlTable } from "../../../react/hooks/useUrlTable";
|
|
7
8
|
import { Badge, ConfirmDeleteModal, Div, FilterChipGroup, ListingLayout, RowActionMenu, Span, Text } from "../../../ui";
|
|
8
9
|
import { MediaImage } from "../../media/MediaImage";
|
|
9
10
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
11
|
+
import { TABLE_KEYS } from "../../../constants/table-keys";
|
|
10
12
|
import { SELLER_PRIZE_DRAW_STATUS_TABS } from "../../admin/constants/filter-tabs";
|
|
11
13
|
import { ROUTES } from "../../../constants";
|
|
12
14
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
13
15
|
import { toRecordArray, toRelativeDate, toCurrency, toStringValue, } from "../../admin/hooks/useAdminListingData";
|
|
14
16
|
import { DataListingView } from "../../admin/components/DataListingView";
|
|
15
17
|
import { useActionDispatch } from "../../../react/hooks/use-action-dispatch";
|
|
18
|
+
// Real ProductStatus is draft|published|in_review|archived — "active"
|
|
19
|
+
// fixed to "published"; "ended"/"cancelled" removed since status never
|
|
20
|
+
// holds those values (ended is derived from prizeRevealWindowEnd, shown
|
|
21
|
+
// via the "Show closed" toolbar toggle below).
|
|
16
22
|
const STATUS_VARIANT = {
|
|
17
|
-
|
|
23
|
+
published: "success",
|
|
18
24
|
draft: "default",
|
|
19
|
-
ended: "secondary",
|
|
20
|
-
cancelled: "danger",
|
|
21
25
|
};
|
|
22
26
|
const PRIZE_DRAW_COLUMNS = [
|
|
23
27
|
{
|
|
@@ -58,6 +62,10 @@ const PRIZE_DRAW_COLUMNS = [
|
|
|
58
62
|
];
|
|
59
63
|
export function SellerPrizeDrawsView({ children, onDelete, ...props }) {
|
|
60
64
|
const dispatch = useActionDispatch();
|
|
65
|
+
// Independent of DataListingView's own internal useUrlTable() — see the
|
|
66
|
+
// same pattern in SellerAuctionsView.tsx.
|
|
67
|
+
const toggleTable = useUrlTable({});
|
|
68
|
+
const showClosed = toggleTable.get(TABLE_KEYS.SHOW_CLOSED) === "true";
|
|
61
69
|
const [deleteTargetId, setDeleteTargetId] = useState(null);
|
|
62
70
|
const { deletingId, handleDelete: performDelete } = useEntityDelete({
|
|
63
71
|
endpoint: SELLER_ENDPOINTS.PRODUCT_BY_ID,
|
|
@@ -77,7 +85,7 @@ export function SellerPrizeDrawsView({ children, onDelete, ...props }) {
|
|
|
77
85
|
title: "Prize Draws",
|
|
78
86
|
searchPlaceholder: "Search prize draws by name…",
|
|
79
87
|
emptyLabel: "No prize draws listed yet",
|
|
80
|
-
filterKeys: ["status"],
|
|
88
|
+
filterKeys: ["status", TABLE_KEYS.SHOW_CLOSED],
|
|
81
89
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
82
90
|
queryKey: ["seller", "prize-draws", "listing"],
|
|
83
91
|
endpoint: SELLER_ENDPOINTS.PRODUCTS,
|
|
@@ -106,8 +114,18 @@ export function SellerPrizeDrawsView({ children, onDelete, ...props }) {
|
|
|
106
114
|
getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
|
|
107
115
|
buildFilters: (state) => {
|
|
108
116
|
const status = state.status && state.status !== "All" ? sieveFilter("status", SIEVE_OP.EQ, state.status) : null;
|
|
109
|
-
|
|
117
|
+
const closed = state[TABLE_KEYS.SHOW_CLOSED] !== "true"
|
|
118
|
+
? sieveFilter("prizeRevealWindowEnd", SIEVE_OP.GTE, new Date().toISOString())
|
|
119
|
+
: null;
|
|
120
|
+
return ["listingType==prize-draw", status, closed].filter(Boolean).join(",");
|
|
110
121
|
},
|
|
122
|
+
toggles: [
|
|
123
|
+
{
|
|
124
|
+
label: "Show closed",
|
|
125
|
+
active: showClosed,
|
|
126
|
+
onChange: (next) => toggleTable.set(TABLE_KEYS.SHOW_CLOSED, next ? "true" : ""),
|
|
127
|
+
},
|
|
128
|
+
],
|
|
111
129
|
rowHrefTemplate: String(ROUTES.STORE.PRIZE_DRAWS_EDIT("{id}")),
|
|
112
130
|
renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
113
131
|
{
|
|
@@ -180,7 +180,7 @@ const TYPE_SPECIFIC_STEPS = {
|
|
|
180
180
|
function StepPricing({ values, onChange, listingType, }) {
|
|
181
181
|
const listingPlugin = pluginForMode(listingType);
|
|
182
182
|
const priceLabel = listingPlugin.priceLabel;
|
|
183
|
-
return (_jsxs(Stack, { gap: "md", children: [_jsx(Alert, { variant: "info", children: "All prices in Indian Rupees (\u20B9)." }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "price", label: priceLabel, type: "number", value: toRupeesString(values.price), onChange: (v) => onChange({ price: fromRupeesString(v) }), placeholder: "999" }), _jsx(FormField, { name: "compareAtPrice", label: "Compare-at Price (\u20B9, optional)", type: "number", value: toRupeesString(values.compareAtPrice), onChange: (v) => onChange({ compareAtPrice: v ? fromRupeesString(v) : undefined }), placeholder: "1299", helpText: "Original price shown as strikethrough" })] }), listingPlugin.showsStockQuantity && (_jsx(FormField, { name: "stockQuantity", label: "Stock Quantity", type: "number", value: String(values.stockQuantity ?? ""), onChange: (v) => onChange({ stockQuantity: Number(v) }), placeholder: "1" })), _jsxs(FormGroup, { columns: 2, children: [_jsx(Toggle, { checked: !!values.featured, onChange: (checked) => onChange({ featured: checked }), label: "Mark as Featured" }), _jsx(Toggle, { checked: !!values.isNew, onChange: (checked) => onChange({ isNew: checked }), label: "Mark as New Arrival" })] })] }));
|
|
183
|
+
return (_jsxs(Stack, { gap: "md", children: [_jsx(Alert, { variant: "info", children: "All prices in Indian Rupees (\u20B9)." }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "price", label: priceLabel, type: "number", value: toRupeesString(values.price), onChange: (v) => onChange({ price: fromRupeesString(v) }), placeholder: "999" }), _jsx(FormField, { name: "compareAtPrice", label: "Compare-at Price (\u20B9, optional)", type: "number", value: toRupeesString(values.compareAtPrice), onChange: (v) => onChange({ compareAtPrice: v ? fromRupeesString(v) : undefined }), placeholder: "1299", helpText: "Original price shown as strikethrough" })] }), listingPlugin.showsStockQuantity && (_jsx(FormField, { name: "stockQuantity", label: "Stock Quantity", type: "number", value: String(values.stockQuantity ?? ""), onChange: (v) => onChange({ stockQuantity: Number(v) }), placeholder: "1" })), _jsxs(FormGroup, { columns: 2, children: [_jsx(Toggle, { checked: !!values.featured, onChange: (checked) => onChange({ featured: checked }), label: "Mark as Featured" }), _jsx(Toggle, { checked: !!values.isNew, onChange: (checked) => onChange({ isNew: checked }), label: "Mark as New Arrival" })] }), _jsx(Toggle, { checked: !!values.allowOffers, onChange: (checked) => onChange({ allowOffers: checked, minOfferPercent: checked ? values.minOfferPercent ?? 50 : undefined }), label: "Allow buyer offers" }), values.allowOffers && (_jsx(FormField, { name: "minOfferPercent", label: "Minimum offer (% of price)", type: "number", value: String(values.minOfferPercent ?? ""), onChange: (v) => onChange({ minOfferPercent: Number(v) }), placeholder: "50" }))] }));
|
|
184
184
|
}
|
|
185
185
|
// ── Step: Shipping ────────────────────────────────────────────────────────
|
|
186
186
|
function StepShipping({ values, onChange, renderAddressSelector, }) {
|
|
@@ -65,7 +65,7 @@ function TypeDropdown({ active, onChange, }) {
|
|
|
65
65
|
flags["digital-code"] && { value: "digital-code", label: "Digital Code" },
|
|
66
66
|
flags.live && { value: "live", label: "Live" },
|
|
67
67
|
].filter(Boolean);
|
|
68
|
-
return (_jsxs(Row, { paddingX: "x-sm-lg-md", border: "bottom", padding: "y-xs", gap: "sm", children: [_jsx(Text, { className: "tracking-wide text-[var(--appkit-color-text-muted)]", size: "xs", weight: "semibold", transform: "uppercase", children: "Listing type" }), _jsx(Select, { options: options, value: active, onValueChange: (v) => onChange(v),
|
|
68
|
+
return (_jsxs(Row, { paddingX: "x-sm-lg-md", border: "bottom", padding: "y-xs", gap: "sm", children: [_jsx(Text, { className: "tracking-wide text-[var(--appkit-color-text-muted)]", size: "xs", weight: "semibold", transform: "uppercase", children: "Listing type" }), _jsx(Select, { options: options, value: active, onValueChange: (v) => onChange(v), wrapperClassName: "sm:max-w-xs", "aria-label": "Filter by listing type" })] }));
|
|
69
69
|
}
|
|
70
70
|
const PRODUCT_COLUMNS = [
|
|
71
71
|
{
|
|
@@ -90,13 +90,15 @@ const PRODUCT_COLUMNS = [
|
|
|
90
90
|
header: "Status",
|
|
91
91
|
className: "w-28",
|
|
92
92
|
render: (row) => {
|
|
93
|
-
|
|
93
|
+
// Real ProductStatus is draft|published|in_review|archived — "active"
|
|
94
|
+
// and "sold" are not stored status values (sold is tracked separately
|
|
95
|
+
// via isSold, shown via the "Show sold" toolbar toggle, not this
|
|
96
|
+
// column), so those two branches never matched and were dead code.
|
|
97
|
+
const variant = row.status === "published"
|
|
94
98
|
? "success"
|
|
95
99
|
: row.status === "draft"
|
|
96
100
|
? "default"
|
|
97
|
-
:
|
|
98
|
-
? "secondary"
|
|
99
|
-
: "danger";
|
|
101
|
+
: "danger";
|
|
100
102
|
return (_jsx(Badge, { variant: variant, children: row.status }));
|
|
101
103
|
},
|
|
102
104
|
},
|
|
@@ -224,7 +226,7 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, children, .
|
|
|
224
226
|
const ids = selection.selectedIds.join(",");
|
|
225
227
|
void dispatch({
|
|
226
228
|
type: "NAVIGATE",
|
|
227
|
-
href: `${String(ROUTES.STORE.
|
|
229
|
+
href: `${String(ROUTES.STORE.PRINT_CENTER)}?type=product&ids=${ids}&autoprint=1`,
|
|
228
230
|
});
|
|
229
231
|
}, [selection.selectedIds, dispatch]);
|
|
230
232
|
const handleSetLocation = useCallback(async (loc) => {
|
|
@@ -2,11 +2,18 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
4
4
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
5
|
-
import { Alert, Badge, BulkActionBar, Button, Checkbox, Div, Modal, Row, Select, SideDrawer, Span, Stack, Text, Textarea } from "../../../ui";
|
|
5
|
+
import { Alert, Badge, BulkActionBar, Button, Checkbox, Div, Input, Modal, Row, Select, SideDrawer, Span, Stack, Text, Textarea } from "../../../ui";
|
|
6
6
|
import { StackedViewShell } from "../../../ui";
|
|
7
7
|
import { useBottomActions } from "../../layout";
|
|
8
8
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
9
9
|
import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
|
+
import { sortBy } from "../../../constants/sort";
|
|
11
|
+
const SORT_OPTIONS = [
|
|
12
|
+
{ value: sortBy("createdAt", "DESC"), label: "Newest" },
|
|
13
|
+
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
14
|
+
{ value: sortBy("rating", "DESC"), label: "Highest rating" },
|
|
15
|
+
{ value: sortBy("rating", "ASC"), label: "Lowest rating" },
|
|
16
|
+
];
|
|
10
17
|
const __P = {
|
|
11
18
|
p3: "p-[var(--appkit-space-3)]",
|
|
12
19
|
p4: "p-[var(--appkit-space-4)]",
|
|
@@ -32,6 +39,8 @@ export function SellerReviewsView({ reviewsApiBase = SELLER_ENDPOINTS.REVIEWS, r
|
|
|
32
39
|
// Filters
|
|
33
40
|
const [rating, setRating] = useState("");
|
|
34
41
|
const [replied, setReplied] = useState("");
|
|
42
|
+
const [sort, setSort] = useState(SORT_OPTIONS[0].value);
|
|
43
|
+
const [search, setSearch] = useState("");
|
|
35
44
|
const [page, setPage] = useState(1);
|
|
36
45
|
// Reply drawer
|
|
37
46
|
const [replyTarget, setReplyTarget] = useState(null);
|
|
@@ -101,7 +110,7 @@ export function SellerReviewsView({ reviewsApiBase = SELLER_ENDPOINTS.REVIEWS, r
|
|
|
101
110
|
setLoading(true);
|
|
102
111
|
setError(null);
|
|
103
112
|
try {
|
|
104
|
-
const params = new URLSearchParams({ page: String(page), pageSize: "20" });
|
|
113
|
+
const params = new URLSearchParams({ page: String(page), pageSize: "20", sorts: sort });
|
|
105
114
|
if (rating)
|
|
106
115
|
params.set("rating", rating);
|
|
107
116
|
if (replied)
|
|
@@ -120,7 +129,19 @@ export function SellerReviewsView({ reviewsApiBase = SELLER_ENDPOINTS.REVIEWS, r
|
|
|
120
129
|
finally {
|
|
121
130
|
setLoading(false);
|
|
122
131
|
}
|
|
123
|
-
}, [reviewsApiBase, rating, replied, page]);
|
|
132
|
+
}, [reviewsApiBase, rating, replied, sort, page]);
|
|
133
|
+
// The API route has no ?q= support (only rating/replied/sorts) — filter
|
|
134
|
+
// the fetched page client-side, same category of post-fetch narrowing the
|
|
135
|
+
// route itself already does for `replied`.
|
|
136
|
+
const visibleReviews = useMemo(() => {
|
|
137
|
+
const q = search.trim().toLowerCase();
|
|
138
|
+
if (!q)
|
|
139
|
+
return reviews;
|
|
140
|
+
return reviews.filter((r) => r.productTitle?.toLowerCase().includes(q) ||
|
|
141
|
+
r.userName?.toLowerCase().includes(q) ||
|
|
142
|
+
r.comment?.toLowerCase().includes(q) ||
|
|
143
|
+
r.title?.toLowerCase().includes(q));
|
|
144
|
+
}, [reviews, search]);
|
|
124
145
|
useEffect(() => { fetchReviews(); }, [fetchReviews]);
|
|
125
146
|
useEffect(() => { fetchReviewsRef.current = fetchReviews; }, [fetchReviews, fetchReviewsRef]);
|
|
126
147
|
const openReply = (review) => {
|
|
@@ -161,7 +182,7 @@ export function SellerReviewsView({ reviewsApiBase = SELLER_ENDPOINTS.REVIEWS, r
|
|
|
161
182
|
},
|
|
162
183
|
] } } : {});
|
|
163
184
|
return (_jsxs(_Fragment, { children: [_jsx(StackedViewShell, { portal: "seller", title: "Reviews", sections: [
|
|
164
|
-
_jsxs(Stack, { gap: "lg", children: [_jsxs(Row, { align: "center", gap: "3", wrap: true, children: [_jsx(Select, { value: rating, onChange: (e) => { setRating(e.target.value); setPage(1); }, "aria-label": "Filter by rating", options: [
|
|
185
|
+
_jsxs(Stack, { gap: "lg", children: [_jsxs(Row, { align: "center", gap: "3", wrap: true, children: [_jsx(Input, { value: search, onChange: (e) => setSearch(e.target.value), placeholder: "Search product, reviewer, or comment\u2026", "aria-label": "Search reviews", className: "min-w-[220px]" }), _jsx(Select, { value: rating, onChange: (e) => { setRating(e.target.value); setPage(1); }, "aria-label": "Filter by rating", options: [
|
|
165
186
|
{ value: "", label: "All ratings" },
|
|
166
187
|
{ value: "5", label: "5 stars" },
|
|
167
188
|
{ value: "4", label: "4 stars" },
|
|
@@ -172,12 +193,12 @@ export function SellerReviewsView({ reviewsApiBase = SELLER_ENDPOINTS.REVIEWS, r
|
|
|
172
193
|
{ value: "", label: "All reply statuses" },
|
|
173
194
|
{ value: "true", label: "Store replied" },
|
|
174
195
|
{ value: "false", label: "Awaiting reply" },
|
|
175
|
-
] }), meta && (_jsxs(Text, { className: "text-[var(--appkit-color-text-muted)] ml-auto", size: "sm", children: [meta.total, " review", meta.total !== 1 ? "s" : ""] }))] }), error && _jsx(Alert, { variant: "error", children: error }), selectedIds.size > 0 && (_jsx(BulkActionBar, { selectedCount: selectedIds.size, onClearSelection: clearSelection, actions: [
|
|
196
|
+
] }), _jsx(Select, { value: sort, onChange: (e) => { setSort(e.target.value); setPage(1); }, "aria-label": "Sort reviews", options: SORT_OPTIONS }), meta && (_jsxs(Text, { className: "text-[var(--appkit-color-text-muted)] ml-auto", size: "sm", children: [meta.total, " review", meta.total !== 1 ? "s" : ""] }))] }), error && _jsx(Alert, { variant: "error", children: error }), selectedIds.size > 0 && (_jsx(BulkActionBar, { selectedCount: selectedIds.size, onClearSelection: clearSelection, actions: [
|
|
176
197
|
{
|
|
177
198
|
id: "bulk-reply",
|
|
178
199
|
label: "Reply to selected",
|
|
179
200
|
onClick: () => setBulkReplyOpen(true),
|
|
180
201
|
},
|
|
181
|
-
] })), loading ? (_jsx(Div, { className: "text-center", padding: "y-xl", children: _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "Loading reviews\u2026" }) })) :
|
|
202
|
+
] })), loading ? (_jsx(Div, { className: "text-center", padding: "y-xl", children: _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "Loading reviews\u2026" }) })) : visibleReviews.length === 0 ? (_jsx(Div, { className: "text-center", padding: "y-3xl", children: _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", children: "No reviews found." }) })) : (_jsx(Stack, { gap: "md", children: visibleReviews.map((review) => (_jsx(Div, { className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", rounded: "lg", padding: "md", children: _jsxs(Row, { align: "start", justify: "between", gap: "3", wrap: true, children: [_jsxs(Row, { align: "start", className: "flex-1 min-w-0", gap: "3", children: [_jsx(Checkbox, { checked: selectedIds.has(review.id), onChange: () => toggleSelected(review.id), "aria-label": "Select review" }), _jsxs(Div, { className: "flex-1 min-w-0", children: [_jsx(Text, { className: "truncate", weight: "medium", children: review.productTitle }), _jsxs(Row, { className: "mt-1", align: "center", gap: "sm", wrap: true, children: [_jsx(Stars, { rating: review.rating }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: ["by ", review.userName] }), review.verified && _jsx(Badge, { variant: "success", children: "Verified" }), statusBadge(review.status), _jsx(Badge, { variant: review.sellerReply ? "success" : "warning", children: review.sellerReply ? "Store replied" : "Awaiting store reply" })] }), review.title && _jsx(Text, { className: "mt-2", weight: "medium", children: review.title }), _jsx(Text, { className: "mt-1 text-[var(--appkit-color-text-secondary)] line-clamp-3", size: "sm", children: review.comment }), review.sellerReply && (_jsxs(Div, { className: "mt-2 pl-[0.75rem] border-l-2 border-[var(--appkit-color-primary)]", children: [_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: "Store reply:" }), _jsx(Text, { size: "sm", children: review.sellerReply })] }))] })] }), _jsxs(Row, { className: "flex-shrink-0", gap: "sm", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => openReply(review), children: review.sellerReply ? "Edit Reply" : ACTIONS.STORE["reply-review"].label }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setContestTarget(review), children: ACTIONS.STORE["contest-review"].label }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => setFeedbackTarget(review), children: ACTIONS.STORE["buyer-feedback"].label })] })] }) }, review.id))) })), meta && meta.totalPages > 1 && (_jsxs(Row, { align: "center", justify: "center", gap: "sm", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: () => setPage((p) => Math.max(1, p - 1)), disabled: page <= 1 || loading, children: "Previous" }), _jsxs(Text, { size: "sm", children: ["Page ", meta.page, " of ", meta.totalPages] }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => setPage((p) => p + 1), disabled: !meta.hasMore || loading, children: "Next" })] }))] }, "reviews"),
|
|
182
203
|
] }), _jsx(SideDrawer, { isOpen: !!replyTarget, onClose: () => setReplyTarget(null), title: replyTarget?.sellerReply ? "Edit Reply" : "Reply to Review", mode: "create", children: _jsxs(Stack, { gap: "md", className: `${__P.p4}`, children: [replyTarget && (_jsxs(Div, { className: `${__P.p3} bg-[var(--appkit-color-surface-muted)]`, rounded: "default", children: [_jsx(Stars, { rating: replyTarget.rating }), _jsx(Text, { className: "mt-1", size: "sm", children: replyTarget.comment })] })), replyError && _jsx(Alert, { variant: "error", children: replyError }), _jsxs(Div, { children: [_jsx(Text, { className: "mb-1.5", size: "sm", weight: "medium", children: "Store reply" }), _jsx(Textarea, { rows: 5, value: replyText, onChange: (e) => setReplyText(e.target.value), maxLength: 1000, placeholder: "Write your response to this review\u2026" }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)] mt-1", size: "xs", align: "end", children: [replyText.length, "/1000"] })] }), _jsxs(Row, { justify: "end", gap: "sm", children: [_jsx(Button, { variant: "outline", onClick: () => setReplyTarget(null), disabled: replySaving, children: "Cancel" }), _jsx(Button, { variant: "primary", onClick: handleReplySave, disabled: replySaving || !replyText.trim(), isLoading: replySaving, children: replyTarget?.sellerReply ? "Update Reply" : "Post Reply" })] })] }) }), _jsx(Modal, { isOpen: bulkReplyOpen, onClose: () => setBulkReplyOpen(false), title: `Bulk reply to ${selectedIds.size} review${selectedIds.size === 1 ? "" : "s"}`, actions: _jsxs(Row, { gap: "sm", children: [_jsx(Button, { variant: "ghost", onClick: () => setBulkReplyOpen(false), disabled: bulkSaving, children: "Cancel" }), _jsx(Button, { variant: "primary", onClick: () => void submitBulkReply(), disabled: !bulkReplyText.trim() || bulkSaving, isLoading: bulkSaving, children: "Send reply" })] }), children: _jsxs(Stack, { gap: "md", padding: "2xs", children: [_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "The same reply will be posted on all selected reviews." }), _jsx(Textarea, { value: bulkReplyText, onChange: (e) => setBulkReplyText(e.target.value), rows: 5, placeholder: "Thanks for your review\u2026", label: "Reply" })] }) }), _jsx(Modal, { isOpen: !!contestTarget, onClose: () => setContestTarget(null), title: "Contest this review", actions: _jsxs(Row, { gap: "sm", children: [_jsx(Button, { variant: "ghost", onClick: () => setContestTarget(null), children: "Cancel" }), _jsx(Button, { variant: "primary", onClick: () => void submitContest(), disabled: !contestReason.trim(), children: "Submit" })] }), children: _jsxs(Stack, { gap: "md", padding: "2xs", children: [_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Flag this review for admin investigation. Provide a clear reason \u2014 fake, abusive, off-topic, etc." }), _jsx(Textarea, { value: contestReason, onChange: (e) => setContestReason(e.target.value), rows: 4, label: "Reason" })] }) }), _jsx(Modal, { isOpen: !!feedbackTarget, onClose: () => setFeedbackTarget(null), title: "Send feedback to buyer", actions: _jsxs(Row, { gap: "sm", children: [_jsx(Button, { variant: "ghost", onClick: () => setFeedbackTarget(null), children: "Cancel" }), _jsx(Button, { variant: "primary", onClick: () => void submitFeedback(), disabled: !feedbackText.trim(), children: "Send" })] }), children: _jsxs(Stack, { gap: "md", padding: "2xs", children: [_jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Private message sent to the buyer's notification inbox. Does not appear on the public review." }), _jsx(Textarea, { value: feedbackText, onChange: (e) => setFeedbackText(e.target.value), rows: 4, label: "Feedback" })] }) })] }));
|
|
183
204
|
}
|
|
@@ -112,8 +112,6 @@ export { SellerAnalyticsAlertsView as StoreAnalyticsAlertsView } from "./SellerA
|
|
|
112
112
|
export type { SellerAnalyticsAlertsViewProps as StoreAnalyticsAlertsViewProps } from "./SellerAnalyticsAlertsView";
|
|
113
113
|
export { SellerStoreCategoriesView } from "./SellerStoreCategoriesView";
|
|
114
114
|
export type { SellerStoreCategoriesViewProps } from "./SellerStoreCategoriesView";
|
|
115
|
-
export { SellerTemplatesView } from "./SellerTemplatesView";
|
|
116
|
-
export type { SellerTemplatesViewProps } from "./SellerTemplatesView";
|
|
117
115
|
export { SellerPayoutMethodsView } from "./SellerPayoutMethodsView";
|
|
118
116
|
export type { SellerPayoutMethodsViewProps } from "./SellerPayoutMethodsView";
|
|
119
117
|
export { SellerShippingConfigsView } from "./SellerShippingConfigsView";
|
|
@@ -59,7 +59,6 @@ export { SellerGroupedListingsView as StoreGroupedListingsView } from "./SellerG
|
|
|
59
59
|
export { SellerAnalyticsAlertsView } from "./SellerAnalyticsAlertsView";
|
|
60
60
|
export { SellerAnalyticsAlertsView as StoreAnalyticsAlertsView } from "./SellerAnalyticsAlertsView";
|
|
61
61
|
export { SellerStoreCategoriesView } from "./SellerStoreCategoriesView";
|
|
62
|
-
export { SellerTemplatesView } from "./SellerTemplatesView";
|
|
63
62
|
export { SellerPayoutMethodsView } from "./SellerPayoutMethodsView";
|
|
64
63
|
export { SellerShippingConfigsView } from "./SellerShippingConfigsView";
|
|
65
64
|
export { SellerGoogleReviewsView } from "./SellerGoogleReviewsView";
|
|
@@ -65,7 +65,8 @@ const STANDARD_STEPS = [
|
|
|
65
65
|
{
|
|
66
66
|
step: "Pricing",
|
|
67
67
|
items: [
|
|
68
|
-
|
|
68
|
+
// audit-money-units-ok: instructs sellers there is NO paise conversion, not a stale reference
|
|
69
|
+
"Price — enter in Indian Rupees (₹). Stored as decimal rupees — no paise conversion.",
|
|
69
70
|
"isOnSale + salePrice — enable the sale badge and show a strikethrough price on the listing.",
|
|
70
71
|
"customFields — add any extra key/value pairs buyers find useful (e.g. PSA cert number, card number, print run).",
|
|
71
72
|
],
|
|
@@ -13,5 +13,5 @@ export function StoreProductsView({ storeSlug, labels = {}, renderProducts, rend
|
|
|
13
13
|
{ value: sortBy("createdAt", "ASC"), label: "Oldest" },
|
|
14
14
|
{ value: sortBy("price", "ASC"), label: "Price: Low to High" },
|
|
15
15
|
{ value: sortBy("price", "DESC"), label: "Price: High to Low" },
|
|
16
|
-
],
|
|
16
|
+
], wrapperClassName: "min-w-44" })), renderFilters: renderFilters, renderActiveFilters: renderActiveFilters, renderBulkActions: () => renderViewToggle?.(viewMode, setViewMode) ?? null, renderTable: () => renderProducts(items, isLoading), renderPagination: () => renderPagination?.(total) ?? null, total: total, isLoading: isLoading }) }));
|
|
17
17
|
}
|