165 lines
6.5 KiB
React
165 lines
6.5 KiB
React
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { format } from "date-fns";
|
|
import { ptBR } from "date-fns/locale";
|
|
import Label from "@/app/(protected)/components/shared/Label";
|
|
import { getPaymentsByObligationAction } from "@/app/lib/payments/actions";
|
|
|
|
export default function ObligationDetails({ obligations }) {
|
|
const [expandedObligation, setExpandedObligation] = useState(null);
|
|
const [obligationPayments, setObligationPayments] = useState({});
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
// Group obligations by their reference (description + dueDate)
|
|
const groupedObligations = obligations.reduce((acc, obligation) => {
|
|
const key = `${obligation.description || "Pagamento"}_${obligation.dueDate || obligation.createdAt}`;
|
|
if (!acc[key]) {
|
|
acc[key] = {
|
|
...obligation,
|
|
users: [],
|
|
};
|
|
}
|
|
acc[key].users.push(obligation);
|
|
return acc;
|
|
}, {});
|
|
|
|
const groupedList = Object.values(groupedObligations);
|
|
|
|
const fetchPaymentsForObligation = async (obligation) => {
|
|
setLoading(true);
|
|
try {
|
|
const result = await getPaymentsByObligationAction(obligation._id);
|
|
if (result.success) {
|
|
setObligationPayments(prev => ({
|
|
...prev,
|
|
[obligation._id]: result.data || [],
|
|
}));
|
|
}
|
|
} catch (error) {
|
|
console.error("Error fetching payments:", error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleToggleExpand = (obligation) => {
|
|
if (expandedObligation === obligation._id) {
|
|
setExpandedObligation(null);
|
|
} else {
|
|
setExpandedObligation(obligation._id);
|
|
if (!obligationPayments[obligation._id]) {
|
|
fetchPaymentsForObligation(obligation);
|
|
}
|
|
}
|
|
};
|
|
|
|
const getStatusBadge = (userObligation) => {
|
|
const payments = obligationPayments[userObligation._id] || [];
|
|
const hasVerified = payments.some(p => p.status === "verified");
|
|
const hasPending = payments.some(p => p.status === "pending_verification");
|
|
|
|
if (hasVerified) {
|
|
return <Label color="emerald" size="sm">✓ Pago</Label>;
|
|
}
|
|
if (hasPending) {
|
|
return <Label color="blue" size="sm">Aguardando Verificação</Label>;
|
|
}
|
|
return <Label color="amber" size="sm">Aguardando Pagamento</Label>;
|
|
};
|
|
|
|
if (obligations.length === 0) {
|
|
return (
|
|
<div className="bg-white dark:bg-neutral-800 rounded-lg shadow p-6 text-center text-gray-500 dark:text-gray-200 label-dark">
|
|
Nenhuma obrigação de pagamento criada ainda.
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
{groupedList.map((group) => (
|
|
<div
|
|
key={group._id}
|
|
className="bg-white dark:bg-neutral-800 rounded-lg shadow overflow-hidden"
|
|
>
|
|
{/* Group Header */}
|
|
<div
|
|
className="p-4 cursor-pointer hover:bg-gray-50 dark:hover:bg-neutral-700/50 transition-colors"
|
|
onClick={() => handleToggleExpand(group)}
|
|
>
|
|
<div className="flex justify-between items-center">
|
|
<div>
|
|
<h3 className="font-semibold text-gray-900 dark:text-neutral-100">
|
|
{group.description || "Pagamento"}
|
|
</h3>
|
|
<p className="text-sm text-gray-500 dark:text-gray-200 label-dark">
|
|
Vencimento: {format(new Date(group.dueDate || group.createdAt), "dd/MM/yyyy", { locale: ptBR })}
|
|
</p>
|
|
</div>
|
|
<div className="flex items-center gap-4">
|
|
<div className="text-right">
|
|
<p className="text-sm text-gray-500 dark:text-gray-200 label-dark">
|
|
{group.users.length} {group.users.length === 1 ? "aluno" : "alunos"}
|
|
</p>
|
|
<p className="text-lg font-bold text-gray-900 dark:text-neutral-100">
|
|
R$ {group.amount?.toFixed(2)}
|
|
</p>
|
|
</div>
|
|
<span className="text-gray-400 dark:text-neutral-500">
|
|
{expandedObligation === group._id ? "▼" : "▶"}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Expanded Details */}
|
|
{expandedObligation === group._id && (
|
|
<div className="border-t border-gray-200 dark:border-neutral-700">
|
|
<table className="min-w-full divide-y divide-gray-200 dark:divide-neutral-700">
|
|
<thead className="bg-gray-50 dark:bg-neutral-700">
|
|
<tr>
|
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-200 label-dark uppercase tracking-wider">
|
|
Aluno
|
|
</th>
|
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-200 label-dark uppercase tracking-wider">
|
|
Email
|
|
</th>
|
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-200 label-dark uppercase tracking-wider">
|
|
Valor
|
|
</th>
|
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-200 label-dark uppercase tracking-wider">
|
|
Status
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="bg-white dark:bg-neutral-800 divide-y divide-gray-200 dark:divide-neutral-700">
|
|
{group.users.map((userObligation) => (
|
|
<tr
|
|
key={userObligation._id}
|
|
className="hover:bg-gray-50 dark:hover:bg-neutral-700/50"
|
|
>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-neutral-100">
|
|
{userObligation.userId?.fullName || "N/A"}
|
|
</td>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-200 label-dark">
|
|
{userObligation.userId?.email || "N/A"}
|
|
</td>
|
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900 dark:text-neutral-100 font-medium">
|
|
R$ {userObligation.amount?.toFixed(2) || "0.00"}
|
|
</td>
|
|
<td className="px-6 py-4 whitespace-nowrap">
|
|
{getStatusBadge(userObligation)}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|