Fix: Implement card deletion and display card details
Added card deletion functionality. Improved card details display to show the month and the total expenses for each card. Fixed dashboard card layout issues on smaller screens. Separated expenses and credit card expenses in the dashboard summary.
This commit is contained in:
parent
c2c12ae5b3
commit
13c4fb0760
@ -2,17 +2,33 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { CartaoCredito } from '@/types/cartaoTypes';
|
import { CartaoCredito } from '@/types/cartaoTypes';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
import { useToast } from '@/components/ui/use-toast';
|
import { useToast } from '@/components/ui/use-toast';
|
||||||
import { CreditCard } from 'lucide-react';
|
import { CreditCard, Trash2 } from 'lucide-react';
|
||||||
|
import { excluirCartao } from '@/services/cartaoCreditoService';
|
||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogTitle,
|
||||||
|
} from "@/components/ui/alert-dialog";
|
||||||
|
|
||||||
interface CartaoCreditoListProps {
|
interface CartaoCreditoListProps {
|
||||||
cartoes: CartaoCredito[];
|
cartoes: CartaoCredito[];
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
onCartaoClick: (cartao: CartaoCredito) => void;
|
onCartaoClick: (cartao: CartaoCredito) => void;
|
||||||
|
onCartaoDeleted?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CartaoCreditoList({ cartoes, isLoading, onCartaoClick }: CartaoCreditoListProps) {
|
export function CartaoCreditoList({ cartoes, isLoading, onCartaoClick, onCartaoDeleted }: CartaoCreditoListProps) {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
|
const [cartaoToDelete, setCartaoToDelete] = useState<CartaoCredito | null>(null);
|
||||||
|
const [isDeleting, setIsDeleting] = useState(false);
|
||||||
|
|
||||||
const formatCurrency = (value: number = 0) => {
|
const formatCurrency = (value: number = 0) => {
|
||||||
return new Intl.NumberFormat('pt-BR', {
|
return new Intl.NumberFormat('pt-BR', {
|
||||||
@ -21,6 +37,53 @@ export function CartaoCreditoList({ cartoes, isLoading, onCartaoClick }: CartaoC
|
|||||||
}).format(value);
|
}).format(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getCurrentMonthYear = () => {
|
||||||
|
const now = new Date();
|
||||||
|
const month = now.toLocaleDateString('pt-BR', { month: 'long' });
|
||||||
|
const year = now.getFullYear();
|
||||||
|
return `${month.charAt(0).toUpperCase() + month.slice(1)} ${year}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteClick = (e: React.MouseEvent, cartao: CartaoCredito) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setCartaoToDelete(cartao);
|
||||||
|
setDeleteDialogOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleConfirmDelete = async () => {
|
||||||
|
if (!cartaoToDelete) return;
|
||||||
|
|
||||||
|
setIsDeleting(true);
|
||||||
|
try {
|
||||||
|
const success = await excluirCartao(cartaoToDelete.id);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
toast({
|
||||||
|
title: "Cartão excluído",
|
||||||
|
description: "O cartão e todas as suas despesas foram removidos com sucesso.",
|
||||||
|
});
|
||||||
|
onCartaoDeleted?.();
|
||||||
|
} else {
|
||||||
|
toast({
|
||||||
|
title: "Erro ao excluir cartão",
|
||||||
|
description: "Não foi possível excluir o cartão. Tente novamente.",
|
||||||
|
variant: "destructive"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erro ao excluir cartão:', error);
|
||||||
|
toast({
|
||||||
|
title: "Erro ao excluir cartão",
|
||||||
|
description: "Ocorreu um erro inesperado. Tente novamente.",
|
||||||
|
variant: "destructive"
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsDeleting(false);
|
||||||
|
setDeleteDialogOpen(false);
|
||||||
|
setCartaoToDelete(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-center p-8">
|
<div className="flex justify-center p-8">
|
||||||
@ -48,20 +111,31 @@ export function CartaoCreditoList({ cartoes, isLoading, onCartaoClick }: CartaoC
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||||
{cartoes.map((cartao) => {
|
{cartoes.map((cartao) => {
|
||||||
console.log(`Cartão na lista: ${cartao.nome}, total: ${cartao.total_despesas}`);
|
console.log(`Cartão na lista: ${cartao.nome}, total: ${cartao.total_despesas}`);
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
key={cartao.id}
|
key={cartao.id}
|
||||||
className="hover:shadow-md transition-shadow cursor-pointer"
|
className="hover:shadow-md transition-shadow cursor-pointer relative group"
|
||||||
onClick={() => onCartaoClick(cartao)}
|
onClick={() => onCartaoClick(cartao)}
|
||||||
>
|
>
|
||||||
<CardHeader className="pb-2">
|
<CardHeader className="pb-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
<CardTitle className="flex items-center text-lg">
|
<CardTitle className="flex items-center text-lg">
|
||||||
<CreditCard className="w-5 h-5 mr-2" />
|
<CreditCard className="w-5 h-5 mr-2" />
|
||||||
{cartao.nome}
|
{cartao.nome}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="opacity-0 group-hover:opacity-100 transition-opacity text-red-500 hover:text-red-700 hover:bg-red-50"
|
||||||
|
onClick={(e) => handleDeleteClick(e, cartao)}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
<div className="text-sm text-muted-foreground space-y-1">
|
<div className="text-sm text-muted-foreground space-y-1">
|
||||||
{cartao.banco && <div>{cartao.banco}</div>}
|
{cartao.banco && <div>{cartao.banco}</div>}
|
||||||
{cartao.bandeira && <div>{cartao.bandeira}</div>}
|
{cartao.bandeira && <div>{cartao.bandeira}</div>}
|
||||||
@ -72,12 +146,36 @@ export function CartaoCreditoList({ cartoes, isLoading, onCartaoClick }: CartaoC
|
|||||||
{formatCurrency(cartao.total_despesas)}
|
{formatCurrency(cartao.total_despesas)}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Total em despesas
|
Fatura de {getCurrentMonthYear()}
|
||||||
</p>
|
</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>Confirmar exclusão</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
Tem certeza que deseja excluir o cartão "{cartaoToDelete?.nome}"?
|
||||||
|
Esta ação removerá permanentemente o cartão e todas as suas despesas associadas.
|
||||||
|
Esta ação não pode ser desfeita.
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel disabled={isDeleting}>Cancelar</AlertDialogCancel>
|
||||||
|
<AlertDialogAction
|
||||||
|
onClick={handleConfirmDelete}
|
||||||
|
disabled={isDeleting}
|
||||||
|
className="bg-red-600 hover:bg-red-700"
|
||||||
|
>
|
||||||
|
{isDeleting ? 'Excluindo...' : 'Excluir'}
|
||||||
|
</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,21 @@
|
|||||||
|
|
||||||
import { CartaoCredito } from "@/types/cartaoTypes";
|
import { CartaoCreditoList } from './CartaoCreditoList';
|
||||||
import { CartaoCreditoList } from "./CartaoCreditoList";
|
import { CartaoCredito } from '@/types/cartaoTypes';
|
||||||
|
|
||||||
interface CartaoListViewProps {
|
interface CartaoListViewProps {
|
||||||
cartoes: CartaoCredito[];
|
cartoes: CartaoCredito[];
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
onCartaoClick: (cartao: CartaoCredito) => void;
|
onCartaoClick: (cartao: CartaoCredito) => void;
|
||||||
|
onCartaoDeleted?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CartaoListView({ cartoes, isLoading, onCartaoClick }: CartaoListViewProps) {
|
export function CartaoListView({ cartoes, isLoading, onCartaoClick, onCartaoDeleted }: CartaoListViewProps) {
|
||||||
return (
|
return (
|
||||||
<CartaoCreditoList
|
<CartaoCreditoList
|
||||||
cartoes={cartoes}
|
cartoes={cartoes}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onCartaoClick={onCartaoClick}
|
onCartaoClick={onCartaoClick}
|
||||||
|
onCartaoDeleted={onCartaoDeleted}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,28 +14,27 @@ const DashboardSummaryCards: React.FC<DashboardSummaryCardsProps> = ({ resumo, f
|
|||||||
const { navigateToTransactions } = useNavigateWithFilter();
|
const { navigateToTransactions } = useNavigateWithFilter();
|
||||||
|
|
||||||
const saldo = resumo ? resumo.totalReceitas - resumo.totalDespesas - (resumo.totalCartoes || 0) : 0;
|
const saldo = resumo ? resumo.totalReceitas - resumo.totalDespesas - (resumo.totalCartoes || 0) : 0;
|
||||||
const totalDespesasGeral = resumo ? resumo.totalDespesas + (resumo.totalCartoes || 0) : 0;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-6 md:grid-cols-4">
|
<div className="grid gap-4 sm:gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-4">
|
||||||
{/* Card Receitas - Clicável */}
|
{/* Card Receitas - Clicável */}
|
||||||
<Card
|
<Card
|
||||||
className="relative overflow-hidden border-2 border-green-200 bg-gradient-to-br from-green-50 to-emerald-100 shadow-lg hover:shadow-xl transition-all duration-300 cursor-pointer group transform hover:scale-105"
|
className="relative overflow-hidden border-2 border-green-200 bg-gradient-to-br from-green-50 to-emerald-100 shadow-lg hover:shadow-xl transition-all duration-300 cursor-pointer group transform hover:scale-105"
|
||||||
onClick={() => navigateToTransactions('receita')}
|
onClick={() => navigateToTransactions('receita')}
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-green-400/10 to-emerald-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
<div className="absolute inset-0 bg-gradient-to-r from-green-400/10 to-emerald-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||||
<CardContent className="p-6 relative">
|
<CardContent className="p-4 sm:p-6 relative">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-2 sm:mb-4">
|
||||||
<div className="p-3 bg-green-500 rounded-full shadow-lg">
|
<div className="p-2 sm:p-3 bg-green-500 rounded-full shadow-lg">
|
||||||
<ArrowUpIcon className="h-6 w-6 text-white" />
|
<ArrowUpIcon className="h-4 w-4 sm:h-6 sm:w-6 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs font-bold px-3 py-1 bg-green-500 text-white rounded-full shadow-sm">
|
<div className="text-xs font-bold px-2 py-1 bg-green-500 text-white rounded-full shadow-sm">
|
||||||
+5%
|
+5%
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-1 sm:space-y-2">
|
||||||
<p className="text-sm font-semibold text-green-700">Receitas</p>
|
<p className="text-xs sm:text-sm font-semibold text-green-700">Receitas</p>
|
||||||
<p className="text-3xl font-bold text-green-600">
|
<p className="text-lg sm:text-2xl lg:text-3xl font-bold text-green-600 break-words">
|
||||||
{resumo ? formatCurrency(resumo.totalReceitas) : 'R$ 0,00'}
|
{resumo ? formatCurrency(resumo.totalReceitas) : 'R$ 0,00'}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-green-600 opacity-80">Clique para ver detalhes</p>
|
<p className="text-xs text-green-600 opacity-80">Clique para ver detalhes</p>
|
||||||
@ -49,22 +48,27 @@ const DashboardSummaryCards: React.FC<DashboardSummaryCardsProps> = ({ resumo, f
|
|||||||
onClick={() => navigateToTransactions('despesa')}
|
onClick={() => navigateToTransactions('despesa')}
|
||||||
>
|
>
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-red-400/10 to-rose-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
<div className="absolute inset-0 bg-gradient-to-r from-red-400/10 to-rose-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||||
<CardContent className="p-6 relative">
|
<CardContent className="p-4 sm:p-6 relative">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-2 sm:mb-4">
|
||||||
<div className="p-3 bg-red-500 rounded-full shadow-lg">
|
<div className="p-2 sm:p-3 bg-red-500 rounded-full shadow-lg">
|
||||||
<ArrowDownIcon className="h-6 w-6 text-white" />
|
<ArrowDownIcon className="h-4 w-4 sm:h-6 sm:w-6 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs font-bold px-3 py-1 bg-red-500 text-white rounded-full shadow-sm">
|
<div className="text-xs font-bold px-2 py-1 bg-red-500 text-white rounded-full shadow-sm">
|
||||||
-2%
|
-2%
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-1 sm:space-y-2">
|
||||||
<p className="text-sm font-semibold text-red-700">Despesas</p>
|
<p className="text-xs sm:text-sm font-semibold text-red-700">Despesas</p>
|
||||||
<p className="text-3xl font-bold text-red-600">
|
<p className="text-lg sm:text-2xl lg:text-3xl font-bold text-red-600 break-words">
|
||||||
{resumo ? formatCurrency(totalDespesasGeral) : 'R$ 0,00'}
|
{resumo ? formatCurrency(resumo.totalDespesas) : 'R$ 0,00'}
|
||||||
</p>
|
</p>
|
||||||
{resumo && resumo.totalCartoes > 0 && (
|
{resumo && resumo.totalCartoes > 0 && (
|
||||||
<p className="text-xs text-red-500">Cartões: {formatCurrency(resumo.totalCartoes)}</p>
|
<div className="pt-1 border-t border-red-200">
|
||||||
|
<p className="text-xs text-red-500">
|
||||||
|
<span className="font-medium">Cartões: </span>
|
||||||
|
{formatCurrency(resumo.totalCartoes)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
<p className="text-xs text-red-600 opacity-80">Clique para ver detalhes</p>
|
<p className="text-xs text-red-600 opacity-80">Clique para ver detalhes</p>
|
||||||
</div>
|
</div>
|
||||||
@ -74,15 +78,15 @@ const DashboardSummaryCards: React.FC<DashboardSummaryCardsProps> = ({ resumo, f
|
|||||||
{/* Card Saldo */}
|
{/* Card Saldo */}
|
||||||
<Card className="relative overflow-hidden border-2 border-blue-200 bg-gradient-to-br from-blue-50 to-sky-100 shadow-lg hover:shadow-xl transition-all duration-300 group transform hover:scale-105">
|
<Card className="relative overflow-hidden border-2 border-blue-200 bg-gradient-to-br from-blue-50 to-sky-100 shadow-lg hover:shadow-xl transition-all duration-300 group transform hover:scale-105">
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-blue-400/10 to-sky-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
<div className="absolute inset-0 bg-gradient-to-r from-blue-400/10 to-sky-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||||
<CardContent className="p-6 relative">
|
<CardContent className="p-4 sm:p-6 relative">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-2 sm:mb-4">
|
||||||
<div className="p-3 bg-blue-500 rounded-full shadow-lg">
|
<div className="p-2 sm:p-3 bg-blue-500 rounded-full shadow-lg">
|
||||||
<CreditCardIcon className="h-6 w-6 text-white" />
|
<CreditCardIcon className="h-4 w-4 sm:h-6 sm:w-6 text-white" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-1 sm:space-y-2">
|
||||||
<p className="text-sm font-semibold text-blue-700">Saldo</p>
|
<p className="text-xs sm:text-sm font-semibold text-blue-700">Saldo</p>
|
||||||
<p className={`text-3xl font-bold ${
|
<p className={`text-lg sm:text-2xl lg:text-3xl font-bold break-words ${
|
||||||
saldo >= 0 ? 'text-blue-600' : 'text-red-600'
|
saldo >= 0 ? 'text-blue-600' : 'text-red-600'
|
||||||
}`}>
|
}`}>
|
||||||
{resumo ? formatCurrency(saldo) : 'R$ 0,00'}
|
{resumo ? formatCurrency(saldo) : 'R$ 0,00'}
|
||||||
@ -94,18 +98,18 @@ const DashboardSummaryCards: React.FC<DashboardSummaryCardsProps> = ({ resumo, f
|
|||||||
{/* Card Economia */}
|
{/* Card Economia */}
|
||||||
<Card className="relative overflow-hidden border-2 border-purple-200 bg-gradient-to-br from-purple-50 to-violet-100 shadow-lg hover:shadow-xl transition-all duration-300 group transform hover:scale-105">
|
<Card className="relative overflow-hidden border-2 border-purple-200 bg-gradient-to-br from-purple-50 to-violet-100 shadow-lg hover:shadow-xl transition-all duration-300 group transform hover:scale-105">
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-purple-400/10 to-violet-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
<div className="absolute inset-0 bg-gradient-to-r from-purple-400/10 to-violet-500/10 opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
|
||||||
<CardContent className="p-6 relative">
|
<CardContent className="p-4 sm:p-6 relative">
|
||||||
<div className="flex items-center justify-between mb-4">
|
<div className="flex items-center justify-between mb-2 sm:mb-4">
|
||||||
<div className="p-3 bg-purple-500 rounded-full shadow-lg">
|
<div className="p-2 sm:p-3 bg-purple-500 rounded-full shadow-lg">
|
||||||
<Target className="h-6 w-6 text-white" />
|
<Target className="h-4 w-4 sm:h-6 sm:w-6 text-white" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-1 sm:space-y-2">
|
||||||
<p className="text-sm font-semibold text-purple-700">Economia</p>
|
<p className="text-xs sm:text-sm font-semibold text-purple-700">Economia</p>
|
||||||
<p className="text-3xl font-bold text-purple-600">
|
<p className="text-lg sm:text-2xl lg:text-3xl font-bold text-purple-600 break-words">
|
||||||
-22.2%
|
-22.2%
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-purple-500">{resumo ? formatCurrency(Math.abs(saldo)) : 'R$ 0,00'}</p>
|
<p className="text-xs text-purple-500 break-words">{resumo ? formatCurrency(Math.abs(saldo)) : 'R$ 0,00'}</p>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@ -20,7 +20,7 @@ export const TransactionSummaryCards = ({
|
|||||||
const totalGeral = totalDespesas + totalCartoes;
|
const totalGeral = totalDespesas + totalCartoes;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-4 md:grid-cols-3">
|
<div className="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
<SimpleCard className="border-green-200">
|
<SimpleCard className="border-green-200">
|
||||||
<div className="pb-2 bg-green-50 rounded-t-lg -m-6 mb-4 p-6">
|
<div className="pb-2 bg-green-50 rounded-t-lg -m-6 mb-4 p-6">
|
||||||
<div className="text-green-700 flex items-center font-bold">
|
<div className="text-green-700 flex items-center font-bold">
|
||||||
@ -29,7 +29,7 @@ export const TransactionSummaryCards = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-0">
|
<div className="pt-0">
|
||||||
<p className="text-2xl font-bold text-green-600">
|
<p className="text-xl sm:text-2xl font-bold text-green-600 break-words">
|
||||||
{formatCurrency(totalReceitas)}
|
{formatCurrency(totalReceitas)}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -42,10 +42,21 @@ export const TransactionSummaryCards = ({
|
|||||||
Gastos do mês
|
Gastos do mês
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-0">
|
<div className="pt-0 space-y-2">
|
||||||
<p className="text-2xl font-bold text-red-600">
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm text-gray-600 font-medium">Despesas:</span>
|
||||||
|
<span className="text-lg sm:text-xl font-bold text-red-600 break-words">
|
||||||
{formatCurrency(totalDespesas)}
|
{formatCurrency(totalDespesas)}
|
||||||
</p>
|
</span>
|
||||||
|
</div>
|
||||||
|
{totalCartoes > 0 && (
|
||||||
|
<div className="flex items-center justify-between pt-1 border-t border-red-100">
|
||||||
|
<span className="text-sm text-gray-600 font-medium">Cartões:</span>
|
||||||
|
<span className="text-lg sm:text-xl font-bold text-red-500 break-words">
|
||||||
|
{formatCurrency(totalCartoes)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</SimpleCard>
|
</SimpleCard>
|
||||||
|
|
||||||
@ -53,17 +64,16 @@ export const TransactionSummaryCards = ({
|
|||||||
<div className="pb-2 bg-blue-50 rounded-t-lg -m-6 mb-4 p-6">
|
<div className="pb-2 bg-blue-50 rounded-t-lg -m-6 mb-4 p-6">
|
||||||
<div className="text-blue-700 flex items-center font-bold">
|
<div className="text-blue-700 flex items-center font-bold">
|
||||||
<CreditCard className="h-5 w-5 mr-2 text-blue-600" />
|
<CreditCard className="h-5 w-5 mr-2 text-blue-600" />
|
||||||
Gastos em cartões
|
Total geral
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-0 space-y-1">
|
<div className="pt-0">
|
||||||
<p className="text-2xl font-bold text-blue-600">
|
<p className="text-xl sm:text-2xl font-bold text-blue-600 break-words">
|
||||||
{formatCurrency(totalCartoes)}
|
{formatCurrency(totalGeral)}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-blue-500 mt-1">
|
||||||
|
Despesas + Cartões
|
||||||
</p>
|
</p>
|
||||||
<div className="flex items-center justify-between pt-2 text-sm border-t">
|
|
||||||
<span className="text-gray-600 font-medium">Total geral:</span>
|
|
||||||
<span className="font-bold text-red-600">{formatCurrency(totalGeral)}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</SimpleCard>
|
</SimpleCard>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -86,6 +86,15 @@ const CartoesCreditoPage = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleCartaoDeleted = () => {
|
||||||
|
loadCartoes();
|
||||||
|
if (detalheCartaoAberto) {
|
||||||
|
setDetalheCartaoAberto(false);
|
||||||
|
setCartaoSelecionado(null);
|
||||||
|
setDespesas([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleCartaoClick = async (cartao: CartaoCredito) => {
|
const handleCartaoClick = async (cartao: CartaoCredito) => {
|
||||||
const totalDespesas = await getTotalDespesasCartao(cartao.id);
|
const totalDespesas = await getTotalDespesasCartao(cartao.id);
|
||||||
const updatedCartao = { ...cartao, total_despesas: totalDespesas };
|
const updatedCartao = { ...cartao, total_despesas: totalDespesas };
|
||||||
@ -120,6 +129,7 @@ const CartoesCreditoPage = () => {
|
|||||||
cartoes={cartoes}
|
cartoes={cartoes}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onCartaoClick={handleCartaoClick}
|
onCartaoClick={handleCartaoClick}
|
||||||
|
onCartaoDeleted={handleCartaoDeleted}
|
||||||
/>
|
/>
|
||||||
) : cartaoSelecionado && (
|
) : cartaoSelecionado && (
|
||||||
<CartaoDetalhesAvancado
|
<CartaoDetalhesAvancado
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import { supabase } from "@/integrations/supabase/client";
|
import { supabase } from "@/integrations/supabase/client";
|
||||||
import { CartaoCredito } from "@/types/cartaoTypes";
|
import { CartaoCredito } from "@/types/cartaoTypes";
|
||||||
import { gerarCartaoCodigo } from "./cartaoCodigoUtils";
|
import { gerarCartaoCodigo } from "./cartaoCodigoUtils";
|
||||||
@ -187,3 +186,75 @@ export async function criarCartao(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a credit card and all its associated expenses
|
||||||
|
* @param cartaoId Card ID to delete
|
||||||
|
* @returns Success status
|
||||||
|
*/
|
||||||
|
export async function excluirCartao(cartaoId: string): Promise<boolean> {
|
||||||
|
const userEmail = localStorage.getItem('userEmail');
|
||||||
|
|
||||||
|
if (!userEmail) {
|
||||||
|
console.error('Email do usuário não encontrado no localStorage');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedEmail = userEmail.trim().toLowerCase();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// First, get the card name to delete associated expenses
|
||||||
|
const { data: cardData, error: cardError } = await supabase
|
||||||
|
.from('cartoes_credito')
|
||||||
|
.select('nome')
|
||||||
|
.eq('id', cartaoId)
|
||||||
|
.eq('login', normalizedEmail)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (cardError) {
|
||||||
|
console.error('Erro ao obter dados do cartão:', cardError);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all expenses associated with this card
|
||||||
|
const { error: expensesError } = await supabase
|
||||||
|
.from('despesas_cartao')
|
||||||
|
.delete()
|
||||||
|
.eq('nome', cardData.nome)
|
||||||
|
.eq('login', normalizedEmail);
|
||||||
|
|
||||||
|
if (expensesError) {
|
||||||
|
console.error('Erro ao excluir despesas do cartão:', expensesError);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all invoices associated with this card
|
||||||
|
const { error: invoicesError } = await supabase
|
||||||
|
.from('faturas_cartao')
|
||||||
|
.delete()
|
||||||
|
.eq('cartao_id', cartaoId)
|
||||||
|
.eq('login', normalizedEmail);
|
||||||
|
|
||||||
|
if (invoicesError) {
|
||||||
|
console.error('Erro ao excluir faturas do cartão:', invoicesError);
|
||||||
|
// Continue with card deletion even if invoices fail
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, delete the card itself
|
||||||
|
const { error: cardDeleteError } = await supabase
|
||||||
|
.from('cartoes_credito')
|
||||||
|
.delete()
|
||||||
|
.eq('id', cartaoId)
|
||||||
|
.eq('login', normalizedEmail);
|
||||||
|
|
||||||
|
if (cardDeleteError) {
|
||||||
|
console.error('Erro ao excluir cartão:', cardDeleteError);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erro ao excluir cartão:', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -7,7 +7,8 @@
|
|||||||
export {
|
export {
|
||||||
getCartoes,
|
getCartoes,
|
||||||
criarCartao,
|
criarCartao,
|
||||||
getCartao
|
getCartao,
|
||||||
|
excluirCartao
|
||||||
} from './cartao/cartoesService';
|
} from './cartao/cartoesService';
|
||||||
|
|
||||||
// Re-export all functions from despesas services
|
// Re-export all functions from despesas services
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user