mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 11:01:45 +00:00
style(ui): padronizar tipografia — font-medium para font-semibold
Padronização de peso tipográfico em títulos, rótulos de seção, nomes de entidades e valores monetários em toda a interface. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6 pt-4">
|
<section className="space-y-6">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
icon={<RiBankLine />}
|
icon={<RiBankLine />}
|
||||||
title="Contas"
|
title="Contas"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6 pt-4">
|
<section className="space-y-6">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
icon={<RiBarChart2Line />}
|
icon={<RiBarChart2Line />}
|
||||||
title="Orçamentos"
|
title="Orçamentos"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6 pt-4">
|
<section className="space-y-6">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
icon={<RiCalendarEventLine />}
|
icon={<RiCalendarEventLine />}
|
||||||
title="Calendário"
|
title="Calendário"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6 pt-4">
|
<section className="space-y-6">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
icon={<RiBankCard2Line />}
|
icon={<RiBankCard2Line />}
|
||||||
title="Cartões"
|
title="Cartões"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6 pt-4">
|
<section className="space-y-6">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
icon={<RiPriceTag3Line />}
|
icon={<RiPriceTag3Line />}
|
||||||
title="Categorias"
|
title="Categorias"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6 pt-4">
|
<section className="space-y-6">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
icon={<RiHistoryLine />}
|
icon={<RiHistoryLine />}
|
||||||
title="Changelog"
|
title="Changelog"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6 pt-4">
|
<section className="space-y-6">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
icon={<RiAtLine />}
|
icon={<RiAtLine />}
|
||||||
title="Pré-Lançamentos"
|
title="Pré-Lançamentos"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6 pt-4">
|
<section className="space-y-6">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
icon={<RiSparklingLine />}
|
icon={<RiSparklingLine />}
|
||||||
title="Insights"
|
title="Insights"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6 pt-4">
|
<section className="space-y-6">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
icon={<RiTodoLine />}
|
icon={<RiTodoLine />}
|
||||||
title="Anotações"
|
title="Anotações"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6 pt-4">
|
<section className="space-y-6">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
icon={<RiGroupLine />}
|
icon={<RiGroupLine />}
|
||||||
title="Pagadores"
|
title="Pagadores"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6 pt-4">
|
<section className="space-y-6">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
icon={<RiBankCard2Line />}
|
icon={<RiBankCard2Line />}
|
||||||
title="Uso de Cartões"
|
title="Uso de Cartões"
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ export default async function RelatorioCartoesPage({
|
|||||||
<div className="flex size-14 items-center justify-center rounded-full bg-muted mb-4">
|
<div className="flex size-14 items-center justify-center rounded-full bg-muted mb-4">
|
||||||
<RiBankCard2Line className="size-7 text-muted-foreground" />
|
<RiBankCard2Line className="size-7 text-muted-foreground" />
|
||||||
</div>
|
</div>
|
||||||
<p className="text-base font-medium">Nenhum cartão selecionado</p>
|
<p className="text-base font-semibold">Nenhum cartão selecionado</p>
|
||||||
<p className="text-sm text-muted-foreground mt-1">
|
<p className="text-sm text-muted-foreground mt-1">
|
||||||
Selecione um cartão para ver os detalhes de uso.
|
Selecione um cartão para ver os detalhes de uso.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6 pt-4">
|
<section className="space-y-6">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
icon={<RiFileChartLine />}
|
icon={<RiFileChartLine />}
|
||||||
title="Tendências"
|
title="Tendências"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6 pt-4">
|
<section className="space-y-6">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
icon={<RiStore2Line />}
|
icon={<RiStore2Line />}
|
||||||
title="Top Estabelecimentos"
|
title="Top Estabelecimentos"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6 pt-4">
|
<section className="space-y-6">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
icon={<RiSecurePaymentLine />}
|
icon={<RiSecurePaymentLine />}
|
||||||
title="Análise de Parcelas"
|
title="Análise de Parcelas"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6 pt-4">
|
<section className="space-y-6">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
icon={<RiSettings2Line />}
|
icon={<RiSettings2Line />}
|
||||||
title="Ajustes"
|
title="Ajustes"
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export default async function Page() {
|
|||||||
<Card className="p-6">
|
<Card className="p-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl font-medium mb-1">Preferências</h2>
|
<h2 className="text-xl font-semibold mb-1">Preferências</h2>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Personalize sua experiência no OpenMonetis ajustando as
|
Personalize sua experiência no OpenMonetis ajustando as
|
||||||
configurações de acordo com suas necessidades.
|
configurações de acordo com suas necessidades.
|
||||||
@@ -92,7 +92,9 @@ export default async function Page() {
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<div className="flex items-center gap-2 mb-1">
|
||||||
<h2 className="text-xl font-medium">OpenMonetis Companion</h2>
|
<h2 className="text-xl font-semibold">
|
||||||
|
OpenMonetis Companion
|
||||||
|
</h2>
|
||||||
<span className="inline-flex items-center gap-1 rounded-full bg-success/10 px-2 py-0.5 text-xs font-medium text-success dark:bg-success/10">
|
<span className="inline-flex items-center gap-1 rounded-full bg-success/10 px-2 py-0.5 text-xs font-medium text-success dark:bg-success/10">
|
||||||
<RiAndroidLine className="h-3 w-3" />
|
<RiAndroidLine className="h-3 w-3" />
|
||||||
Android
|
Android
|
||||||
@@ -114,7 +116,7 @@ export default async function Page() {
|
|||||||
<Card className="p-6">
|
<Card className="p-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl font-medium mb-1">Alterar nome</h2>
|
<h2 className="text-xl font-semibold mb-1">Alterar nome</h2>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Atualize como seu nome aparece no OpenMonetis. Esse nome pode
|
Atualize como seu nome aparece no OpenMonetis. Esse nome pode
|
||||||
ser exibido em diferentes seções do app e em comunicações.
|
ser exibido em diferentes seções do app e em comunicações.
|
||||||
@@ -130,7 +132,7 @@ export default async function Page() {
|
|||||||
<Card className="p-6">
|
<Card className="p-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl font-medium mb-1">Alterar senha</h2>
|
<h2 className="text-xl font-semibold mb-1">Alterar senha</h2>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Defina uma nova senha para sua conta. Guarde-a em local
|
Defina uma nova senha para sua conta. Guarde-a em local
|
||||||
seguro.
|
seguro.
|
||||||
@@ -146,7 +148,7 @@ export default async function Page() {
|
|||||||
<Card className="p-6">
|
<Card className="p-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl font-medium mb-1">Passkeys</h2>
|
<h2 className="text-xl font-semibold mb-1">Passkeys</h2>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Passkeys permitem login sem senha, usando biometria (Face ID,
|
Passkeys permitem login sem senha, usando biometria (Face ID,
|
||||||
Touch ID, Windows Hello) ou chaves de segurança.
|
Touch ID, Windows Hello) ou chaves de segurança.
|
||||||
@@ -162,7 +164,7 @@ export default async function Page() {
|
|||||||
<Card className="p-6">
|
<Card className="p-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl font-medium mb-1">Alterar e-mail</h2>
|
<h2 className="text-xl font-semibold mb-1">Alterar e-mail</h2>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Atualize o e-mail associado à sua conta. Você precisará
|
Atualize o e-mail associado à sua conta. Você precisará
|
||||||
confirmar os links enviados para o novo e também para o e-mail
|
confirmar os links enviados para o novo e também para o e-mail
|
||||||
@@ -182,7 +184,7 @@ export default async function Page() {
|
|||||||
<Card className="p-6">
|
<Card className="p-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl font-medium mb-1 text-destructive">
|
<h2 className="text-xl font-semibold mb-1 text-destructive">
|
||||||
Ações perigosas
|
Ações perigosas
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="space-y-6 pt-4">
|
<section className="space-y-6">
|
||||||
<PageDescription
|
<PageDescription
|
||||||
icon={<RiArrowLeftRightLine />}
|
icon={<RiArrowLeftRightLine />}
|
||||||
title="Lançamentos"
|
title="Lançamentos"
|
||||||
|
|||||||
@@ -120,13 +120,13 @@ export default async function Page() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="max-w-8xl mx-auto px-4 relative">
|
<div className="max-w-8xl mx-auto px-4 relative">
|
||||||
<div className="mx-auto flex max-w-3xl flex-col items-center text-center gap-5 md:gap-6 pb-10 md:pb-14">
|
<div className="mx-auto flex max-w-4xl flex-col items-center text-center gap-5 md:gap-6 pb-10 md:pb-14">
|
||||||
<Badge variant="outline">
|
<Badge variant="outline">
|
||||||
<RiGithubFill className="size-4 mr-1" />
|
<RiGithubFill className="size-4 mr-1" />
|
||||||
Projeto Open Source
|
Projeto Open Source
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|
||||||
<h1 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-medium tracking-tight">
|
<h1 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-semibold">
|
||||||
Suas finanças,
|
Suas finanças,
|
||||||
<span className="text-primary"> do seu jeito</span>
|
<span className="text-primary"> do seu jeito</span>
|
||||||
</h1>
|
</h1>
|
||||||
@@ -207,9 +207,7 @@ export default async function Page() {
|
|||||||
className="flex flex-col items-center text-center gap-1.5"
|
className="flex flex-col items-center text-center gap-1.5"
|
||||||
>
|
>
|
||||||
<Icon className="size-5" style={{ color: colorVar }} />
|
<Icon className="size-5" style={{ color: colorVar }} />
|
||||||
<span className="text-2xl md:text-3xl font-medium">
|
<span className="text-2xl md:text-3xl ">{value}</span>
|
||||||
{value}
|
|
||||||
</span>
|
|
||||||
<span className="text-xs md:text-sm text-muted-foreground">
|
<span className="text-xs md:text-sm text-muted-foreground">
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
@@ -229,7 +227,7 @@ export default async function Page() {
|
|||||||
<Badge variant="outline" className="mb-4">
|
<Badge variant="outline" className="mb-4">
|
||||||
Conheça as telas
|
Conheça as telas
|
||||||
</Badge>
|
</Badge>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-medium tracking-tight mb-3 md:mb-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl mb-3 md:mb-4">
|
||||||
Veja o que você pode fazer
|
Veja o que você pode fazer
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-base md:text-lg text-muted-foreground max-w-2xl mx-auto px-4 sm:px-0">
|
<p className="text-base md:text-lg text-muted-foreground max-w-2xl mx-auto px-4 sm:px-0">
|
||||||
@@ -254,7 +252,7 @@ export default async function Page() {
|
|||||||
<Badge variant="outline" className="mb-4">
|
<Badge variant="outline" className="mb-4">
|
||||||
O que tem aqui
|
O que tem aqui
|
||||||
</Badge>
|
</Badge>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-medium tracking-tight mb-3 md:mb-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl mb-3 md:mb-4">
|
||||||
Funcionalidades que importam
|
Funcionalidades que importam
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-base md:text-lg text-muted-foreground max-w-2xl mx-auto px-4 sm:px-0">
|
<p className="text-base md:text-lg text-muted-foreground max-w-2xl mx-auto px-4 sm:px-0">
|
||||||
@@ -282,7 +280,7 @@ export default async function Page() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium text-base md:text-lg mb-1.5 md:mb-2">
|
<h3 className="font-semibold text-base md:text-lg mb-1.5 md:mb-2">
|
||||||
{feature.title}
|
{feature.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||||
@@ -298,7 +296,7 @@ export default async function Page() {
|
|||||||
|
|
||||||
<AnimateOnScroll>
|
<AnimateOnScroll>
|
||||||
<div className="mt-8 md:mt-12">
|
<div className="mt-8 md:mt-12">
|
||||||
<h3 className="text-sm font-medium text-center mb-4 md:mb-6 text-muted-foreground">
|
<h3 className="text-sm font-semibold text-center mb-4 md:mb-6 text-muted-foreground">
|
||||||
Também inclui
|
Também inclui
|
||||||
</h3>
|
</h3>
|
||||||
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
@@ -319,7 +317,7 @@ export default async function Page() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<h4 className="font-medium text-sm mb-0.5">
|
<h4 className="font-semibold text-sm mb-0.5">
|
||||||
{feature.title}
|
{feature.title}
|
||||||
</h4>
|
</h4>
|
||||||
<p className="text-xs text-muted-foreground leading-relaxed">
|
<p className="text-xs text-muted-foreground leading-relaxed">
|
||||||
@@ -346,7 +344,7 @@ export default async function Page() {
|
|||||||
<RiSmartphoneLine className="size-3.5 mr-1" />
|
<RiSmartphoneLine className="size-3.5 mr-1" />
|
||||||
Mobile
|
Mobile
|
||||||
</Badge>
|
</Badge>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-medium tracking-tight mb-3 md:mb-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl mb-3 md:mb-4">
|
||||||
Use o OpenMonetis no celular sem perder o fluxo
|
Use o OpenMonetis no celular sem perder o fluxo
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-base md:text-lg text-muted-foreground max-w-2xl mx-auto px-4 sm:px-0">
|
<p className="text-base md:text-lg text-muted-foreground max-w-2xl mx-auto px-4 sm:px-0">
|
||||||
@@ -384,7 +382,7 @@ export default async function Page() {
|
|||||||
<RiSmartphoneLine className="size-3.5 mr-1" />
|
<RiSmartphoneLine className="size-3.5 mr-1" />
|
||||||
PWA instalável
|
PWA instalável
|
||||||
</Badge>
|
</Badge>
|
||||||
<h3 className="text-2xl md:text-3xl font-medium tracking-tight mb-3">
|
<h3 className="text-2xl md:text-3xl font-semibold tracking-tight mb-3">
|
||||||
Leve o OpenMonetis para a tela inicial
|
Leve o OpenMonetis para a tela inicial
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-muted-foreground mb-6 leading-relaxed">
|
<p className="text-muted-foreground mb-6 leading-relaxed">
|
||||||
@@ -430,7 +428,7 @@ export default async function Page() {
|
|||||||
Companion Android
|
Companion Android
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<h3 className="text-2xl md:text-3xl font-medium tracking-tight mb-3">
|
<h3 className="text-2xl md:text-3xl font-semibold tracking-tight mb-3">
|
||||||
Capture, envie e revise no mesmo fluxo
|
Capture, envie e revise no mesmo fluxo
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-muted-foreground mb-6 leading-relaxed">
|
<p className="text-muted-foreground mb-6 leading-relaxed">
|
||||||
@@ -529,7 +527,7 @@ export default async function Page() {
|
|||||||
<Badge variant="outline" className="mb-4">
|
<Badge variant="outline" className="mb-4">
|
||||||
Stack técnica
|
Stack técnica
|
||||||
</Badge>
|
</Badge>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-medium tracking-tight mb-3 md:mb-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl mb-3 md:mb-4">
|
||||||
O que roda por baixo
|
O que roda por baixo
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-base md:text-lg text-muted-foreground max-w-2xl mx-auto px-4 sm:px-0">
|
<p className="text-base md:text-lg text-muted-foreground max-w-2xl mx-auto px-4 sm:px-0">
|
||||||
@@ -556,7 +554,7 @@ export default async function Page() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium text-base md:text-lg mb-1.5 md:mb-2">
|
<h3 className="font-semibold text-base md:text-lg mb-1.5 md:mb-2">
|
||||||
{item.title}
|
{item.title}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-muted-foreground mb-2 md:mb-3">
|
<p className="text-sm text-muted-foreground mb-2 md:mb-3">
|
||||||
@@ -582,7 +580,7 @@ export default async function Page() {
|
|||||||
<Badge variant="outline" className="mb-4">
|
<Badge variant="outline" className="mb-4">
|
||||||
Como usar
|
Como usar
|
||||||
</Badge>
|
</Badge>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-medium tracking-tight mb-3 md:mb-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl mb-3 md:mb-4">
|
||||||
Rode no seu computador
|
Rode no seu computador
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-base md:text-lg text-muted-foreground px-4 sm:px-0">
|
<p className="text-base md:text-lg text-muted-foreground px-4 sm:px-0">
|
||||||
@@ -617,7 +615,7 @@ export default async function Page() {
|
|||||||
<Badge variant="outline" className="mb-4">
|
<Badge variant="outline" className="mb-4">
|
||||||
Para quem é?
|
Para quem é?
|
||||||
</Badge>
|
</Badge>
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-medium tracking-tight mb-3 md:mb-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl mb-3 md:mb-4">
|
||||||
Feito para quem gosta de controle
|
Feito para quem gosta de controle
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-base md:text-lg text-muted-foreground px-4 sm:px-0">
|
<p className="text-base md:text-lg text-muted-foreground px-4 sm:px-0">
|
||||||
@@ -644,7 +642,7 @@ export default async function Page() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium mb-1">{item.title}</h3>
|
<h3 className="font-semibold mb-1">{item.title}</h3>
|
||||||
<p className="text-xs sm:text-sm text-muted-foreground">
|
<p className="text-xs sm:text-sm text-muted-foreground">
|
||||||
{item.description}
|
{item.description}
|
||||||
</p>
|
</p>
|
||||||
@@ -664,7 +662,7 @@ export default async function Page() {
|
|||||||
<div className="max-w-8xl mx-auto px-4">
|
<div className="max-w-8xl mx-auto px-4">
|
||||||
<AnimateOnScroll>
|
<AnimateOnScroll>
|
||||||
<div className="mx-auto max-w-4xl rounded-2xl border bg-card px-8 py-12 md:py-16 text-center">
|
<div className="mx-auto max-w-4xl rounded-2xl border bg-card px-8 py-12 md:py-16 text-center">
|
||||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-medium tracking-tight mb-3 md:mb-4">
|
<h2 className="text-2xl sm:text-3xl md:text-4xl mb-3 md:mb-4">
|
||||||
Pronto para testar?
|
Pronto para testar?
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-base md:text-lg text-muted-foreground mb-6 md:mb-8">
|
<p className="text-base md:text-lg text-muted-foreground mb-6 md:mb-8">
|
||||||
@@ -715,7 +713,7 @@ export default async function Page() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium mb-3 md:mb-4">Projeto</h3>
|
<h3 className="font-semibold mb-3 md:mb-4">Projeto</h3>
|
||||||
<ul className="space-y-2.5 md:space-y-3 text-sm text-muted-foreground">
|
<ul className="space-y-2.5 md:space-y-3 text-sm text-muted-foreground">
|
||||||
<li>
|
<li>
|
||||||
<Link
|
<Link
|
||||||
@@ -749,7 +747,7 @@ export default async function Page() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium mb-3 md:mb-4">Companion</h3>
|
<h3 className="font-semibold mb-3 md:mb-4">Companion</h3>
|
||||||
<ul className="space-y-2.5 md:space-y-3 text-sm text-muted-foreground">
|
<ul className="space-y-2.5 md:space-y-3 text-sm text-muted-foreground">
|
||||||
<li>
|
<li>
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@@ -88,7 +88,9 @@ export function AccountCard({
|
|||||||
{icon}
|
{icon}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<h2 className="text-lg font-medium text-foreground">{accountName}</h2>
|
<h2 className="text-lg font-semibold text-foreground">
|
||||||
|
{accountName}
|
||||||
|
</h2>
|
||||||
|
|
||||||
{(excludeFromBalance || excludeInitialBalanceFromIncome) && (
|
{(excludeFromBalance || excludeInitialBalanceFromIncome) && (
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export function AccountStatementCard({
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<h2 className="truncate text-sm font-medium text-foreground">
|
<h2 className="truncate text-sm font-semibold text-foreground">
|
||||||
{accountName}
|
{accountName}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
@@ -81,12 +81,12 @@ export function AccountStatementCard({
|
|||||||
|
|
||||||
{/* Linha 2 — saldo final (hero) */}
|
{/* Linha 2 — saldo final (hero) */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<p className="text-sm font-medium text-muted-foreground ">
|
<p className="text-sm text-muted-foreground ">
|
||||||
Saldo ao final do período
|
Saldo ao final do período
|
||||||
</p>
|
</p>
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
amount={currentBalance}
|
amount={currentBalance}
|
||||||
className="text-3xl leading-none font-medium tracking-tight sm:text-[2rem]"
|
className="text-3xl leading-none tracking-tighter sm:text-[2rem]"
|
||||||
/>
|
/>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Badge
|
<Badge
|
||||||
|
|||||||
@@ -69,9 +69,7 @@ function PdfCanvas({ url }: PdfCanvasProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full flex-col items-center justify-center gap-2 bg-muted/50">
|
<div className="flex h-full w-full flex-col items-center justify-center gap-2 bg-muted/50">
|
||||||
<RiFilePdf2Line className="size-12 text-muted-foreground/40" />
|
<RiFilePdf2Line className="size-12 text-muted-foreground/40" />
|
||||||
<span className="text-xs font-medium text-muted-foreground/60">
|
<span className="text-xs text-muted-foreground/60">PDF Protegido</span>
|
||||||
PDF Protegido
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -153,7 +151,7 @@ export function AttachmentGridItem({
|
|||||||
|
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<p className="truncate text-sm font-medium leading-tight text-foreground">
|
<p className="truncate text-sm font-semibold leading-tight text-foreground">
|
||||||
{attachment.fileName}
|
{attachment.fileName}
|
||||||
</p>
|
</p>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
@@ -180,25 +178,21 @@ export function AttachmentGridItem({
|
|||||||
{attachment.transactionName}
|
{attachment.transactionName}
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<span
|
<span className={cn("shrink-0 text-sm font-medium tracking-tighter")}>
|
||||||
className={cn(
|
|
||||||
"shrink-0 text-sm font-medium tracking-tighter tabular-nums",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{formatCurrency(amount)}
|
{formatCurrency(amount)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Footer: Tamanho + Botão Detalhes */}
|
{/* Footer: Tamanho + Botão Detalhes */}
|
||||||
<div className="mt-auto flex items-center justify-between border-t pt-3">
|
<div className="mt-auto flex items-center justify-between border-t pt-3">
|
||||||
<span className="text-xs font-medium text-muted-foreground/70">
|
<span className="text-xs text-muted-foreground/70">
|
||||||
{formatBytes(attachment.fileSize)}
|
{formatBytes(attachment.fileSize)}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onDetails}
|
onClick={onDetails}
|
||||||
disabled={isLoadingDetails}
|
disabled={isLoadingDetails}
|
||||||
className="text-xs font-medium text-muted-foreground/70 underline-offset-2 hover:underline focus-visible:outline-none disabled:opacity-50"
|
className="text-xs text-muted-foreground/70 underline-offset-2 hover:underline focus-visible:outline-none disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{isLoadingDetails ? "Carregando..." : "Detalhes"}
|
{isLoadingDetails ? "Carregando..." : "Detalhes"}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export function AttachmentPreview({
|
|||||||
>
|
>
|
||||||
<RiArrowLeftSLine className="size-4" />
|
<RiArrowLeftSLine className="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
<span className="select-none text-xs text-muted-foreground tabular-nums">
|
<span className="select-none text-xs text-muted-foreground">
|
||||||
{currentIndex + 1} / {attachments.length}
|
{currentIndex + 1} / {attachments.length}
|
||||||
</span>
|
</span>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -19,8 +19,6 @@ import { TransactionDetailsDialog } from "@/features/transactions/components/dia
|
|||||||
import { TransactionDialog } from "@/features/transactions/components/dialogs/transaction-dialog/transaction-dialog";
|
import { TransactionDialog } from "@/features/transactions/components/dialogs/transaction-dialog/transaction-dialog";
|
||||||
import type { TransactionItem } from "@/features/transactions/components/types";
|
import type { TransactionItem } from "@/features/transactions/components/types";
|
||||||
import { EmptyState } from "@/shared/components/empty-state";
|
import { EmptyState } from "@/shared/components/empty-state";
|
||||||
import MonthNavigation from "@/shared/components/month-picker/month-navigation";
|
|
||||||
import PageDescription from "@/shared/components/page-description";
|
|
||||||
import { Card, CardContent } from "@/shared/components/ui/card";
|
import { Card, CardContent } from "@/shared/components/ui/card";
|
||||||
import { cn } from "@/shared/utils/ui";
|
import { cn } from "@/shared/utils/ui";
|
||||||
|
|
||||||
@@ -143,14 +141,6 @@ export function AttachmentsPage({ attachments }: AttachmentsPageProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full space-y-6">
|
<div className="w-full space-y-6">
|
||||||
<PageDescription
|
|
||||||
icon={<RiAttachmentLine className="size-5" />}
|
|
||||||
title="Anexos"
|
|
||||||
subtitle="Comprovantes e documentos dos seus lançamentos no mês."
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MonthNavigation />
|
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
{attachments.length === 0 ? (
|
{attachments.length === 0 ? (
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ interface AuthHeaderProps {
|
|||||||
export function AuthHeader({ title, description }: AuthHeaderProps) {
|
export function AuthHeader({ title, description }: AuthHeaderProps) {
|
||||||
return (
|
return (
|
||||||
<div className={cn("flex flex-col gap-2.5")}>
|
<div className={cn("flex flex-col gap-2.5")}>
|
||||||
<h1 className="text-2xl font-medium tracking-tight text-card-foreground">
|
<h1 className="text-2xl font-semibold tracking-tight text-card-foreground">
|
||||||
{title}
|
{title}
|
||||||
</h1>
|
</h1>
|
||||||
{description ? (
|
{description ? (
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export function BudgetCard({
|
|||||||
size="lg"
|
size="lg"
|
||||||
/>
|
/>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<h3 className="text-base font-medium leading-tight">
|
<h3 className="text-base font-semibold leading-tight">
|
||||||
{formatCategoryName(budget)}
|
{formatCategoryName(budget)}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ export function CalendarGrid({
|
|||||||
}: CalendarGridProps) {
|
}: CalendarGridProps) {
|
||||||
return (
|
return (
|
||||||
<div className="overflow-hidden rounded-lg bg-card drop-shadow-xs border-none">
|
<div className="overflow-hidden rounded-lg bg-card drop-shadow-xs border-none">
|
||||||
<div className="grid grid-cols-7 text-sm font-medium uppercase tracking-wide text-muted-foreground">
|
<div className="grid grid-cols-7 text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
||||||
{WEEK_DAYS_SHORT.map((dayName) => (
|
{WEEK_DAYS_SHORT.map((dayName) => (
|
||||||
<span key={dayName} className="px-3 py-2 text-center text-primary">
|
<span key={dayName} className="px-3 py-2 text-center">
|
||||||
{dayName}
|
{dayName}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ const renderCard = (event: Extract<CalendarEvent, { type: "card" }>) => (
|
|||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
<div className="flex gap-1 items-center">
|
<div className="flex gap-1 items-center">
|
||||||
<span className="text-sm font-medium leading-tight">
|
<span className="text-sm font-medium leading-tight">
|
||||||
Vencimento Invoice - {event.card.name}
|
Vencimento Fatura - {event.card.name}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ export function CardItem({
|
|||||||
|
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<h3 className="truncate text-sm font-medium text-foreground sm:text-base">
|
<h3 className="truncate text-sm font-semibold text-foreground sm:text-base">
|
||||||
{name}
|
{name}
|
||||||
</h3>
|
</h3>
|
||||||
{note ? (
|
{note ? (
|
||||||
@@ -206,29 +206,29 @@ export function CardItem({
|
|||||||
<>
|
<>
|
||||||
<div className="grid grid-cols-3 gap-4">
|
<div className="grid grid-cols-3 gap-4">
|
||||||
<div className="flex flex-col items-start gap-1">
|
<div className="flex flex-col items-start gap-1">
|
||||||
<p className="text-sm font-medium text-foreground">
|
<p className="text-sm font-semibold text-foreground">
|
||||||
<MoneyValues amount={metrics[0].value} />
|
<MoneyValues amount={metrics[0].value} />
|
||||||
</p>
|
</p>
|
||||||
<span className="text-xs font-medium text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
{metrics[0].label}
|
{metrics[0].label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col items-center gap-1">
|
<div className="flex flex-col items-center gap-1">
|
||||||
<p className="flex items-center gap-1.5 text-sm font-medium text-foreground">
|
<p className="flex items-center gap-1.5 text-sm font-semibold text-foreground">
|
||||||
<span className="size-2 rounded-full bg-primary" />
|
<span className="size-2 rounded-full bg-primary" />
|
||||||
<MoneyValues amount={metrics[1].value} />
|
<MoneyValues amount={metrics[1].value} />
|
||||||
</p>
|
</p>
|
||||||
<span className="text-xs font-medium text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
{metrics[1].label}
|
{metrics[1].label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col items-end gap-1">
|
<div className="flex flex-col items-end gap-1">
|
||||||
<p className="text-sm font-medium text-foreground">
|
<p className="text-sm font-semibold text-foreground">
|
||||||
<MoneyValues amount={metrics[2].value} />
|
<MoneyValues amount={metrics[2].value} />
|
||||||
</p>
|
</p>
|
||||||
<span className="text-xs font-medium text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
{metrics[2].label}
|
{metrics[2].label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ export function CategoriesPage({ categories }: CategoriesPageProps) {
|
|||||||
<TableCell className="font-medium">
|
<TableCell className="font-medium">
|
||||||
<Link
|
<Link
|
||||||
href={`/categories/${category.id}`}
|
href={`/categories/${category.id}`}
|
||||||
className="inline-flex items-center gap-1 underline-offset-2 hover:text-primary hover:underline"
|
className="inline-flex items-center gap-1 underline-offset-2 hover:text-primary hover:underline font-semibold"
|
||||||
>
|
>
|
||||||
{category.name}
|
{category.name}
|
||||||
<RiExternalLinkLine
|
<RiExternalLinkLine
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export function CategoryDetailHeader({
|
|||||||
size="lg"
|
size="lg"
|
||||||
/>
|
/>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<h1 className="text-xl font-medium leading-tight">
|
<h1 className="text-xl font-semibold leading-tight">
|
||||||
{category.name}
|
{category.name}
|
||||||
</h1>
|
</h1>
|
||||||
<div className="flex flex-wrap items-center gap-2 text-sm text-muted-foreground">
|
<div className="flex flex-wrap items-center gap-2 text-sm text-muted-foreground">
|
||||||
@@ -99,7 +99,7 @@ export function CategoryDetailHeader({
|
|||||||
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
||||||
Total em {currentPeriodLabel}
|
Total em {currentPeriodLabel}
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-1 text-2xl font-medium">
|
<p className="mt-1 text-2xl font-semibold">
|
||||||
{currencyFormatter.format(currentTotal)}
|
{currencyFormatter.format(currentTotal)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -107,7 +107,7 @@ export function CategoryDetailHeader({
|
|||||||
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
||||||
Total em {previousPeriodLabel}
|
Total em {previousPeriodLabel}
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-1 text-lg font-medium text-muted-foreground">
|
<p className="mt-1 text-lg font-semibold text-muted-foreground">
|
||||||
{currencyFormatter.format(previousTotal)}
|
{currencyFormatter.format(previousTotal)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -117,7 +117,7 @@ export function CategoryDetailHeader({
|
|||||||
</p>
|
</p>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"mt-1 flex items-center gap-1 text-xl font-medium",
|
"mt-1 flex items-center gap-1 text-lg font-semibold",
|
||||||
variationColor,
|
variationColor,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export function CategoryPickerDialog({
|
|||||||
<div className="flex max-h-96 flex-col gap-4 overflow-y-auto pr-1">
|
<div className="flex max-h-96 flex-col gap-4 overflow-y-auto pr-1">
|
||||||
{filteredGroups.map((group) => (
|
{filteredGroups.map((group) => (
|
||||||
<div key={group.label}>
|
<div key={group.label}>
|
||||||
<p className="mb-2 text-xs font-medium text-muted-foreground">
|
<p className="mb-2 text-xs text-muted-foreground">
|
||||||
{group.label}
|
{group.label}
|
||||||
</p>
|
</p>
|
||||||
<div className="grid grid-cols-8 gap-1.5">
|
<div className="grid grid-cols-8 gap-1.5">
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import {
|
|||||||
ACCOUNT_AUTO_INVOICE_NOTE_PREFIX,
|
ACCOUNT_AUTO_INVOICE_NOTE_PREFIX,
|
||||||
INITIAL_BALANCE_NOTE,
|
INITIAL_BALANCE_NOTE,
|
||||||
} from "@/shared/lib/accounts/constants";
|
} from "@/shared/lib/accounts/constants";
|
||||||
import type { CategoryType } from "@/shared/lib/categories/constants";
|
import {
|
||||||
|
type CategoryType,
|
||||||
|
INVOICE_PAYMENT_CATEGORY_NAME,
|
||||||
|
} from "@/shared/lib/categories/constants";
|
||||||
import { db } from "@/shared/lib/db";
|
import { db } from "@/shared/lib/db";
|
||||||
import { getAdminPayerId } from "@/shared/lib/payers/get-admin-id";
|
import { getAdminPayerId } from "@/shared/lib/payers/get-admin-id";
|
||||||
import { calculatePercentageChange } from "@/shared/utils/math";
|
import { calculatePercentageChange } from "@/shared/utils/math";
|
||||||
@@ -45,6 +48,7 @@ export async function fetchCategoryDetails(
|
|||||||
const previousPeriod = getPreviousPeriod(period);
|
const previousPeriod = getPreviousPeriod(period);
|
||||||
const transactionType = category.type === "receita" ? "Receita" : "Despesa";
|
const transactionType = category.type === "receita" ? "Receita" : "Despesa";
|
||||||
const adminPayerId = await getAdminPayerId(userId);
|
const adminPayerId = await getAdminPayerId(userId);
|
||||||
|
const isInvoiceCategory = category.name === INVOICE_PAYMENT_CATEGORY_NAME;
|
||||||
|
|
||||||
const sanitizedNote = or(
|
const sanitizedNote = or(
|
||||||
isNull(transactions.note),
|
isNull(transactions.note),
|
||||||
@@ -59,7 +63,7 @@ export async function fetchCategoryDetails(
|
|||||||
eq(transactions.transactionType, transactionType),
|
eq(transactions.transactionType, transactionType),
|
||||||
eq(transactions.period, period),
|
eq(transactions.period, period),
|
||||||
eq(transactions.payerId, adminPayerId),
|
eq(transactions.payerId, adminPayerId),
|
||||||
sanitizedNote,
|
...(isInvoiceCategory ? [] : [sanitizedNote]),
|
||||||
),
|
),
|
||||||
with: {
|
with: {
|
||||||
payer: true,
|
payer: true,
|
||||||
@@ -108,7 +112,7 @@ export async function fetchCategoryDetails(
|
|||||||
eq(transactions.categoryId, categoryId),
|
eq(transactions.categoryId, categoryId),
|
||||||
eq(transactions.transactionType, transactionType),
|
eq(transactions.transactionType, transactionType),
|
||||||
eq(transactions.payerId, adminPayerId),
|
eq(transactions.payerId, adminPayerId),
|
||||||
sanitizedNote,
|
...(isInvoiceCategory ? [] : [sanitizedNote]),
|
||||||
eq(transactions.period, previousPeriod),
|
eq(transactions.period, previousPeriod),
|
||||||
or(
|
or(
|
||||||
isNull(transactions.note),
|
isNull(transactions.note),
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export function BillListItem({ bill, onPay }: BillListItemProps) {
|
|||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"cursor-help rounded-full py-0.5",
|
"cursor-help rounded-full py-0.5",
|
||||||
bill.isSettled && "text-success",
|
bill.isSettled && "text-success font-semibold",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{statusLabel}
|
{statusLabel}
|
||||||
@@ -60,7 +60,7 @@ export function BillListItem({ bill, onPay }: BillListItemProps) {
|
|||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-full py-0.5",
|
"rounded-full py-0.5",
|
||||||
bill.isSettled && "text-success",
|
bill.isSettled && "text-success font-semibold",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{statusLabel}
|
{statusLabel}
|
||||||
@@ -72,7 +72,7 @@ export function BillListItem({ bill, onPay }: BillListItemProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex shrink-0 flex-col items-end">
|
<div className="flex shrink-0 flex-col items-end">
|
||||||
<MoneyValues amount={bill.amount} />
|
<MoneyValues className="font-medium" amount={bill.amount} />
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ export function BillPaymentDialog({
|
|||||||
<p className="mb-1 text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
<p className="mb-1 text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
||||||
Boleto
|
Boleto
|
||||||
</p>
|
</p>
|
||||||
<p className="text-base font-medium text-foreground">
|
<p className="text-base font-semibold text-foreground">
|
||||||
{bill.name}
|
{bill.name}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -113,7 +113,7 @@ export function BillPaymentDialog({
|
|||||||
</div>
|
</div>
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
amount={bill.amount}
|
amount={bill.amount}
|
||||||
className="text-lg font-medium"
|
className="text-lg font-semibold"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -281,12 +281,12 @@ export function CategoryBreakdownWidgetView({
|
|||||||
|
|
||||||
<div className="flex shrink-0 flex-col items-end gap-0.5">
|
<div className="flex shrink-0 flex-col items-end gap-0.5">
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
className="text-foreground"
|
className="text-foreground font-medium"
|
||||||
amount={category.currentAmount}
|
amount={category.currentAmount}
|
||||||
/>
|
/>
|
||||||
{category.percentageChange !== null ? (
|
{category.percentageChange !== null ? (
|
||||||
<span
|
<span
|
||||||
className={`flex items-center gap-0.5 text-xs ${changeClassName}`}
|
className={`flex items-center gap-0.5 text-xs font-medium ${changeClassName}`}
|
||||||
>
|
>
|
||||||
{hasIncrease ? (
|
{hasIncrease ? (
|
||||||
<RiArrowUpSFill className="size-3" />
|
<RiArrowUpSFill className="size-3" />
|
||||||
|
|||||||
@@ -197,7 +197,9 @@ export function CategoryHistoryWidget({ data }: CategoryHistoryWidgetProps) {
|
|||||||
style={{ backgroundColor: color }}
|
style={{ backgroundColor: color }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<span className="text-foreground">{category.name}</span>
|
<span className="text-sm font-medium text-foreground">
|
||||||
|
{category.name}
|
||||||
|
</span>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
@@ -398,7 +400,7 @@ export function CategoryHistoryWidget({ data }: CategoryHistoryWidgetProps) {
|
|||||||
{config?.label}
|
{config?.label}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs font-medium tabular-nums">
|
<span className="text-xs font-medium">
|
||||||
{formatCurrency(value)}
|
{formatCurrency(value)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -116,13 +116,14 @@ const getPercentChange = (current: number, previous: number): string => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const change = ((current - previous) / Math.abs(previous)) * 100;
|
const change = ((current - previous) / Math.abs(previous)) * 100;
|
||||||
return Number.isFinite(change) && Math.abs(change) < 1000000
|
if (!Number.isFinite(change)) return "—";
|
||||||
? formatPercentage(change, {
|
if (change > 999) return "+999%";
|
||||||
maximumFractionDigits: 1,
|
if (change < -999) return "-999%";
|
||||||
minimumFractionDigits: 1,
|
return formatPercentage(change, {
|
||||||
signDisplay: "always",
|
maximumFractionDigits: 2,
|
||||||
})
|
minimumFractionDigits: 2,
|
||||||
: "—";
|
signDisplay: "always",
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTrendBadgeClass = (trend: Trend, invertTrend: boolean): string => {
|
const getTrendBadgeClass = (trend: Trend, invertTrend: boolean): string => {
|
||||||
@@ -159,7 +160,7 @@ export function DashboardMetricsCards({ metrics }: DashboardMetricsCardsProps) {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div>
|
<div>
|
||||||
<CardTitle className="flex items-center gap-1.5 tracking-tight">
|
<CardTitle className="flex items-center gap-1.5 ">
|
||||||
<Icon className={cn("size-4", iconClass)} aria-hidden />
|
<Icon className={cn("size-4", iconClass)} aria-hidden />
|
||||||
{label}
|
{label}
|
||||||
<MetricsCardInfoButton
|
<MetricsCardInfoButton
|
||||||
@@ -179,12 +180,12 @@ export function DashboardMetricsCards({ metrics }: DashboardMetricsCardsProps) {
|
|||||||
<CardContent className="flex flex-col gap-3">
|
<CardContent className="flex flex-col gap-3">
|
||||||
<div className="flex flex-wrap items-center justify-between gap-2 mt-1">
|
<div className="flex flex-wrap items-center justify-between gap-2 mt-1">
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
className="text-2xl leading-none"
|
className="text-2xl leading-none font-medium"
|
||||||
amount={metric.current}
|
amount={metric.current}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center gap-1 text-xs ",
|
"inline-flex items-center gap-1 text-xs font-medium",
|
||||||
trendBadgeClass,
|
trendBadgeClass,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -195,7 +196,7 @@ export function DashboardMetricsCards({ metrics }: DashboardMetricsCardsProps) {
|
|||||||
|
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground">
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
className="inline text-xs text-muted-foreground"
|
className="inline text-xs font-medium text-muted-foreground"
|
||||||
amount={metric.previous}
|
amount={metric.previous}
|
||||||
/>
|
/>
|
||||||
<span className="ml-1">no mês anterior</span>
|
<span className="ml-1">no mês anterior</span>
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
import { formatCurrentDate, getGreeting } from "./welcome-widget";
|
import { formatCurrentDate, getGreeting } from "./welcome-widget";
|
||||||
|
|
||||||
export function DashboardWelcome({ name }: { name?: string | null }) {
|
type DashboardWelcomeProps = {
|
||||||
|
name?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function DashboardWelcome({ name }: DashboardWelcomeProps) {
|
||||||
const displayName = name && name.trim().length > 0 ? name : "Administrador";
|
const displayName = name && name.trim().length > 0 ? name : "Administrador";
|
||||||
const formattedDate = formatCurrentDate();
|
const formattedDate = formatCurrentDate();
|
||||||
const greeting = getGreeting();
|
const greeting = getGreeting();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="py-4">
|
<section className="py-4">
|
||||||
<div className="tracking-tight">
|
<div>
|
||||||
<h1 className="text-xl font-medium">
|
<h1 className="text-xl tracking-tight">
|
||||||
{greeting}, {displayName}
|
{greeting}, {displayName}
|
||||||
</h1>
|
</h1>
|
||||||
<h2 className="text-sm mt-1 text-muted-foreground">{formattedDate}</h2>
|
<h2 className="mt-1 text-sm text-muted-foreground">{formattedDate}</h2>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -44,8 +44,9 @@ export function GoalProgressItem({
|
|||||||
{item.categoryName}
|
{item.categoryName}
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-0.5 text-xs text-muted-foreground">
|
<p className="mt-0.5 text-xs text-muted-foreground">
|
||||||
<MoneyValues amount={item.spentAmount} /> de{" "}
|
<MoneyValues className="font-medium" amount={item.spentAmount} />{" "}
|
||||||
<MoneyValues amount={item.budgetAmount} />
|
de{" "}
|
||||||
|
<MoneyValues className="font-medium" amount={item.budgetAmount} />
|
||||||
<span className={`ml-1.5 font-medium ${deltaColor}`}>
|
<span className={`ml-1.5 font-medium ${deltaColor}`}>
|
||||||
{formatGoalProgressPercentage(percentageDelta, true)}
|
{formatGoalProgressPercentage(percentageDelta, true)}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -132,12 +132,12 @@ export function InstallmentAnalysisPage({
|
|||||||
{/* Card de resumo principal */}
|
{/* Card de resumo principal */}
|
||||||
<Card className="border-none bg-primary/15">
|
<Card className="border-none bg-primary/15">
|
||||||
<CardContent className="flex flex-col items-start justify-center gap-2 py-2">
|
<CardContent className="flex flex-col items-start justify-center gap-2 py-2">
|
||||||
<p className="text-sm font-medium text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Se você pagar tudo que está selecionado:
|
Se você pagar tudo que está selecionado:
|
||||||
</p>
|
</p>
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
amount={grandTotal}
|
amount={grandTotal}
|
||||||
className="text-3xl font-medium text-primary"
|
className="text-3xl font-semibold text-primary"
|
||||||
/>
|
/>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
{selectedCount} {selectedCount === 1 ? "parcela" : "parcelas"}{" "}
|
{selectedCount} {selectedCount === 1 ? "parcela" : "parcelas"}{" "}
|
||||||
@@ -167,7 +167,7 @@ export function InstallmentAnalysisPage({
|
|||||||
|
|
||||||
{/* Seção de Lançamentos Parcelados */}
|
{/* Seção de Lançamentos Parcelados */}
|
||||||
{data.installmentGroups.length > 0 && (
|
{data.installmentGroups.length > 0 && (
|
||||||
<div className="flex flex-col gap-3">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||||
{data.installmentGroups.map((group) => (
|
{data.installmentGroups.map((group) => (
|
||||||
<InstallmentGroupCard
|
<InstallmentGroupCard
|
||||||
key={group.seriesId}
|
key={group.seriesId}
|
||||||
|
|||||||
@@ -59,7 +59,10 @@ export function InstallmentExpenseListItem({
|
|||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<MoneyValues amount={expense.amount} className="shrink-0" />
|
<MoneyValues
|
||||||
|
amount={expense.amount}
|
||||||
|
className="shrink-0 font-medium"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
@@ -67,7 +70,7 @@ export function InstallmentExpenseListItem({
|
|||||||
{" · Restante "}
|
{" · Restante "}
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
amount={remainingAmount}
|
amount={remainingAmount}
|
||||||
className="inline-block font-medium"
|
className="inline-block font-semibold"
|
||||||
/>{" "}
|
/>{" "}
|
||||||
({remainingInstallments})
|
({remainingInstallments})
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -116,7 +116,10 @@ export function InvoiceListItem({ invoice, onPay }: InvoiceListItemProps) {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm font-medium text-foreground">
|
<div className="text-sm font-medium text-foreground">
|
||||||
<MoneyValues amount={share.amount} />
|
<MoneyValues
|
||||||
|
className="font-medium"
|
||||||
|
amount={share.amount}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
@@ -144,7 +147,7 @@ export function InvoiceListItem({ invoice, onPay }: InvoiceListItemProps) {
|
|||||||
paymentTooltipLabel ? (
|
paymentTooltipLabel ? (
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<span className="cursor-help text-success">
|
<span className="cursor-help text-success font-semibold">
|
||||||
{paymentInfo.label}
|
{paymentInfo.label}
|
||||||
</span>
|
</span>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
@@ -153,7 +156,9 @@ export function InvoiceListItem({ invoice, onPay }: InvoiceListItemProps) {
|
|||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-success">{paymentInfo.label}</span>
|
<span className="text-success font-semibold">
|
||||||
|
{paymentInfo.label}
|
||||||
|
</span>
|
||||||
)
|
)
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
@@ -161,7 +166,10 @@ export function InvoiceListItem({ invoice, onPay }: InvoiceListItemProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex shrink-0 flex-col items-end">
|
<div className="flex shrink-0 flex-col items-end">
|
||||||
<MoneyValues amount={Math.abs(invoice.totalAmount)} />
|
<MoneyValues
|
||||||
|
className="font-medium"
|
||||||
|
amount={Math.abs(invoice.totalAmount)}
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ export function InvoicePaymentDialog({
|
|||||||
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
||||||
Cartão
|
Cartão
|
||||||
</p>
|
</p>
|
||||||
<p className="truncate text-base font-medium text-foreground">
|
<p className="truncate text-base font-semibold text-foreground">
|
||||||
{invoice.cardName}
|
{invoice.cardName}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -130,7 +130,7 @@ export function InvoicePaymentDialog({
|
|||||||
</div>
|
</div>
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
amount={Math.abs(invoice.totalAmount)}
|
amount={Math.abs(invoice.totalAmount)}
|
||||||
className="text-lg font-medium"
|
className="text-lg font-semibold"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export function MyAccountsWidget({
|
|||||||
<div className="flex items-start justify-between gap-3 py-1">
|
<div className="flex items-start justify-between gap-3 py-1">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<p className="text-sm text-muted-foreground">Saldo Total</p>
|
<p className="text-sm text-muted-foreground">Saldo Total</p>
|
||||||
<MoneyValues className="text-2xl" amount={totalBalance} />
|
<MoneyValues className="text-2xl font-medium" amount={totalBalance} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{excludedAccountsCount > 0 ? (
|
{excludedAccountsCount > 0 ? (
|
||||||
@@ -137,7 +137,7 @@ export function MyAccountsWidget({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<ul className="flex flex-col">
|
<ul className="flex flex-col">
|
||||||
{displayedAccounts.map((account) => {
|
{displayedAccounts.map((account, index) => {
|
||||||
const logoSrc = resolveLogoSrc(account.logo);
|
const logoSrc = resolveLogoSrc(account.logo);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -154,6 +154,7 @@ export function MyAccountsWidget({
|
|||||||
fill
|
fill
|
||||||
sizes="38px"
|
sizes="38px"
|
||||||
className="object-contain rounded-full"
|
className="object-contain rounded-full"
|
||||||
|
priority={index === 0}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
@@ -199,7 +200,10 @@ export function MyAccountsWidget({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col items-end gap-0.5 text-right">
|
<div className="flex flex-col items-end gap-0.5 text-right">
|
||||||
<MoneyValues amount={account.balance} />
|
<MoneyValues
|
||||||
|
className="font-medium"
|
||||||
|
amount={account.balance}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -83,10 +83,13 @@ export function PayersWidget({ payers }: PayersWidgetProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex shrink-0 flex-col items-end">
|
<div className="flex shrink-0 flex-col items-end">
|
||||||
<MoneyValues amount={payer.totalExpenses} />
|
<MoneyValues
|
||||||
|
className="font-medium"
|
||||||
|
amount={payer.totalExpenses}
|
||||||
|
/>
|
||||||
{percentageChange !== null && (
|
{percentageChange !== null && (
|
||||||
<span
|
<span
|
||||||
className={`flex items-center gap-0.5 text-xs ${
|
className={`flex items-center gap-0.5 text-xs font-medium ${
|
||||||
percentageChange > 0
|
percentageChange > 0
|
||||||
? "text-destructive"
|
? "text-destructive"
|
||||||
: percentageChange < 0
|
: percentageChange < 0
|
||||||
|
|||||||
@@ -8,11 +8,15 @@ import { PaymentOverviewWidgetView } from "./payment-overview/payment-overview-w
|
|||||||
type PaymentOverviewWidgetProps = {
|
type PaymentOverviewWidgetProps = {
|
||||||
paymentConditionsData: PaymentConditionsData;
|
paymentConditionsData: PaymentConditionsData;
|
||||||
paymentMethodsData: PaymentMethodsData;
|
paymentMethodsData: PaymentMethodsData;
|
||||||
|
period: string;
|
||||||
|
adminPayerSlug: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function PaymentOverviewWidget({
|
export function PaymentOverviewWidget({
|
||||||
paymentConditionsData,
|
paymentConditionsData,
|
||||||
paymentMethodsData,
|
paymentMethodsData,
|
||||||
|
period,
|
||||||
|
adminPayerSlug,
|
||||||
}: PaymentOverviewWidgetProps) {
|
}: PaymentOverviewWidgetProps) {
|
||||||
const { activeTab, handleTabChange } = usePaymentOverviewWidgetController();
|
const { activeTab, handleTabChange } = usePaymentOverviewWidgetController();
|
||||||
|
|
||||||
@@ -22,6 +26,8 @@ export function PaymentOverviewWidget({
|
|||||||
paymentConditionsData={paymentConditionsData}
|
paymentConditionsData={paymentConditionsData}
|
||||||
paymentMethodsData={paymentMethodsData}
|
paymentMethodsData={paymentMethodsData}
|
||||||
onTabChange={handleTabChange}
|
onTabChange={handleTabChange}
|
||||||
|
period={period}
|
||||||
|
adminPayerSlug={adminPayerSlug}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { RiExternalLinkLine } from "@remixicon/react";
|
||||||
|
import Link from "next/link";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
import {
|
import {
|
||||||
formatPaymentBreakdownPercentage,
|
formatPaymentBreakdownPercentage,
|
||||||
@@ -17,6 +19,7 @@ export type PaymentBreakdownListItemData = {
|
|||||||
amount: number;
|
amount: number;
|
||||||
transactions: number;
|
transactions: number;
|
||||||
percentage: number;
|
percentage: number;
|
||||||
|
href?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type PaymentBreakdownListItemProps = {
|
type PaymentBreakdownListItemProps = {
|
||||||
@@ -40,8 +43,21 @@ export function PaymentBreakdownListItem({
|
|||||||
|
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<p className="text-sm font-medium text-foreground">{item.title}</p>
|
{item.href ? (
|
||||||
<MoneyValues amount={item.amount} />
|
<Link
|
||||||
|
href={item.href}
|
||||||
|
className="inline-flex items-center gap-1 text-sm font-medium text-foreground underline-offset-2 hover:text-primary hover:underline"
|
||||||
|
>
|
||||||
|
<span className="truncate">{item.title}</span>
|
||||||
|
<RiExternalLinkLine
|
||||||
|
className="size-3 shrink-0 text-muted-foreground"
|
||||||
|
aria-hidden
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<p className="text-sm font-medium text-foreground">{item.title}</p>
|
||||||
|
)}
|
||||||
|
<MoneyValues className="font-medium" amount={item.amount} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center justify-between text-xs text-muted-foreground">
|
<div className="flex items-center justify-between text-xs text-muted-foreground">
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { RiCheckLine, RiSlideshowLine } from "@remixicon/react";
|
import { RiCheckLine, RiSlideshowLine } from "@remixicon/react";
|
||||||
import type { PaymentConditionsData } from "@/features/dashboard/payments/payment-conditions-queries";
|
import type { PaymentConditionsData } from "@/features/dashboard/payments/payment-conditions-queries";
|
||||||
import { getConditionIcon } from "@/shared/utils/icons";
|
import { getConditionIcon } from "@/shared/utils/icons";
|
||||||
|
import { formatPeriodForUrl } from "@/shared/utils/period";
|
||||||
|
import { slugify } from "@/shared/utils/string";
|
||||||
import {
|
import {
|
||||||
PaymentBreakdownList,
|
PaymentBreakdownList,
|
||||||
type PaymentBreakdownListItemData,
|
type PaymentBreakdownListItemData,
|
||||||
@@ -8,6 +10,8 @@ import {
|
|||||||
|
|
||||||
type PaymentConditionsWidgetProps = {
|
type PaymentConditionsWidgetProps = {
|
||||||
data: PaymentConditionsData;
|
data: PaymentConditionsData;
|
||||||
|
period: string;
|
||||||
|
adminPayerSlug: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const resolveConditionIcon = (condition: string) =>
|
const resolveConditionIcon = (condition: string) =>
|
||||||
@@ -15,16 +19,27 @@ const resolveConditionIcon = (condition: string) =>
|
|||||||
|
|
||||||
export function PaymentConditionsWidget({
|
export function PaymentConditionsWidget({
|
||||||
data,
|
data,
|
||||||
|
period,
|
||||||
|
adminPayerSlug,
|
||||||
}: PaymentConditionsWidgetProps) {
|
}: PaymentConditionsWidgetProps) {
|
||||||
const items: PaymentBreakdownListItemData[] = data.conditions.map(
|
const items: PaymentBreakdownListItemData[] = data.conditions.map(
|
||||||
(condition) => ({
|
(condition) => {
|
||||||
id: condition.condition,
|
const params = new URLSearchParams({
|
||||||
title: condition.condition,
|
type: slugify("Despesa"),
|
||||||
icon: resolveConditionIcon(condition.condition),
|
condition: slugify(condition.condition),
|
||||||
amount: condition.amount,
|
periodo: formatPeriodForUrl(period),
|
||||||
transactions: condition.transactions,
|
});
|
||||||
percentage: condition.percentage,
|
if (adminPayerSlug) params.set("payer", adminPayerSlug);
|
||||||
}),
|
return {
|
||||||
|
id: condition.condition,
|
||||||
|
title: condition.condition,
|
||||||
|
icon: resolveConditionIcon(condition.condition),
|
||||||
|
amount: condition.amount,
|
||||||
|
transactions: condition.transactions,
|
||||||
|
percentage: condition.percentage,
|
||||||
|
href: `/transactions?${params.toString()}`,
|
||||||
|
};
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { RiBankCard2Line, RiMoneyDollarCircleLine } from "@remixicon/react";
|
import { RiBankCard2Line, RiMoneyDollarCircleLine } from "@remixicon/react";
|
||||||
import type { PaymentMethodsData } from "@/features/dashboard/payments/payment-methods-queries";
|
import type { PaymentMethodsData } from "@/features/dashboard/payments/payment-methods-queries";
|
||||||
import { getPaymentMethodIcon } from "@/shared/utils/icons";
|
import { getPaymentMethodIcon } from "@/shared/utils/icons";
|
||||||
|
import { formatPeriodForUrl } from "@/shared/utils/period";
|
||||||
|
import { slugify } from "@/shared/utils/string";
|
||||||
import {
|
import {
|
||||||
PaymentBreakdownList,
|
PaymentBreakdownList,
|
||||||
type PaymentBreakdownListItemData,
|
type PaymentBreakdownListItemData,
|
||||||
@@ -8,6 +10,8 @@ import {
|
|||||||
|
|
||||||
type PaymentMethodsWidgetProps = {
|
type PaymentMethodsWidgetProps = {
|
||||||
data: PaymentMethodsData;
|
data: PaymentMethodsData;
|
||||||
|
period: string;
|
||||||
|
adminPayerSlug: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const resolvePaymentMethodIcon = (paymentMethod: string) =>
|
const resolvePaymentMethodIcon = (paymentMethod: string) =>
|
||||||
@@ -15,15 +19,28 @@ const resolvePaymentMethodIcon = (paymentMethod: string) =>
|
|||||||
<RiBankCard2Line className="size-5" aria-hidden />
|
<RiBankCard2Line className="size-5" aria-hidden />
|
||||||
);
|
);
|
||||||
|
|
||||||
export function PaymentMethodsWidget({ data }: PaymentMethodsWidgetProps) {
|
export function PaymentMethodsWidget({
|
||||||
const items: PaymentBreakdownListItemData[] = data.methods.map((method) => ({
|
data,
|
||||||
id: method.paymentMethod,
|
period,
|
||||||
title: method.paymentMethod,
|
adminPayerSlug,
|
||||||
icon: resolvePaymentMethodIcon(method.paymentMethod),
|
}: PaymentMethodsWidgetProps) {
|
||||||
amount: method.amount,
|
const items: PaymentBreakdownListItemData[] = data.methods.map((method) => {
|
||||||
transactions: method.transactions,
|
const params = new URLSearchParams({
|
||||||
percentage: method.percentage,
|
type: slugify("Despesa"),
|
||||||
}));
|
payment: slugify(method.paymentMethod),
|
||||||
|
periodo: formatPeriodForUrl(period),
|
||||||
|
});
|
||||||
|
if (adminPayerSlug) params.set("payer", adminPayerSlug);
|
||||||
|
return {
|
||||||
|
id: method.paymentMethod,
|
||||||
|
title: method.paymentMethod,
|
||||||
|
icon: resolvePaymentMethodIcon(method.paymentMethod),
|
||||||
|
amount: method.amount,
|
||||||
|
transactions: method.transactions,
|
||||||
|
percentage: method.percentage,
|
||||||
|
href: `/transactions?${params.toString()}`,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PaymentBreakdownList
|
<PaymentBreakdownList
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ type PaymentOverviewWidgetViewProps = {
|
|||||||
paymentConditionsData: PaymentConditionsData;
|
paymentConditionsData: PaymentConditionsData;
|
||||||
paymentMethodsData: PaymentMethodsData;
|
paymentMethodsData: PaymentMethodsData;
|
||||||
onTabChange: (value: string) => void;
|
onTabChange: (value: string) => void;
|
||||||
|
period: string;
|
||||||
|
adminPayerSlug: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function PaymentOverviewWidgetView({
|
export function PaymentOverviewWidgetView({
|
||||||
@@ -23,6 +25,8 @@ export function PaymentOverviewWidgetView({
|
|||||||
paymentConditionsData,
|
paymentConditionsData,
|
||||||
paymentMethodsData,
|
paymentMethodsData,
|
||||||
onTabChange,
|
onTabChange,
|
||||||
|
period,
|
||||||
|
adminPayerSlug,
|
||||||
}: PaymentOverviewWidgetViewProps) {
|
}: PaymentOverviewWidgetViewProps) {
|
||||||
return (
|
return (
|
||||||
<Tabs value={activeTab} onValueChange={onTabChange} className="w-full">
|
<Tabs value={activeTab} onValueChange={onTabChange} className="w-full">
|
||||||
@@ -38,11 +42,19 @@ export function PaymentOverviewWidgetView({
|
|||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<TabsContent value="conditions" className="mt-2">
|
<TabsContent value="conditions" className="mt-2">
|
||||||
<PaymentConditionsWidget data={paymentConditionsData} />
|
<PaymentConditionsWidget
|
||||||
|
data={paymentConditionsData}
|
||||||
|
period={period}
|
||||||
|
adminPayerSlug={adminPayerSlug}
|
||||||
|
/>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="methods" className="mt-2">
|
<TabsContent value="methods" className="mt-2">
|
||||||
<PaymentMethodsWidget data={paymentMethodsData} />
|
<PaymentMethodsWidget
|
||||||
|
data={paymentMethodsData}
|
||||||
|
period={period}
|
||||||
|
adminPayerSlug={adminPayerSlug}
|
||||||
|
/>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,10 +24,7 @@ export function PaymentStatusCategorySection({
|
|||||||
<div className="mt-4 space-y-3">
|
<div className="mt-4 space-y-3">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm font-medium text-foreground">{title}</span>
|
<span className="text-sm font-medium text-foreground">{title}</span>
|
||||||
<MoneyValues
|
<MoneyValues amount={total} className="font-medium" />
|
||||||
amount={total}
|
|
||||||
className="text-sm font-medium tabular-nums"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Progress value={confirmedPercentage} className="h-2" />
|
<Progress value={confirmedPercentage} className="h-2" />
|
||||||
@@ -35,13 +32,13 @@ export function PaymentStatusCategorySection({
|
|||||||
<div className="flex flex-col gap-1 text-sm sm:flex-row sm:items-center sm:justify-between sm:gap-4">
|
<div className="flex flex-col gap-1 text-sm sm:flex-row sm:items-center sm:justify-between sm:gap-4">
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<StatusDot color="bg-primary" />
|
<StatusDot color="bg-primary" />
|
||||||
<MoneyValues amount={confirmed} className="tabular-nums" />
|
<MoneyValues amount={confirmed} className="font-medium" />
|
||||||
<span className="text-xs text-muted-foreground">confirmados</span>
|
<span className="text-xs text-muted-foreground">confirmados</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<StatusDot color="bg-warning/40" />
|
<StatusDot color="bg-warning/40" />
|
||||||
<MoneyValues amount={pending} className="tabular-nums" />
|
<MoneyValues amount={pending} className="font-medium" />
|
||||||
<span className="text-xs text-muted-foreground">pendentes</span>
|
<span className="text-xs text-muted-foreground">pendentes</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -178,7 +178,10 @@ export function PurchasesByCategoryWidget({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="shrink-0 text-foreground">
|
<div className="shrink-0 text-foreground">
|
||||||
<MoneyValues amount={transaction.amount} />
|
<MoneyValues
|
||||||
|
className="font-medium"
|
||||||
|
amount={transaction.amount}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export function RecurringExpensesWidget({
|
|||||||
{expense.name}
|
{expense.name}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<MoneyValues amount={expense.amount} />
|
<MoneyValues className="font-medium" amount={expense.amount} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center justify-between text-xs text-muted-foreground">
|
<div className="flex items-center justify-between text-xs text-muted-foreground">
|
||||||
|
|||||||
@@ -48,7 +48,10 @@ export function TopEstablishmentsWidget({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="shrink-0 text-foreground">
|
<div className="shrink-0 text-foreground">
|
||||||
<MoneyValues amount={establishment.amount} />
|
<MoneyValues
|
||||||
|
className="font-medium"
|
||||||
|
amount={establishment.amount}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -113,7 +113,10 @@ export function TopExpensesWidget({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="shrink-0 text-foreground">
|
<div className="shrink-0 text-foreground">
|
||||||
<MoneyValues amount={expense.amount} />
|
<MoneyValues
|
||||||
|
className="font-medium"
|
||||||
|
amount={expense.amount}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export function InboxDetailsDialog({
|
|||||||
<Separator />
|
<Separator />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h4 className="mb-1 text-sm font-medium text-muted-foreground">
|
<h4 className="mb-1 text-sm font-semibold text-muted-foreground">
|
||||||
Notificação Original
|
Notificação Original
|
||||||
</h4>
|
</h4>
|
||||||
{item.originalTitle && (
|
{item.originalTitle && (
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ export function InsightsGrid({ insights }: InsightsGridProps) {
|
|||||||
<CardHeader>
|
<CardHeader>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Icon className={cn("size-5", colors.chatAiIcon)} />
|
<Icon className={cn("size-5", colors.chatAiIcon)} />
|
||||||
<CardTitle className={cn("font-medium", colors.titleText)}>
|
<CardTitle className={cn("font-semibold", colors.titleText)}>
|
||||||
{categoryConfig.title}
|
{categoryConfig.title}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -301,7 +301,7 @@ function ErrorState({
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center gap-4 py-12 px-4 text-center">
|
<div className="flex flex-col items-center justify-center gap-4 py-12 px-4 text-center">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h3 className="text-lg font-medium text-destructive">{title}</h3>
|
<h3 className="text-lg font-semibold text-destructive">{title}</h3>
|
||||||
<p className="text-sm text-muted-foreground max-w-md">{error}</p>
|
<p className="text-sm text-muted-foreground max-w-md">{error}</p>
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={onRetry} variant="outline">
|
<Button onClick={onRetry} variant="outline">
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ export function ModelSelector({
|
|||||||
<Card className="grid grid-cols-1 lg:grid-cols-[1fr,auto] gap-6 items-start p-6">
|
<Card className="grid grid-cols-1 lg:grid-cols-[1fr,auto] gap-6 items-start p-6">
|
||||||
{/* Descrição */}
|
{/* Descrição */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<h3 className="text-lg font-medium">Definir modelo de análise</h3>
|
<h3 className="text-lg font-semibold">Definir modelo de análise</h3>
|
||||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||||
Escolha o provedor de IA e o modelo específico que será utilizado para
|
Escolha o provedor de IA e o modelo específico que será utilizado para
|
||||||
gerar insights sobre seus dados financeiros. <br />
|
gerar insights sobre seus dados financeiros. <br />
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ export function InvoiceSummaryCard({
|
|||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<h2 className="truncate text-sm font-medium text-foreground">
|
<h2 className="truncate text-sm font-semibold text-foreground">
|
||||||
{cardName}
|
{cardName}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
@@ -189,13 +189,11 @@ export function InvoiceSummaryCard({
|
|||||||
|
|
||||||
{/* Linha 2 — valor da fatura (hero) */}
|
{/* Linha 2 — valor da fatura (hero) */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<p className="text-sm font-medium text-muted-foreground">
|
<p className="text-sm text-muted-foreground">Valor da fatura</p>
|
||||||
Valor da fatura
|
|
||||||
</p>
|
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
amount={totalAmount}
|
amount={totalAmount}
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-3xl leading-none font-medium tracking-tight sm:text-[2rem]",
|
"text-3xl leading-none tracking-tighter sm:text-[2rem]",
|
||||||
isPaid ? "text-success" : "text-foreground",
|
isPaid ? "text-success" : "text-foreground",
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ function StepCard({
|
|||||||
{step}
|
{step}
|
||||||
</div>
|
</div>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<h3 className="font-medium mb-1.5 md:mb-2">{title}</h3>
|
<h3 className="font-semibold mb-1.5 md:mb-2">{title}</h3>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export function NoteCard({
|
|||||||
<CardContent className="flex min-h-0 flex-1 flex-col gap-4">
|
<CardContent className="flex min-h-0 flex-1 flex-col gap-4">
|
||||||
<div className="flex shrink-0 items-start justify-between gap-3">
|
<div className="flex shrink-0 items-start justify-between gap-3">
|
||||||
<div className="flex min-w-0 flex-col gap-1">
|
<div className="flex min-w-0 flex-col gap-1">
|
||||||
<h3 className="text-lg font-medium leading-tight text-foreground wrap-break-word">
|
<h3 className="text-lg font-semibold text-foreground wrap-break-word">
|
||||||
{displayTitle}
|
{displayTitle}
|
||||||
</h3>
|
</h3>
|
||||||
{createdAtLabel && (
|
{createdAtLabel && (
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ export function PayerHeaderCard({
|
|||||||
|
|
||||||
<div className="flex flex-1 flex-col gap-2">
|
<div className="flex flex-1 flex-col gap-2">
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
<CardTitle className="text-xl font-medium text-foreground">
|
<CardTitle className="text-xl font-semibold text-foreground">
|
||||||
{payer.name}
|
{payer.name}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
{isAdmin ? (
|
{isAdmin ? (
|
||||||
@@ -215,10 +215,10 @@ export function PayerHeaderCard({
|
|||||||
<RiExchangeDollarLine className="size-5 text-primary" />
|
<RiExchangeDollarLine className="size-5 text-primary" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Total de Despesas
|
Total de Despesas
|
||||||
</p>
|
</p>
|
||||||
<p className="text-2xl font-medium text-foreground">
|
<p className="text-2xl font-semibold text-foreground">
|
||||||
{formatCurrency(summary.totalExpenses)}
|
{formatCurrency(summary.totalExpenses)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -239,7 +239,7 @@ export function PayerHeaderCard({
|
|||||||
Cartões
|
Cartões
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-lg font-medium text-foreground">
|
<p className="text-lg font-semibold text-foreground">
|
||||||
{formatCurrency(summary.paymentSplits.card)}
|
{formatCurrency(summary.paymentSplits.card)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -251,7 +251,7 @@ export function PayerHeaderCard({
|
|||||||
Boletos
|
Boletos
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-lg font-medium text-foreground">
|
<p className="text-lg font-semibold text-foreground">
|
||||||
{formatCurrency(summary.paymentSplits.boleto)}
|
{formatCurrency(summary.paymentSplits.boleto)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -263,7 +263,7 @@ export function PayerHeaderCard({
|
|||||||
Pix/Débito
|
Pix/Débito
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-lg font-medium text-foreground">
|
<p className="text-lg font-semibold text-foreground">
|
||||||
{formatCurrency(summary.paymentSplits.instant)}
|
{formatCurrency(summary.paymentSplits.instant)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export function PayerHistoryCard({ data }: PagadorHistoryCardProps) {
|
|||||||
return (
|
return (
|
||||||
<Card className="border">
|
<Card className="border">
|
||||||
<CardHeader className="gap-1.5 pb-3">
|
<CardHeader className="gap-1.5 pb-3">
|
||||||
<CardTitle className="text-lg font-medium">
|
<CardTitle className="text-lg font-semibold">
|
||||||
Evolução (últimos 6 meses)
|
Evolução (últimos 6 meses)
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export function PagadorInfoCard({ payer }: PayerInfoCardProps) {
|
|||||||
return (
|
return (
|
||||||
<Card className="border gap-4">
|
<Card className="border gap-4">
|
||||||
<CardHeader className="gap-1.5">
|
<CardHeader className="gap-1.5">
|
||||||
<CardTitle className="text-lg font-medium">
|
<CardTitle className="text-lg font-semibold">
|
||||||
Detalhes do pagador
|
Detalhes do pagador
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
@@ -106,7 +106,7 @@ export function PagadorInfoCard({ payer }: PayerInfoCardProps) {
|
|||||||
|
|
||||||
const resolveRoleLabel = (role: string | null) => {
|
const resolveRoleLabel = (role: string | null) => {
|
||||||
if (role === PAYER_ROLE_ADMIN) return "Administrador";
|
if (role === PAYER_ROLE_ADMIN) return "Administrador";
|
||||||
return "Payer";
|
return "Pagador";
|
||||||
};
|
};
|
||||||
|
|
||||||
type InfoItemProps = {
|
type InfoItemProps = {
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export function PayerLeaveShareCard({
|
|||||||
return (
|
return (
|
||||||
<Card className="border">
|
<Card className="border">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-base font-medium">
|
<CardTitle className="text-base font-semibold">
|
||||||
Acesso Compartilhado
|
Acesso Compartilhado
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export function PayerMonthlySummaryCard({
|
|||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader className="flex flex-col gap-1.5">
|
<CardHeader className="flex flex-col gap-1.5">
|
||||||
<CardTitle className="text-lg font-medium">Totais do mês</CardTitle>
|
<CardTitle className="text-lg font-semibold">Totais do mês</CardTitle>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
{periodLabel} - Despesas por forma de pagamento
|
{periodLabel} - Despesas por forma de pagamento
|
||||||
</p>
|
</p>
|
||||||
@@ -65,7 +65,7 @@ export function PayerMonthlySummaryCard({
|
|||||||
</span>
|
</span>
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
amount={breakdown.totalExpenses}
|
amount={breakdown.totalExpenses}
|
||||||
className="block text-2xl font-medium text-foreground"
|
className="block text-2xl font-semibold text-foreground"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ export function PayerMonthlySummaryCard({
|
|||||||
totalBase > 0 ? Math.round((entry.value / totalBase) * 100) : 0;
|
totalBase > 0 ? Math.round((entry.value / totalBase) * 100) : 0;
|
||||||
return (
|
return (
|
||||||
<div key={entry.key} className="space-y-1 rounded-lg border p-3">
|
<div key={entry.key} className="space-y-1 rounded-lg border p-3">
|
||||||
<span className="flex items-center gap-2 text-xs font-medium uppercase tracking-wide text-muted-foreground/70">
|
<span className="flex items-center gap-2 text-xs uppercase tracking-wide text-muted-foreground/70">
|
||||||
<span
|
<span
|
||||||
className={cn("size-2 rounded-full", entry.color)}
|
className={cn("size-2 rounded-full", entry.color)}
|
||||||
aria-hidden
|
aria-hidden
|
||||||
@@ -109,7 +109,7 @@ export function PayerMonthlySummaryCard({
|
|||||||
</span>
|
</span>
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
amount={entry.value}
|
amount={entry.value}
|
||||||
className="block text-lg font-medium text-foreground"
|
className="block text-lg font-semibold text-foreground"
|
||||||
/>
|
/>
|
||||||
<span className="text-xs text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
{percent}% das despesas
|
{percent}% das despesas
|
||||||
|
|||||||
@@ -84,7 +84,9 @@ export function PayerSharingCard({
|
|||||||
return (
|
return (
|
||||||
<Card className="border">
|
<Card className="border">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-lg font-medium">Compartilhamentos</CardTitle>
|
<CardTitle className="text-lg font-semibold">
|
||||||
|
Compartilhamentos
|
||||||
|
</CardTitle>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Compartilhe o código abaixo com outra pessoa. Ela poderá adicioná-lo
|
Compartilhe o código abaixo com outra pessoa. Ela poderá adicioná-lo
|
||||||
na página de pagadores usando a opção Adicionar por código para ter
|
na página de pagadores usando a opção Adicionar por código para ter
|
||||||
|
|||||||
@@ -42,9 +42,7 @@ export function PayerCard({ payer, onEdit, onRemove }: PayerCardProps) {
|
|||||||
|
|
||||||
{/* Nome e badges */}
|
{/* Nome e badges */}
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="flex items-center gap-1.5">
|
||||||
<h3 className="text-base font-medium text-foreground">
|
<h3 className="font-semibold text-foreground">{payer.name}</h3>
|
||||||
{payer.name}
|
|
||||||
</h3>
|
|
||||||
{isAdmin ? (
|
{isAdmin ? (
|
||||||
<RiVerifiedBadgeFill className="size-4 text-blue-500" aria-hidden />
|
<RiVerifiedBadgeFill className="size-4 text-blue-500" aria-hidden />
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ export function PayerDialog({
|
|||||||
const payerId = payer?.id;
|
const payerId = payer?.id;
|
||||||
|
|
||||||
if (mode === "update" && !payerId) {
|
if (mode === "update" && !payerId) {
|
||||||
const message = "Payer inválido.";
|
const message = "Pagador inválido.";
|
||||||
setErrorMessage(message);
|
setErrorMessage(message);
|
||||||
toast.error(message);
|
toast.error(message);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ export function CardUsageChart({ data, limit, card }: CardUsageChartProps) {
|
|||||||
<span className="text-xs text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
Uso
|
Uso
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs font-medium tabular-nums">
|
<span className="text-xs font-medium">
|
||||||
{formatCurrency(value, {
|
{formatCurrency(value, {
|
||||||
maximumFractionDigits: 0,
|
maximumFractionDigits: 0,
|
||||||
minimumFractionDigits: 0,
|
minimumFractionDigits: 0,
|
||||||
@@ -154,7 +154,7 @@ export function CardUsageChart({ data, limit, card }: CardUsageChartProps) {
|
|||||||
<span className="text-xs text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
% do Limite
|
% do Limite
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs font-medium tabular-nums">
|
<span className="text-xs font-medium">
|
||||||
{formatPercentage(usagePercent, {
|
{formatPercentage(usagePercent, {
|
||||||
maximumFractionDigits: 0,
|
maximumFractionDigits: 0,
|
||||||
minimumFractionDigits: 0,
|
minimumFractionDigits: 0,
|
||||||
|
|||||||
@@ -67,11 +67,11 @@ export function CardsOverview({ data }: CardsOverviewProps) {
|
|||||||
<p className="text-xs text-muted-foreground">{card.title}</p>
|
<p className="text-xs text-muted-foreground">{card.title}</p>
|
||||||
{card.isMoney ? (
|
{card.isMoney ? (
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
className="text-2xl font-medium"
|
className="text-2xl font-semibold"
|
||||||
amount={card.value}
|
amount={card.value}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-2xl font-medium">
|
<p className="text-2xl font-semibold">
|
||||||
{formatPercentage(card.value, {
|
{formatPercentage(card.value, {
|
||||||
maximumFractionDigits: 0,
|
maximumFractionDigits: 0,
|
||||||
minimumFractionDigits: 0,
|
minimumFractionDigits: 0,
|
||||||
@@ -83,7 +83,7 @@ export function CardsOverview({ data }: CardsOverviewProps) {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-base font-medium ml-2 py-2">Meus cartões</p>
|
<p className="text-base font-semibold ml-2 py-2">Meus cartões</p>
|
||||||
|
|
||||||
{/* Cards list */}
|
{/* Cards list */}
|
||||||
<div className="grid gap-2 grid-cols-2 lg:grid-cols-4 xl:grid-cols-4">
|
<div className="grid gap-2 grid-cols-2 lg:grid-cols-4 xl:grid-cols-4">
|
||||||
@@ -116,7 +116,7 @@ export function CardsOverview({ data }: CardsOverviewProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="min-w-0 flex-1 space-y-1">
|
<div className="min-w-0 flex-1 space-y-1">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-base font-medium truncate">
|
<span className="text-base font-semibold truncate">
|
||||||
{card.name}
|
{card.name}
|
||||||
</span>
|
</span>
|
||||||
{brandAsset && (
|
{brandAsset && (
|
||||||
@@ -129,7 +129,7 @@ export function CardsOverview({ data }: CardsOverviewProps) {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground tabular-nums">
|
<p className="text-xs text-muted-foreground">
|
||||||
{formatCurrency(card.currentUsage)} /{" "}
|
{formatCurrency(card.currentUsage)} /{" "}
|
||||||
{formatCurrency(card.limit)}
|
{formatCurrency(card.limit)}
|
||||||
</p>
|
</p>
|
||||||
@@ -141,7 +141,7 @@ export function CardsOverview({ data }: CardsOverviewProps) {
|
|||||||
`[&>div]:${getUsageColor(card.usagePercent)}`,
|
`[&>div]:${getUsageColor(card.usagePercent)}`,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<span className="text-xs font-medium tabular-nums">
|
<span className="text-xs font-medium">
|
||||||
{formatPercentage(card.usagePercent, {
|
{formatPercentage(card.usagePercent, {
|
||||||
maximumFractionDigits: 0,
|
maximumFractionDigits: 0,
|
||||||
minimumFractionDigits: 0,
|
minimumFractionDigits: 0,
|
||||||
|
|||||||
@@ -53,7 +53,9 @@ export function CategoryCell({
|
|||||||
>
|
>
|
||||||
{isIncrease && <RiArrowUpSFill className="h-3 w-3" />}
|
{isIncrease && <RiArrowUpSFill className="h-3 w-3" />}
|
||||||
{isDecrease && <RiArrowDownSFill className="h-3 w-3" />}
|
{isDecrease && <RiArrowDownSFill className="h-3 w-3" />}
|
||||||
<span>{formatPercentageChange(percentageChange)}</span>
|
<span className="font-medium">
|
||||||
|
{formatPercentageChange(percentageChange)}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ function AreaTooltip({
|
|||||||
{entry.name}
|
{entry.name}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="shrink-0 text-xs font-medium tabular-nums text-foreground">
|
<span className="shrink-0 text-xs font-medium text-foreground">
|
||||||
{currencyFormatter.format(Number(entry.value))}
|
{currencyFormatter.format(Number(entry.value))}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -78,12 +78,12 @@ export function CategoryTable({
|
|||||||
{periods.map((period) => (
|
{periods.map((period) => (
|
||||||
<TableHead
|
<TableHead
|
||||||
key={period}
|
key={period}
|
||||||
className="text-right min-w-[120px] font-medium"
|
className="text-right min-w-[120px] font-semibold"
|
||||||
>
|
>
|
||||||
{formatPeriodLabel(period)}
|
{formatPeriodLabel(period)}
|
||||||
</TableHead>
|
</TableHead>
|
||||||
))}
|
))}
|
||||||
<TableHead className="text-right min-w-[140px] font-medium">
|
<TableHead className="text-right min-w-[140px] font-semibold">
|
||||||
<div className="flex items-center justify-end gap-1">
|
<div className="flex items-center justify-end gap-1">
|
||||||
Média
|
Média
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
@@ -100,7 +100,7 @@ export function CategoryTable({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead className="text-right min-w-[120px] font-medium">
|
<TableHead className="text-right min-w-[120px] font-semibold">
|
||||||
Total
|
Total
|
||||||
</TableHead>
|
</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
@@ -128,7 +128,7 @@ export function CategoryTable({
|
|||||||
/>
|
/>
|
||||||
<Link
|
<Link
|
||||||
href={`/categories/${category.categoryId}?periodo=${periodParam}`}
|
href={`/categories/${category.categoryId}?periodo=${periodParam}`}
|
||||||
className="flex items-center gap-1.5 truncate hover:underline underline-offset-2"
|
className="flex items-center gap-1.5 truncate hover:underline underline-offset-2 font-semibold"
|
||||||
>
|
>
|
||||||
{category.name}
|
{category.name}
|
||||||
</Link>
|
</Link>
|
||||||
@@ -149,7 +149,7 @@ export function CategoryTable({
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<TableCell className="text-right font-medium text-info">
|
<TableCell className="text-right font-semibold text-info">
|
||||||
{(() => {
|
{(() => {
|
||||||
const nonZeroCount = periods.filter(
|
const nonZeroCount = periods.filter(
|
||||||
(p) => (category.monthlyData.get(p)?.amount ?? 0) > 0,
|
(p) => (category.monthlyData.get(p)?.amount ?? 0) > 0,
|
||||||
@@ -178,10 +178,10 @@ export function CategoryTable({
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<TableCell className="text-right font-medium text-info">
|
<TableCell className="text-right font-semibold text-info">
|
||||||
{formatCurrency(sectionTotals.averageMonthlyTotal)}
|
{formatCurrency(sectionTotals.averageMonthlyTotal)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-right font-medium">
|
<TableCell className="text-right font-semibold">
|
||||||
{formatCurrency(sectionTotals.grandTotal)}
|
{formatCurrency(sectionTotals.grandTotal)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export function HighlightsCards({ summary }: HighlightsCardsProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<p className="text-xs font-medium">Mais Frequente</p>
|
<p className="text-xs font-medium">Mais Frequente</p>
|
||||||
<p className="font-medium text-2xl truncate">
|
<p className="font-semibold text-2xl truncate">
|
||||||
{summary.mostFrequent || "—"}
|
{summary.mostFrequent || "—"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -35,7 +35,7 @@ export function HighlightsCards({ summary }: HighlightsCardsProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<p className="text-xs font-medium">Maior Gasto Total</p>
|
<p className="text-xs font-medium">Maior Gasto Total</p>
|
||||||
<p className="font-medium text-2xl truncate">
|
<p className="font-semibold text-2xl truncate">
|
||||||
{summary.highestSpending || "—"}
|
{summary.highestSpending || "—"}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -53,16 +53,14 @@ export function SummaryCards({ summary }: SummaryCardsProps) {
|
|||||||
<CardContent className="px-4 py-2">
|
<CardContent className="px-4 py-2">
|
||||||
<div className="flex items-start justify-between gap-3">
|
<div className="flex items-start justify-between gap-3">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<p className="text-xs font-medium text-muted-foreground">
|
<p className="text-xs text-muted-foreground">{card.title}</p>
|
||||||
{card.title}
|
|
||||||
</p>
|
|
||||||
{card.isMoney ? (
|
{card.isMoney ? (
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
className="text-2xl font-medium"
|
className="text-2xl font-semibold"
|
||||||
amount={card.value}
|
amount={card.value}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-2xl font-medium">{card.value}</p>
|
<p className="text-2xl font-semibold">{card.value}</p>
|
||||||
)}
|
)}
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
{card.description}
|
{card.description}
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ export function ApiTokensForm({ tokens }: ApiTokensFormProps) {
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium">Dispositivos conectados</h3>
|
<h3 className="font-semibold">Dispositivos conectados</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Gerencie os dispositivos que podem enviar notificações para o
|
Gerencie os dispositivos que podem enviar notificações para o
|
||||||
OpenMonetis.
|
OpenMonetis.
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export function ChangelogTab({ versions }: { versions: ChangelogVersion[] }) {
|
|||||||
{versions.map((version) => (
|
{versions.map((version) => (
|
||||||
<Card key={version.version} className="p-6">
|
<Card key={version.version} className="p-6">
|
||||||
<div className="flex items-baseline gap-3">
|
<div className="flex items-baseline gap-3">
|
||||||
<h3 className="text-lg font-medium">v{version.version}</h3>
|
<h3 className="text-lg font-semibold">v{version.version}</h3>
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">
|
||||||
{version.date}
|
{version.date}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export function DeleteAccountForm() {
|
|||||||
<div className="rounded-lg border p-4">
|
<div className="rounded-lg border p-4">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium">Zerar conta</h3>
|
<h3 className="font-semibold">Zerar conta</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Apaga todos os dados do OpenMonetis e deixa sua conta no estado
|
Apaga todos os dados do OpenMonetis e deixa sua conta no estado
|
||||||
inicial, mantendo seu login e credenciais de acesso.
|
inicial, mantendo seu login e credenciais de acesso.
|
||||||
@@ -120,7 +120,7 @@ export function DeleteAccountForm() {
|
|||||||
<div className="rounded-lg border border-destructive/30 bg-destructive/5 p-4">
|
<div className="rounded-lg border border-destructive/30 bg-destructive/5 p-4">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium text-destructive">Deletar conta</h3>
|
<h3 className="font-semibold text-destructive">Deletar conta</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Remove seu usuário e todos os dados associados de forma
|
Remove seu usuário e todos os dados associados de forma
|
||||||
permanente.
|
permanente.
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ export function PasskeysForm() {
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium">Suas passkeys</h3>
|
<h3 className="font-semibold">Suas passkeys</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Gerencie suas passkeys para login sem senha.
|
Gerencie suas passkeys para login sem senha.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ export function PreferencesForm({
|
|||||||
{/* Seção: Lançamentos */}
|
{/* Seção: Lançamentos */}
|
||||||
<section className="space-y-4">
|
<section className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-base font-medium">Lançamentos</h3>
|
<h3 className="text-base font-semibold">Lançamentos</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Configurações de exibição da tabela de movimentações.
|
Configurações de exibição da tabela de movimentações.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ export function UpdatePasswordForm({ authProvider }: UpdatePasswordFormProps) {
|
|||||||
<div className="flex gap-3">
|
<div className="flex gap-3">
|
||||||
<RiAlertLine className="h-5 w-5 text-warning shrink-0 mt-0.5" />
|
<RiAlertLine className="h-5 w-5 text-warning shrink-0 mt-0.5" />
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium text-warning">
|
<h3 className="font-semibold text-warning">
|
||||||
Alteração de senha não disponível
|
Alteração de senha não disponível
|
||||||
</h3>
|
</h3>
|
||||||
<p className="mt-1 text-sm text-warning">
|
<p className="mt-1 text-sm text-warning">
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ function AttachmentPreview({
|
|||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent
|
<DialogContent
|
||||||
showCloseButton={false}
|
showCloseButton={false}
|
||||||
|
aria-describedby={undefined}
|
||||||
className="flex h-[92vh] w-[min(96vw,1400px)] max-w-none flex-col gap-0 overflow-hidden p-0 sm:p-0"
|
className="flex h-[92vh] w-[min(96vw,1400px)] max-w-none flex-col gap-0 overflow-hidden p-0 sm:p-0"
|
||||||
>
|
>
|
||||||
<DialogHeader className="flex-row items-center justify-between gap-3 border-b px-4 py-3 sm:px-5">
|
<DialogHeader className="flex-row items-center justify-between gap-3 border-b px-4 py-3 sm:px-5">
|
||||||
|
|||||||
@@ -44,8 +44,10 @@ export function AttachmentSection({
|
|||||||
} = useTransactionAttachments(transactionId);
|
} = useTransactionAttachments(transactionId);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onLoaded?.(items.length);
|
if (!isLoading) {
|
||||||
}, [items.length, onLoaded]);
|
onLoaded?.(items.length);
|
||||||
|
}
|
||||||
|
}, [items.length, isLoading, onLoaded]);
|
||||||
|
|
||||||
const invalidateAttachments = () => {
|
const invalidateAttachments = () => {
|
||||||
void queryClient.invalidateQueries({
|
void queryClient.invalidateQueries({
|
||||||
|
|||||||
@@ -342,21 +342,21 @@ export function AnticipateInstallmentsDialog({
|
|||||||
{/* Seção 3: Resumo */}
|
{/* Seção 3: Resumo */}
|
||||||
{selectedIds.length > 0 && (
|
{selectedIds.length > 0 && (
|
||||||
<div className="rounded-lg border bg-muted/20 p-3">
|
<div className="rounded-lg border bg-muted/20 p-3">
|
||||||
<h4 className="text-sm font-medium mb-2">Resumo</h4>
|
<h4 className="text-sm font-semibold mb-2">Resumo</h4>
|
||||||
<dl className="space-y-1.5 text-sm">
|
<dl className="space-y-1.5 text-sm">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<dt className="text-muted-foreground">
|
<dt className="text-muted-foreground">
|
||||||
{selectedIds.length} parcela
|
{selectedIds.length} parcela
|
||||||
{selectedIds.length > 1 ? "s" : ""}
|
{selectedIds.length > 1 ? "s" : ""}
|
||||||
</dt>
|
</dt>
|
||||||
<dd className="font-medium tabular-nums">
|
<dd className="font-medium">
|
||||||
<MoneyValues amount={totalAmount} className="text-sm" />
|
<MoneyValues amount={totalAmount} className="text-sm" />
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
{Number(formState.discount) > 0 && (
|
{Number(formState.discount) > 0 && (
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<dt className="text-muted-foreground">Desconto</dt>
|
<dt className="text-muted-foreground">Desconto</dt>
|
||||||
<dd className="font-medium tabular-nums text-success">
|
<dd className="font-medium text-success">
|
||||||
-{" "}
|
-{" "}
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
amount={Number(formState.discount)}
|
amount={Number(formState.discount)}
|
||||||
@@ -367,7 +367,7 @@ export function AnticipateInstallmentsDialog({
|
|||||||
)}
|
)}
|
||||||
<div className="flex items-center justify-between border-t pt-1.5">
|
<div className="flex items-center justify-between border-t pt-1.5">
|
||||||
<dt className="font-medium">Total</dt>
|
<dt className="font-medium">Total</dt>
|
||||||
<dd className="text-base font-medium tabular-nums text-primary">
|
<dd className="text-base font-semibold text-primary">
|
||||||
<MoneyValues amount={finalAmount} className="text-sm" />
|
<MoneyValues amount={finalAmount} className="text-sm" />
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ export function InstallmentSelectionTable({
|
|||||||
<TableCell className="text-muted-foreground">
|
<TableCell className="text-muted-foreground">
|
||||||
{formatDate(inst.dueDate)}
|
{formatDate(inst.dueDate)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-right font-medium tabular-nums">
|
<TableCell className="text-right font-medium">
|
||||||
<MoneyValues amount={Number(inst.amount)} />
|
<MoneyValues amount={Number(inst.amount)} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
|||||||
@@ -279,7 +279,7 @@ export function MassAddDialog({
|
|||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* Fixed Fields Section */}
|
{/* Fixed Fields Section */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h3 className="text-sm font-medium">Valores Padrão</h3>
|
<h3 className="text-sm font-semibold">Valores Padrão</h3>
|
||||||
<div className="grid gap-3 grid-cols-1 sm:grid-cols-3">
|
<div className="grid gap-3 grid-cols-1 sm:grid-cols-3">
|
||||||
{/* Transaction Type */}
|
{/* Transaction Type */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -452,7 +452,7 @@ export function MassAddDialog({
|
|||||||
|
|
||||||
{/* Transactions Section */}
|
{/* Transactions Section */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h3 className="text-sm font-medium">Lançamentos</h3>
|
<h3 className="text-sm font-semibold">Lançamentos</h3>
|
||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{transactions.map((transaction, index) => (
|
{transactions.map((transaction, index) => (
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export function TransactionDetailsDialog({
|
|||||||
<p className="text-xs uppercase tracking-wide text-muted-foreground">
|
<p className="text-xs uppercase tracking-wide text-muted-foreground">
|
||||||
Resumo
|
Resumo
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-1 text-2xl font-medium">
|
<p className="mt-1 text-2xl font-semibold">
|
||||||
{currencyFormatter.format(valorTotal)}
|
{currencyFormatter.format(valorTotal)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -116,7 +116,7 @@ export function TransactionDetailsDialog({
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="space-y-2">
|
<section className="space-y-2">
|
||||||
<h3 className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
<h3 className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
||||||
Detalhes
|
Detalhes
|
||||||
</h3>
|
</h3>
|
||||||
<ul className="min-w-0 grid gap-2 rounded-lg border p-3">
|
<ul className="min-w-0 grid gap-2 rounded-lg border p-3">
|
||||||
@@ -167,7 +167,7 @@ export function TransactionDetailsDialog({
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="space-y-2">
|
<section className="space-y-2">
|
||||||
<h3 className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
<h3 className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
||||||
Valores
|
Valores
|
||||||
</h3>
|
</h3>
|
||||||
<ul className="min-w-0 grid gap-2 rounded-lg border p-3">
|
<ul className="min-w-0 grid gap-2 rounded-lg border p-3">
|
||||||
@@ -207,7 +207,7 @@ export function TransactionDetailsDialog({
|
|||||||
|
|
||||||
{transaction.note ? (
|
{transaction.note ? (
|
||||||
<section className="space-y-2">
|
<section className="space-y-2">
|
||||||
<h3 className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
<h3 className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
||||||
Notas
|
Notas
|
||||||
</h3>
|
</h3>
|
||||||
<div className="rounded-lg border p-3 text-foreground">
|
<div className="rounded-lg border p-3 text-foreground">
|
||||||
@@ -218,7 +218,7 @@ export function TransactionDetailsDialog({
|
|||||||
|
|
||||||
{attachmentCount !== 0 && (
|
{attachmentCount !== 0 && (
|
||||||
<section className="space-y-2">
|
<section className="space-y-2">
|
||||||
<h3 className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
<h3 className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
||||||
Anexos
|
Anexos
|
||||||
</h3>
|
</h3>
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ export function ReviewTable({
|
|||||||
aria-label={`Selecionar ${row.description}`}
|
aria-label={`Selecionar ${row.description}`}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-muted-foreground text-sm tabular-nums">
|
<TableCell className="text-muted-foreground text-sm">
|
||||||
{formatDate(row.date)}
|
{formatDate(row.date)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="max-w-[200px] text-sm">
|
<TableCell className="max-w-[200px] text-sm">
|
||||||
@@ -204,7 +204,7 @@ export function ReviewTable({
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-right tabular-nums text-sm">
|
<TableCell className="text-right text-sm">
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
amount={
|
amount={
|
||||||
row.transactionType === "expense"
|
row.transactionType === "expense"
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export function AnticipationCard({
|
|||||||
<dl className="grid grid-cols-2 gap-3 text-sm">
|
<dl className="grid grid-cols-2 gap-3 text-sm">
|
||||||
<div>
|
<div>
|
||||||
<dt className="text-muted-foreground">Valor Original</dt>
|
<dt className="text-muted-foreground">Valor Original</dt>
|
||||||
<dd className="mt-1 font-medium tabular-nums">
|
<dd className="mt-1 font-medium">
|
||||||
<MoneyValues amount={Number(anticipation.totalAmount)} />
|
<MoneyValues amount={Number(anticipation.totalAmount)} />
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@@ -92,7 +92,7 @@ export function AnticipationCard({
|
|||||||
{Number(anticipation.discount) > 0 && (
|
{Number(anticipation.discount) > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<dt className="text-muted-foreground">Desconto</dt>
|
<dt className="text-muted-foreground">Desconto</dt>
|
||||||
<dd className="mt-1 font-medium tabular-nums text-success">
|
<dd className="mt-1 font-medium text-success">
|
||||||
- <MoneyValues amount={Number(anticipation.discount)} />
|
- <MoneyValues amount={Number(anticipation.discount)} />
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
@@ -110,7 +110,7 @@ export function AnticipationCard({
|
|||||||
? "Valor Final"
|
? "Valor Final"
|
||||||
: "Valor Total"}
|
: "Valor Total"}
|
||||||
</dt>
|
</dt>
|
||||||
<dd className="mt-1 text-lg font-medium tabular-nums text-primary">
|
<dd className="mt-1 text-lg font-semibold text-primary">
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
amount={
|
amount={
|
||||||
Number(anticipation.totalAmount) < 0
|
Number(anticipation.totalAmount) < 0
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export function CalculatorDisplay({
|
|||||||
<div className="mt-auto flex items-end justify-end gap-2">
|
<div className="mt-auto flex items-end justify-end gap-2">
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"truncate text-right font-medium tracking-tight tabular-nums leading-none transition-all",
|
"truncate text-right font-medium tracking-tight leading-none transition-all",
|
||||||
isResultView ? "text-2xl" : "text-3xl",
|
isResultView ? "text-2xl" : "text-3xl",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ function MoneyValues({ amount, className, showPositiveSign = false }: Props) {
|
|||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-baseline transition-all duration-200 tracking-tighter",
|
"inline-flex items-baseline tabular-nums transition-all duration-200 tracking-tighter",
|
||||||
privacyMode &&
|
privacyMode &&
|
||||||
"blur-[6px] select-none hover:blur-none focus-within:blur-none",
|
"blur-sm select-none hover:blur-none focus-within:blur-none",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
aria-label={privacyMode ? "Valor oculto" : displayValue}
|
aria-label={privacyMode ? "Valor oculto" : displayValue}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default function MonthNavigation() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="sticky top-16 z-10 flex w-full flex-row p-4 backdrop-blur-xs supports-backdrop-filter:bg-card/80">
|
<Card className="sticky top-16 z-10 flex w-full flex-row p-4 backdrop-blur-xs supports-backdrop-filter:bg-card/80 ">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<NavigationButton
|
<NavigationButton
|
||||||
direction="left"
|
direction="left"
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export function FeedbackDialogBody({ onClose }: { onClose?: () => void }) {
|
|||||||
<Icon className={cn("h-5 w-5", item.color)} />
|
<Icon className={cn("h-5 w-5", item.color)} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-1 text-left space-y-1">
|
<div className="flex-1 text-left space-y-1">
|
||||||
<h3 className="font-medium text-sm flex items-center gap-2">
|
<h3 className="font-semibold text-sm flex items-center gap-2">
|
||||||
{item.title}
|
{item.title}
|
||||||
<RiExternalLinkLine className="h-3.5 w-3.5 text-muted-foreground" />
|
<RiExternalLinkLine className="h-3.5 w-3.5 text-muted-foreground" />
|
||||||
</h3>
|
</h3>
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export function NavDropdown({ items }: NavDropdownProps) {
|
|||||||
{item.icon}
|
{item.icon}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex flex-col min-w-0">
|
<span className="flex flex-col min-w-0">
|
||||||
<span className="font-medium">{item.label}</span>
|
<span className="font-semibold">{item.label}</span>
|
||||||
{item.description && (
|
{item.description && (
|
||||||
<span className="text-xs text-muted-foreground truncate lowercase">
|
<span className="text-xs text-muted-foreground truncate lowercase">
|
||||||
{item.description}
|
{item.description}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export function NavMenu() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Desktop */}
|
{/* Desktop */}
|
||||||
<nav className="hidden md:flex items-center justify-center flex-1 ">
|
<nav className="hidden md:flex items-center justify-center flex-1">
|
||||||
<NavigationMenu viewport={false}>
|
<NavigationMenu viewport={false}>
|
||||||
<NavigationMenuList className="gap-0">
|
<NavigationMenuList className="gap-0">
|
||||||
<NavigationMenuItem>
|
<NavigationMenuItem>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export function NavToolsDropdown({ onOpenCalculator }: NavToolsDropdownProps) {
|
|||||||
<RiCalculatorLine className="size-4" />
|
<RiCalculatorLine className="size-4" />
|
||||||
</span>
|
</span>
|
||||||
<span className="flex flex-col flex-1 text-left">
|
<span className="flex flex-col flex-1 text-left">
|
||||||
<span className="font-medium">calculadora</span>
|
<span className="font-semibold">calculadora</span>
|
||||||
<span className="text-xs text-muted-foreground lowercase">
|
<span className="text-xs text-muted-foreground lowercase">
|
||||||
Faça cálculos rápidos
|
Faça cálculos rápidos
|
||||||
</span>
|
</span>
|
||||||
@@ -39,7 +39,7 @@ export function NavToolsDropdown({ onOpenCalculator }: NavToolsDropdownProps) {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span className="flex flex-col flex-1 text-left">
|
<span className="flex flex-col flex-1 text-left">
|
||||||
<span className="font-medium">privacidade</span>
|
<span className="font-semibold">privacidade</span>
|
||||||
<span className="text-xs text-muted-foreground lowercase">
|
<span className="text-xs text-muted-foreground lowercase">
|
||||||
Oculta valores na tela
|
Oculta valores na tela
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -126,7 +126,9 @@ export function NavbarUser({
|
|||||||
>
|
>
|
||||||
<RiHistoryLine className="size-4 text-muted-foreground shrink-0" />
|
<RiHistoryLine className="size-4 text-muted-foreground shrink-0" />
|
||||||
<span className="flex-1">Changelog</span>
|
<span className="flex-1">Changelog</span>
|
||||||
<Badge variant="outline">v{version}</Badge>
|
<Badge variant="outline" className="text-xs font-semibold">
|
||||||
|
v{version}
|
||||||
|
</Badge>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<DialogTrigger asChild>
|
<DialogTrigger asChild>
|
||||||
@@ -147,7 +149,7 @@ export function NavbarUser({
|
|||||||
className={cn(itemClass, "text-success")}
|
className={cn(itemClass, "text-success")}
|
||||||
>
|
>
|
||||||
<RiMegaphoneLine className="size-4 text-success shrink-0" />
|
<RiMegaphoneLine className="size-4 text-success shrink-0" />
|
||||||
<span className="flex-1 tracking-wide text-xs font-medium">
|
<span className="flex-1 tracking-wide text-xs font-bold">
|
||||||
Atualização {updateCheck.latestVersion} disponível
|
Atualização {updateCheck.latestVersion} disponível
|
||||||
</span>
|
</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user