185 lines
6.0 KiB
TypeScript
185 lines
6.0 KiB
TypeScript
import {
|
|
PencilIcon,
|
|
CheckIcon,
|
|
XMarkIcon,
|
|
ExclamationTriangleIcon,
|
|
TrashIcon,
|
|
} from "@heroicons/react/24/outline";
|
|
import { format } from "date-fns";
|
|
import { ptBR } from "date-fns/locale";
|
|
import React from "react";
|
|
|
|
import { useAuth } from "../../contexts/AuthContext";
|
|
import { useTarefas } from "../../hooks/useTarefas";
|
|
import { Tarefa } from "../../types/entities";
|
|
|
|
interface TarefaCardProps {
|
|
tarefa: Tarefa;
|
|
onEdit: (tarefa: Tarefa) => void;
|
|
onStatusChange: (id: string, status: string, progresso?: number) => void;
|
|
}
|
|
|
|
export const TarefaCard: React.FC<TarefaCardProps> = ({
|
|
tarefa,
|
|
onEdit,
|
|
onStatusChange,
|
|
}) => {
|
|
const { user } = useAuth();
|
|
const { deleteTarefa } = useTarefas();
|
|
|
|
const canDelete =
|
|
user && ["ADVOGADO_ADMIN", "ADVOGADO"].includes(user.perfil);
|
|
|
|
const handleDelete = async (e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
if (!window.confirm("Tem certeza que deseja excluir esta tarefa?")) return;
|
|
|
|
try {
|
|
await deleteTarefa.mutateAsync(tarefa.id);
|
|
} catch (error) {
|
|
console.error("Erro ao excluir tarefa:", error);
|
|
alert("Erro ao excluir tarefa");
|
|
}
|
|
};
|
|
|
|
const getPrioridadeColor = () => {
|
|
switch (tarefa.prioridade) {
|
|
case "BAIXA":
|
|
return "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200";
|
|
case "MEDIA":
|
|
return "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200";
|
|
case "ALTA":
|
|
return "bg-orange-100 text-orange-800 dark:bg-orange-900 dark:text-orange-200";
|
|
case "URGENTE":
|
|
return "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200";
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="bg-white dark:bg-gray-700 rounded-lg shadow p-4">
|
|
<div className="flex justify-between items-start mb-3">
|
|
<h3 className="text-gray-900 dark:text-white font-medium">
|
|
{tarefa.titulo}
|
|
</h3>
|
|
<div className="flex items-center space-x-2">
|
|
<button
|
|
onClick={() => onEdit(tarefa)}
|
|
className="text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300"
|
|
title="Editar tarefa"
|
|
>
|
|
<PencilIcon className="h-5 w-5" />
|
|
</button>
|
|
<span
|
|
className={`px-2 py-1 text-xs rounded-full ${getPrioridadeColor()}`}
|
|
>
|
|
{tarefa.prioridade}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<p className="mt-2 text-sm text-gray-600 dark:text-gray-300 line-clamp-2">
|
|
{tarefa.descricao}
|
|
</p>
|
|
|
|
<div className="mt-4">
|
|
<div className="w-full bg-gray-200 dark:bg-gray-600 rounded-full h-2">
|
|
<div
|
|
className="bg-blue-600 h-2 rounded-full"
|
|
style={{ width: `${tarefa.progresso}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-4 flex items-center justify-between text-sm">
|
|
<div className="flex items-center space-x-4">
|
|
<div className="flex -space-x-2">
|
|
{tarefa.subtarefas.map((subtarefa, index) => (
|
|
<div
|
|
key={subtarefa.id}
|
|
className={`h-6 w-6 rounded-full flex items-center justify-center text-xs
|
|
${
|
|
subtarefa.concluida
|
|
? "bg-green-100 text-green-600 dark:bg-green-900 dark:text-green-200"
|
|
: "bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400"
|
|
}`}
|
|
>
|
|
{subtarefa.concluida ? (
|
|
<CheckIcon className="h-3 w-3" />
|
|
) : (
|
|
index + 1
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
<span className="text-gray-500 dark:text-gray-400">
|
|
{format(new Date(tarefa.dataVencimento), "dd/MM/yyyy", {
|
|
locale: ptBR,
|
|
})}
|
|
</span>
|
|
</div>
|
|
|
|
<button
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
onEdit(tarefa);
|
|
}}
|
|
className="text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300"
|
|
>
|
|
<PencilIcon className="h-4 w-4" />
|
|
</button>
|
|
</div>
|
|
|
|
<div className="mt-4 flex items-center justify-between">
|
|
<div className="flex space-x-2">
|
|
{tarefa.status !== "CONCLUIDA" && tarefa.status !== "CANCELADA" && (
|
|
<>
|
|
<button
|
|
onClick={() => onStatusChange(tarefa.id, "CONCLUIDA", 100)}
|
|
className="p-2 text-green-600 hover:bg-green-50 rounded-lg"
|
|
title="Concluir tarefa"
|
|
>
|
|
<CheckIcon className="h-5 w-5" />
|
|
</button>
|
|
<button
|
|
onClick={() => onStatusChange(tarefa.id, "CANCELADA", 0)}
|
|
className="p-2 text-red-600 hover:bg-red-50 rounded-lg"
|
|
title="Cancelar tarefa"
|
|
>
|
|
<XMarkIcon className="h-5 w-5" />
|
|
</button>
|
|
<button
|
|
onClick={() => {
|
|
const novoProgresso = prompt(
|
|
"Digite o percentual de conclusão (0-100):",
|
|
tarefa.progresso?.toString(),
|
|
);
|
|
if (novoProgresso !== null) {
|
|
const progresso = Math.min(
|
|
100,
|
|
Math.max(0, parseInt(novoProgresso)),
|
|
);
|
|
onStatusChange(tarefa.id, "EM_ANDAMENTO", progresso);
|
|
}
|
|
}}
|
|
className="p-2 text-yellow-600 hover:bg-yellow-50 rounded-lg"
|
|
title="Atualizar progresso"
|
|
>
|
|
<ExclamationTriangleIcon className="h-5 w-5" />
|
|
</button>
|
|
</>
|
|
)}
|
|
{canDelete && (
|
|
<button
|
|
onClick={handleDelete}
|
|
className="p-2 text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20 rounded-lg"
|
|
title="Excluir tarefa"
|
|
>
|
|
<TrashIcon className="h-5 w-5" />
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|