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

@@ -130,6 +130,7 @@ interface LancamentosFiltersProps {
contaCartaoOptions: ContaCartaoFilterOption[];
className?: string;
exportButton?: ReactNode;
hideAdvancedFilters?: boolean;
}
export function LancamentosFilters({
@@ -138,6 +139,7 @@ export function LancamentosFilters({
contaCartaoOptions,
className,
exportButton,
hideAdvancedFilters = false,
}: LancamentosFiltersProps) {
const router = useRouter();
const pathname = usePathname();
@@ -277,22 +279,31 @@ export function LancamentosFilters({
return (
<div className={cn("flex flex-wrap items-center gap-2", className)}>
<Input
value={searchValue}
onChange={(event) => setSearchValue(event.target.value)}
placeholder="Buscar"
aria-label="Buscar lançamentos"
className="w-[250px] text-sm border-dashed"
/>
{exportButton}
<Drawer direction="right" open={drawerOpen} onOpenChange={setDrawerOpen}>
<DrawerTrigger asChild>
<Button
variant="outline"
className="text-sm border-dashed relative"
aria-label="Abrir filtros"
>
<RiFilter3Line className="size-4" />
Filtros
{hasActiveFilters && (
<span className="absolute -top-1 -right-1 size-2 rounded-full bg-primary" />
)}
</Button>
</DrawerTrigger>
{!hideAdvancedFilters && (
<Drawer direction="right" open={drawerOpen} onOpenChange={setDrawerOpen}>
<DrawerTrigger asChild>
<Button
variant="outline"
className="text-sm border-dashed relative"
aria-label="Abrir filtros"
>
<RiFilter3Line className="size-4" />
Filtros
{hasActiveFilters && (
<span className="absolute -top-1 -right-1 size-2 rounded-full bg-primary" />
)}
</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Filtros</DrawerTitle>
@@ -319,7 +330,9 @@ export function LancamentosFilters({
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Condição</label>
<label className="text-sm font-medium">
Condição de Lançamento
</label>
<FilterSelect
param="condicao"
placeholder="Todas"
@@ -335,7 +348,7 @@ export function LancamentosFilters({
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Pagamento</label>
<label className="text-sm font-medium">Forma de Pagamento</label>
<FilterSelect
param="pagamento"
placeholder="Todos"
@@ -532,15 +545,8 @@ export function LancamentosFilters({
</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>
<Input
value={searchValue}
onChange={(event) => setSearchValue(event.target.value)}
placeholder="Buscar"
aria-label="Buscar lançamentos"
className="w-[250px] text-sm border-dashed"
/>
</Drawer>
)}
</div>
);
}

View File

@@ -734,9 +734,8 @@ export function LancamentosTable({
0
);
// Check if all data belongs to current user to determine if filters should be shown
const isOwnData = data.every((item) => item.userId === currentUserId);
const shouldShowFilters = showFilters && isOwnData;
// Check if there's any data from other users
const hasOtherUserData = data.some((item) => item.userId !== currentUserId);
const handleBulkDelete = () => {
if (onBulkDelete && selectedCount > 0) {
@@ -755,7 +754,7 @@ export function LancamentosTable({
};
const showTopControls =
Boolean(onCreate) || Boolean(onMassAdd) || shouldShowFilters;
Boolean(onCreate) || Boolean(onMassAdd) || showFilters;
return (
<TooltipProvider>
@@ -791,15 +790,16 @@ export function LancamentosTable({
) : null}
</div>
) : (
<span className={shouldShowFilters ? "hidden sm:block" : ""} />
<span className={showFilters ? "hidden sm:block" : ""} />
)}
{shouldShowFilters ? (
{showFilters ? (
<LancamentosFilters
pagadorOptions={pagadorFilterOptions}
categoriaOptions={categoriaFilterOptions}
contaCartaoOptions={contaCartaoFilterOptions}
className="w-full lg:flex-1 lg:justify-end"
hideAdvancedFilters={hasOtherUserData}
exportButton={
selectedPeriod ? (
<LancamentosExport