Skip to content

Commit c2023ce

Browse files
Thales-Chagasclaude
andcommitted
Design (parte 3): metas em cards com anel de progresso + saidas em vermelho
- PaginaMetas: cards elaborados com AnelProgresso (SVG circular, traco em gradiente da cor automatica do nome), valor grande, "Faltam R$ X" e selo "Meta alcancada!" quando 100%. Estado vazio ilustrado. - Grafico Entradas x Saidas: barra/legenda/tooltip de Saidas agora em vermelho (gradiente #f87171->#dc2626), a pedido da usuaria. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 1aceafe commit c2023ce

1 file changed

Lines changed: 89 additions & 33 deletions

File tree

src/App.jsx

Lines changed: 89 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
18361867
function 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

Comments
 (0)