silancar/app/dashboard/etle/page.tsx

191 lines
5.8 KiB
TypeScript

"use client"
import { ArrowLeftIcon, ChevronLeft } from "lucide-react"
import Link from "next/link"
import { Drawer, DrawerContent, DrawerTrigger } from "@/components/ui/drawer"
import { Button } from "@/components/ui/button"
import Image from "next/image"
import { useEffect, useState } from "react"
import { socket } from "@/components/socket"
import { usePlateStore } from "@/components/zustand/plate-history"
import { Spinner } from "@/components/ui/spinner"
const stream_dummy = [
{ id: "asssd", url: "/app/static/uploads/ds1.mp4" },
{ id: "asaggssd", url: "/app/static/uploads/ds2.mp4" },
{ id: "asss112d", url: "/app/static/uploads/ds3.mp4" },
{ id: "asgsasssd", url: "/app/static/uploads/ds4.mp4" },
]
export default function Etle() {
const [sessionId, setSessionId] = useState<string | null>(null)
const [imageSrc, setImageSrc] = useState<string>("")
const [plate, setPlate] = useState<string>("")
const [confidence, setConfidence] = useState<number | null>(null)
const activeStream = usePlateStore((s) => s.activeStreamId)
const setActiveStream = usePlateStore((s) => s.setActiveStream)
const history = usePlateStore((s) => s.history)
const addPlate = usePlateStore((s) => s.addPlate)
const [loading, setLoading] = useState(true)
const currentStream = usePlateStore.getState().activeStreamId
const startDetection = (index: number) => {
setLoading(true)
if (history[activeStream]) setPlate("")
setConfidence(null)
const stream = stream_dummy[index].url
socket.emit("stop_detection")
socket.emit("start_detection", {
stream_url: stream,
})
setTimeout(() => {
setLoading(false)
}, 2000)
}
useEffect(() => {
if (!socket.connected) {
socket.connect()
}
socket.on("connected", (data) => {
setSessionId(data.session_id)
const stream = stream_dummy.find((a) => a.id == currentStream)
setActiveStream(currentStream)
socket.emit("start_detection", {
stream_url: stream?.url ?? stream_dummy[0].url,
})
setTimeout(() => {
setLoading(false)
}, 2000)
})
socket.on("video_frame", (data) => {
const thumb = `data:image/jpeg;base64,${data.frame}`
setImageSrc(thumb)
})
socket.on("plate_detected", (data) => {
setPlate(data.plate_text)
setConfidence(data.confidence ?? null)
const thumb = `data:image/jpeg;base64,${data.frame_thumb}`
if (!currentStream) return
addPlate(currentStream, {
imgThumb: thumb,
plate: data.plate_text,
confidence: data.confidence,
})
})
socket.on("error", (data) => {
console.error(data.message)
})
socket.on("detection_stopped", (data) => {
console.log("Selesai. Total:", data.total)
})
return () => {
socket.emit("stop_detection")
socket.removeAllListeners()
socket.disconnect()
}
}, [])
const [hasMounted, setHasMounted] = useState(false)
useEffect(() => {
setHasMounted(true)
}, [])
if (!hasMounted) {
return null
}
return (
<div className="flex flex-col">
<div className="flex h-[8vh] flex-row items-center gap-10 bg-[#0057B3] px-8 py-5">
<Link href="/dashboard">
<ArrowLeftIcon />
</Link>
<p className="text-2xl font-semibold">ETLE Toll</p>
</div>
<div className="flex h-[92vh] flex-col items-start gap-5 bg-gray-200 p-8 text-black">
<p className="text-2xl font-semibold">TRAFFIC VIOLATIONS</p>
<div className="flex gap-2">
<p>now {currentStream}</p>
{stream_dummy.map((stream, index) => (
<Button
key={stream.id}
onClick={() => {
setActiveStream(stream.id)
startDetection(index)
}}
className={`cursor-pointer ${currentStream == stream.id ? "border-2 bg-gray-50 p-2" : ""}`}
>
DS{index + 1}
</Button>
))}
</div>
{loading ? (
<div className="w-screen rounded-lg md:w-full xl:w-180">
<Spinner className="h-50 w-50" />
</div>
) : (
imageSrc && (
<img
src={imageSrc}
alt="Realtime"
className="w-screen rounded-lg md:w-full xl:w-180"
/>
)
)}
<Button
variant="ghost"
className="h-12 bg-white px-10 text-xl shadow-sm"
>
{plate || "Belum ada plat"}
</Button>
<p className="text-2xl font-semibold">VIOLATIONS</p>
<p>
{plate
? `Plat ${plate} terdeteksi dengan confidence ${confidence}`
: "Menunggu deteksi..."}
</p>
</div>
<Drawer direction="right">
<DrawerTrigger asChild title="">
<Button className="fixed top-1/2 right-0 h-20 w-6 -translate-y-1/2 cursor-pointer rounded-none rounded-l-md bg-white">
<ChevronLeft />
</Button>
</DrawerTrigger>
<DrawerContent>
<div className="no-scrollbar space-y-3 overflow-y-auto bg-[#CBCBCBCC] px-4 py-4 text-black">
<div className="rounded-lg bg-white p-3 text-lg">
Total Image: {history[activeStream]?.length}
</div>{" "}
{history[currentStream]?.map((item, index) => (
<div key={index} className="rounded-lg bg-white p-3 text-lg">
<Image
src={item.imgThumb}
width={1280}
height={960}
alt={"image" + index}
className="rounded-lg"
/>
<p>Plate : {item.plate}</p>
<p>Confidence: {item.confidence}</p>
</div>
))}
</div>
</DrawerContent>
</Drawer>
</div>
)
}