447 lines
10 KiB
React
447 lines
10 KiB
React
import { useMemo, useState } from 'react'
|
|
|
|
import Sidebar from '../components/Sidebar.jsx'
|
|
import Topbar from '../components/Topbar.jsx'
|
|
|
|
import {
|
|
loadWarga,
|
|
saveWarga
|
|
} from '../lib/storage.js'
|
|
|
|
export default function Verifikasi() {
|
|
|
|
const [rows, setRows] =
|
|
useState(() => loadWarga())
|
|
|
|
const [search, setSearch] =
|
|
useState('')
|
|
|
|
const [filterStatus, setFilterStatus] =
|
|
useState('Semua')
|
|
|
|
const [editId, setEditId] =
|
|
useState(null)
|
|
|
|
const [editForm, setEditForm] =
|
|
useState({
|
|
nama: '',
|
|
wilayah: '',
|
|
penghasilan: ''
|
|
})
|
|
|
|
function updateStatus(id, status) {
|
|
|
|
const updated = rows.map(r => {
|
|
|
|
if (r.id === id) {
|
|
|
|
return {
|
|
...r,
|
|
statusValidasi: status
|
|
}
|
|
|
|
}
|
|
|
|
return r
|
|
})
|
|
|
|
setRows(updated)
|
|
|
|
saveWarga(updated)
|
|
}
|
|
|
|
function mulaiEdit(row) {
|
|
|
|
setEditId(row.id)
|
|
|
|
setEditForm({
|
|
nama: row.nama,
|
|
wilayah: row.wilayah,
|
|
penghasilan: row.penghasilan
|
|
})
|
|
}
|
|
|
|
function simpanEdit(id) {
|
|
|
|
const updated = rows.map(r => {
|
|
|
|
if (r.id === id) {
|
|
|
|
return {
|
|
...r,
|
|
nama: editForm.nama,
|
|
wilayah: editForm.wilayah,
|
|
penghasilan:
|
|
editForm.penghasilan
|
|
}
|
|
|
|
}
|
|
|
|
return r
|
|
})
|
|
|
|
setRows(updated)
|
|
|
|
saveWarga(updated)
|
|
|
|
setEditId(null)
|
|
}
|
|
|
|
function hapusData(id) {
|
|
|
|
const confirmDelete =
|
|
confirm(
|
|
'Yakin ingin menghapus data?'
|
|
)
|
|
|
|
if (!confirmDelete) return
|
|
|
|
const filtered =
|
|
rows.filter(r => r.id !== id)
|
|
|
|
setRows(filtered)
|
|
|
|
saveWarga(filtered)
|
|
}
|
|
|
|
const filteredRows = useMemo(() => {
|
|
|
|
return rows.filter(r => {
|
|
|
|
const cocokNama =
|
|
r.nama
|
|
.toLowerCase()
|
|
.includes(
|
|
search.toLowerCase()
|
|
)
|
|
|
|
const cocokStatus =
|
|
filterStatus === 'Semua'
|
|
? true
|
|
: r.statusValidasi ===
|
|
filterStatus
|
|
|
|
return cocokNama &&
|
|
cocokStatus
|
|
})
|
|
|
|
}, [
|
|
rows,
|
|
search,
|
|
filterStatus
|
|
])
|
|
|
|
return (
|
|
<div className="flex min-h-screen bg-slate-100">
|
|
|
|
<Sidebar />
|
|
|
|
<div className="flex-1">
|
|
|
|
<Topbar />
|
|
|
|
<main className="p-6">
|
|
|
|
<div className="bg-white rounded-3xl shadow p-6">
|
|
|
|
<div className="flex items-center justify-between mb-6">
|
|
|
|
<div>
|
|
|
|
<h1 className="text-3xl font-extrabold">
|
|
Verifikasi Data
|
|
</h1>
|
|
|
|
<p className="text-slate-500 mt-1">
|
|
Approve atau reject data warga
|
|
</p>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="flex gap-4 mb-6">
|
|
|
|
<input
|
|
placeholder="Cari nama warga..."
|
|
value={search}
|
|
onChange={e =>
|
|
setSearch(
|
|
e.target.value
|
|
)
|
|
}
|
|
className="border rounded-2xl px-4 py-3 w-full"
|
|
/>
|
|
|
|
<select
|
|
value={filterStatus}
|
|
onChange={e =>
|
|
setFilterStatus(
|
|
e.target.value
|
|
)
|
|
}
|
|
className="border rounded-2xl px-4 py-3"
|
|
>
|
|
|
|
<option>
|
|
Semua
|
|
</option>
|
|
|
|
<option>
|
|
Pending
|
|
</option>
|
|
|
|
<option>
|
|
Verified
|
|
</option>
|
|
|
|
<option>
|
|
Rejected
|
|
</option>
|
|
|
|
</select>
|
|
|
|
</div>
|
|
|
|
<div className="overflow-auto">
|
|
|
|
<table className="w-full">
|
|
|
|
<thead>
|
|
|
|
<tr className="border-b">
|
|
|
|
<th className="text-left py-4">
|
|
ID
|
|
</th>
|
|
|
|
<th className="text-left py-4">
|
|
Nama
|
|
</th>
|
|
|
|
<th className="text-left py-4">
|
|
Wilayah
|
|
</th>
|
|
|
|
<th className="text-left py-4">
|
|
Penghasilan
|
|
</th>
|
|
|
|
<th className="text-left py-4">
|
|
Status
|
|
</th>
|
|
|
|
<th className="text-left py-4">
|
|
Aksi
|
|
</th>
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
<tbody>
|
|
|
|
{filteredRows.map(r => (
|
|
|
|
<tr
|
|
key={r.id}
|
|
className="border-b hover:bg-slate-50"
|
|
>
|
|
|
|
<td className="py-4">
|
|
{r.id}
|
|
</td>
|
|
|
|
<td className="py-4">
|
|
|
|
{editId === r.id ? (
|
|
|
|
<input
|
|
value={editForm.nama}
|
|
onChange={e =>
|
|
setEditForm({
|
|
...editForm,
|
|
nama:
|
|
e.target.value
|
|
})
|
|
}
|
|
className="border rounded-xl px-3 py-2"
|
|
/>
|
|
|
|
) : (
|
|
r.nama
|
|
)}
|
|
|
|
</td>
|
|
|
|
<td className="py-4">
|
|
|
|
{editId === r.id ? (
|
|
|
|
<input
|
|
value={editForm.wilayah}
|
|
onChange={e =>
|
|
setEditForm({
|
|
...editForm,
|
|
wilayah:
|
|
e.target.value
|
|
})
|
|
}
|
|
className="border rounded-xl px-3 py-2"
|
|
/>
|
|
|
|
) : (
|
|
r.wilayah
|
|
)}
|
|
|
|
</td>
|
|
|
|
<td className="py-4">
|
|
|
|
{editId === r.id ? (
|
|
|
|
<input
|
|
value={
|
|
editForm.penghasilan
|
|
}
|
|
onChange={e =>
|
|
setEditForm({
|
|
...editForm,
|
|
penghasilan:
|
|
e.target.value
|
|
})
|
|
}
|
|
className="border rounded-xl px-3 py-2"
|
|
/>
|
|
|
|
) : (
|
|
<>Rp {r.penghasilan}</>
|
|
)}
|
|
|
|
</td>
|
|
|
|
<td className="py-4">
|
|
|
|
{r.statusValidasi ===
|
|
'Pending' && (
|
|
|
|
<span className="bg-yellow-100 text-yellow-700 px-3 py-1 rounded-full text-sm">
|
|
Pending
|
|
</span>
|
|
|
|
)}
|
|
|
|
{r.statusValidasi ===
|
|
'Verified' && (
|
|
|
|
<span className="bg-green-100 text-green-700 px-3 py-1 rounded-full text-sm">
|
|
Verified
|
|
</span>
|
|
|
|
)}
|
|
|
|
{r.statusValidasi ===
|
|
'Rejected' && (
|
|
|
|
<span className="bg-red-100 text-red-700 px-3 py-1 rounded-full text-sm">
|
|
Rejected
|
|
</span>
|
|
|
|
)}
|
|
|
|
</td>
|
|
|
|
<td className="py-4">
|
|
|
|
<div className="flex gap-2 flex-wrap">
|
|
|
|
{r.statusValidasi ===
|
|
'Pending' && (
|
|
|
|
<>
|
|
|
|
<button
|
|
onClick={() =>
|
|
updateStatus(
|
|
r.id,
|
|
'Verified'
|
|
)
|
|
}
|
|
className="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-xl"
|
|
>
|
|
Approve
|
|
</button>
|
|
|
|
<button
|
|
onClick={() =>
|
|
updateStatus(
|
|
r.id,
|
|
'Rejected'
|
|
)
|
|
}
|
|
className="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-xl"
|
|
>
|
|
Reject
|
|
</button>
|
|
|
|
</>
|
|
|
|
)}
|
|
|
|
{editId === r.id ? (
|
|
|
|
<button
|
|
onClick={() =>
|
|
simpanEdit(r.id)
|
|
}
|
|
className="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-xl"
|
|
>
|
|
Simpan
|
|
</button>
|
|
|
|
) : (
|
|
|
|
<button
|
|
onClick={() =>
|
|
mulaiEdit(r)
|
|
}
|
|
className="bg-slate-800 hover:bg-slate-900 text-white px-4 py-2 rounded-xl"
|
|
>
|
|
Edit
|
|
</button>
|
|
|
|
)}
|
|
|
|
<button
|
|
onClick={() =>
|
|
hapusData(r.id)
|
|
}
|
|
className="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-xl"
|
|
>
|
|
Hapus
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
))}
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</main>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
)
|
|
}
|