satupeta-main/app/(modules)/admin/mapset-upload/_components/step-5-success.tsx

274 lines
10 KiB
TypeScript
Raw Normal View History

2026-01-28 05:48:46 +00:00
"use client";
import { useEffect, useState, useRef } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useUploadContext } from "../_context/upload-context";
import { Button } from "@/shared/components/ui/button"; // Sesuaikan dengan UI kit Anda
// Pastikan Anda memiliki variabel env atau konstanta untuk WS_URL
// Jika belum ada, bisa ganti sementara dengan string hardcoded atau process.env
const WS_URL = process.env.NEXT_PUBLIC_WS_URL || "ws://localhost:8000";
export default function StepSuccess() {
const { state, reset } = useUploadContext();
const { validatedData } = state;
const router = useRouter();
const geomIcons: Record<string, string> = {
Point: "📍",
MultiPoint: "🔹",
LineString: "📏",
MultiLineString: "🛣️",
Polygon: "⬛",
MultiPolygon: "🗾",
GeometryCollection: "🧩",
};
const PROCESS_STEPS = [
{ key: "upload", label: "Upload data" },
{ key: "cleansing", label: "Cleansing data" },
{ key: "publish_geoserver", label: "Publish GeoServer" },
{ key: "done", label: "Publish GeoNetwork" },
];
const INITIAL_STEP_STATUS: Record<string, "pending" | "running" | "done" | "error"> = {
upload: "done",
cleansing: "pending",
publish_geoserver: "pending",
done: "pending",
};
const [stepStatus, setStepStatus] = useState(INITIAL_STEP_STATUS);
const wsRef = useRef<WebSocket | null>(null);
// Jika user refresh halaman dan data hilang, kembalikan ke awal
useEffect(() => {
if (!validatedData) {
router.replace("/admin/mapset-upload?step=VALIDATE");
}
}, [validatedData, router]);
// WebSocket Logic
// useEffect(() => {
// if (!validatedData?.job_id || validatedData.job_status === "done") return;
// // Construct WS URL
// const wsUrl = `${WS_URL}/ws/job/${validatedData.job_id}`;
// const ws = new WebSocket(wsUrl);
// wsRef.current = ws;
// ws.onopen = () => {
// console.log("WS connected:", validatedData.job_id);
// };
// ws.onmessage = (event) => {
// try {
// const data = JSON.parse(event.data);
// const finishedStep = data.step;
// setStepStatus((prev) => {
// const updated = { ...prev };
// const stepIndex = PROCESS_STEPS.findIndex((s) => s.key === finishedStep);
// // 1⃣ step yang dikirim WS → DONE
// if (stepIndex >= 0) {
// updated[finishedStep] = "done";
// }
// // 2⃣ step setelahnya → RUNNING
// const nextStep = PROCESS_STEPS[stepIndex + 1];
// if (nextStep) {
// updated[nextStep.key] = "running";
// }
// // 3⃣ step setelah itu → PENDING
// PROCESS_STEPS.slice(stepIndex + 2).forEach((s) => {
// if (updated[s.key] !== "done") {
// updated[s.key] = "pending";
// }
// });
// return updated;
// });
// // 🔥 AUTO CLOSE WS JIKA SELESAI
// if (finishedStep === "done") {
// setTimeout(() => {
// wsRef.current?.close();
// }, 2000);
// }
// } catch (e) {
// console.error("Error parsing WS message", e);
// }
// };
// ws.onerror = (err) => {
// console.error("WS error", err);
// };
// return () => {
// ws.close();
// };
// }, [validatedData]);
// Render Helpers
const Spinner = () => (
<span className="inline-block w-4 h-4 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" />
);
const renderIcon = (status: string) => {
if (status === "running") return <Spinner />;
if (status === "done") return "✔";
if (status === "error") return "❌";
return "⬜";
};
2026-01-28 05:57:20 +00:00
if (!validatedData){ return null;}else{console.log("val", validatedData);} // Prevent render if redirecting
2026-01-28 05:48:46 +00:00
return (
<div className="max-w-4xl mx-auto text-center py-10">
<h1 className="text-3xl font-bold text-green-600 mb-4"> Upload Berhasil!</h1>
<p className="text-gray-700 mb-8">
Data Anda berhasil disimpan ke database.
</p>
<div className="relative border border-gray-200 bg-gradient-to-b from-white to-gray-50 rounded-2xl shadow-md p-8 mb-10 text-left overflow-hidden">
{/* Background Accents */}
<div className="absolute top-0 right-0 w-32 h-32 bg-green-100 rounded-full blur-3xl opacity-50 pointer-events-none"></div>
<div className="absolute bottom-0 left-0 w-32 h-32 bg-blue-100 rounded-full blur-3xl opacity-50 pointer-events-none"></div>
<div className="flex items-center gap-3 mb-6 relative z-10">
<div className="p-2 bg-green-100 text-green-600 rounded-full shadow-inner">
<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="M4.5 12.75l6 6 9-13.5"
/>
</svg>
</div>
<h2 className="text-2xl font-bold text-gray-800 tracking-tight">
Ringkasan Hasil Upload
</h2>
</div>
<div className="space-y-4 relative z-10">
2026-01-28 05:57:20 +00:00
{validatedData.data.table_name && (
2026-01-28 05:48:46 +00:00
<div className="flex justify-between items-center bg-gray-50 px-4 py-3 rounded-lg border border-gray-200 hover:shadow-sm transition">
<span className="text-gray-600 font-medium">📁 Nama Tabel</span>
2026-01-28 05:57:20 +00:00
<span className="text-gray-900 font-semibold">{validatedData.data.table_name}</span>
2026-01-28 05:48:46 +00:00
</div>
)}
2026-01-28 05:57:20 +00:00
{validatedData.data.total_rows && (
2026-01-28 05:48:46 +00:00
<div className="flex justify-between items-center bg-gray-50 px-4 py-3 rounded-lg border border-gray-200 hover:shadow-sm transition">
<span className="text-gray-600 font-medium">📊 Jumlah Baris</span>
<span className="text-gray-900 font-semibold">
2026-01-28 05:57:20 +00:00
{validatedData.data.total_rows.toLocaleString()} data
2026-01-28 05:48:46 +00:00
</span>
</div>
)}
2026-01-28 05:57:20 +00:00
{validatedData.data.geometry_type && (
2026-01-28 05:48:46 +00:00
<div className="flex justify-between items-center bg-gray-50 px-4 py-3 rounded-lg border border-gray-200 hover:shadow-sm transition">
<span className="text-gray-600 font-medium">🧭 Jenis Geometry</span>
<span className="text-gray-900 font-semibold">
2026-01-28 05:57:20 +00:00
{Array.isArray(validatedData.data.geometry_type)
? validatedData.data.geometry_type.map((g: string) => `${geomIcons[g] || "❓"} ${g}`).join(", ")
: validatedData.data.geometry_type
2026-01-28 05:48:46 +00:00
}
</span>
</div>
)}
2026-01-28 05:57:20 +00:00
{validatedData.data.upload_time && (
2026-01-28 05:48:46 +00:00
<div className="flex justify-between items-center bg-gray-50 px-4 py-3 rounded-lg border border-gray-200 hover:shadow-sm transition">
<span className="text-gray-600 font-medium">🕒 Waktu Upload</span>
<span className="text-gray-900 font-semibold">
2026-01-28 05:57:20 +00:00
{new Date(validatedData.data.upload_time).toLocaleString("id-ID", {
2026-01-28 05:48:46 +00:00
dateStyle: "full",
timeStyle: "short",
})}
</span>
</div>
)}
{/* PROGRESS STEPS (WS LIVE) */}
2026-01-28 05:57:20 +00:00
{(validatedData.data.message && validatedData.data.job_status !== "done") && (
2026-01-28 05:48:46 +00:00
<div className="border border-gray-200 rounded-lg mt-4 overflow-hidden">
{PROCESS_STEPS.map((step) => (
<div
key={step.key}
className={`px-4 flex items-center gap-3 text-sm py-3 border-b border-gray-200 ${
stepStatus[step.key] === "done"
? "bg-green-50"
: stepStatus[step.key] === "running"
? "bg-blue-50"
: "bg-gray-50"
}`}
>
<span className="w-5 flex justify-center">
{renderIcon(stepStatus[step.key] || "-")}
</span>
<span
className={
stepStatus[step.key] === "done"
? "text-green-600 font-medium"
: stepStatus[step.key] === "running"
? "text-blue-600 font-medium"
: "text-gray-500"
}
>
{step.label}
</span>
</div>
))}
</div>
)}
</div>
2026-01-28 05:57:20 +00:00
{(validatedData.data.job_status !== "done") && (
2026-01-28 05:48:46 +00:00
<p className="mt-3 text-center text-gray-500 text-sm">
Sistem sedang melakukan cleansing data dan publikasi ke GeoServer dan GeoNetwork.<br />
Anda tidak perlu menunggu di halaman ini.
</p>
)}
{/* Metadata Section JSON View */}
2026-01-28 05:57:20 +00:00
{validatedData.data.metadata && (
2026-01-28 05:48:46 +00:00
<div className="mt-8 relative z-10">
<h3 className="text-sm font-semibold text-gray-600 mb-2">Metadata</h3>
<div className="bg-slate-900 text-slate-100 text-xs rounded-lg overflow-auto shadow-inner p-4 max-h-60 font-mono">
2026-01-28 05:57:20 +00:00
<pre>{JSON.stringify(validatedData.data.metadata, null, 2)}</pre>
2026-01-28 05:48:46 +00:00
</div>
</div>
)}
</div>
<div className="flex flex-col w-full items-center gap-3">
{/* Tombol ke Dashboard (Next.js Link) */}
2026-01-28 05:57:20 +00:00
<Link href="/admin/mapset">
2026-01-28 05:48:46 +00:00
<Button className="w-fit bg-blue-600 hover:bg-blue-700 px-8 py-6 text-lg shadow-lg shadow-blue-200">
Kembali ke Dashboard
</Button>
</Link>
{/* Tombol Upload Lagi (Reset Context) */}
<button
onClick={reset}
className="text-gray-500 hover:text-gray-700 text-sm font-medium hover:underline transition"
>
Upload data lagi
</button>
</div>
</div>
);
}