208 lines
7.3 KiB
TypeScript
208 lines
7.3 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { Search, Filter, Lightbulb, Projector, Wind } from "lucide-react";
|
|
|
|
// --- Tipe Data Mock ---
|
|
type Room = {
|
|
id: number;
|
|
name: string;
|
|
category: "Teori" | "Laboratorium";
|
|
capacity: number;
|
|
status: "Available" | "In Use" | "Maintenance";
|
|
devices: {
|
|
ac: boolean;
|
|
lamp: boolean;
|
|
projector: boolean;
|
|
};
|
|
floor: string;
|
|
};
|
|
|
|
// --- Data Dummy Ruangan (Mock Data) ---
|
|
const initialRooms: Room[] = [
|
|
{
|
|
id: 1,
|
|
name: "Ruang T-301",
|
|
category: "Teori",
|
|
capacity: 40,
|
|
status: "Available",
|
|
devices: { ac: false, lamp: false, projector: false },
|
|
floor: "Lantai 3",
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "Ruang T-302",
|
|
category: "Teori",
|
|
capacity: 40,
|
|
status: "In Use",
|
|
devices: { ac: true, lamp: true, projector: true }, // Sedang dipakai
|
|
floor: "Lantai 3",
|
|
},
|
|
{
|
|
id: 3,
|
|
name: "Lab. Sistem Kendali",
|
|
category: "Laboratorium",
|
|
capacity: 20,
|
|
status: "Available",
|
|
devices: { ac: false, lamp: true, projector: false }, // Mungkin lampunya lupa dimatikan?
|
|
floor: "Lantai 2",
|
|
},
|
|
{
|
|
id: 4,
|
|
name: "Lab. IoT & Embedded",
|
|
category: "Laboratorium",
|
|
capacity: 25,
|
|
status: "In Use",
|
|
devices: { ac: true, lamp: true, projector: false },
|
|
floor: "Lantai 2",
|
|
},
|
|
{
|
|
id: 5,
|
|
name: "Ruang Sidang TA",
|
|
category: "Teori",
|
|
capacity: 15,
|
|
status: "Maintenance",
|
|
devices: { ac: false, lamp: false, projector: false },
|
|
floor: "Lantai 1",
|
|
},
|
|
];
|
|
|
|
export default function RoomsPage() {
|
|
const [rooms] = useState<Room[]>(initialRooms);
|
|
const [filterStatus, setFilterStatus] = useState<string>("All");
|
|
const [searchQuery, setSearchQuery] = useState("");
|
|
|
|
// --- Logika Filtering ---
|
|
const filteredRooms = rooms.filter((room) => {
|
|
// Filter berdasarkan Tab Status (All / Available / In Use)
|
|
const matchStatus =
|
|
filterStatus === "All" || room.status === filterStatus;
|
|
|
|
// Filter berdasarkan Search Box (Nama Ruangan)
|
|
const matchSearch = room.name
|
|
.toLowerCase()
|
|
.includes(searchQuery.toLowerCase());
|
|
|
|
return matchStatus && matchSearch;
|
|
});
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* --- HEADER & CONTROLS --- */}
|
|
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between bg-white p-6 rounded-xl shadow-sm border border-gray-200">
|
|
<div>
|
|
<h2 className="text-2xl font-bold text-gray-800">Daftar Ruangan</h2>
|
|
<p className="text-sm text-gray-500">
|
|
Monitoring status ruangan dan perangkat IoT.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="flex flex-col sm:flex-row gap-3">
|
|
{/* Search Box */}
|
|
<div className="relative">
|
|
<input
|
|
type="text"
|
|
placeholder="Cari ruangan..."
|
|
value={searchQuery}
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
className="pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-yellow-400 w-full sm:w-64"
|
|
/>
|
|
<Search className="absolute left-3 top-2.5 text-gray-400" size={18} />
|
|
</div>
|
|
|
|
{/* Filter Dropdown (Sederhana) */}
|
|
<div className="flex items-center gap-2 bg-gray-50 px-3 rounded-lg border border-gray-200">
|
|
<Filter size={18} className="text-gray-500" />
|
|
<select
|
|
value={filterStatus}
|
|
onChange={(e) => setFilterStatus(e.target.value)}
|
|
className="bg-transparent py-2 outline-none text-sm font-medium text-gray-700 cursor-pointer"
|
|
>
|
|
<option value="All">Semua Status</option>
|
|
<option value="Available">Tersedia (Available)</option>
|
|
<option value="In Use">Sedang Dipakai (In Use)</option>
|
|
<option value="Maintenance">Perbaikan</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* --- ROOM GRID --- */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
|
{filteredRooms.map((room) => (
|
|
<div
|
|
key={room.id}
|
|
className={`relative overflow-hidden rounded-xl border bg-white p-6 shadow-sm hover:shadow-md transition-shadow duration-300
|
|
${room.status === "In Use" ? "border-blue-200" :
|
|
room.status === "Available" ? "border-green-200" : "border-gray-200"}`}
|
|
>
|
|
{/* Status Badge di Pojok Kanan Atas */}
|
|
<span
|
|
className={`absolute top-4 right-4 px-3 py-1 rounded-full text-xs font-semibold
|
|
${
|
|
room.status === "Available"
|
|
? "bg-green-100 text-green-700"
|
|
: room.status === "In Use"
|
|
? "bg-blue-100 text-blue-700"
|
|
: "bg-gray-100 text-gray-600"
|
|
}`}
|
|
>
|
|
{room.status}
|
|
</span>
|
|
|
|
{/* Info Utama Ruangan */}
|
|
<div className="mb-4">
|
|
<h3 className="text-lg font-bold text-gray-800">{room.name}</h3>
|
|
<p className="text-xs text-gray-500 uppercase tracking-wider mt-1">
|
|
{room.category} • {room.floor}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2 mb-6">
|
|
<span className="text-sm text-gray-600 bg-gray-100 px-2 py-1 rounded">
|
|
Kapasitas: {room.capacity} Org
|
|
</span>
|
|
</div>
|
|
|
|
{/* Status Perangkat IoT (Visualisasi) */}
|
|
<div className="pt-4 border-t border-gray-100">
|
|
<p className="text-xs text-gray-400 font-medium mb-3">STATUS PERANGKAT</p>
|
|
<div className="flex justify-between gap-2">
|
|
|
|
{/* AC Status */}
|
|
<div className={`flex flex-1 flex-col items-center p-2 rounded-lg text-xs font-medium transition-colors
|
|
${room.devices.ac ? "bg-blue-50 text-blue-600" : "bg-gray-50 text-gray-400"}`}>
|
|
<Wind size={20} className="mb-1" />
|
|
AC {room.devices.ac ? "ON" : "OFF"}
|
|
</div>
|
|
|
|
{/* Lampu Status */}
|
|
<div className={`flex flex-1 flex-col items-center p-2 rounded-lg text-xs font-medium transition-colors
|
|
${room.devices.lamp ? "bg-yellow-50 text-yellow-600" : "bg-gray-50 text-gray-400"}`}>
|
|
<Lightbulb size={20} className="mb-1" />
|
|
Light {room.devices.lamp ? "ON" : "OFF"}
|
|
</div>
|
|
|
|
{/* Proyektor Status */}
|
|
<div className={`flex flex-1 flex-col items-center p-2 rounded-lg text-xs font-medium transition-colors
|
|
${room.devices.projector ? "bg-purple-50 text-purple-600" : "bg-gray-50 text-gray-400"}`}>
|
|
<Projector size={20} className="mb-1" />
|
|
LCD {room.devices.projector ? "ON" : "OFF"}
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Empty State jika filter tidak menemukan hasil */}
|
|
{filteredRooms.length === 0 && (
|
|
<div className="text-center py-20 bg-gray-50 rounded-xl border-dashed border-2 border-gray-200">
|
|
<p className="text-gray-500">Tidak ada ruangan yang cocok dengan filter Anda.</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
} |