From 3f2ff21eac2c24e04d5957bbd15a6b8a5d0c021d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 21 Sep 2025 23:05:12 +0000 Subject: [PATCH] feat(frontend): Rename notifications page to System Status and add sync operations section Co-authored-by: elisiariocouto <818914+elisiariocouto@users.noreply.github.com> --- frontend/src/components/AppSidebar.tsx | 4 +- frontend/src/components/Notifications.tsx | 117 +++++++++++++++++++++- frontend/src/lib/api.ts | 12 +++ frontend/src/types/api.ts | 21 ++++ 4 files changed, 148 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/AppSidebar.tsx b/frontend/src/components/AppSidebar.tsx index 6083e8a..d5b615e 100644 --- a/frontend/src/components/AppSidebar.tsx +++ b/frontend/src/components/AppSidebar.tsx @@ -3,7 +3,7 @@ import { Link, useLocation } from "@tanstack/react-router"; import { List, BarChart3, - Bell, + Activity, Settings, Building2, TrendingUp, @@ -33,7 +33,7 @@ import { const navigation = [ { name: "Overview", icon: List, to: "/" }, { name: "Analytics", icon: BarChart3, to: "/analytics" }, - { name: "Notifications", icon: Bell, to: "/notifications" }, + { name: "System Status", icon: Activity, to: "/notifications" }, { name: "Settings", icon: Settings, to: "/settings" }, ]; diff --git a/frontend/src/components/Notifications.tsx b/frontend/src/components/Notifications.tsx index a8520fd..fbc32ea 100644 --- a/frontend/src/components/Notifications.tsx +++ b/frontend/src/components/Notifications.tsx @@ -10,6 +10,10 @@ import { CheckCircle, Settings, TestTube, + Activity, + Clock, + TrendingUp, + User, } from "lucide-react"; import { apiClient } from "../lib/api"; import NotificationsSkeleton from "./NotificationsSkeleton"; @@ -32,7 +36,7 @@ import { SelectTrigger, SelectValue, } from "./ui/select"; -import type { NotificationSettings, NotificationService } from "../types/api"; +import type { NotificationSettings, NotificationService, SyncOperationsResponse } from "../types/api"; export default function Notifications() { const [testService, setTestService] = useState(""); @@ -61,6 +65,16 @@ export default function Notifications() { queryFn: apiClient.getNotificationServices, }); + const { + data: syncOperations, + isLoading: syncOperationsLoading, + error: syncOperationsError, + refetch: refetchSyncOperations, + } = useQuery({ + queryKey: ["syncOperations"], + queryFn: () => apiClient.getSyncOperations(10, 0), // Get latest 10 operations + }); + const testMutation = useMutation({ mutationFn: apiClient.testNotification, onSuccess: () => { @@ -80,15 +94,15 @@ export default function Notifications() { }, }); - if (settingsLoading || servicesLoading) { + if (settingsLoading || servicesLoading || syncOperationsLoading) { return ; } - if (settingsError || servicesError) { + if (settingsError || servicesError || syncOperationsError) { return ( - Failed to load notifications + Failed to load system data

Unable to connect to the Leggen API. Please check your configuration @@ -98,6 +112,7 @@ export default function Notifications() { onClick={() => { refetchSettings(); refetchServices(); + refetchSyncOperations(); }} variant="outline" size="sm" @@ -131,6 +146,100 @@ export default function Notifications() { return (

+ {/* Sync Operations Section */} + + + + + Sync Operations + + Recent synchronization activities + + + {!syncOperations || syncOperations.operations.length === 0 ? ( +
+ +

+ No sync operations yet +

+

+ Sync operations will appear here once you start syncing your accounts. +

+
+ ) : ( +
+ {syncOperations.operations.slice(0, 5).map((operation) => { + const startedAt = new Date(operation.started_at); + const isRunning = !operation.completed_at; + const duration = operation.duration_seconds + ? `${Math.round(operation.duration_seconds)}s` + : ''; + + return ( +
+
+
+ {isRunning ? ( + + ) : operation.success ? ( + + ) : ( + + )} +
+
+
+

+ {isRunning ? 'Sync Running' : operation.success ? 'Sync Completed' : 'Sync Failed'} +

+ + {operation.trigger_type} + +
+
+ + + {startedAt.toLocaleDateString()} {startedAt.toLocaleTimeString()} + + {duration && ( + Duration: {duration} + )} +
+
+
+
+
+ + {operation.accounts_processed} accounts +
+
+ + {operation.transactions_added} new transactions +
+ {operation.errors.length > 0 && ( +
+ + {operation.errors.length} errors +
+ )} +
+
+ ); + })} +
+ )} +
+
+ {/* Test Notification Section */} diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 9686ccc..aa1b20e 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -12,6 +12,7 @@ import type { HealthData, AccountUpdate, TransactionStats, + SyncOperationsResponse, } from "../types/api"; // Use VITE_API_URL for development, relative URLs for production @@ -219,6 +220,17 @@ export const apiClient = { >(`/transactions/monthly-stats?${queryParams.toString()}`); return response.data.data; }, + + // Get sync operations history + getSyncOperations: async ( + limit: number = 50, + offset: number = 0, + ): Promise => { + const response = await api.get>( + `/sync/operations?limit=${limit}&offset=${offset}`, + ); + return response.data.data; + }, }; export default apiClient; diff --git a/frontend/src/types/api.ts b/frontend/src/types/api.ts index 49dad46..4ba7e2a 100644 --- a/frontend/src/types/api.ts +++ b/frontend/src/types/api.ts @@ -220,3 +220,24 @@ export interface TransactionStats { average_transaction: number; accounts_included: number; } + +// Sync operations types +export interface SyncOperation { + id: number; + started_at: string; + completed_at?: string; + success?: boolean; + accounts_processed: number; + transactions_added: number; + transactions_updated: number; + balances_updated: number; + duration_seconds?: number; + errors: string[]; + logs: string[]; + trigger_type: 'manual' | 'scheduled' | 'api'; +} + +export interface SyncOperationsResponse { + operations: SyncOperation[]; + count: number; +}