From 386f016eecaa20814bb12322c88bc51456ae5341 Mon Sep 17 00:00:00 2001 From: CDevmina Date: Wed, 21 May 2025 00:25:34 +0530 Subject: [PATCH 1/2] feat: Add details modal for user activity entries with view functionality --- tapiro-api-internal/utils/cacheConfig.js | 1 + .../pages/UserDashboard/UserAnalyticsPage.tsx | 123 +++++++++++++++++- 2 files changed, 117 insertions(+), 7 deletions(-) diff --git a/tapiro-api-internal/utils/cacheConfig.js b/tapiro-api-internal/utils/cacheConfig.js index f3cba85..32dfdad 100644 --- a/tapiro-api-internal/utils/cacheConfig.js +++ b/tapiro-api-internal/utils/cacheConfig.js @@ -9,6 +9,7 @@ const CACHE_TTL = { API_KEY: 1800, // API keys - 30 minutes INVALIDATION: 1, // Short TTL for invalidation AI_REQUEST: 60, // AI service requests - 1 minute + TAXONOMY: 86400, // Taxonomy data - 1 day (added) }; /** diff --git a/web/src/pages/UserDashboard/UserAnalyticsPage.tsx b/web/src/pages/UserDashboard/UserAnalyticsPage.tsx index 9f5b99f..1b4755c 100644 --- a/web/src/pages/UserDashboard/UserAnalyticsPage.tsx +++ b/web/src/pages/UserDashboard/UserAnalyticsPage.tsx @@ -17,10 +17,13 @@ import { Modal, // Added Modal ModalHeader, ModalBody, + ModalFooter, // Added ModalFooter Toast, ToastToggle, DropdownItem, DropdownDivider, // Added Toast + List, // Added List for modal details + ListItem, // Added ListItem for modal details } from "flowbite-react"; import { ResponsiveContainer, @@ -41,6 +44,7 @@ import { HiExclamation, // Added Exclamation icon for modal HiCheckCircle, // For success toast HiXCircle, // For error toast + HiOutlineEye, // Added Eye icon for view details } from "react-icons/hi"; import { useRecentUserData, @@ -136,6 +140,11 @@ const UserAnalyticsPage: React.FC = () => { type: "success" | "error"; } | null>(null); + // --- State for Details Modal --- + const [showDetailsModal, setShowDetailsModal] = useState(false); + const [selectedEntryForDetails, setSelectedEntryForDetails] = + useState(null); + // --- Data Fetching --- const { data: spendingData, @@ -240,8 +249,6 @@ const UserAnalyticsPage: React.FC = () => { }; // --- Pagination Logic --- - // Determine if there might be a next page - // We infer this if the current page loaded the maximum number of items const hasMoreData = useMemo(() => { return activityData && activityData.length === activityLimit; }, [activityData, activityLimit]); @@ -317,10 +324,14 @@ const UserAnalyticsPage: React.FC = () => { }); }; + // --- Handler for View Details --- + const handleViewDetails = (entry: RecentUserDataEntry) => { + setSelectedEntryForDetails(entry); + setShowDetailsModal(true); + }; + // --- Render Logic --- const isLoading = spendingLoading || activityLoading || storesLoading; - // Remove combinedError - // const combinedError = spendingError || activityError || storesError; if (isLoading && !spendingData && !activityData) { return ; @@ -530,7 +541,6 @@ const UserAnalyticsPage: React.FC = () => { {/* Activity Table */} {activityError || storesError ? ( - // ... error display ... { Date Type Store - Details + Summary Actions @@ -608,7 +618,16 @@ const UserAnalyticsPage: React.FC = () => { Query: "{searchDetail.query}" )} - + + + + + )} ); }; From 7b32a32525410e637b1ad94373884baf3c177852 Mon Sep 17 00:00:00 2001 From: CDevmina Date: Wed, 21 May 2025 00:29:17 +0530 Subject: [PATCH 2/2] feat: Enhance User Analytics Modal with detailed entry overview and improved currency formatting --- .../pages/UserDashboard/UserAnalyticsPage.tsx | 241 +++++++++++++----- 1 file changed, 172 insertions(+), 69 deletions(-) diff --git a/web/src/pages/UserDashboard/UserAnalyticsPage.tsx b/web/src/pages/UserDashboard/UserAnalyticsPage.tsx index 1b4755c..7b64ba5 100644 --- a/web/src/pages/UserDashboard/UserAnalyticsPage.tsx +++ b/web/src/pages/UserDashboard/UserAnalyticsPage.tsx @@ -77,7 +77,8 @@ const formatDate = (dateString: string | Date | undefined) => { }); }; -const formatCurrency = (value: number) => { +const formatCurrency = (value: number | undefined | null) => { + if (value === undefined || value === null) return "N/A"; return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", @@ -409,7 +410,9 @@ const UserAnalyticsPage: React.FC = () => { > - + formatCurrency(value as number)} + /> formatCurrency(value)} labelFormatter={formatMonth} @@ -609,8 +612,10 @@ const UserAnalyticsPage: React.FC = () => { purchaseDetail.items.length > 0 && ( {purchaseDetail.items + .slice(0, 2) // Show first 2 items as summary .map((item: PurchaseItem) => item.name) .join(", ")} + {purchaseDetail.items.length > 2 && "..."} )} {entry.dataType === "search" && @@ -740,82 +745,180 @@ const UserAnalyticsPage: React.FC = () => { setShowDetailsModal(false)} - size="lg" // Or "xl" for more space + size="xl" // Increased modal size > Activity Entry Details - -
-
- -

- {formatDate(selectedEntryForDetails.timestamp)} -

-
-
- -

- {selectedEntryForDetails.dataType} -

+ + +

+ Entry Overview +

+
+
+ +

+ {formatDate(selectedEntryForDetails.timestamp)} +

+
+
+ +

+ {selectedEntryForDetails.dataType} +

+
+
+ +

+ {selectedEntryForDetails.storeId + ? (storeNameMap.get(selectedEntryForDetails.storeId) ?? + `ID: ${selectedEntryForDetails.storeId}`) + : "N/A"} +

+
-
- -

- {selectedEntryForDetails.storeId - ? (storeNameMap.get(selectedEntryForDetails.storeId) ?? - selectedEntryForDetails.storeId) - : "N/A"} -

-
- - {selectedEntryForDetails.details?.map((detail, index) => ( - -
- Detail Entry #{index + 1} (Timestamp:{" "} - {formatDate(detail.timestamp)}) + + + {selectedEntryForDetails.details && + selectedEntryForDetails.details.length > 0 && ( +

+ Detailed Entries ({selectedEntryForDetails.details.length}) +

+ )} + + {selectedEntryForDetails.details?.map((detail, index) => ( + +
+
+ Detail #{index + 1}
- {selectedEntryForDetails.dataType === "purchase" && - (detail as PurchaseEntry).items && ( -
- - - {(detail as PurchaseEntry).items.map( - (item: PurchaseItem, itemIndex: number) => ( - - {item.name} - {item.quantity && ` (Qty: ${item.quantity})`} - {item.price && - ` - ${formatCurrency(item.price)}`} - {item.category && - ` [Category: ${item.category}]`} - - ), - )} - -
- )} - {selectedEntryForDetails.dataType === "search" && - (detail as SearchEntry).query && ( + + {formatDate(detail.timestamp)} + +
+ + {selectedEntryForDetails.dataType === "purchase" && + (detail as PurchaseEntry).items && ( +
+ + + {(detail as PurchaseEntry).items.map( + (item: PurchaseItem, itemIndex: number) => ( + +
+ + {item.name} + + {item.price !== undefined && + item.price !== null && ( + + {formatCurrency(item.price)} + + )} +
+
+ {item.quantity && ( + + Qty:{" "} + + {item.quantity} + + + )} + {item.category && ( + + Category:{" "} + + {item.category} + + + )} + {item.sku && ( + + SKU:{" "} + + {item.sku} + + + )} +
+
+ ), + )} +
+
+ )} + {selectedEntryForDetails.dataType === "search" && + (detail as SearchEntry).query && ( +
- -

+ +

{(detail as SearchEntry).query}

- {(detail as SearchEntry).results !== undefined && ( - <> - -

- {(detail as SearchEntry).results} -

- - )}
- )} - + {(detail as SearchEntry).results !== undefined && ( +
+ +

+ {(detail as SearchEntry).results} +

+
+ )} +
+ )} +
+ ))} + {!selectedEntryForDetails.details || + (selectedEntryForDetails.details.length === 0 && ( +

+ No detailed entries recorded for this submission. +

))} -