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..7b64ba5 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, @@ -73,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", @@ -136,6 +141,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 +250,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 +325,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 ; @@ -398,7 +410,9 @@ const UserAnalyticsPage: React.FC = () => { > - + formatCurrency(value as number)} + /> formatCurrency(value)} labelFormatter={formatMonth} @@ -530,7 +544,6 @@ const UserAnalyticsPage: React.FC = () => { {/* Activity Table */} {activityError || storesError ? ( - // ... error display ... { Date Type Store - Details + Summary Actions @@ -599,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" && @@ -608,7 +623,16 @@ const UserAnalyticsPage: React.FC = () => { Query: "{searchDetail.query}" )} - + + + + + )} ); };