Add delete meta option to menu
Adds a delete option to the meta menu for deleting registered metas.
This commit is contained in:
parent
002836773d
commit
9a2f18c74e
@ -1,90 +1,83 @@
|
||||
|
||||
import { format } from 'date-fns';
|
||||
import { ptBR } from 'date-fns/locale';
|
||||
import { Trophy, Flag, FlagOff } from 'lucide-react';
|
||||
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Card, CardContent, CardFooter } from '@/components/ui/card';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ResultadoMeta } from '@/types/financialTypes';
|
||||
import { Check, Edit2, Trash2 } from 'lucide-react';
|
||||
|
||||
interface MetaProgressCardProps {
|
||||
meta: ResultadoMeta;
|
||||
onEditClick?: () => void;
|
||||
onEditClick: () => void;
|
||||
onDeleteClick: () => void;
|
||||
}
|
||||
|
||||
export function MetaProgressCard({ meta, onEditClick }: MetaProgressCardProps) {
|
||||
const formatCurrency = (value: number) => {
|
||||
return new Intl.NumberFormat('pt-BR', {
|
||||
style: 'currency',
|
||||
currency: 'BRL',
|
||||
}).format(value);
|
||||
};
|
||||
|
||||
const getMesNome = (mes: number) => {
|
||||
const date = new Date(2000, mes - 1, 1);
|
||||
return format(date, 'MMMM', { locale: ptBR });
|
||||
};
|
||||
|
||||
const getStatusIcon = () => {
|
||||
if (meta.meta_batida) {
|
||||
return <Trophy className="h-8 w-8 text-yellow-500" />;
|
||||
} else {
|
||||
return <FlagOff className="h-8 w-8 text-gray-400" />;
|
||||
}
|
||||
};
|
||||
|
||||
const getProgressColor = () => {
|
||||
if (meta.meta_batida) {
|
||||
return 'bg-green-500';
|
||||
} else if (meta.percentual_atingido >= 75) {
|
||||
return 'bg-yellow-500';
|
||||
} else {
|
||||
return 'bg-red-500';
|
||||
}
|
||||
};
|
||||
export function MetaProgressCard({ meta, onEditClick, onDeleteClick }: MetaProgressCardProps) {
|
||||
// Format the month name in Portuguese
|
||||
const mesFormatado = format(new Date(meta.ano, meta.mes - 1, 1), 'MMMM', { locale: ptBR });
|
||||
const mesCapitalizado = mesFormatado.charAt(0).toUpperCase() + mesFormatado.slice(1);
|
||||
|
||||
return (
|
||||
<Card className="overflow-hidden">
|
||||
<CardHeader className={`pb-2 ${meta.meta_batida ? 'bg-green-50' : 'bg-gray-50'}`}>
|
||||
<CardTitle className="flex items-center justify-between">
|
||||
<span className="capitalize">{getMesNome(meta.mes)} {meta.ano}</span>
|
||||
<div className="flex items-center">
|
||||
{getStatusIcon()}
|
||||
<CardContent className="p-4">
|
||||
<div className="flex justify-between items-center mb-3">
|
||||
<div>
|
||||
<h3 className="text-lg font-medium">{mesCapitalizado}</h3>
|
||||
<p className="text-sm text-muted-foreground">{meta.ano}</p>
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8"
|
||||
onClick={onEditClick}
|
||||
>
|
||||
<Edit2 className="h-4 w-4" />
|
||||
<span className="sr-only">Editar</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-8 w-8 text-destructive hover:text-destructive hover:bg-destructive/10"
|
||||
onClick={onDeleteClick}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
<span className="sr-only">Deletar</span>
|
||||
</Button>
|
||||
</div>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="pt-4 space-y-4">
|
||||
<div className="flex justify-between text-sm text-muted-foreground mb-1">
|
||||
<span>Meta: {formatCurrency(meta.valor_meta)}</span>
|
||||
<span>Economia: {formatCurrency(meta.economia_real)}</span>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<div className="h-2 w-full">
|
||||
<Progress
|
||||
value={meta.percentual_atingido}
|
||||
max={100}
|
||||
className={getProgressColor()}
|
||||
/>
|
||||
<div className="mb-4">
|
||||
<div className="flex justify-between mb-1">
|
||||
<span className="text-sm">Meta de Economia</span>
|
||||
<span className="text-sm font-semibold">R$ {meta.valor_meta.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}</span>
|
||||
</div>
|
||||
<div className="flex justify-between text-xs">
|
||||
<span>0%</span>
|
||||
<span className="font-medium">{meta.percentual_atingido.toFixed(0)}% atingido</span>
|
||||
<span>100%</span>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-sm">Economia Real</span>
|
||||
<span className="text-sm font-semibold">R$ {meta.economia_real.toLocaleString('pt-BR', { minimumFractionDigits: 2 })}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex justify-between mb-1">
|
||||
<span className="text-sm font-semibold">Progresso</span>
|
||||
<span className="text-sm font-medium">
|
||||
{meta.percentual_atingido.toFixed(0)}%
|
||||
</span>
|
||||
</div>
|
||||
<Progress value={meta.percentual_atingido} className="h-2" />
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
{onEditClick && (
|
||||
<CardFooter className="bg-muted/50 pt-1 px-4 pb-2">
|
||||
<button
|
||||
onClick={onEditClick}
|
||||
className="text-sm text-muted-foreground hover:text-primary w-full text-center"
|
||||
>
|
||||
Editar meta
|
||||
</button>
|
||||
</CardFooter>
|
||||
)}
|
||||
<CardFooter className={`p-3 ${meta.meta_batida ? 'bg-green-100 dark:bg-green-900/20' : 'bg-muted/50'}`}>
|
||||
<div className="flex items-center w-full justify-between">
|
||||
<span className="text-sm font-medium">
|
||||
{meta.meta_batida ? 'Meta atingida!' : 'Meta em andamento'}
|
||||
</span>
|
||||
{meta.meta_batida && <Check className="h-4 w-4 text-green-600 dark:text-green-500" />}
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@ -9,7 +9,7 @@ import { Button } from '@/components/ui/button';
|
||||
import { Card } from '@/components/ui/card';
|
||||
import { ResultadoMeta } from '@/types/financialTypes';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { calcularResultadosMetas, getMeta } from '@/services/metasService';
|
||||
import { calcularResultadosMetas, getMeta, deletarMeta } from '@/services/metasService';
|
||||
import { PlusCircle } from 'lucide-react';
|
||||
import {
|
||||
Dialog,
|
||||
@ -18,6 +18,16 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from '@/components/ui/alert-dialog';
|
||||
|
||||
const MetasPage = () => {
|
||||
const { toast } = useToast();
|
||||
@ -25,8 +35,11 @@ const MetasPage = () => {
|
||||
const [resultados, setResultados] = useState<ResultadoMeta[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||
const [metaParaEditar, setMetaParaEditar] = useState<{ mes: number, ano: number } | null>(null);
|
||||
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
||||
const [metaParaEditar, setMetaParaEditar] = useState<{ mes: number, ano: number, id?: string } | null>(null);
|
||||
const [metaParaDeletar, setMetaParaDeletar] = useState<{ mes: number, ano: number, id: string } | null>(null);
|
||||
const [valorMetaAtual, setValorMetaAtual] = useState(0);
|
||||
const [isDeletingMeta, setIsDeletingMeta] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Obter userId armazenado no localStorage
|
||||
@ -72,7 +85,7 @@ const MetasPage = () => {
|
||||
const meta = await getMeta(userId, mes, ano);
|
||||
if (meta) {
|
||||
setValorMetaAtual(meta.valor_meta);
|
||||
setMetaParaEditar({ mes, ano });
|
||||
setMetaParaEditar({ mes, ano, id: meta.id });
|
||||
setIsDialogOpen(true);
|
||||
}
|
||||
} catch (error) {
|
||||
@ -80,6 +93,39 @@ const MetasPage = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeletarMeta = (mes: number, ano: number, id: string) => {
|
||||
setMetaParaDeletar({ mes, ano, id });
|
||||
setIsDeleteDialogOpen(true);
|
||||
};
|
||||
|
||||
const confirmarDeletarMeta = async () => {
|
||||
if (!metaParaDeletar?.id) return;
|
||||
|
||||
try {
|
||||
setIsDeletingMeta(true);
|
||||
await deletarMeta(metaParaDeletar.id);
|
||||
|
||||
// Atualizar lista de metas após deleção
|
||||
await loadData(userId);
|
||||
|
||||
toast({
|
||||
title: 'Meta excluída',
|
||||
description: `A meta de ${format(new Date(metaParaDeletar.ano, metaParaDeletar.mes - 1, 1), 'MMMM', { locale: ptBR })} ${metaParaDeletar.ano} foi excluída com sucesso.`,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Erro ao deletar meta:', error);
|
||||
toast({
|
||||
title: 'Erro ao deletar meta',
|
||||
description: 'Não foi possível excluir a meta',
|
||||
variant: 'destructive',
|
||||
});
|
||||
} finally {
|
||||
setIsDeletingMeta(false);
|
||||
setIsDeleteDialogOpen(false);
|
||||
setMetaParaDeletar(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleNovaMeta = () => {
|
||||
setMetaParaEditar(null);
|
||||
setValorMetaAtual(0);
|
||||
@ -161,6 +207,20 @@ const MetasPage = () => {
|
||||
key={`${meta.ano}-${meta.mes}`}
|
||||
meta={meta}
|
||||
onEditClick={() => handleEditarMeta(meta.mes, meta.ano)}
|
||||
onDeleteClick={() => {
|
||||
// Aqui precisamos buscar o ID primeiro pois resultadoMeta não tem o ID
|
||||
// Vamos usar o metaParaEditar.id que já foi carregado se o usuário clicou em editar antes
|
||||
if (metaParaEditar && metaParaEditar.mes === meta.mes && metaParaEditar.ano === meta.ano && metaParaEditar.id) {
|
||||
handleDeletarMeta(meta.mes, meta.ano, metaParaEditar.id);
|
||||
} else {
|
||||
// Buscar o ID antes de deletar
|
||||
getMeta(userId, meta.mes, meta.ano).then(metaCompleta => {
|
||||
if (metaCompleta && metaCompleta.id) {
|
||||
handleDeletarMeta(meta.mes, meta.ano, metaCompleta.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@ -191,6 +251,34 @@ const MetasPage = () => {
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<AlertDialog
|
||||
open={isDeleteDialogOpen}
|
||||
onOpenChange={setIsDeleteDialogOpen}
|
||||
>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
Confirmar exclusão
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{metaParaDeletar && (
|
||||
`Tem certeza que deseja excluir a meta de ${format(new Date(metaParaDeletar.ano, metaParaDeletar.mes - 1, 1), 'MMMM', { locale: ptBR })} de ${metaParaDeletar.ano}? Esta ação não pode ser desfeita.`
|
||||
)}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel disabled={isDeletingMeta}>Cancelar</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={confirmarDeletarMeta}
|
||||
disabled={isDeletingMeta}
|
||||
className="bg-destructive hover:bg-destructive/90"
|
||||
>
|
||||
{isDeletingMeta ? 'Deletando...' : 'Deletar Meta'}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user