diff --git a/lentera-backend/config/cors.php b/lentera-backend/config/cors.php index 0671458..2408118 100644 --- a/lentera-backend/config/cors.php +++ b/lentera-backend/config/cors.php @@ -20,7 +20,7 @@ 'allowed_methods' => ['*'], // URL FRONTEND MASUKIN KESINI - 'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:3000')], + 'allowed_origins' => [env('FRONTEND_URL', 'http://100.68.76.32:3000')], 'allowed_origins_patterns' => [], diff --git a/lentera-frontend/app/admin/dashboard/page.tsx b/lentera-frontend/app/admin/dashboard/page.tsx index c2be40b..f644fd8 100644 --- a/lentera-frontend/app/admin/dashboard/page.tsx +++ b/lentera-frontend/app/admin/dashboard/page.tsx @@ -28,6 +28,14 @@ export default function AdminDashboard() { const [showSudoModal, setShowSudoModal] = useState(false); const [showLogoutModal, setShowLogoutModal] = useState(false); const [sudoPassword, setSudoPassword] = useState(''); + const [sudoExpires, setSudoExpires] = useState(() => { + if (typeof window !== 'undefined') { + const saved = localStorage.getItem('sudoExpires'); + const val = saved ? parseInt(saved, 10) : null; + return val && val > Date.now() ? val : null; + } + return null; + }); const [pendingAction, setPendingAction] = useState<{type: string, id?: number, payload?: any} | null>(null); const [catModal, setCatModal] = useState<{isOpen: boolean, type: 'add'|'edit', data: any}>({isOpen: false, type: 'add', data: {name: ''}}); @@ -74,6 +82,9 @@ export default function AdminDashboard() { console.log("Logout dari server gagal, hapus token lokal."); } finally { localStorage.removeItem('token'); + localStorage.removeItem('sudoExpires'); + localStorage.removeItem('sudoToken'); + setSudoHeader(null); toast.info("Berhasil logout. Sampai jumpa!"); router.push('/'); } @@ -82,7 +93,13 @@ export default function AdminDashboard() { const handleVerifySudo = async () => { try { const res = await api.post('/admin/sudo', { password: sudoPassword }); - setSudoHeader(res.data.sudo_token); + const sudoToken = res.data.sudo_token; + const expiresAt = Date.now() + 10 * 60 * 1000; + + setSudoHeader(sudoToken); + setSudoExpires(expiresAt); + localStorage.setItem('sudoExpires', expiresAt.toString()); + localStorage.setItem('sudoToken', sudoToken); if (pendingAction?.type === 'DELETE_ASSET') await api.delete(`/assets/${pendingAction.id}`); else if (pendingAction?.type === 'UPDATE_STATUS') await api.put(`/assets/${pendingAction.id}/status`, { status: pendingAction.payload }); @@ -91,10 +108,9 @@ export default function AdminDashboard() { else if (pendingAction?.type === 'EDIT_CATEGORY') await api.put(`/categories/${pendingAction.id}`, pendingAction.payload); else if (pendingAction?.type === 'UPDATE_SETTINGS') await api.put('/admin/settings', pendingAction.payload); - toast.success("Aksi berhasil dieksekusi!"); + toast.success(res.data.message || "Aksi berhasil dieksekusi!"); setSudoPassword(''); setShowSudoModal(false); - setSudoHeader(null); fetchData(); if (pendingAction?.type === 'DELETE_CATEGORY' && selectedCat === pendingAction.id) setSelectedCat(null); } catch (err: any) { @@ -113,10 +129,46 @@ export default function AdminDashboard() { toast.warning("Alat sedang dipinjam mahasiswa, tidak bisa diubah."); return; } + + if (sudoExpires && sudoExpires > Date.now()) { + const savedToken = localStorage.getItem('sudoToken'); + if (savedToken) { + setSudoHeader(savedToken); + executeAction(type, id, payload).finally(() => { + setSudoHeader(null); + localStorage.removeItem('sudoToken'); + }); + return; + } + } + setPendingAction({ type, id, payload }); setShowSudoModal(true); }; + const executeAction = async (type: string, id?: number, payload?: any) => { + try { + if (type === 'DELETE_ASSET') await api.delete(`/assets/${id}`); + else if (type === 'UPDATE_STATUS') await api.put(`/assets/${id}/status`, { status: payload }); + else if (type === 'EDIT_ASSET') await api.put(`/assets/${id}`, payload); + else if (type === 'DELETE_CATEGORY') await api.delete(`/categories/${id}`); + else if (type === 'EDIT_CATEGORY') await api.put(`/categories/${id}`, payload); + else if (type === 'UPDATE_SETTINGS') await api.put('/admin/settings', payload); + + toast.success("Aksi berhasil dieksekusi!"); + fetchData(); + if (type === 'DELETE_CATEGORY' && selectedCat === id) setSelectedCat(null); + } catch (err: any) { + if (err.response?.status === 401) { + toast.error("Sesi habis, silakan login ulang."); + localStorage.removeItem('token'); + router.push('/'); + } else { + toast.error(err.response?.data?.message || "Gagal mengeksekusi aksi."); + } + } + }; + const saveCategory = async () => { if (!catModal.data.name.trim()) { toast.warning("Nama kategori tidak boleh kosong."); @@ -513,7 +565,9 @@ export default function AdminDashboard() {
-

{getAssetQRUrl(qrModal.asset.code)}

+ + {getAssetQRUrl(qrModal.asset.code)} +

Scan QR ini untuk langsung membuka halaman detail alat {qrModal.asset.code}