190 lines
5.6 KiB
TypeScript
190 lines
5.6 KiB
TypeScript
"use client"
|
|
|
|
import { ArrowLeftIcon, ChevronLeft } from "lucide-react"
|
|
import Link from "next/link"
|
|
import { useRouter } from "next/navigation"
|
|
import {
|
|
Drawer,
|
|
DrawerClose,
|
|
DrawerContent,
|
|
DrawerDescription,
|
|
DrawerFooter,
|
|
DrawerHeader,
|
|
DrawerTitle,
|
|
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"
|
|
|
|
const dummy = [
|
|
{ url: "/sample-1.jpg" },
|
|
{ url: "/sample-2.jpg" },
|
|
{ url: "/sample-3.jpg" },
|
|
{ url: "/sample-4.jpg" },
|
|
{ url: "/sample-1.jpg" },
|
|
{ url: "/sample-2.jpg" },
|
|
{ url: "/sample-3.jpg" },
|
|
{ url: "/sample-4.jpg" },
|
|
{ url: "/sample-1.jpg" },
|
|
]
|
|
|
|
const stream_dummy = [
|
|
"/app/static/uploads/ds1.mp4",
|
|
"/app/static/uploads/ds2.mp4",
|
|
"/app/static/uploads/ds3.mp4",
|
|
"/app/static/uploads/ds4.mp4",
|
|
]
|
|
|
|
export default function Etle() {
|
|
const router = useRouter()
|
|
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 [history, setHistory] = useState<string[]>([])
|
|
|
|
const startDetection = (index: number) => {
|
|
const stream = stream_dummy[index]
|
|
|
|
socket.emit("stop_detection")
|
|
socket.emit("start_detection", {
|
|
stream_url: stream,
|
|
})
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (!socket.connected) {
|
|
socket.connect()
|
|
}
|
|
|
|
socket.on("connected", (data) => {
|
|
setSessionId(data.session_id)
|
|
socket.emit("start_detection", {
|
|
stream_url: "/app/static/uploads/ds1.mp4",
|
|
})
|
|
})
|
|
|
|
socket.on("video_frame", (data) => {
|
|
const thumb = `data:image/jpeg;base64,${data.frame}`
|
|
setImageSrc(thumb)
|
|
setHistory((prev) => [thumb, ...prev].slice(0, 20))
|
|
})
|
|
|
|
socket.on("plate_detected", (data) => {
|
|
setPlate(data.plate_text)
|
|
setConfidence(data.confidence ?? null)
|
|
|
|
const thumb = `data:image/jpeg;base64,${data.frame_thumb}`
|
|
setHistory((prev) => [thumb, ...prev].slice(0, 20))
|
|
})
|
|
|
|
// socket.on("detection_stopped", (data) => {
|
|
// console.log("Total:", data.total)
|
|
// })
|
|
|
|
socket.on("error", (data) => {
|
|
console.error(data.message)
|
|
})
|
|
|
|
return () => {
|
|
socket.emit("stop_detection")
|
|
socket.removeAllListeners()
|
|
socket.disconnect()
|
|
}
|
|
}, [])
|
|
|
|
// const startDetection = () => {
|
|
// socket.emit("start_detection", {
|
|
// stream_url: "/app/static/uploads/ds1",
|
|
// })
|
|
// }
|
|
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>
|
|
{/* <Button onClick={startDetection}>START DETECTION</Button> */}
|
|
<div className="flex gap-2">
|
|
{stream_dummy.map((stream, index) => (
|
|
<Button key={index} onClick={() => startDetection(index)}>
|
|
DS{index + 1}
|
|
</Button>
|
|
))}
|
|
</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>
|
|
<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.length}
|
|
</div>{" "}
|
|
{history.map((item, index) => (
|
|
<div key={index} className="rounded-lg bg-white p-3 text-lg">
|
|
<Image
|
|
src={item}
|
|
width={1280}
|
|
height={960}
|
|
alt={"image" + index}
|
|
className="rounded-lg"
|
|
/>
|
|
</div>
|
|
))}
|
|
{history.map((img, index) => (
|
|
<div key={index} className="rounded-lg bg-white p-3">
|
|
<img src={img} className="rounded-lg" />
|
|
</div>
|
|
))}
|
|
</div>
|
|
</DrawerContent>
|
|
</Drawer>
|
|
{/* <Sheet>
|
|
<SheetTrigger asChild>
|
|
<Button className="fixed top-1/2 right-0 h-20 w-6 -translate-y-1/2 rounded-l-md">
|
|
<ChevronLeft />
|
|
</Button>
|
|
</SheetTrigger>
|
|
<SheetContent>
|
|
<div className="grid flex-1 auto-rows-min gap-6 bg-[#CBCBCBCC] px-4 py-10 text-black">
|
|
<div className="flex flex-col gap-4">
|
|
<div className="rounded-lg bg-white p-3 text-lg">
|
|
Total Image: 10
|
|
</div>{" "}
|
|
</div>
|
|
</div>
|
|
</SheetContent>
|
|
</Sheet> */}
|
|
</div>
|
|
)
|
|
}
|