adding error alert

This commit is contained in:
dmsanhrProject 2025-11-06 18:38:46 +07:00
parent 079af2e7fd
commit e7098a1354
5 changed files with 114 additions and 19 deletions

View File

@ -0,0 +1,61 @@
import { useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
export default function ErrorNotification({ message, onClose, duration = 4000 }) {
// Auto close setelah beberapa detik
useEffect(() => {
if (!message) return;
const timer = setTimeout(() => {
onClose && onClose();
}, duration);
return () => clearTimeout(timer);
}, [message, onClose, duration]);
return (
<AnimatePresence>
{message && (
<motion.div
initial={{ opacity: 0, y: -30 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3 }}
className="fixed bottom-5 right-5 z-50 w-90 max-w-[90vw]"
>
<div className="flex items-start gap-3 bg-red-50 border border-red-200 rounded-lg p-4 shadow-lg">
{/* Icon */}
<div className="text-red-500 mt-0.5">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={2}
stroke="currentColor"
className="w-6 h-6"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M12 9v3.75m0 3.75h.007v.007H12v-.007zM21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</div>
{/* Text */}
<div className="flex-1 text-sm text-red-800">
<p className="font-semibold mb-1">Terjadi Kesalahan</p>
<p className="leading-tight">{message}</p>
</div>
{/* Close Button */}
<button
onClick={onClose}
className="text-red-500 hover:text-red-700 transition"
>
</button>
</div>
</motion.div>
)}
</AnimatePresence>
);
}

View File

@ -72,8 +72,9 @@ export function useUploadController() {
}else{
setLoading(false);
}
} finally {
// setLoading(false);
} catch(err){
setLoading(false);
throw err
}
};
@ -84,8 +85,9 @@ export function useUploadController() {
const res = await uploadPdf(selectedTable);
dispatch(setResult(res));
navigate("/admin/upload/validate");
} finally {
// setLoading(false);
} catch(err){
setLoading(false);
throw err
}
};
@ -100,8 +102,9 @@ export function useUploadController() {
const res = await saveToDatabase(data);
dispatch(setValidatedData(res));
navigate("/admin/upload/success");
} finally {
// setLoading(false);
} catch(err){
setLoading(false);
throw err
}
};

View File

@ -40,8 +40,7 @@ export async function uploadFile(file, page = null, sheet = null) {
});
return response.data;
} catch (error) {
console.error("Upload gagal:", error);
throw error.response?.data || { message: "Gagal mengunggah file." };
throw error.response?.data.detail || "Gagal proses file.";
}
}
@ -52,8 +51,7 @@ export async function uploadPdf(data) {
});
return response.data;
} catch (error) {
console.error("Gagal kirim JSON:", error);
throw error.response?.data || { message: "Terjadi kesalahan." };
throw error.response?.data.detail || { message: "Gagal proses file." };
}
}
@ -65,7 +63,6 @@ export async function saveToDatabase(data) {
});
return response.data;
} catch (error) {
console.error("Gagal kirim JSON:", error);
throw error.response?.data || { message: "Terjadi kesalahan." };
throw error.response?.data.detail || { message: "Gagal upload data." };
}
}

View File

@ -1,10 +1,11 @@
import { useEffect, useState } from "react";
import { useUploadController } from "./controller_admin_upload";
import { useDispatch } from "react-redux";
import LoadingOverlay from "../../../components/LoadingOverlay";
import ErrorNotification from "../../../components/ErrorNotification";
import FileDropzone from "../../../components/FileDropzone";
import PdfPageSelector from "../../../components/PdfPageSelector";
import { Link } from "react-router-dom";
import { useEffect } from "react";
import { reset } from "../../../store/slices/uploadSlice";
export default function ViewsAdminUploadStep1() {
@ -26,6 +27,8 @@ export default function ViewsAdminUploadStep1() {
handleUpload,
handleNextPdf
} = useUploadController();
const [errorMsg, setErrorMsg] = useState("");
const ext = file ? file.name.split(".").pop().toLowerCase() : "";
const handlePageSelection = (pages) => {
console.log("Halaman PDF yang dipilih:", pages);
@ -33,16 +36,34 @@ export default function ViewsAdminUploadStep1() {
dispatch(setSelectedPages(pages));
};
const ext = file ? file.name.split(".").pop().toLowerCase() : "";
const handleProcess = async () => {
try {
await handleUpload();
} catch (err) {
setErrorMsg(err);
}
};
const handleProcessPdf = async () => {
try {
await handleNextPdf();
} catch (err) {
setErrorMsg(err);
}
};
useEffect(() => {
dispatch(reset())
}, [])
return (
<div className="max-w-4xl mx-auto py-10">
<ErrorNotification
message={errorMsg}
onClose={() => setErrorMsg("")}
/>
<LoadingOverlay show={loading} text="Processing..." />
<div className="mb-6 flex justify-between items-center">
@ -119,7 +140,7 @@ export default function ViewsAdminUploadStep1() {
{/* Tombol Upload */}
<div className={`mt-6 flex justify-center ${(result && result.file_type === ".pdf" && result.tables?.length > 1) ? 'hidden' : 'block' }`}>
<button
onClick={handleUpload}
onClick={handleProcess}
disabled={
loading ||
(result && result.file_type === ".pdf" && result.tables?.length > 1) ||
@ -210,7 +231,7 @@ export default function ViewsAdminUploadStep1() {
</ul>
<button
onClick={handleNextPdf}
onClick={handleProcessPdf}
disabled={!selectedTable}
className="w-full mt-4 px-5 py-2 bg-green-600 text-white rounded hover:bg-green-700 disabled:bg-gray-300"
>

View File

@ -3,6 +3,7 @@ import { useUploadController } from "./controller_admin_upload";
import { useSelector } from "react-redux";
import LoadingOverlay from "../../../components/LoadingOverlay";
import Notification from "../../../components/Notification";
import ErrorNotification from "../../../components/ErrorNotification";
import { Navigate } from "react-router-dom";
import FilePreview from "../../../components/upload/FilePreview";
@ -18,6 +19,7 @@ export default function ViewsAdminUploadValidate() {
const [showAlert, setShowAlert] = useState(false);
const [alertMessage, setAlertMessage] = useState("");
const [alertType, setAlertType] = useState("info");
const [errorMsg, setErrorMsg] = useState("");
useEffect(() => {
const handleBeforeUnload = (e) => {
@ -55,7 +57,7 @@ export default function ViewsAdminUploadValidate() {
if (!result) return <Navigate to="/admin/upload" />;
const handleUploadClick = () => {
const handleUploadClick = async () => {
if (!tableTitle.trim()) {
setAlertMessage(
"❗Judul tabel belum diisi. Silakan isi sebelum melanjutkan."
@ -64,7 +66,13 @@ export default function ViewsAdminUploadValidate() {
setShowAlert(true);
return;
}
handleConfirmUpload();
// handleConfirmUpload();
try {
await handleConfirmUpload();
} catch (err) {
setErrorMsg(err);
}
};
return (
@ -77,6 +85,11 @@ export default function ViewsAdminUploadValidate() {
/>
)}
<ErrorNotification
message={errorMsg}
onClose={() => setErrorMsg("")}
/>
<LoadingOverlay show={loading} text="Upload to database..." />
<h1 className="text-2xl font-bold mb-4"> Validasi & Konfirmasi Data</h1>