Basdat/web/js/pages/admin/tabs/ClaimsTabAdmin.js
2025-12-20 00:01:08 +07:00

285 lines
12 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// assets/js/pages/admin/tabs/ClaimsTabAdmin.js - WITH FULL CRUD
const ClaimsTabAdmin = ({ state, handlers }) => {
const {
claims,
filteredClaims,
claimStatusFilter,
setClaimStatusFilter,
claimSearchTerm,
setClaimSearchTerm,
showCreateClaimModal,
setShowCreateClaimModal,
showEditClaimModal,
setShowEditClaimModal,
} = state;
const {
handleViewClaimDetail,
handleDeleteClaim,
handleEditClaimClick,
loadClaims,
} = handlers;
// Filter claims locally
React.useEffect(() => {
let filtered = claims.filter((claim) => {
const matchesSearch =
claim.item_name.toLowerCase().includes(claimSearchTerm.toLowerCase()) ||
claim.user_name.toLowerCase().includes(claimSearchTerm.toLowerCase());
const matchesStatus =
!claimStatusFilter || claim.status === claimStatusFilter;
return matchesSearch && matchesStatus;
});
state.setFilteredClaims(filtered);
}, [claimSearchTerm, claimStatusFilter, claims]);
return (
<div className="bg-gradient-to-br from-slate-800 to-slate-900 rounded-xl shadow-xl border border-slate-700">
<div className="p-6 border-b border-slate-700">
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-semibold text-white">Kelola Klaim</h2>
<div className="flex gap-3">
<button
onClick={() => setShowCreateClaimModal(true)}
className="px-4 py-2 bg-gradient-to-r from-green-600 to-green-700 text-white rounded-lg font-semibold hover:from-green-700 hover:to-green-800 transition shadow-lg"
>
Tambah Klaim Manual
</button>
<button
onClick={loadClaims}
className="px-4 py-2 bg-gradient-to-r from-blue-600 to-blue-700 text-white rounded-lg font-semibold hover:from-blue-700 hover:to-blue-800 transition shadow-lg"
>
🔄 Refresh
</button>
</div>
</div>
<div className="flex gap-3 flex-wrap">
<input
type="text"
placeholder="Cari klaim..."
value={claimSearchTerm}
onChange={(e) => setClaimSearchTerm(e.target.value)}
className="flex-1 min-w-[200px] px-4 py-2 bg-slate-700 border-2 border-slate-600 rounded-xl text-white placeholder-slate-400 focus:border-blue-500 focus:outline-none"
/>
<select
value={claimStatusFilter}
onChange={(e) => setClaimStatusFilter(e.target.value)}
className="px-4 py-2 bg-slate-700 border-2 border-slate-600 rounded-xl text-white focus:border-blue-500 focus:outline-none"
>
<option value="">Semua Status</option>
<option value="pending">Pending</option>
<option value="approved">Approved</option>
<option value="rejected">Rejected</option>
</select>
</div>
{/* Stats Summary */}
<div className="grid grid-cols-3 gap-4 mt-4">
<div className="bg-yellow-500/10 border border-yellow-500/30 p-3 rounded-lg text-center">
<div className="text-yellow-400 font-bold text-2xl">
{claims.filter((c) => c.status === "pending").length}
</div>
<div className="text-slate-400 text-sm">Pending</div>
</div>
<div className="bg-green-500/10 border border-green-500/30 p-3 rounded-lg text-center">
<div className="text-green-400 font-bold text-2xl">
{claims.filter((c) => c.status === "approved").length}
</div>
<div className="text-slate-400 text-sm">Approved</div>
</div>
<div className="bg-red-500/10 border border-red-500/30 p-3 rounded-lg text-center">
<div className="text-red-400 font-bold text-2xl">
{claims.filter((c) => c.status === "rejected").length}
</div>
<div className="text-slate-400 text-sm">Rejected</div>
</div>
</div>
</div>
<div className="p-6">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{filteredClaims.map((claim) => (
<div
key={claim.id}
className="bg-gradient-to-br from-slate-700 to-slate-800 border-2 border-slate-600 rounded-xl p-5 hover:border-blue-500 hover:shadow-xl hover:shadow-blue-500/20 transition-all"
>
{/* Header */}
<div className="flex justify-between items-start mb-4 border-b border-slate-600 pb-3">
<div className="flex-1">
<h3 className="text-lg font-bold text-white">
{claim.item_name}
</h3>
<p className="text-xs text-slate-400 mt-1">
Pengklaim:{" "}
<span className="text-white font-medium">
{claim.user_name}
</span>{" "}
{claim.contact}
</p>
</div>
<span
className={`px-3 py-1 rounded-full text-xs font-bold uppercase tracking-wider ${Helpers.getStatusBadgeClass(
claim.status
)}`}
>
{claim.status}
</span>
</div>
{/* Match Percentage */}
{claim.match_percentage && (
<div
className={`mb-4 p-3 rounded-lg text-center ${
claim.match_percentage >= 70
? "bg-green-500/10 border border-green-500/30"
: "bg-yellow-500/10 border border-yellow-500/30"
}`}
>
<div className="text-xs text-slate-400 mb-1">
Similarity Match
</div>
<div
className={`text-2xl font-bold ${
claim.match_percentage >= 70
? "text-green-400"
: "text-yellow-400"
}`}
>
{claim.match_percentage}%
</div>
</div>
)}
{/* Description */}
<div className="bg-slate-900/50 p-3 rounded-lg border border-slate-600 mb-4">
<strong className="text-sm text-slate-300">
Deskripsi Klaim:
</strong>
<p className="text-sm text-slate-400 mt-1 line-clamp-3">
{claim.description}
</p>
</div>
{/* Timestamps */}
<div className="space-y-1 text-xs text-slate-400 mb-4">
<div>
📅 Diajukan: {Helpers.formatDateTime(claim.created_at)}
</div>
{claim.verified_at && (
<div>
Diverifikasi: {Helpers.formatDateTime(claim.verified_at)}
</div>
)}
{claim.verified_by_name && (
<div>👤 Oleh: {claim.verified_by_name}</div>
)}
</div>
{/* Notes */}
{claim.notes && (
<div
className={`p-3 rounded-lg border-2 mb-4 ${
claim.status === "approved"
? "bg-green-500/10 border-green-500/30"
: "bg-red-500/10 border-red-500/30"
}`}
>
<strong className="text-sm text-slate-300">
Catatan Manager:
</strong>
<p className="text-sm text-slate-400 mt-1">{claim.notes}</p>
</div>
)}
{/* Case Closed Info */}
{claim.status === "approved" && claim.berita_acara_no && (
<div className="bg-green-500/10 border-2 border-green-500/30 p-4 rounded-lg mb-4">
<div className="flex items-center gap-2 mb-2">
<span className="text-2xl">📋</span>
<strong className="text-green-400">Case Closed</strong>
</div>
<div className="space-y-1 text-sm text-slate-300">
<div>
No. BA:{" "}
<strong className="text-white">
{claim.berita_acara_no}
</strong>
</div>
{claim.case_closed_at && (
<div>
Ditutup: {Helpers.formatDateTime(claim.case_closed_at)}
</div>
)}
{claim.case_closed_by_name && (
<div>Oleh: {claim.case_closed_by_name}</div>
)}
</div>
{claim.bukti_serah_terima && (
<a
href={claim.bukti_serah_terima}
target="_blank"
rel="noopener noreferrer"
className="inline-block mt-2 px-3 py-1 bg-blue-600 text-white text-xs rounded hover:bg-blue-700 transition"
>
📄 Lihat Bukti
</a>
)}
</div>
)}
{/* Actions */}
<div className="flex gap-2">
<button
onClick={() => handleViewClaimDetail(claim)}
className="flex-1 px-3 py-2 bg-gradient-to-r from-blue-600 to-blue-700 text-white text-sm rounded-lg hover:from-blue-700 hover:to-blue-800 transition shadow-lg"
>
📋 Detail
</button>
{/* Edit button - only for pending claims */}
{claim.status === "pending" && (
<button
onClick={() => handleEditClaimClick(claim)}
className="px-3 py-2 bg-gradient-to-r from-yellow-600 to-yellow-700 text-white text-sm rounded-lg hover:from-yellow-700 hover:to-yellow-800 transition shadow-lg"
>
</button>
)}
{/* Delete button */}
<button
onClick={() => {
if (
confirm(
`⚠️ Yakin ingin menghapus klaim dari "${claim.user_name}"?\n\nBarang: ${claim.item_name}`
)
) {
handleDeleteClaim(claim.id);
}
}}
className="px-3 py-2 bg-gradient-to-r from-red-600 to-red-700 text-white text-sm rounded-lg hover:from-red-700 hover:to-red-800 transition shadow-lg"
>
🗑
</button>
</div>
</div>
))}
</div>
{filteredClaims.length === 0 && (
<div className="text-center py-12 text-slate-400">
<div className="text-6xl mb-4">🤝</div>
<p>
{claimSearchTerm || claimStatusFilter
? "Tidak ada klaim yang sesuai filter"
: "Belum ada klaim"}
</p>
</div>
)}
</div>
</div>
);
};