@mohasinac/appkit 4.1.0 → 4.1.3
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/maintenance/cloud-logs-data.d.ts +50 -0
- package/dist/_internal/server/features/maintenance/cloud-logs-data.js +152 -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/onProductWrite.js +14 -2
- 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/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 +6 -7
- 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/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/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/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 +44 -2
- 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/schemas/firestore.d.ts +5 -0
- 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/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/hooks/useOrders.js +16 -4
- package/dist/features/orders/schemas/firestore.d.ts +5 -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 +1 -0
- package/dist/features/products/constants/action-defs.js +1 -0
- 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 +1 -1
- 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/bids-tester-seed-data.d.ts +2 -0
- package/dist/features/tester/seed-data/bids-tester-seed-data.js +37 -0
- package/dist/features/tester/seed-data/coupons-tester-seed-data.d.ts +2 -0
- package/dist/features/tester/seed-data/coupons-tester-seed-data.js +76 -0
- package/dist/features/tester/seed-data/events-tester-seed-data.js +50 -2
- package/dist/features/tester/seed-data/index.d.ts +3 -0
- package/dist/features/tester/seed-data/index.js +3 -0
- package/dist/features/tester/seed-data/orders-tester-seed-data.d.ts +2 -0
- package/dist/features/tester/seed-data/orders-tester-seed-data.js +265 -0
- package/dist/features/tester/seed-data/products-tester-seed-data.js +213 -8
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +294 -12
- 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/types/index.d.ts +1 -1
- package/dist/features/wishlist/types/index.d.ts +3 -0
- package/dist/index.d.ts +11 -4
- package/dist/index.js +15 -7
- package/dist/next/api/routeHandler.js +6 -10
- package/dist/next/components/NotFoundView.js +2 -1
- package/dist/next/routing/route-map.d.ts +4 -12
- package/dist/next/routing/route-map.js +2 -4
- package/dist/providers/db-firebase/admin.d.ts +1 -0
- package/dist/providers/db-firebase/admin.js +1 -1
- 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/index.d.ts +1 -1
- package/dist/seed/index.js +1 -1
- package/dist/seed/manifest.js +4 -4
- 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 +125 -106
- package/dist/seed/payouts-seed-data.js +42 -42
- package/dist/seed/products-auctions-seed-data.js +10 -2
- package/dist/seed/products-classifieds-seed-data.js +48 -5
- package/dist/seed/products-digital-codes-seed-data.js +48 -5
- package/dist/seed/products-live-items-seed-data.js +6 -1
- package/dist/seed/products-prize-draws-seed-data.js +58 -5
- 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 +82 -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 +2 -1
- package/dist/server.js +5 -5
- 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 +27 -19
|
@@ -19,9 +19,9 @@ export type PayoutStatus = "pending" | "processing" | "paid" | "failed";
|
|
|
19
19
|
export interface PayoutRefundDeduction {
|
|
20
20
|
orderId: string;
|
|
21
21
|
refundId: string;
|
|
22
|
-
/** Gross refund amount in
|
|
22
|
+
/** Gross refund amount in decimal rupees (for audit trail). */
|
|
23
23
|
refundedAmount: number;
|
|
24
|
-
/** Net amount deducted from the seller payout in
|
|
24
|
+
/** Net amount deducted from the seller payout in decimal rupees. */
|
|
25
25
|
deductedAmount: number;
|
|
26
26
|
reason: string;
|
|
27
27
|
appliedAt: Date;
|
|
@@ -26,6 +26,13 @@ function buildPreOrderFilters(params) {
|
|
|
26
26
|
const preOrderProductionStatus = sp(params, "preOrderProductionStatus");
|
|
27
27
|
if (preOrderProductionStatus)
|
|
28
28
|
parts.push(sieveFilter("preOrderProductionStatus", SIEVE_OP.EQ, preOrderProductionStatus));
|
|
29
|
+
// Mirror PreOrdersIndexListing's client-side default (same Root Cause pattern as
|
|
30
|
+
// auctions' dateFrom default — SSR initialData is seeded into React Query with
|
|
31
|
+
// staleTime:Infinity, so if this filter doesn't already exclude closed/out-of-stock
|
|
32
|
+
// pre-orders, the client never refetches and they leak into the default view).
|
|
33
|
+
const showClosed = sp(params, "showClosed") === "true";
|
|
34
|
+
if (!showClosed)
|
|
35
|
+
parts.push(sieveFilter("stockQuantity", SIEVE_OP.GT, 0));
|
|
29
36
|
return parts.join(",");
|
|
30
37
|
}
|
|
31
38
|
export async function PreOrdersListView({ searchParams = {} }) {
|
|
@@ -42,6 +42,11 @@ function buildArtStickersFilters(params) {
|
|
|
42
42
|
const storeId = sp(params, "seller");
|
|
43
43
|
if (storeId)
|
|
44
44
|
parts.push(sieveFilter(PRODUCT_FIELDS.STORE_ID, SIEVE_OP.EQ, storeId));
|
|
45
|
+
// Mirror ProductsIndexListing's client-side "Show sold" default — see
|
|
46
|
+
// ProductsIndexPageView.tsx's buildProductFilters for the full explanation.
|
|
47
|
+
const showSold = sp(params, "showSold") === "true";
|
|
48
|
+
if (!showSold)
|
|
49
|
+
parts.push(sieveFilter(PRODUCT_FIELDS.STOCK_QUANTITY, SIEVE_OP.GT, 0));
|
|
45
50
|
return sieveAnd(...parts);
|
|
46
51
|
}
|
|
47
52
|
export async function ArtStickersListView({ searchParams = {} }) {
|
|
@@ -44,7 +44,13 @@ export interface ProductDetailPageViewProps {
|
|
|
44
44
|
productFeatures?: ProductFeatureDocument[];
|
|
45
45
|
/** Site-wide COD availability (`siteSettings.payment.codEnabled`), threaded from the page's data layer. Drives the "Cash on Delivery" feature badge. */
|
|
46
46
|
codEnabled?: boolean;
|
|
47
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Fully-resolved EMI eligibility for this product — site-wide flag AND the
|
|
49
|
+
* seller's `StoreDocument.emiEnabled` opt-in AND price above
|
|
50
|
+
* `siteSettings.emi.minOrderValue`, computed by the page's data layer
|
|
51
|
+
* (mirrors `checkEmiEligibility()`'s checkout-time rule). Drives the "EMI
|
|
52
|
+
* Available" feature badge.
|
|
53
|
+
*/
|
|
48
54
|
emiEnabled?: boolean;
|
|
49
55
|
}
|
|
50
56
|
export declare function ProductDetailPageView({ slug, initialProduct, renderOfferAction, renderPrimaryActions, productFeatures, codEnabled, emiEnabled, }: ProductDetailPageViewProps): Promise<import("react").JSX.Element>;
|
|
@@ -132,7 +132,20 @@ export function ProductForm({ product, onChange, isReadonly = false, renderDescr
|
|
|
132
132
|
thumbnailUrl: media.thumbnailUrl,
|
|
133
133
|
},
|
|
134
134
|
});
|
|
135
|
-
}, onUpload: handleVideoUpload, accept: "video/*", maxSizeMB: 50, helperText: t("formVideoHelper"), onAbort: onMediaAbort })), _jsx(TagInput, { label: t("formTags"), value: product.tags || [], onChange: (tags) => update({ tags }), disabled: isReadonly, placeholder: t("formTagsPlaceholder") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(Checkbox, { label: t("formFeatured"), checked: !!product.featured, onChange: (e) => update({ featured: e.target.checked }), disabled: isReadonly }), _jsx(Checkbox, { label: t("formIsPromoted"), checked: !!product.isPromoted, onChange: (e) => update({ isPromoted: e.target.checked }), disabled: isReadonly }), _jsx(Checkbox, { label: t("formAllowShipBeforeEmiComplete"), checked: !!product.allowShipBeforeEmiComplete, onChange: (e) => update({ allowShipBeforeEmiComplete: e.target.checked }), disabled: isReadonly })
|
|
135
|
+
}, onUpload: handleVideoUpload, accept: "video/*", maxSizeMB: 50, helperText: t("formVideoHelper"), onAbort: onMediaAbort })), _jsx(TagInput, { label: t("formTags"), value: product.tags || [], onChange: (tags) => update({ tags }), disabled: isReadonly, placeholder: t("formTagsPlaceholder") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(Checkbox, { label: t("formFeatured"), checked: !!product.featured, onChange: (e) => update({ featured: e.target.checked }), disabled: isReadonly }), _jsx(Checkbox, { label: t("formIsPromoted"), checked: !!product.isPromoted, onChange: (e) => update({ isPromoted: e.target.checked }), disabled: isReadonly }), _jsx(Checkbox, { label: t("formAllowShipBeforeEmiComplete"), checked: !!product.allowShipBeforeEmiComplete, onChange: (e) => update({ allowShipBeforeEmiComplete: e.target.checked }), disabled: isReadonly }), _jsx(Checkbox, { label: t("formAllowOffers"), checked: !!product.allowOffers, onChange: (e) => update({
|
|
136
|
+
allowOffers: e.target.checked,
|
|
137
|
+
minOfferPercent: e.target.checked ? product.minOfferPercent ?? 50 : undefined,
|
|
138
|
+
}), disabled: isReadonly })] }), product.allowOffers && (_jsxs(_Fragment, { children: [_jsx(Alert, { variant: "info", title: t("formAllowOffersHelp"), children: t("formAllowOffersHelp") }), _jsx(FormField, { name: "minOfferPercent", label: t("formMinOfferPercent"), type: "number", value: String(product.minOfferPercent ?? ""), onChange: (value) => update({ minOfferPercent: Number(value) }), disabled: isReadonly, placeholder: "50" })] })), _jsx(Heading, { level: 4, className: "mt-4", children: t("sectionTaxGst") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "gstRate", label: t("formGstRate"), type: "select",
|
|
139
|
+
// Unset is semantically "exempt" (see ProductDocument.gstRate JSDoc),
|
|
140
|
+
// same as 0% — default the control to 0 so the select stays a fixed
|
|
141
|
+
// 5-option enum instead of adding a 6th "unset" placeholder entry.
|
|
142
|
+
value: String(product.gstRate ?? 0), onChange: (value) => update({ gstRate: Number(value) }), disabled: isReadonly, options: [
|
|
143
|
+
{ value: "0", label: "0%" },
|
|
144
|
+
{ value: "5", label: "5%" },
|
|
145
|
+
{ value: "12", label: "12%" },
|
|
146
|
+
{ value: "18", label: "18%" },
|
|
147
|
+
{ value: "28", label: "28%" },
|
|
148
|
+
] }), _jsx(FormField, { name: "hsnCode", label: t("formHsnCode"), type: "text", value: product.hsnCode ?? "", onChange: (value) => update({ hsnCode: value }), disabled: isReadonly, placeholder: "e.g. 9503" })] }), _jsx(Heading, { level: 4, className: "mt-4", children: t("sectionConditionShipping") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "condition", label: t("formCondition"), type: "select", value: product.condition || "new", onChange: (value) => update({
|
|
136
149
|
condition: value,
|
|
137
150
|
}), disabled: isReadonly, options: [
|
|
138
151
|
{ value: "new", label: t("formConditionNew") },
|
|
@@ -47,6 +47,13 @@ function buildProductFilters(params) {
|
|
|
47
47
|
if (freeShipping === "true") {
|
|
48
48
|
parts.push(sieveFilter(PRODUCT_FIELDS.SHIPPING_PAID_BY, SIEVE_OP.EQ, PRODUCT_FIELDS.SHIPPING_PAID_BY_VALUES.SELLER));
|
|
49
49
|
}
|
|
50
|
+
// Mirror ProductsIndexListing's client-side "Show sold" default (same Root Cause
|
|
51
|
+
// pattern as auctions' dateFrom default — SSR initialData is seeded into React
|
|
52
|
+
// Query with staleTime:Infinity, so if this filter doesn't already exclude sold /
|
|
53
|
+
// out-of-stock products, the client never refetches and they leak into the default view).
|
|
54
|
+
const showSold = sp(params, "showSold") === "true";
|
|
55
|
+
if (!showSold)
|
|
56
|
+
parts.push(sieveFilter(PRODUCT_FIELDS.STOCK_QUANTITY, SIEVE_OP.GT, 0));
|
|
50
57
|
return sieveAnd(...parts);
|
|
51
58
|
}
|
|
52
59
|
export async function ProductsIndexPageView({ searchParams = {} }) {
|
|
@@ -350,6 +350,7 @@ export declare const ADMIN_BULK_ACTIONS: {
|
|
|
350
350
|
};
|
|
351
351
|
export declare const SELLER_BULK_ACTIONS: {
|
|
352
352
|
readonly products: readonly ["row-publish", "row-archive", "row-delete"];
|
|
353
|
+
readonly payouts: readonly ["row-export"];
|
|
353
354
|
readonly coupons: readonly ["row-archive", "row-delete"];
|
|
354
355
|
readonly bids: readonly ["row-cancel"];
|
|
355
356
|
readonly bundles: readonly ["row-delete"];
|
|
@@ -451,6 +451,7 @@ export const ADMIN_BULK_ACTIONS = {
|
|
|
451
451
|
// Seller bulk actions per listing entity
|
|
452
452
|
export const SELLER_BULK_ACTIONS = {
|
|
453
453
|
products: [ROW_ACTION_ID.PUBLISH, ROW_ACTION_ID.ARCHIVE, ROW_ACTION_ID.DELETE],
|
|
454
|
+
payouts: [ROW_ACTION_ID.EXPORT],
|
|
454
455
|
coupons: [ROW_ACTION_ID.ARCHIVE, ROW_ACTION_ID.DELETE],
|
|
455
456
|
bids: [ROW_ACTION_ID.CANCEL],
|
|
456
457
|
bundles: [ROW_ACTION_ID.DELETE],
|
|
@@ -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) => ({
|
|
@@ -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) => {
|