feat: implementar melhorias em importação, compartilhamento e contas inativas

- Corrigir cálculo de valor na importação de lançamentos parcelados
    - Exibir valor total (parcela × quantidade) ao invés do valor da parcela individual
    - Permite recriar parcelamentos importados com valor correto

  - Permitir que usuários compartilhados se descompartilhem de pagadores
    - Adicionar componente PagadorLeaveShareCard na aba Perfil
    - Usuário filho pode sair do compartilhamento sem precisar do usuário pai
    - Manter autorização bidirecionada na action de remoção de share

  - Implementar submenu "Inativos" para contas bancárias
    - Criar página /contas/inativos seguindo padrão de cartões
    - Filtrar contas ativas e inativas em páginas separadas
    - Adicionar ícone e navegação no sidebar
This commit is contained in:
Felipe Coutinho
2026-01-11 22:44:20 +00:00
parent 147857c5bd
commit 6a45a5110d
26 changed files with 812 additions and 405 deletions

View File

@@ -15,7 +15,7 @@ export const CATEGORY_ICON_OPTIONS: CategoryIconOption[] = [
{ label: "Dinheiro", value: "RiMoneyDollarCircleLine" },
{ label: "Carteira", value: "RiWallet3Line" },
{ label: "Carteira 2", value: "RiWalletLine" },
{ label: "Cartão", value: "RiBankCardLine" },
{ label: "Cartão", value: "RiBankCard2Line" },
{ label: "Banco", value: "RiBankLine" },
{ label: "Moedas", value: "RiHandCoinLine" },
{ label: "Gráfico", value: "RiLineChartLine" },

View File

@@ -78,6 +78,19 @@ export function buildLancamentoInitialState(
? getTodayDateString()
: "");
// Calcular o valor correto para importação de parcelados
let amountValue = "";
if (typeof lancamento?.amount === "number") {
let baseAmount = Math.abs(lancamento.amount);
// Se está importando e é parcelado, usar o valor total (parcela * quantidade)
if (isImporting && lancamento.condition === "Parcelado" && lancamento.installmentCount) {
baseAmount = baseAmount * lancamento.installmentCount;
}
amountValue = (Math.round(baseAmount * 100) / 100).toFixed(2);
}
return {
purchaseDate,
period:
@@ -87,10 +100,7 @@ export function buildLancamentoInitialState(
name: lancamento?.name ?? "",
transactionType:
lancamento?.transactionType ?? LANCAMENTO_TRANSACTION_TYPES[0],
amount:
typeof lancamento?.amount === "number"
? (Math.round(Math.abs(lancamento.amount) * 100) / 100).toFixed(2)
: "",
amount: amountValue,
condition: lancamento?.condition ?? LANCAMENTO_CONDITIONS[0],
paymentMethod,
pagadorId: fallbackPagadorId ?? undefined,

View File

@@ -15,7 +15,7 @@ import {
} from "@/lib/lancamentos/constants";
import { PAGADOR_ROLE_ADMIN, PAGADOR_ROLE_TERCEIRO } from "@/lib/pagadores/constants";
import type { SQL } from "drizzle-orm";
import { eq, ilike, or } from "drizzle-orm";
import { and, eq, ilike, isNotNull, or } from "drizzle-orm";
type PagadorRow = typeof pagadores.$inferSelect;
type ContaRow = typeof contas.$inferSelect;
@@ -370,8 +370,12 @@ export const buildLancamentoWhere = ({
where.push(
or(
ilike(lancamentos.name, searchPattern),
ilike(lancamentos.note, searchPattern)
)
ilike(lancamentos.note, searchPattern),
ilike(lancamentos.paymentMethod, searchPattern),
ilike(lancamentos.condition, searchPattern),
and(isNotNull(contas.name), ilike(contas.name, searchPattern)),
and(isNotNull(cartoes.name), ilike(cartoes.name, searchPattern))
)!
);
}

View File

@@ -49,14 +49,16 @@ export const getPaymentMethodIcon = (paymentMethod: string): ReactNode => {
<RemixIcons.RiMoneyDollarCircleLine className={ICON_CLASS} aria-hidden />
),
cartaodecredito: (
<RemixIcons.RiBankCardLine className={ICON_CLASS} aria-hidden />
<RemixIcons.RiBankCard2Line className={ICON_CLASS} aria-hidden />
),
cartaodedebito: (
<RemixIcons.RiBankCardLine className={ICON_CLASS} aria-hidden />
<RemixIcons.RiBankCard2Line className={ICON_CLASS} aria-hidden />
),
debito: <RemixIcons.RiBankCardLine className={ICON_CLASS} aria-hidden />,
debito: <RemixIcons.RiBankCard2Line className={ICON_CLASS} aria-hidden />,
prepagovrva: <RemixIcons.RiCouponLine className={ICON_CLASS} aria-hidden />,
transferenciabancaria: <RemixIcons.RiExchangeLine className={ICON_CLASS} aria-hidden />,
transferenciabancaria: (
<RemixIcons.RiExchangeLine className={ICON_CLASS} aria-hidden />
),
};
return registry[key] ?? null;