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:
76
frontend-web/src/components/ErrorBoundary.tsx
Normal file
76
frontend-web/src/components/ErrorBoundary.tsx
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user