Frontend
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user