From 62cd55e48fff7c2f5db9dd8230a7bd500e8f6eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elisi=C3=A1rio=20Couto?= Date: Wed, 24 Sep 2025 15:20:08 +0100 Subject: [PATCH] feat(frontend): Improve System page and TransactionsTable UX. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit System page improvements: - Add View Logs button to each sync operation with modal dialog - Implement responsive design for mobile devices - Remove redundant error count indicators - Show full transaction text on mobile ("X new transactions") TransactionsTable improvements: - Use display_name instead of name • institution_id format - Show only clean account display names in transaction rows 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- frontend/src/components/System.tsx | 238 ++++++++++++++---- frontend/src/components/TransactionsTable.tsx | 6 +- frontend/src/components/ui/scroll-area.tsx | 21 ++ 3 files changed, 206 insertions(+), 59 deletions(-) create mode 100644 frontend/src/components/ui/scroll-area.tsx diff --git a/frontend/src/components/System.tsx b/frontend/src/components/System.tsx index d2c6a38..31ef097 100644 --- a/frontend/src/components/System.tsx +++ b/frontend/src/components/System.tsx @@ -1,4 +1,5 @@ import { useQuery } from "@tanstack/react-query"; +import { useState } from "react"; import { RefreshCw, AlertCircle, @@ -7,6 +8,7 @@ import { Clock, TrendingUp, User, + FileText, } from "lucide-react"; import { apiClient } from "../lib/api"; import { @@ -19,7 +21,73 @@ import { import { Alert, AlertDescription, AlertTitle } from "./ui/alert"; import { Button } from "./ui/button"; import { Badge } from "./ui/badge"; -import type { SyncOperationsResponse } from "../types/api"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "./ui/dialog"; +import { ScrollArea } from "./ui/scroll-area"; +import type { SyncOperationsResponse, SyncOperation } from "../types/api"; + +// Component for viewing sync operation logs +function LogsDialog({ operation }: { operation: SyncOperation }) { + return ( + + + + + + + Sync Operation Logs + + Operation #{operation.id} - Started at{" "} + {new Date(operation.started_at).toLocaleString()} + + + +
+ {operation.logs.length === 0 ? ( +

No logs available

+ ) : ( + operation.logs.map((log, index) => ( +
+ {log} +
+ )) + )} +
+ {operation.errors.length > 0 && ( + <> +
+ Errors: +
+
+ {operation.errors.map((error, index) => ( +
+ {error} +
+ ))} +
+ + )} +
+
+
+ ); +} export default function System() { const { @@ -111,68 +179,128 @@ export default function System() { return (
-
-
- {isRunning ? ( - - ) : operation.success ? ( - - ) : ( - - )} -
-
-
-

- {isRunning - ? "Sync Running" + {/* Desktop Layout */} +
+
+
- - {operation.trigger_type} - + ? "bg-green-100 text-green-600" + : "bg-red-100 text-red-600" + }`} + > + {isRunning ? ( + + ) : operation.success ? ( + + ) : ( + + )}
-
- - - - {startedAt.toLocaleDateString()}{" "} - {startedAt.toLocaleTimeString()} +
+
+

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

+ + {operation.trigger_type} + +
+
+ + + + {startedAt.toLocaleDateString()}{" "} + {startedAt.toLocaleTimeString()} + - - {duration && Duration: {duration}} + {duration && Duration: {duration}} +
+
+
+
+ + {operation.accounts_processed} accounts +
+
+ + + {operation.transactions_added} new transactions + +
+
+ +
-
-
- - {operation.accounts_processed} accounts -
-
- - - {operation.transactions_added} new transactions - -
- {operation.errors.length > 0 && ( -
- - {operation.errors.length} errors + + {/* Mobile Layout */} +
+
+
+
+ {isRunning ? ( + + ) : operation.success ? ( + + ) : ( + + )} +
+
+

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

+ + {operation.trigger_type} + +
- )} + +
+ +
+
+ + + {startedAt.toLocaleDateString()}{" "} + {startedAt.toLocaleTimeString()} + + {duration && • {duration}} +
+ +
+
+ + {operation.accounts_processed} accounts +
+
+ + {operation.transactions_added} new transactions +
+
+
); diff --git a/frontend/src/components/TransactionsTable.tsx b/frontend/src/components/TransactionsTable.tsx index e6f7be4..794b00c 100644 --- a/frontend/src/components/TransactionsTable.tsx +++ b/frontend/src/components/TransactionsTable.tsx @@ -190,8 +190,7 @@ export default function TransactionsTable() {
{account && (

- {account.name || "Unnamed Account"} •{" "} - {account.institution_id} + {account.display_name || "Unnamed Account"}

)} {(transaction.creditor_name || transaction.debtor_name) && ( @@ -486,8 +485,7 @@ export default function TransactionsTable() {
{account && (

- {account.name || "Unnamed Account"} •{" "} - {account.institution_id} + {account.display_name || "Unnamed Account"}

)} {(transaction.creditor_name || diff --git a/frontend/src/components/ui/scroll-area.tsx b/frontend/src/components/ui/scroll-area.tsx new file mode 100644 index 0000000..a9b5644 --- /dev/null +++ b/frontend/src/components/ui/scroll-area.tsx @@ -0,0 +1,21 @@ +import * as React from "react"; +import { cn } from "../../lib/utils"; + +const ScrollArea = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, children, ...props }, ref) => ( +
+ {children} +
+)); +ScrollArea.displayName = "ScrollArea"; + +export { ScrollArea };