Refactor and enhance frontend with security, validation, and performance improvements

## Summary
  Complete frontend overhaul implementing best practices, security hardening,
  full CRUD operations, and performance optimizations across the application.

  ## Key Changes

  ### Architecture & Performance
  - Implement code splitting with React.lazy() reducing main bundle from 795KB to 308KB (61% improvement)
  - Add error boundary component for graceful error handling
  - Create shared utility modules for formatters, validation, and calculations

  ### Security Enhancements
  - Add input sanitization to prevent XSS attacks
  - Implement comprehensive validation (email, phone, positive numbers, required fields)
  - Sanitize all user inputs before storage
  - Add confirmation dialogs for destructive operations

  ### Features & Functionality
  - Implement complete edit/delete operations for assets, liabilities, clients, and invoices
  - Add invoice status update functionality (draft/sent/paid/overdue/cancelled)
  - Connect "Record Snapshot" button with proper functionality
  - Fix hardcoded statistics with dynamic calculations (monthly change, YTD growth)
  - Make all list items clickable with hover effects and visual feedback

  ### Code Quality
  - Replace inline currency formatting with shared formatters
  - Add comprehensive input validation across all forms
  - Display inline error messages for validation failures
  - Implement loading states for lazy-loaded routes
  - Ensure type safety throughout with zero TypeScript errors

  ### UI/UX Improvements
  - Add hover states to all clickable items
  - Display validation errors inline with user-friendly messages
  - Add loading indicators during page transitions
  - Color-code financial metrics (green for positive, red for negative)
  - Improve form user experience with real-time validation

  ## Technical Details
  - Created src/lib/formatters.ts for currency and percentage formatting
  - Created src/lib/validation.ts for input validation and sanitization
  - Created src/lib/calculations.ts for financial calculations
  - Added EditAssetDialog, EditLiabilityDialog, EditClientDialog, InvoiceDetailsDialog
  - Wrapped app in ErrorBoundary for robust error handling
This commit is contained in:
2025-12-07 12:19:02 -05:00
parent 613e8fdb70
commit a62782a58f
14 changed files with 1050 additions and 50 deletions

View File

@@ -0,0 +1,76 @@
import {Component, type ReactNode} from 'react';
import {Button} from '@/components/ui/button';
import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card';
interface Props {
children: ReactNode;
fallback?: ReactNode;
}
interface State {
hasError: boolean;
error: Error | null;
}
export class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {hasError: false, error: null};
}
static getDerivedStateFromError(error: Error): State {
return {hasError: true, error};
}
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
// Log error to console in development
if (import.meta.env.DEV) {
console.error('Error caught by boundary:', error, errorInfo);
}
// In production, you would send this to an error tracking service
}
handleReset = () => {
this.setState({hasError: false, error: null});
};
render() {
if (this.state.hasError) {
if (this.props.fallback) {
return this.props.fallback;
}
return (
<div className="flex items-center justify-center min-h-screen p-4">
<Card className="card-elevated max-w-md w-full">
<CardHeader>
<CardTitle className="text-red-400">Something went wrong</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground">
An unexpected error occurred. Please try refreshing the page.
</p>
{import.meta.env.DEV && this.state.error && (
<div className="p-3 bg-destructive/10 rounded-md">
<p className="text-xs font-mono text-destructive break-all">
{this.state.error.message}
</p>
</div>
)}
<div className="flex gap-2">
<Button onClick={this.handleReset} variant="secondary" size="sm">
Try Again
</Button>
<Button onClick={() => window.location.reload()} size="sm">
Reload Page
</Button>
</div>
</CardContent>
</Card>
</div>
);
}
return this.props.children;
}
}