From fdf8738bb25c5730ecb912bac5b1dbb05d12b7f3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 03:16:41 +0000 Subject: [PATCH 1/5] Initial plan From 2a079041f1a46afcaf6bbbb48ef6bfc5e01589ef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 03:22:53 +0000 Subject: [PATCH 2/5] Add UI components and enhance Visualization and Summary pages Co-authored-by: teoat <68715844+teoat@users.noreply.github.com> --- frontend/src/components/ui/Badge.tsx | 47 +++++ frontend/src/components/ui/Breadcrumbs.tsx | 46 +++++ frontend/src/components/ui/EmptyState.tsx | 41 +++++ .../src/components/ui/LoadingSkeleton.tsx | 53 ++++++ frontend/src/components/ui/Tooltip.tsx | 59 +++++++ frontend/src/index.css | 30 ++++ frontend/src/pages/FinalSummary.tsx | 128 ++++++++++++-- frontend/src/pages/Visualization.tsx | 167 ++++++++++++++---- 8 files changed, 516 insertions(+), 55 deletions(-) create mode 100644 frontend/src/components/ui/Badge.tsx create mode 100644 frontend/src/components/ui/Breadcrumbs.tsx create mode 100644 frontend/src/components/ui/EmptyState.tsx create mode 100644 frontend/src/components/ui/LoadingSkeleton.tsx create mode 100644 frontend/src/components/ui/Tooltip.tsx diff --git a/frontend/src/components/ui/Badge.tsx b/frontend/src/components/ui/Badge.tsx new file mode 100644 index 0000000..7ef0bed --- /dev/null +++ b/frontend/src/components/ui/Badge.tsx @@ -0,0 +1,47 @@ +import { FC, ReactNode } from 'react'; +import { cn } from '../../lib/utils'; + +interface BadgeProps { + children: ReactNode; + variant?: 'default' | 'success' | 'warning' | 'error' | 'info' | 'neutral'; + size?: 'sm' | 'md' | 'lg'; + className?: string; + animated?: boolean; +} + +export const Badge: FC = ({ + children, + variant = 'default', + size = 'md', + className, + animated = false, +}) => { + const variants = { + default: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300', + success: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300', + warning: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300', + error: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300', + info: 'bg-cyan-100 text-cyan-700 dark:bg-cyan-900/30 dark:text-cyan-300', + neutral: 'bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300', + }; + + const sizes = { + sm: 'px-2 py-0.5 text-xs', + md: 'px-2.5 py-1 text-sm', + lg: 'px-3 py-1.5 text-base', + }; + + return ( + + {children} + + ); +}; diff --git a/frontend/src/components/ui/Breadcrumbs.tsx b/frontend/src/components/ui/Breadcrumbs.tsx new file mode 100644 index 0000000..6cf742f --- /dev/null +++ b/frontend/src/components/ui/Breadcrumbs.tsx @@ -0,0 +1,46 @@ +import { FC } from 'react'; +import { Link } from 'react-router-dom'; +import { ChevronRight, Home } from 'lucide-react'; +import { cn } from '../../lib/utils'; + +interface BreadcrumbItem { + label: string; + href?: string; +} + +interface BreadcrumbsProps { + items: BreadcrumbItem[]; + className?: string; +} + +export const Breadcrumbs: FC = ({ items, className }) => { + return ( + + ); +}; diff --git a/frontend/src/components/ui/EmptyState.tsx b/frontend/src/components/ui/EmptyState.tsx new file mode 100644 index 0000000..90f91f2 --- /dev/null +++ b/frontend/src/components/ui/EmptyState.tsx @@ -0,0 +1,41 @@ +import { FC, ReactNode } from 'react'; +import { FileQuestion } from 'lucide-react'; +import { cn } from '../../lib/utils'; + +interface EmptyStateProps { + icon?: ReactNode; + title: string; + description?: string; + action?: ReactNode; + className?: string; +} + +export const EmptyState: FC = ({ + icon, + title, + description, + action, + className, +}) => { + return ( +
+
+ {icon || } +
+

+ {title} +

+ {description && ( +

+ {description} +

+ )} + {action &&
{action}
} +
+ ); +}; diff --git a/frontend/src/components/ui/LoadingSkeleton.tsx b/frontend/src/components/ui/LoadingSkeleton.tsx new file mode 100644 index 0000000..e1d70a1 --- /dev/null +++ b/frontend/src/components/ui/LoadingSkeleton.tsx @@ -0,0 +1,53 @@ +import { FC } from 'react'; +import { cn } from '../../lib/utils'; + +interface LoadingSkeletonProps { + variant?: 'text' | 'circular' | 'rectangular' | 'card'; + width?: string | number; + height?: string | number; + className?: string; + count?: number; +} + +export const LoadingSkeleton: FC = ({ + variant = 'rectangular', + width, + height, + className, + count = 1, +}) => { + const baseClasses = 'animate-ui-shimmer bg-gradient-to-r from-slate-200 via-slate-300 to-slate-200 dark:from-slate-700 dark:via-slate-600 dark:to-slate-700'; + + const variants = { + text: 'rounded h-4', + circular: 'rounded-full', + rectangular: 'rounded-md', + card: 'rounded-xl h-32', + }; + + const style = { + width: width || (variant === 'text' ? '100%' : variant === 'circular' ? '48px' : '100%'), + height: height || (variant === 'circular' ? '48px' : variant === 'card' ? '128px' : '16px'), + }; + + if (count > 1) { + return ( +
+ {Array.from({ length: count }).map((_, index) => ( +
+ ))} +
+ ); + } + + return ( +
+ ); +}; diff --git a/frontend/src/components/ui/Tooltip.tsx b/frontend/src/components/ui/Tooltip.tsx new file mode 100644 index 0000000..22853d9 --- /dev/null +++ b/frontend/src/components/ui/Tooltip.tsx @@ -0,0 +1,59 @@ +import { FC, ReactNode, useState } from 'react'; +import { cn } from '../../lib/utils'; + +interface TooltipProps { + content: string | ReactNode; + children: ReactNode; + position?: 'top' | 'bottom' | 'left' | 'right'; + className?: string; +} + +export const Tooltip: FC = ({ + content, + children, + position = 'top', + className, +}) => { + const [isVisible, setIsVisible] = useState(false); + + const positionClasses = { + top: 'bottom-full left-1/2 -translate-x-1/2 mb-2', + bottom: 'top-full left-1/2 -translate-x-1/2 mt-2', + left: 'right-full top-1/2 -translate-y-1/2 mr-2', + right: 'left-full top-1/2 -translate-y-1/2 ml-2', + }; + + return ( +
+
setIsVisible(true)} + onMouseLeave={() => setIsVisible(false)} + onFocus={() => setIsVisible(true)} + onBlur={() => setIsVisible(false)} + > + {children} +
+ {isVisible && ( +
+ {content} +
+
+ )} +
+ ); +}; diff --git a/frontend/src/index.css b/frontend/src/index.css index 2e81f44..8435093 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -23,3 +23,33 @@ body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } + +/* Custom Animations */ +@keyframes shimmer { + 0% { + background-position: -1000px 0; + } + 100% { + background-position: 1000px 0; + } +} + +@keyframes pulse-glow { + 0%, 100% { + opacity: 1; + box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4); + } + 50% { + opacity: 0.9; + box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.1); + } +} + +.animate-ui-shimmer { + animation: shimmer 2s infinite linear; + background-size: 2000px 100%; +} + +.animate-ui-pulse-glow { + animation: pulse-glow 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} diff --git a/frontend/src/pages/FinalSummary.tsx b/frontend/src/pages/FinalSummary.tsx index 7f309c2..551c7cf 100644 --- a/frontend/src/pages/FinalSummary.tsx +++ b/frontend/src/pages/FinalSummary.tsx @@ -1,7 +1,7 @@ import { FC, useEffect, useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { motion } from 'framer-motion'; -import { FileInput, GitMerge, Gavel, ArrowLeft } from 'lucide-react'; +import { FileInput, GitMerge, Gavel, ArrowLeft, TrendingUp, Clock } from 'lucide-react'; import { toast } from 'react-hot-toast'; import { SuccessBanner } from '../components/summary/SuccessBanner'; @@ -11,6 +11,10 @@ import { ChartEmbed } from '../components/summary/ChartEmbed'; import { PDFGenerator } from '../components/summary/PDFGenerator'; import { ActionButtons } from '../components/summary/ActionButtons'; import { Button } from '../components/ui/Button'; +import { Breadcrumbs } from '../components/ui/Breadcrumbs'; +import { Badge } from '../components/ui/Badge'; +import { Tooltip } from '../components/ui/Tooltip'; +import { LoadingSkeleton } from '../components/ui/LoadingSkeleton'; import { apiRequest } from '../lib/api'; @@ -106,10 +110,31 @@ export const FinalSummary: FC = () => { if (loading) { return ( -
-
-
-

Generating Case Summary...

+
+
+ {/* Header Skeleton */} +
+ +
+ + {/* Success Banner Skeleton */} + + + {/* Summary Cards Skeleton */} +
+
+
+ +
+ +
+ +
+
+
+ +
+
); @@ -121,15 +146,42 @@ export const FinalSummary: FC = () => {
+ {/* Breadcrumbs */} + + {/* Header */} -
- -

- Case Summary / {caseId} +
+
+

+ Case Summary

+ + {caseId} + + + = 90 ? 'success' : data.dataQuality >= 70 ? 'info' : 'warning'} + size="sm" + > + {data.dataQuality}% Quality + + +
+
+ +
+ + {data.daysToResolution} days +
+
+
{/* Success Banner */} @@ -214,11 +266,53 @@ export const FinalSummary: FC = () => { - {/* Additional context or quick stats could go here */} -
-

Analyst Note

-

This case exceeds the threshold for automatic reporting. Please review all findings before generating the final compliance PDF.

-
+ {/* Case Metrics Summary */} + +

+ + Case Metrics +

+
+
+ Status + + {data.status} + +
+
+ Data Quality + {data.dataQuality}% +
+
+ Time to Resolve + {data.daysToResolution} days +
+
+
+ + {/* Analyst Note */} + +

+ + Analyst Note +

+

+ This case exceeds the threshold for automatic reporting. Please review all findings before generating the final compliance PDF. +

+

diff --git a/frontend/src/pages/Visualization.tsx b/frontend/src/pages/Visualization.tsx index 35f969a..dc33b7c 100644 --- a/frontend/src/pages/Visualization.tsx +++ b/frontend/src/pages/Visualization.tsx @@ -8,11 +8,18 @@ import { Calendar, Download, RefreshCw, - Share2 + Share2, + Info, + ArrowUpRight, + ArrowDownRight } from 'lucide-react'; import { motion } from 'framer-motion'; import { Card, CardContent, CardHeader, CardTitle } from '../components/ui/Card'; import { Button } from '../components/ui/Button'; +import { Breadcrumbs } from '../components/ui/Breadcrumbs'; +import { Tooltip } from '../components/ui/Tooltip'; +import { Badge } from '../components/ui/Badge'; +import { LoadingSkeleton } from '../components/ui/LoadingSkeleton'; import { MilestoneTracker } from '../components/visualization/MilestoneTracker'; import { FraudDetectionPanel } from '../components/visualization/FraudDetectionPanel'; import { VisualizationDashboard } from '../components/visualization/VisualizationDashboard'; @@ -170,10 +177,28 @@ export function Visualization() { if (isLoading) { return ( -
-
- -

Loading visualization...

+
+
+ {/* Header Skeleton */} +
+
+ + +
+
+ + + +
+
+ + {/* KPI Cards Skeleton */} +
+ +
+ + {/* Content Skeleton */} +
); @@ -201,85 +226,151 @@ export function Visualization() { return (
+ {/* Breadcrumbs */} + + {/* Header */}
-

- Financial Visualization -

+
+

+ Financial Visualization +

+ + Live Data + +

Case {caseId} - Interactive financial analysis and fraud detection

- - - + + + + + + + + +
{/* KPI Cards */}
- + - Total Inflow - + + Total Inflow + + + + +
+ +
${(data?.total_inflow || 0).toLocaleString()}
-

All incoming transactions

+
+ +

All incoming transactions

+
- + - Total Outflow - + + Total Outflow + + + + +
+ +
${(data?.total_outflow || 0).toLocaleString()}
-

All outgoing transactions

+
+ +

All outgoing transactions

+
- + - Net Cashflow - + + Net Cashflow + + + + +
+ +
= 0 ? 'text-blue-600 dark:text-blue-400' : 'text-red-600 dark:text-red-400'}`}> ${Math.abs(data?.net_cashflow || 0).toLocaleString()}
-

- {(data?.net_cashflow || 0) >= 0 ? 'Surplus' : 'Deficit'} -

+
+ = 0 ? 'success' : 'warning'} size="sm"> + {(data?.net_cashflow || 0) >= 0 ? 'Surplus' : 'Deficit'} + +
- + - Suspect Items - + + Suspect Items + + + + +
+ +
{data?.suspect_transactions || 0}
-

Flagged transactions

+
+ {(data?.suspect_transactions || 0) > 0 ? ( + + Needs Review + + ) : ( + + All Clear + + )} +
From c0b6cb123552b7a947b5f806caaf79aa8a6ff510 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 03:29:14 +0000 Subject: [PATCH 3/5] Complete UI enhancements for Visualization and Summary pages Co-authored-by: teoat <68715844+teoat@users.noreply.github.com> --- frontend/src/pages/FinalSummary.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/pages/FinalSummary.tsx b/frontend/src/pages/FinalSummary.tsx index 551c7cf..f178df1 100644 --- a/frontend/src/pages/FinalSummary.tsx +++ b/frontend/src/pages/FinalSummary.tsx @@ -1,7 +1,7 @@ import { FC, useEffect, useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { motion } from 'framer-motion'; -import { FileInput, GitMerge, Gavel, ArrowLeft, TrendingUp, Clock } from 'lucide-react'; +import { FileInput, GitMerge, Gavel, TrendingUp, Clock } from 'lucide-react'; import { toast } from 'react-hot-toast'; import { SuccessBanner } from '../components/summary/SuccessBanner'; @@ -10,7 +10,6 @@ import { KeyFindings, Finding } from '../components/summary/KeyFindings'; import { ChartEmbed } from '../components/summary/ChartEmbed'; import { PDFGenerator } from '../components/summary/PDFGenerator'; import { ActionButtons } from '../components/summary/ActionButtons'; -import { Button } from '../components/ui/Button'; import { Breadcrumbs } from '../components/ui/Breadcrumbs'; import { Badge } from '../components/ui/Badge'; import { Tooltip } from '../components/ui/Tooltip'; From 7450ded408b113e18a1c4cfcbce426cf54c08bd5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 03:31:55 +0000 Subject: [PATCH 4/5] Address code review feedback - add null checks and documentation Co-authored-by: teoat <68715844+teoat@users.noreply.github.com> --- frontend/src/components/ui/Badge.tsx | 6 ++++++ frontend/src/components/ui/LoadingSkeleton.tsx | 5 +++++ frontend/src/components/ui/Tooltip.tsx | 14 +++++++++++++- frontend/src/pages/FinalSummary.tsx | 8 ++++---- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/ui/Badge.tsx b/frontend/src/components/ui/Badge.tsx index 7ef0bed..f674e0d 100644 --- a/frontend/src/components/ui/Badge.tsx +++ b/frontend/src/components/ui/Badge.tsx @@ -9,6 +9,12 @@ interface BadgeProps { animated?: boolean; } +/** + * Badge component for displaying status indicators + * + * Note: The animated prop uses the 'animate-ui-pulse-glow' CSS class + * which is defined in src/index.css + */ export const Badge: FC = ({ children, variant = 'default', diff --git a/frontend/src/components/ui/LoadingSkeleton.tsx b/frontend/src/components/ui/LoadingSkeleton.tsx index e1d70a1..2c91190 100644 --- a/frontend/src/components/ui/LoadingSkeleton.tsx +++ b/frontend/src/components/ui/LoadingSkeleton.tsx @@ -9,6 +9,11 @@ interface LoadingSkeletonProps { count?: number; } +/** + * LoadingSkeleton component for showing loading states + * + * Note: Uses the 'animate-ui-shimmer' CSS class defined in src/index.css + */ export const LoadingSkeleton: FC = ({ variant = 'rectangular', width, diff --git a/frontend/src/components/ui/Tooltip.tsx b/frontend/src/components/ui/Tooltip.tsx index 22853d9..41d0a88 100644 --- a/frontend/src/components/ui/Tooltip.tsx +++ b/frontend/src/components/ui/Tooltip.tsx @@ -1,4 +1,4 @@ -import { FC, ReactNode, useState } from 'react'; +import { FC, ReactNode, useState, KeyboardEvent } from 'react'; import { cn } from '../../lib/utils'; interface TooltipProps { @@ -8,6 +8,11 @@ interface TooltipProps { className?: string; } +/** + * Tooltip component for displaying contextual help + * + * Supports keyboard navigation - press Escape to close + */ export const Tooltip: FC = ({ content, children, @@ -23,6 +28,12 @@ export const Tooltip: FC = ({ right: 'left-full top-1/2 -translate-y-1/2 ml-2', }; + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + setIsVisible(false); + } + }; + return (
= ({ onMouseLeave={() => setIsVisible(false)} onFocus={() => setIsVisible(true)} onBlur={() => setIsVisible(false)} + onKeyDown={handleKeyDown} > {children}
diff --git a/frontend/src/pages/FinalSummary.tsx b/frontend/src/pages/FinalSummary.tsx index f178df1..97a7d9d 100644 --- a/frontend/src/pages/FinalSummary.tsx +++ b/frontend/src/pages/FinalSummary.tsx @@ -164,12 +164,12 @@ export const FinalSummary: FC = () => { {caseId} - + = 90 ? 'success' : data.dataQuality >= 70 ? 'info' : 'warning'} + variant={data && data.dataQuality >= 90 ? 'success' : data && data.dataQuality >= 70 ? 'info' : 'warning'} size="sm" > - {data.dataQuality}% Quality + {data ? `${data.dataQuality}% Quality` : 'Loading...'}
@@ -177,7 +177,7 @@ export const FinalSummary: FC = () => {
- {data.daysToResolution} days + {data ? `${data.daysToResolution} days` : 'Loading...'}
From 7283d919b2f5e15af0e971b6aef32e34333168fe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Dec 2025 03:34:32 +0000 Subject: [PATCH 5/5] Final improvements: enhanced keyboard navigation and code quality Co-authored-by: teoat <68715844+teoat@users.noreply.github.com> --- frontend/src/components/ui/LoadingSkeleton.tsx | 7 ++++++- frontend/src/components/ui/Tooltip.tsx | 7 ++++++- frontend/src/pages/FinalSummary.tsx | 8 ++++---- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/ui/LoadingSkeleton.tsx b/frontend/src/components/ui/LoadingSkeleton.tsx index 2c91190..4269796 100644 --- a/frontend/src/components/ui/LoadingSkeleton.tsx +++ b/frontend/src/components/ui/LoadingSkeleton.tsx @@ -21,7 +21,12 @@ export const LoadingSkeleton: FC = ({ className, count = 1, }) => { - const baseClasses = 'animate-ui-shimmer bg-gradient-to-r from-slate-200 via-slate-300 to-slate-200 dark:from-slate-700 dark:via-slate-600 dark:to-slate-700'; + const baseClasses = [ + 'animate-ui-shimmer', + 'bg-gradient-to-r', + 'from-slate-200 via-slate-300 to-slate-200', + 'dark:from-slate-700 dark:via-slate-600 dark:to-slate-700' + ].join(' '); const variants = { text: 'rounded h-4', diff --git a/frontend/src/components/ui/Tooltip.tsx b/frontend/src/components/ui/Tooltip.tsx index 41d0a88..0bb5f6a 100644 --- a/frontend/src/components/ui/Tooltip.tsx +++ b/frontend/src/components/ui/Tooltip.tsx @@ -11,7 +11,9 @@ interface TooltipProps { /** * Tooltip component for displaying contextual help * - * Supports keyboard navigation - press Escape to close + * Keyboard navigation: + * - Press Enter or Space to toggle tooltip visibility + * - Press Escape to close the tooltip */ export const Tooltip: FC = ({ content, @@ -31,6 +33,9 @@ export const Tooltip: FC = ({ const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape') { setIsVisible(false); + } else if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + setIsVisible(prev => !prev); } }; diff --git a/frontend/src/pages/FinalSummary.tsx b/frontend/src/pages/FinalSummary.tsx index 97a7d9d..789e8b3 100644 --- a/frontend/src/pages/FinalSummary.tsx +++ b/frontend/src/pages/FinalSummary.tsx @@ -280,19 +280,19 @@ export const FinalSummary: FC = () => {
Status - {data.status} + {data?.status || 'Unknown'}
Data Quality - {data.dataQuality}% + {data?.dataQuality || 0}%
Time to Resolve - {data.daysToResolution} days + {data?.daysToResolution || 0} days