From 0188dcd32b7ef47bace66bbb233a13c7abf39614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Henrique?= Date: Tue, 3 Jun 2025 16:26:48 -0300 Subject: [PATCH] improving list of assets --- eslint.config.js | 11 ++ .../CandidatePage/AssetsComponent.tsx | 138 ++++++++++++++---- src/components/MatrixBackground.tsx | 2 - src/components/SearchBar.tsx | 4 +- src/index.css | 16 ++ 5 files changed, 138 insertions(+), 33 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 092408a..e51b828 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -23,6 +23,17 @@ export default tseslint.config( 'warn', { allowConstantExport: true }, ], + // no used vars ignored + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + vars: 'all', + args: 'after-used', + ignoreRestSiblings: true, + varsIgnorePattern: '^_', + argsIgnorePattern: '^_', + }, + ], }, }, ) diff --git a/src/components/CandidatePage/AssetsComponent.tsx b/src/components/CandidatePage/AssetsComponent.tsx index 67319c1..a3b77a0 100644 --- a/src/components/CandidatePage/AssetsComponent.tsx +++ b/src/components/CandidatePage/AssetsComponent.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import { CurrencyDollarIcon } from '@heroicons/react/24/outline'; +import React, { useState, useMemo } from 'react'; +import { CurrencyDollarIcon, ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/24/outline'; import { type Asset } from '../../api'; interface AssetsComponentProps { @@ -8,6 +8,8 @@ interface AssetsComponentProps { } const AssetsComponent: React.FC = ({ assets, isLoading }) => { + const [expandedYears, setExpandedYears] = useState>(new Set()); + const formatCurrency = (value: number) => { return new Intl.NumberFormat('pt-BR', { style: 'currency', @@ -15,6 +17,44 @@ const AssetsComponent: React.FC = ({ assets, isLoading }) }).format(value); }; + const groupedAssets = useMemo(() => { + if (!assets) return {}; + + return assets.reduce((acc, asset) => { + const year = asset.ano; + if (!acc[year]) { + acc[year] = []; + } + acc[year].push(asset); + return acc; + }, {} as Record); + }, [assets]); + + const sortedYears = useMemo(() => { + return Object.keys(groupedAssets).map(Number).sort((a, b) => b - a); + }, [groupedAssets]); + + const toggleYear = (year: number) => { + setExpandedYears(prev => { + const newSet = new Set(prev); + if (newSet.has(year)) { + newSet.delete(year); + } else { + newSet.add(year); + } + return newSet; + }); + }; + + const getTotalForYear = (yearAssets: Asset[]) => { + return yearAssets.reduce((total, asset) => total + asset.valor, 0); + }; + + const getTotalAssets = () => { + if (!assets) return 0; + return assets.reduce((total, asset) => total + asset.valor, 0); + }; + return (
@@ -28,37 +68,77 @@ const AssetsComponent: React.FC = ({ assets, isLoading })
) : assets && assets.length > 0 ? ( <> -
- - - - - - - - - - - {assets.map((asset: Asset, index: number) => ( - - - - - - - ))} - -
AnoTipoDescriçãoValor
{asset.ano}{asset.tipoBem}{asset.descricao} - {formatCurrency(asset.valor)} -
+
+ {sortedYears.map((year) => { + const yearAssets = groupedAssets[year]; + const isExpanded = expandedYears.has(year); + + return ( +
+ {/* Year Header - Clickable */} + + + {/* Assets Table - Collapsible */} + {isExpanded && ( +
+
+ + + + + + + + + + {yearAssets.map((asset: Asset, index: number) => ( + + + + + + ))} + +
TipoDescriçãoValor
{asset.tipoBem}{asset.descricao} + {formatCurrency(asset.valor)} +
+
+
+ )} +
+ ); + })}
{/* Total Assets */} -
+
- Total Declarado: - - {formatCurrency(assets.reduce((total, asset) => total + asset.valor, 0))} + Total Geral Declarado: + + {formatCurrency(getTotalAssets())}
diff --git a/src/components/MatrixBackground.tsx b/src/components/MatrixBackground.tsx index 4e768e6..33bb1d3 100644 --- a/src/components/MatrixBackground.tsx +++ b/src/components/MatrixBackground.tsx @@ -123,14 +123,12 @@ const MatrixBackground: React.FC = () => { }); // Check which dots are near the mouse - let hoveredCount = 0; dots.forEach((dot, index) => { const dx = dot.x - mouseX; const dy = dot.y - mouseY; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < config.hoverRadius) { - hoveredCount++; const intensity = 1 - (distance / config.hoverRadius); dot.targetBrightness = config.baseBrightness + (config.hoverBrightness - config.baseBrightness) * intensity; diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index 5984f3b..18e29a5 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -31,7 +31,7 @@ const SearchBar: React.FC = ({ className = '' }) => { setError(null); try { - let result = await openCandApi.searchCandidates(query.trim()); + const result = await openCandApi.searchCandidates(query.trim()); setSearchResults(result.candidatos); // Limit to 8 results setShowResults(true); } catch (err) { @@ -93,7 +93,7 @@ const SearchBar: React.FC = ({ className = '' }) => { }, []); const getCandidateDescription = (candidate: Candidate) => { - let desc = ['']; + const desc = ['']; if (candidate.cpf) desc.push(`CPF: ${maskCpf(candidate.cpf)}`); if (candidate.ocupacao && candidate.ocupacao != 'OUTROS') desc.push(`${candidate.ocupacao}`); diff --git a/src/index.css b/src/index.css index 8716ae7..09252ee 100644 --- a/src/index.css +++ b/src/index.css @@ -89,3 +89,19 @@ html, body, #root { #root { min-height: 100vh; } + +/* Custom fade-in animation for dropdown content */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.animate-fadeIn { + animation: fadeIn 0.3s ease-out forwards; +}