@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
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { normalizeError } from "../../../errors/normalize";
|
|
4
|
-
import { useApiMutation } from "@mohasinac/appkit/client";
|
|
4
|
+
import { useApiMutation, useBulkEvent, RTDB_PATHS } from "@mohasinac/appkit/client";
|
|
5
5
|
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
|
|
6
6
|
import { sortBy } from "@mohasinac/appkit";
|
|
7
|
-
import React, { useCallback, useState } from "react";
|
|
7
|
+
import React, { useCallback, useEffect, useState } from "react";
|
|
8
8
|
import { useQueryClient } from "@tanstack/react-query";
|
|
9
9
|
import { Button, ConfirmDeleteModal, FilterChipGroup, ListingLayout, RowActionMenu, useToast } from "../../../ui";
|
|
10
10
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
@@ -33,24 +33,55 @@ export function AdminNewsletterView({ children, onBulkUnsubscribe, ...props }) {
|
|
|
33
33
|
showToast(err?.message ?? "Failed to unsubscribe.", "error");
|
|
34
34
|
},
|
|
35
35
|
});
|
|
36
|
+
// Export runs as an async `newsletterExport` job (Async Job Primitive) —
|
|
37
|
+
// the route only enqueues; this subscribes to the same RTDB bulk-event
|
|
38
|
+
// channel every other async job in the admin panel uses.
|
|
39
|
+
const [exporting, setExporting] = useState(false);
|
|
40
|
+
const exportEvent = useBulkEvent({ rtdbPath: RTDB_PATHS.BULK_EVENTS });
|
|
36
41
|
const handleExportCsv = useCallback(async () => {
|
|
42
|
+
setExporting(true);
|
|
37
43
|
try {
|
|
38
44
|
const response = await fetch(ADMIN_ENDPOINTS.NEWSLETTER_EXPORT);
|
|
39
45
|
if (!response.ok)
|
|
40
46
|
throw new Error("Export failed");
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
const body = (await response.json());
|
|
48
|
+
const { jobId, customToken } = body.data ?? {};
|
|
49
|
+
if (jobId && customToken) {
|
|
50
|
+
exportEvent.subscribe(jobId, customToken);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
throw new Error("Export failed to start");
|
|
54
|
+
}
|
|
48
55
|
}
|
|
49
56
|
catch (_err) {
|
|
50
57
|
void normalizeError(_err);
|
|
58
|
+
setExporting(false);
|
|
51
59
|
showToast("Failed to export CSV.", "error");
|
|
52
60
|
}
|
|
53
|
-
}, [showToast]);
|
|
61
|
+
}, [showToast, exportEvent]);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (exportEvent.status === "success") {
|
|
64
|
+
setExporting(false);
|
|
65
|
+
const csv = exportEvent.result?.data?.csv;
|
|
66
|
+
if (csv) {
|
|
67
|
+
const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
|
|
68
|
+
const url = URL.createObjectURL(blob);
|
|
69
|
+
const a = document.createElement("a");
|
|
70
|
+
a.href = url;
|
|
71
|
+
a.download = `newsletter-subscribers-${new Date().toISOString().slice(0, 10)}.csv`;
|
|
72
|
+
a.click();
|
|
73
|
+
URL.revokeObjectURL(url);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
showToast("Export finished with no data.", "error");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else if (exportEvent.status === "failed" || exportEvent.status === "timeout") {
|
|
80
|
+
setExporting(false);
|
|
81
|
+
showToast(exportEvent.error ?? "Failed to export CSV.", "error");
|
|
82
|
+
}
|
|
83
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
84
|
+
}, [exportEvent.status]);
|
|
54
85
|
if (React.Children.count(children) > 0) {
|
|
55
86
|
return (_jsx(ListingLayout, { portal: "admin", ...props, children: children }));
|
|
56
87
|
}
|
|
@@ -85,7 +116,7 @@ export function AdminNewsletterView({ children, onBulkUnsubscribe, ...props }) {
|
|
|
85
116
|
return mappedRows.length;
|
|
86
117
|
},
|
|
87
118
|
buildFilters: (state) => state.status && state.status !== "All" ? sieveFilter("status", SIEVE_OP.EQ, state.status) : undefined,
|
|
88
|
-
toolbarExtra: (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: handleExportCsv, children: ACTIONS.ADMIN["export-csv"].label })),
|
|
119
|
+
toolbarExtra: (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: handleExportCsv, isLoading: exporting, disabled: exporting, children: ACTIONS.ADMIN["export-csv"].label })),
|
|
89
120
|
buildBulkActions: onBulkUnsubscribe
|
|
90
121
|
? (selection) => [
|
|
91
122
|
buildBulkAction(ACTIONS.ADMIN["unsubscribe-newsletter"], async () => {
|
|
@@ -8,7 +8,7 @@ import { useQueryClient } from "@tanstack/react-query";
|
|
|
8
8
|
import { ConfirmDeleteModal, FilterChipGroup, ListingLayout, RowActionMenu, useToast } from "../../../ui";
|
|
9
9
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
10
10
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
11
|
-
import { ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
|
|
11
|
+
import { ADMIN_BULK_ACTIONS, ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
|
|
12
12
|
import { useBulkAction } from "../../../react";
|
|
13
13
|
import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
|
|
14
14
|
import { DataListingView } from "./DataListingView";
|
|
@@ -64,7 +64,7 @@ export function AdminNotificationsView({ children, ...props }) {
|
|
|
64
64
|
title: "Notifications",
|
|
65
65
|
searchPlaceholder: "Search by title or user ID",
|
|
66
66
|
emptyLabel: "No notifications found",
|
|
67
|
-
filterKeys: ["type"],
|
|
67
|
+
filterKeys: ["type", "readState"],
|
|
68
68
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
69
69
|
queryKey: ["admin", "notifications", "listing"],
|
|
70
70
|
endpoint: ADMIN_ENDPOINTS.ADMIN_NOTIFICATIONS,
|
|
@@ -83,25 +83,33 @@ export function AdminNotificationsView({ children, ...props }) {
|
|
|
83
83
|
type: toStringValue(item.type, ""),
|
|
84
84
|
})),
|
|
85
85
|
getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
|
|
86
|
-
buildFilters: (state) =>
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
86
|
+
buildFilters: (state) => {
|
|
87
|
+
const parts = [];
|
|
88
|
+
if (state.type && state.type !== "All")
|
|
89
|
+
parts.push(sieveFilter("type", SIEVE_OP.EQ, state.type));
|
|
90
|
+
if (state.readState === "unread")
|
|
91
|
+
parts.push(sieveFilter("isRead", SIEVE_OP.EQ, false));
|
|
92
|
+
else if (state.readState === "read")
|
|
93
|
+
parts.push(sieveFilter("isRead", SIEVE_OP.EQ, true));
|
|
94
|
+
return parts.join(",") || undefined;
|
|
95
|
+
},
|
|
96
|
+
// Rule #7: bulk-action array sourced from the ADMIN_BULK_ACTIONS preset.
|
|
97
|
+
buildBulkActions: (selection) => ADMIN_BULK_ACTIONS.notifications.map((id) => ({
|
|
98
|
+
id,
|
|
99
|
+
label: ROW_ACTION_META[id].label,
|
|
100
|
+
destructive: ROW_ACTION_META[id].destructive,
|
|
101
|
+
variant: id === ROW_ACTION_ID.MARK_READ ? "primary" : "secondary",
|
|
102
|
+
loading: id === ROW_ACTION_ID.MARK_READ ? bulkNotifs.isLoading : undefined,
|
|
103
|
+
onClick: () => {
|
|
104
|
+
if (id === ROW_ACTION_ID.MARK_READ) {
|
|
94
105
|
void bulkNotifs.execute({ action: "mark_read", ids: selection.selectedIds });
|
|
95
106
|
selection.clearSelection();
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
label: ACTIONS.ADMIN["delete-notification"].label,
|
|
101
|
-
variant: "secondary",
|
|
102
|
-
onClick: () => setBulkDeleteIds(selection.selectedIds),
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
setBulkDeleteIds(selection.selectedIds);
|
|
110
|
+
}
|
|
103
111
|
},
|
|
104
|
-
|
|
112
|
+
})),
|
|
105
113
|
renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
|
|
106
114
|
{
|
|
107
115
|
label: ACTIONS.ADMIN["resend-notification"].label,
|
|
@@ -113,7 +121,11 @@ export function AdminNotificationsView({ children, ...props }) {
|
|
|
113
121
|
onClick: () => setDeleteTarget(row),
|
|
114
122
|
},
|
|
115
123
|
] })),
|
|
116
|
-
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Type", tabs: NOTIF_TYPES.map((opt) => ({ id: opt, label: opt })), value: pendingFilters.type || "All", onChange: (v) => setPendingFilters((p) => ({ ...p, type: v })) })
|
|
124
|
+
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Type", tabs: NOTIF_TYPES.map((opt) => ({ id: opt, label: opt })), value: pendingFilters.type || "All", onChange: (v) => setPendingFilters((p) => ({ ...p, type: v })) }), _jsx(FilterChipGroup, { label: "Read state", tabs: [
|
|
125
|
+
{ id: "", label: "All" },
|
|
126
|
+
{ id: "unread", label: "Unread" },
|
|
127
|
+
{ id: "read", label: "Read" },
|
|
128
|
+
], value: pendingFilters.readState ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, readState: id })), allId: "" })] })),
|
|
117
129
|
};
|
|
118
130
|
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(ConfirmDeleteModal, { isOpen: Boolean(deleteTarget) || Boolean(bulkDeleteIds), onClose: () => {
|
|
119
131
|
setDeleteTarget(null);
|
|
@@ -3,10 +3,12 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
3
3
|
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
|
|
4
4
|
import { sortBy } from "@mohasinac/appkit";
|
|
5
5
|
import React, { useState, useCallback } from "react";
|
|
6
|
+
import { useRouter } from "next/navigation";
|
|
6
7
|
import { FilterChipGroup, ListingLayout, useToast } from "../../../ui";
|
|
7
8
|
import { apiClient } from "../../../http";
|
|
8
9
|
import { QuickEditMenu } from "./QuickEditMenu";
|
|
9
10
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
11
|
+
import { ROUTES } from "../../../next/routing/route-map";
|
|
10
12
|
import { ADMIN_BULK_ACTIONS, ROW_ACTION_META, ROW_ACTION_ID } from "../../products/constants/action-defs";
|
|
11
13
|
import { ADMIN_ORDER_STATUS_TABS } from "../constants/filter-tabs";
|
|
12
14
|
import { toRecordArray, toRelativeDate, toCurrency, toStringValue, } from "../hooks/useAdminListingData";
|
|
@@ -14,6 +16,7 @@ import { DataListingView } from "./DataListingView";
|
|
|
14
16
|
import { AdminOrderEditorView } from "./AdminOrderEditorView";
|
|
15
17
|
export function AdminOrdersView({ children, ...props }) {
|
|
16
18
|
const { showToast } = useToast();
|
|
19
|
+
const router = useRouter();
|
|
17
20
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
18
21
|
const [selectedRow, setSelectedRow] = useState(null);
|
|
19
22
|
const handleQuickStatus = useCallback(async (id, status) => {
|
|
@@ -94,6 +97,10 @@ export function AdminOrdersView({ children, ...props }) {
|
|
|
94
97
|
setDrawerOpen(true);
|
|
95
98
|
},
|
|
96
99
|
},
|
|
100
|
+
{
|
|
101
|
+
label: "Open full page",
|
|
102
|
+
onClick: () => router.push(String(ROUTES.ADMIN.ORDER_DETAIL(row.id))),
|
|
103
|
+
},
|
|
97
104
|
{
|
|
98
105
|
label: "Update status",
|
|
99
106
|
separator: true,
|
|
@@ -104,7 +104,7 @@ export function AdminPayoutsView({ children, ...props }) {
|
|
|
104
104
|
title: "Payouts",
|
|
105
105
|
searchPlaceholder: "Search stores, payout IDs, or order groups",
|
|
106
106
|
emptyLabel: "No payouts found",
|
|
107
|
-
filterKeys: ["status"],
|
|
107
|
+
filterKeys: ["status", "showAllPayouts"],
|
|
108
108
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
109
109
|
queryKey: ["admin", "payouts", "listing"],
|
|
110
110
|
endpoint: ADMIN_ENDPOINTS.PAYOUTS,
|
|
@@ -121,7 +121,19 @@ export function AdminPayoutsView({ children, ...props }) {
|
|
|
121
121
|
updatedAt: toRelativeDate(item.updatedAt ?? item.createdAt),
|
|
122
122
|
})),
|
|
123
123
|
getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
|
|
124
|
-
buildFilters: (state) =>
|
|
124
|
+
buildFilters: (state) => {
|
|
125
|
+
// An explicit status chip always wins. Otherwise, hide the noise
|
|
126
|
+
// (already-paid payouts) by default — mirrors the isSold-hide-by-
|
|
127
|
+
// default precedent — with an escape hatch to see everything.
|
|
128
|
+
// Pipe-joined EQ, not NEQ — the Firestore rule requiring orderBy to
|
|
129
|
+
// match the inequality field does not apply to IN queries, so this
|
|
130
|
+
// stays compatible with the default createdAt sort.
|
|
131
|
+
if (state.status && state.status !== "All")
|
|
132
|
+
return sieveFilter("status", SIEVE_OP.EQ, state.status);
|
|
133
|
+
return state.showAllPayouts !== "true"
|
|
134
|
+
? sieveFilter("status", SIEVE_OP.EQ, "pending|processing|failed|cancelled")
|
|
135
|
+
: undefined;
|
|
136
|
+
},
|
|
125
137
|
toolbarExtra: (_jsxs(_Fragment, { children: [_jsx(Button, { type: "button", action: ACTIONS.ADMIN["calculate-payouts"], size: "sm", isLoading: calculating, disabled: calculating, onClick: handleCalculatePayouts }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: handleExportCsv, children: ACTIONS.ADMIN["export-csv"].label })] })),
|
|
126
138
|
// Rule #7: bulk-action array sourced from the ADMIN_BULK_ACTIONS preset.
|
|
127
139
|
buildBulkActions: (selection) => ADMIN_BULK_ACTIONS.payouts.map((id) => ({
|
|
@@ -140,7 +152,10 @@ export function AdminPayoutsView({ children, ...props }) {
|
|
|
140
152
|
disabled: row.status.toLowerCase() === "paid" || row.status.toLowerCase() === "cancelled",
|
|
141
153
|
},
|
|
142
154
|
] })),
|
|
143
|
-
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_PAYOUT_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) })
|
|
155
|
+
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: [
|
|
156
|
+
{ id: "", label: "Hide paid" },
|
|
157
|
+
{ id: "true", label: "Show all" },
|
|
158
|
+
], value: pendingFilters.showAllPayouts ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, showAllPayouts: id })), allId: "" })] })),
|
|
144
159
|
};
|
|
145
160
|
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(Modal, { isOpen: markPaidOpen, onClose: closePaidModal, title: "Mark payout as paid", children: _jsxs(Form, { onSubmit: (e) => {
|
|
146
161
|
e.preventDefault();
|
|
@@ -108,7 +108,7 @@ export function AdminProductsView({ children, ...props }) {
|
|
|
108
108
|
title: "Products",
|
|
109
109
|
searchPlaceholder: "Search products, SKUs, or seller names",
|
|
110
110
|
emptyLabel: "No products found",
|
|
111
|
-
filterKeys: ["status", "type", "showSold"],
|
|
111
|
+
filterKeys: ["status", "type", "showSold", "showEnded", "showClosed"],
|
|
112
112
|
defaultSort: sortBy("createdAt", "DESC"),
|
|
113
113
|
queryKey: ["admin", "products", "listing"],
|
|
114
114
|
endpoint: ADMIN_ENDPOINTS.PRODUCTS,
|
|
@@ -149,6 +149,15 @@ export function AdminProductsView({ children, ...props }) {
|
|
|
149
149
|
const typeFilter = TYPE_FILTER_MAP[state.type];
|
|
150
150
|
if (typeFilter)
|
|
151
151
|
parts.push(typeFilter);
|
|
152
|
+
// Ended/closed only apply to auctions/prize-draws — auctionEndDate
|
|
153
|
+
// and prizeRevealWindowEnd do not exist on other listing types, so
|
|
154
|
+
// these are no-ops unless the matching Type tab is also selected.
|
|
155
|
+
if (typeFilter === "listingType==auction" && state.showEnded !== "true") {
|
|
156
|
+
parts.push(sieveFilter("auctionEndDate", SIEVE_OP.GTE, new Date().toISOString()));
|
|
157
|
+
}
|
|
158
|
+
if (typeFilter === "listingType==prize-draw" && state.showClosed !== "true") {
|
|
159
|
+
parts.push(sieveFilter("prizeRevealWindowEnd", SIEVE_OP.GTE, new Date().toISOString()));
|
|
160
|
+
}
|
|
152
161
|
return parts.join(",") || undefined;
|
|
153
162
|
},
|
|
154
163
|
primaryAction: {
|
|
@@ -222,7 +231,13 @@ export function AdminProductsView({ children, ...props }) {
|
|
|
222
231
|
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_PRODUCT_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Type", tabs: ADMIN_PRODUCT_LISTING_TYPE_TABS, value: pendingFilters.type ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, type: id })) }), _jsx(FilterChipGroup, { label: "Sold", tabs: [
|
|
223
232
|
{ id: "", label: "Hide sold" },
|
|
224
233
|
{ id: "true", label: "Show sold" },
|
|
225
|
-
], value: pendingFilters.showSold ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, showSold: id })), allId: "" })
|
|
234
|
+
], value: pendingFilters.showSold ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, showSold: id })), allId: "" }), _jsx(FilterChipGroup, { label: "Ended", tabs: [
|
|
235
|
+
{ id: "", label: "Hide ended" },
|
|
236
|
+
{ id: "true", label: "Show ended" },
|
|
237
|
+
], value: pendingFilters.showEnded ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, showEnded: id })), allId: "" }), _jsx(FilterChipGroup, { label: "Closed", tabs: [
|
|
238
|
+
{ id: "", label: "Hide closed" },
|
|
239
|
+
{ id: "true", label: "Show closed" },
|
|
240
|
+
], value: pendingFilters.showClosed ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, showClosed: id })), allId: "" })] })),
|
|
226
241
|
renderEditor: ({ editId, closePanel }) => (_jsx(AdminProductEditorView, { productId: editId ?? undefined, initialData: editId ? rawByIdRef.current[editId] : undefined, onSaved: closePanel, onDeleted: closePanel, embedded: true })),
|
|
227
242
|
resolveEditorTitle: ({ isCreate }) => (isCreate ? "Add Product" : "Edit Product"),
|
|
228
243
|
};
|
|
@@ -99,8 +99,22 @@ export function AdminReviewsView({ children, ...props }) {
|
|
|
99
99
|
getTotal: (response, mappedRows) => typeof response.total === "number" ? response.total : mappedRows.length,
|
|
100
100
|
buildFilters: (f) => {
|
|
101
101
|
const parts = [];
|
|
102
|
-
|
|
102
|
+
// Default (f.status === "") to pending-only — hides the moderation
|
|
103
|
+
// noise (already-approved/rejected reviews) without a redundant
|
|
104
|
+
// separate toggle, since Status is already a 1-click 3-value chip
|
|
105
|
+
// group. Explicit "All" is reachable via renderFilterPanel allId
|
|
106
|
+
// override below, which makes the FilterChipGroup write the literal
|
|
107
|
+
// string "All" (not "") when clicked — otherwise "never touched" and
|
|
108
|
+
// "explicitly chose All" are indistinguishable.
|
|
109
|
+
if (f.status === "All") {
|
|
110
|
+
// explicit "show everything" — no status filter
|
|
111
|
+
}
|
|
112
|
+
else if (f.status) {
|
|
103
113
|
parts.push(sieveFilter("status", SIEVE_OP.EQ, f.status));
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
parts.push(sieveFilter("status", SIEVE_OP.EQ, "pending"));
|
|
117
|
+
}
|
|
104
118
|
if (f.rating && f.rating !== "All")
|
|
105
119
|
parts.push(sieveFilter("rating", SIEVE_OP.EQ, f.rating));
|
|
106
120
|
return parts.join(",") || undefined;
|
|
@@ -145,7 +159,7 @@ export function AdminReviewsView({ children, ...props }) {
|
|
|
145
159
|
onClick: () => handleViewReview(row),
|
|
146
160
|
},
|
|
147
161
|
] })),
|
|
148
|
-
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_REVIEW_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) }), _jsx(FilterChipGroup, { label: "Rating", tabs: ADMIN_REVIEW_RATING_TABS, value: pendingFilters.rating ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, rating: id })) })] })),
|
|
162
|
+
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsxs(_Fragment, { children: [_jsx(FilterChipGroup, { label: "Status (defaults to Pending)", tabs: ADMIN_REVIEW_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })), allId: "__all__" }), _jsx(FilterChipGroup, { label: "Rating", tabs: ADMIN_REVIEW_RATING_TABS, value: pendingFilters.rating ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, rating: id })) })] })),
|
|
149
163
|
};
|
|
150
164
|
return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), _jsx(ViewReviewModal, { review: viewReview, isOpen: Boolean(viewReview), onClose: () => setViewReview(null) }), _jsx(Modal, { isOpen: replyOpen, onClose: () => {
|
|
151
165
|
setReplyOpen(false);
|
|
@@ -3,9 +3,11 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { sieveFilter, SIEVE_OP } from "@mohasinac/appkit";
|
|
4
4
|
import { sortBy } from "@mohasinac/appkit";
|
|
5
5
|
import React, { useState } from "react";
|
|
6
|
+
import { useRouter } from "next/navigation";
|
|
6
7
|
import { FilterChipGroup, ListingLayout, RowActionMenu, Span, Stack, Text } from "../../../ui";
|
|
7
8
|
import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
|
|
8
9
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
10
|
+
import { ROUTES } from "../../../next/routing/route-map";
|
|
9
11
|
import { ADMIN_SCAMMER_STATUS_TABS } from "../constants/filter-tabs";
|
|
10
12
|
import { toRecordArray, toRelativeDate, toStringValue, } from "../hooks/useAdminListingData";
|
|
11
13
|
import { DataListingView } from "./DataListingView";
|
|
@@ -36,6 +38,7 @@ const SCAMMER_COLUMNS = [
|
|
|
36
38
|
},
|
|
37
39
|
];
|
|
38
40
|
export function AdminScammersView({ children, ...props }) {
|
|
41
|
+
const router = useRouter();
|
|
39
42
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
40
43
|
const [selectedRow, setSelectedRow] = useState(null);
|
|
41
44
|
if (React.Children.count(children) > 0) {
|
|
@@ -91,6 +94,10 @@ export function AdminScammersView({ children, ...props }) {
|
|
|
91
94
|
setDrawerOpen(true);
|
|
92
95
|
},
|
|
93
96
|
},
|
|
97
|
+
{
|
|
98
|
+
label: "Open full page",
|
|
99
|
+
onClick: () => router.push(String(ROUTES.ADMIN.SCAMMER_BY_ID(row.id))),
|
|
100
|
+
},
|
|
94
101
|
] })),
|
|
95
102
|
renderFilterPanel: ({ pendingFilters, setPendingFilters }) => (_jsx(FilterChipGroup, { label: "Status", tabs: ADMIN_SCAMMER_STATUS_TABS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: id })) })),
|
|
96
103
|
};
|
|
@@ -200,6 +200,8 @@ export function AdminSectionsView({ children }) {
|
|
|
200
200
|
},
|
|
201
201
|
onSuccess: async () => {
|
|
202
202
|
await queryClient.invalidateQueries({ queryKey: ["admin", "sections", "listing"] });
|
|
203
|
+
// Public-facing useHomepageSections() reads under a separate key.
|
|
204
|
+
await queryClient.invalidateQueries({ queryKey: ["homepage-sections"] });
|
|
203
205
|
toast.showToast(mode === "create" ? "Section created." : "Section updated.", "success");
|
|
204
206
|
setIsModalOpen(false);
|
|
205
207
|
},
|
|
@@ -215,6 +217,8 @@ export function AdminSectionsView({ children }) {
|
|
|
215
217
|
},
|
|
216
218
|
onSuccess: async () => {
|
|
217
219
|
await queryClient.invalidateQueries({ queryKey: ["admin", "sections", "listing"] });
|
|
220
|
+
// Public-facing useHomepageSections() reads under a separate key.
|
|
221
|
+
await queryClient.invalidateQueries({ queryKey: ["homepage-sections"] });
|
|
218
222
|
setReorderServerSnapshot(cloneReorderItems(reorderDraft));
|
|
219
223
|
setReorderUndoStack([]);
|
|
220
224
|
toast.showToast("Section order updated.", "success");
|
|
@@ -13,7 +13,7 @@ export function AdminSiteConfigGuideView() {
|
|
|
13
13
|
{ name: "watermark", desc: "Watermark text and opacity applied to all product images via the /api/media proxy." },
|
|
14
14
|
{ name: "fees", desc: "platformFee (%), Razorpay gateway fee pass-through toggle, GST rate on platform fees." },
|
|
15
15
|
{ name: "integrations", desc: "API keys: Razorpay (live/test, disabled by default — manual payment is the default), Resend, WhatsApp Cloud API. Keys are encrypted at rest — never log or export." },
|
|
16
|
-
{ name: "shipping", desc: "Default shipping carriers, free shipping threshold (
|
|
16
|
+
{ name: "shipping", desc: "Default shipping carriers, free shipping threshold (₹), COD availability toggle." },
|
|
17
17
|
{ name: "auctionConfig", desc: "Global bid increment default, auto-extend window (minutes), minimum auction duration." },
|
|
18
18
|
{ name: "platformLimits", desc: "Cart item cap (50), wishlist cap (20), history cap (50), support ticket limits (2 general + 1 per order)." },
|
|
19
19
|
{ name: "legalPages", desc: "Body HTML for Terms of Service, Privacy Policy, Refund Policy, and Shipping Policy. Rendered on their respective static pages." },
|