@@ -1508,7 +1508,7 @@ function PaginaDashboard({ espaco, ano, mesIdx, escuro, irPara }) {
15081508 Entradas
15091509 </ span >
15101510 < span className = "flex items-center gap-1.5" >
1511- < span className = "h-2.5 w-2.5 rounded-full" style = { { background : escuro ? "linear-gradient(180deg,#64748b,#334155)" : "linear-gradient(180deg,#cbd5e1,#94a3b8 )" } } />
1511+ < span className = "h-2.5 w-2.5 rounded-full" style = { { background : "linear-gradient(180deg,#f87171,#dc2626 )" } } />
15121512 Saídas
15131513 </ span >
15141514 </ div >
@@ -1525,17 +1525,8 @@ function PaginaDashboard({ espaco, ano, mesIdx, escuro, irPara }) {
15251525 < stop offset = "100%" stopColor = "#059669" />
15261526 </ linearGradient >
15271527 < linearGradient id = "gradSaidas" x1 = "0" y1 = "0" x2 = "0" y2 = "1" >
1528- { escuro ? (
1529- < >
1530- < stop offset = "0%" stopColor = "#64748b" />
1531- < stop offset = "100%" stopColor = "#334155" />
1532- </ >
1533- ) : (
1534- < >
1535- < stop offset = "0%" stopColor = "#cbd5e1" />
1536- < stop offset = "100%" stopColor = "#94a3b8" />
1537- </ >
1538- ) }
1528+ < stop offset = "0%" stopColor = "#f87171" />
1529+ < stop offset = "100%" stopColor = "#dc2626" />
15391530 </ linearGradient >
15401531 </ defs >
15411532 < CartesianGrid strokeDasharray = "3 3" stroke = { escuro ? "#1e293b" : "#f1f5f9" } vertical = { false } />
@@ -1548,7 +1539,7 @@ function PaginaDashboard({ espaco, ano, mesIdx, escuro, irPara }) {
15481539 />
15491540 < Tooltip
15501541 cursor = { { fill : escuro ? "rgba(148,163,184,0.07)" : "rgba(100,116,139,0.06)" , radius : 8 } }
1551- content = { < TooltipGrafico cores = { { Entradas : "#10b981" , Saídas : "#94a3b8 " } } /> }
1542+ content = { < TooltipGrafico cores = { { Entradas : "#10b981" , Saídas : "#ef4444 " } } /> }
15521543 />
15531544 < Bar dataKey = "Entradas" fill = "url(#gradEntradas)" radius = { [ 7 , 7 , 2 , 2 ] } maxBarSize = { 26 } />
15541545 < Bar dataKey = "Saídas" fill = "url(#gradSaidas)" radius = { [ 7 , 7 , 2 , 2 ] } maxBarSize = { 26 } />
@@ -1833,6 +1824,46 @@ function PaginaFluxo({ espaco }) {
18331824 METAS FINANCEIRAS
18341825 ============================================================ */
18351826
1827+ // Anel de progresso circular com traço em gradiente (usado nas Metas).
1828+ function AnelProgresso ( { pct, g, completa } ) {
1829+ const r = 36 ;
1830+ const c = 2 * Math . PI * r ;
1831+ const off = c * ( 1 - Math . min ( 100 , pct ) / 100 ) ;
1832+ const gid = "gradAnel-" + g . id ;
1833+ return (
1834+ < div className = "relative h-[88px] w-[88px] shrink-0" >
1835+ < svg viewBox = "0 0 88 88" className = "h-full w-full -rotate-90" >
1836+ < defs >
1837+ < linearGradient id = { gid } x1 = "0" y1 = "0" x2 = "1" y2 = "1" >
1838+ < stop offset = "0%" stopColor = { g . de } />
1839+ < stop offset = "100%" stopColor = { g . para } />
1840+ </ linearGradient >
1841+ </ defs >
1842+ < circle cx = "44" cy = "44" r = { r } fill = "none" strokeWidth = "8" className = "stroke-slate-100 dark:stroke-slate-800" />
1843+ < circle
1844+ cx = "44"
1845+ cy = "44"
1846+ r = { r }
1847+ fill = "none"
1848+ strokeWidth = "8"
1849+ stroke = { `url(#${ gid } )` }
1850+ strokeLinecap = "round"
1851+ strokeDasharray = { c }
1852+ strokeDashoffset = { off }
1853+ className = "transition-[stroke-dashoffset] duration-700"
1854+ />
1855+ </ svg >
1856+ < div className = "absolute inset-0 flex items-center justify-center" >
1857+ { completa ? (
1858+ < span className = "text-xl" > 🎉</ span >
1859+ ) : (
1860+ < span className = "text-sm font-bold text-slate-700 dark:text-slate-100" > { Math . round ( pct ) } %</ span >
1861+ ) }
1862+ </ div >
1863+ </ div >
1864+ ) ;
1865+ }
1866+
18361867function PaginaMetas ( { espaco, atualizar } ) {
18371868 const [ form , setForm ] = useState ( null ) ;
18381869
@@ -1890,23 +1921,56 @@ function PaginaMetas({ espaco, atualizar }) {
18901921 </ div >
18911922 { espaco . metas . length === 0 ? (
18921923 < Card >
1893- < p className = "py-8 text-center text-sm text-slate-400" >
1894- Nenhuma meta ainda. Crie uma meta de economia e acompanhe o progresso!
1895- </ p >
1924+ < div className = "py-10 text-center" >
1925+ < p className = "mb-1 text-3xl" > 🎯</ p >
1926+ < p className = "text-sm font-medium text-slate-500 dark:text-slate-300" > Nenhuma meta ainda</ p >
1927+ < p className = "mt-1 text-xs text-slate-400" >
1928+ Crie uma meta de economia — uma viagem, uma reserva, um sonho — e acompanhe o progresso aqui.
1929+ </ p >
1930+ </ div >
18961931 </ Card >
18971932 ) : (
18981933 < div className = "grid gap-4 sm:grid-cols-2" >
18991934 { espaco . metas . map ( ( m ) => {
19001935 const pct = m . alvo > 0 ? Math . min ( 100 , ( m . atual / m . alvo ) * 100 ) : 0 ;
19011936 const completa = pct >= 100 ;
1937+ const g = gradientePorNome ( m . nome ) ; // cor automática pelo nome, igual às categorias
1938+ const falta = Math . max ( 0 , m . alvo - m . atual ) ;
19021939 return (
1903- < Card key = { m . id } >
1904- < div className = "mb-1 flex items-start justify-between gap-2" >
1905- < p className = "text-sm font-semibold text-slate-700 dark:text-slate-200" >
1906- { m . nome } { completa && "🎉" }
1907- </ p >
1908- < div className = "flex gap-0.5" >
1909- < button onClick = { ( ) => setForm ( m ) } className = "rounded-lg p-1.5 text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-800" aria-label = "Editar" >
1940+ < div
1941+ key = { m . id }
1942+ className = "group relative overflow-hidden rounded-2xl border border-slate-200 bg-white p-5 transition-shadow hover:shadow-md dark:border-slate-700 dark:bg-slate-900"
1943+ >
1944+ { /* filete gradiente no topo, como nas categorias */ }
1945+ < div className = "absolute inset-x-0 top-0 h-1" style = { { background : cssGrad ( g ) } } />
1946+ < div className = "flex items-center gap-4" >
1947+ < AnelProgresso pct = { pct } g = { g } completa = { completa } />
1948+ < div className = "min-w-0 flex-1" >
1949+ < p className = "truncate text-sm font-semibold text-slate-700 dark:text-slate-200" > { m . nome } </ p >
1950+ < p className = "mt-1 text-lg font-bold leading-tight text-slate-800 dark:text-slate-100" >
1951+ { fmtBRL ( m . atual ) }
1952+ < span className = "ml-1 text-xs font-normal text-slate-400" > de { fmtBRL ( m . alvo ) } </ span >
1953+ </ p >
1954+ { completa ? (
1955+ < span
1956+ className = "mt-1.5 inline-block rounded-full px-2.5 py-0.5 text-[11px] font-semibold text-white"
1957+ style = { { background : cssGrad ( g ) } }
1958+ >
1959+ Meta alcançada! 🎉
1960+ </ span >
1961+ ) : (
1962+ < p className = "mt-1 text-xs text-slate-400" >
1963+ Faltam < b className = "text-slate-500 dark:text-slate-300" > { fmtBRL ( falta ) } </ b >
1964+ </ p >
1965+ ) }
1966+ </ div >
1967+ < div className = "flex shrink-0 flex-col gap-0.5 opacity-60 transition group-hover:opacity-100" >
1968+ < button
1969+ onClick = { ( ) => setForm ( m ) }
1970+ className = "rounded-lg p-1.5 text-slate-400 hover:bg-slate-100 hover:text-slate-600 dark:hover:bg-slate-800 dark:hover:text-slate-300"
1971+ aria-label = "Editar"
1972+ title = "Editar"
1973+ >
19101974 < Pencil size = { 14 } />
19111975 </ button >
19121976 < button
@@ -1916,21 +1980,13 @@ function PaginaMetas({ espaco, atualizar }) {
19161980 } }
19171981 className = "rounded-lg p-1.5 text-slate-400 hover:bg-red-50 hover:text-red-500 dark:hover:bg-red-950"
19181982 aria-label = "Excluir"
1983+ title = "Excluir"
19191984 >
19201985 < Trash2 size = { 14 } />
19211986 </ button >
19221987 </ div >
19231988 </ div >
1924- < p className = "mb-2 text-xs text-slate-400" >
1925- { fmtBRL ( m . atual ) } de { fmtBRL ( m . alvo ) } · { pct . toFixed ( 0 ) } %
1926- </ p >
1927- < div className = "h-2.5 overflow-hidden rounded-full bg-slate-100 dark:bg-slate-800" >
1928- < div
1929- className = { "h-full rounded-full transition-all " + ( completa ? "bg-emerald-500" : "bg-emerald-400" ) }
1930- style = { { width : pct + "%" } }
1931- />
1932- </ div >
1933- </ Card >
1989+ </ div >
19341990 ) ;
19351991 } ) }
19361992 </ div >
0 commit comments