import { useState, useEffect, useRef } from "react";
/* ═══════════════════════════════════════════
SIMPLO COURT — DUAL INTERFACE
1. Mobile Booking App (Penyewa)
2. Tablet Room Management Display (Venue)
═══════════════════════════════════════════ */
const BRAND = {
primary: "#004AAD",
primaryDark: "#00296B",
primaryMid: "#1B5FCC",
primaryLight: "#4B8BF5",
primaryPale: "#EBF2FF",
primaryTint: "#F6F9FF",
accent: "#00D4A1",
accentDark: "#00B087",
accentPale: "#E6FBF5",
orange: "#FF7A3D",
orangePale: "#FFF2EC",
red: "#EF4444",
redPale: "#FEF2F2",
yellow: "#F59E0B",
yellowPale: "#FFFBEB",
dark: "#0A1628",
darkCard: "#111D33",
darkBorder: "#1E3050",
darkMuted: "#3A5070",
darkText: "#8BA3C7",
text: "#1A2340",
textSoft: "#5B6B8A",
textMuted: "#94A3BF",
border: "#E2E8F4",
bg: "#F4F6FB",
white: "#FFFFFF",
};
const COURTS = [
{ id: 1, name: "Court A", type: "Full Court", capacity: 10, priceWeekday: 150000, priceWeekend: 200000, amenities: ["Lighting", "Scoreboard", "AC", "Sound System"], image: "🏀" },
{ id: 2, name: "Court B", type: "Half Court", capacity: 6, priceWeekday: 100000, priceWeekend: 130000, amenities: ["Lighting", "AC"], image: "🏀" },
{ id: 3, name: "Court C", type: "Full Court", capacity: 10, priceWeekday: 150000, priceWeekend: 200000, amenities: ["Lighting", "Scoreboard", "AC", "Sound System", "CCTV"], image: "🏀" },
];
const TIMESLOTS = [
{ time: "06:00", label: "06:00" }, { time: "07:00", label: "07:00" },
{ time: "08:00", label: "08:00" }, { time: "09:00", label: "09:00" },
{ time: "10:00", label: "10:00" }, { time: "11:00", label: "11:00" },
{ time: "12:00", label: "12:00" }, { time: "13:00", label: "13:00" },
{ time: "14:00", label: "14:00" }, { time: "15:00", label: "15:00" },
{ time: "16:00", label: "16:00" }, { time: "17:00", label: "17:00" },
{ time: "18:00", label: "18:00" }, { time: "19:00", label: "19:00" },
{ time: "20:00", label: "20:00" }, { time: "21:00", label: "21:00" },
{ time: "22:00", label: "22:00" },
];
const BOOKED_SLOTS = {
1: ["08:00", "09:00", "14:00", "15:00", "19:00", "20:00", "21:00"],
2: ["10:00", "11:00", "18:00", "19:00"],
3: ["07:00", "08:00", "09:00", "16:00", "17:00", "20:00", "21:00", "22:00"],
};
const TABLET_SCHEDULE = [
{ court: "Court A", time: "08:00 - 10:00", tenant: "Raka & Friends", status: "completed", players: 8 },
{ court: "Court A", time: "14:00 - 16:00", tenant: "BasketID Club", status: "active", players: 10 },
{ court: "Court A", time: "19:00 - 22:00", tenant: "Night Hoopers", status: "upcoming", players: 10 },
{ court: "Court B", time: "10:00 - 12:00", tenant: "Kevin Santoso", status: "completed", players: 4 },
{ court: "Court B", time: "18:00 - 20:00", tenant: "Pickup Game", status: "upcoming", players: 6 },
{ court: "Court C", time: "07:00 - 10:00", tenant: "Morning Drills", status: "completed", players: 6 },
{ court: "Court C", time: "16:00 - 18:00", tenant: "SMA 5 Training", status: "active", players: 10 },
{ court: "Court C", time: "20:00 - 23:00", tenant: "Pro League Practice", status: "upcoming", players: 10 },
];
const PAYMENT_METHODS = [
{ id: "qris", name: "QRIS", icon: "📱", desc: "Scan & pay" },
{ id: "va_bca", name: "VA BCA", icon: "🏦", desc: "Transfer BCA" },
{ id: "va_mandiri", name: "VA Mandiri", icon: "🏦", desc: "Transfer Mandiri" },
{ id: "gopay", name: "GoPay", icon: "💚", desc: "Gojek wallet" },
{ id: "ovo", name: "OVO", icon: "💜", desc: "OVO wallet" },
{ id: "dana", name: "DANA", icon: "💙", desc: "DANA wallet" },
];
const formatRp = (n) => `Rp ${n.toLocaleString("id-ID")}`;
const getDayName = (d) => ["Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"][d.getDay()];
const isWeekend = (d) => d.getDay() === 0 || d.getDay() === 6;
/* ═══════════════════════════════════════════
MOBILE BOOKING APP
═══════════════════════════════════════════ */
function MobileBookingApp() {
const [step, setStep] = useState(0); // 0=courts, 1=detail, 2=timeslot, 3=confirm, 4=payment, 5=success
const [selectedCourt, setSelectedCourt] = useState(null);
const [selectedDate, setSelectedDate] = useState(new Date());
const [selectedSlots, setSelectedSlots] = useState([]);
const [paymentMethod, setPaymentMethod] = useState(null);
const [isProcessing, setIsProcessing] = useState(false);
const [animIn, setAnimIn] = useState(true);
const dates = Array.from({ length: 7 }, (_, i) => {
const d = new Date();
d.setDate(d.getDate() + i);
return d;
});
const goStep = (s) => {
setAnimIn(false);
setTimeout(() => { setStep(s); setAnimIn(true); }, 150);
};
const toggleSlot = (time) => {
if (BOOKED_SLOTS[selectedCourt?.id]?.includes(time)) return;
setSelectedSlots((prev) =>
prev.includes(time) ? prev.filter((t) => t !== time) : [...prev, time].sort()
);
};
const court = selectedCourt;
const pricePerHour = court ? (isWeekend(selectedDate) ? court.priceWeekend : court.priceWeekday) : 0;
const totalPrice = selectedSlots.length * pricePerHour;
const serviceFee = Math.round(totalPrice * 0.03);
const handlePay = () => {
setIsProcessing(true);
setTimeout(() => { setIsProcessing(false); goStep(5); }, 2000);
};
return (
{/* Status Bar */}
= 4 ? BRAND.primaryDark : BRAND.white, padding: "8px 24px 6px",
display: "flex", justifyContent: "space-between", fontSize: 12, fontWeight: 600,
color: step >= 4 ? "#fff" : BRAND.text, flexShrink: 0,
}}>
9:41
●●●●
WiFi
🔋
{/* Content */}
{/* ── STEP 0: Court Listing ── */}
{step === 0 && (
Selamat datang 👋
Arena Basket
👤
{/* Search */}
🔍
Cari lapangan...
{/* Date Selector */}
{dates.map((d, i) => {
const isSelected = d.toDateString() === selectedDate.toDateString();
return (
);
})}
{/* Courts */}
Pilih Lapangan
{COURTS.map((c) => {
const booked = BOOKED_SLOTS[c.id]?.length || 0;
const available = TIMESLOTS.length - booked;
return (
);
})}
)}
{/* ── STEP 1: Court Detail ── */}
{step === 1 && court && (
{court.image}
{court.name}
{court.type} · Max {court.capacity} pemain
WEEKDAY
{formatRp(court.priceWeekday)}
/jam
WEEKEND
{formatRp(court.priceWeekend)}
/jam
Fasilitas
{court.amenities.map((a) => (
{a === "Lighting" ? "💡" : a === "AC" ? "❄️" : a === "Scoreboard" ? "📊" : a === "Sound System" ? "🔊" : a === "CCTV" ? "📹" : "✅"}
{a}
))}
Ulasan
{[
{ name: "Raka S.", stars: 5, text: "Lapangan bersih, lighting bagus banget!" },
{ name: "Deni P.", stars: 4, text: "AC dingin, cuma parkir agak sempit" },
].map((r, i) => (
{r.name}
{"⭐".repeat(r.stars)}
{r.text}
))}
{/* CTA */}
)}
{/* ── STEP 2: Time Slot Selection ── */}
{step === 2 && court && (
{/* Header */}
{court.name}
Pilih jam yang tersedia
{/* Date Selector */}
{dates.map((d, i) => {
const sel = d.toDateString() === selectedDate.toDateString();
return (
);
})}
{isWeekend(selectedDate) && (
🔥 Weekend rate: {formatRp(court.priceWeekend)}/jam
)}
{/* Legend */}
{[
{ color: BRAND.accent, label: "Tersedia" },
{ color: BRAND.primary, label: "Dipilih" },
{ color: "#D1D5DB", label: "Terisi" },
].map((l) => (
))}
{/* Time Slots Grid */}
{TIMESLOTS.map((slot) => {
const isBooked = BOOKED_SLOTS[court.id]?.includes(slot.time);
const isSelected = selectedSlots.includes(slot.time);
return (
);
})}
{/* Bottom Bar */}
{selectedSlots.length > 0 && (
{selectedSlots.length} jam dipilih
{selectedSlots[0]} - {parseInt(selectedSlots[selectedSlots.length-1]) + 1}:00
{formatRp(totalPrice)}
)}
)}
{/* ── STEP 3: Confirmation & Payment ── */}
{step === 3 && court && (
Konfirmasi Booking
{/* Summary Card */}
🏀
{court.name}
{court.type}
{[
{ label: "Tanggal", value: selectedDate.toLocaleDateString("id-ID", { weekday: "long", day: "numeric", month: "long", year: "numeric" }) },
{ label: "Waktu", value: `${selectedSlots[0]} - ${parseInt(selectedSlots[selectedSlots.length-1]) + 1}:00` },
{ label: "Durasi", value: `${selectedSlots.length} jam` },
].map((r, i) => (
{r.label}
{r.value}
))}
{/* Payment Method */}
Metode Pembayaran
{PAYMENT_METHODS.map((pm) => (
))}
{/* Price Breakdown */}
Rincian Harga
{[
{ label: `${selectedSlots.length}x ${formatRp(pricePerHour)}`, value: formatRp(totalPrice) },
{ label: "Biaya layanan (3%)", value: formatRp(serviceFee) },
].map((r, i) => (
{r.label}{r.value}
))}
Total
{formatRp(totalPrice + serviceFee)}
{/* Pay Button */}
)}
{/* ── STEP 4: Processing ── */}
{step === 4 && (
⏳
Memproses Pembayaran...
Mohon tunggu sebentar
)}
{/* ── STEP 5: Success ── */}
{step === 5 && court && (
✅
Booking Berhasil!
Kode booking kamu sudah dikirim via WhatsApp
{/* QR Code area */}
{Array.from({ length: 49 }).map((_, i) => (
0.4 ? BRAND.primaryDark : "transparent",
}} />
))}
SC-2026-0847
Tunjukkan QR ini saat check-in di venue
{/* Booking summary */}
{[
{ label: "Lapangan", value: court.name },
{ label: "Tanggal", value: selectedDate.toLocaleDateString("id-ID", { weekday: "short", day: "numeric", month: "short" }) },
{ label: "Waktu", value: `${selectedSlots[0]} - ${parseInt(selectedSlots[selectedSlots.length-1]) + 1}:00` },
{ label: "Total", value: formatRp(totalPrice + serviceFee), bold: true },
].map((r, i) => (
{r.label}
{r.value}
))}
)}
{/* Bottom Navigation (step 0 only) */}
{step === 0 && (
{[
{ icon: "🏠", label: "Home", active: true },
{ icon: "📅", label: "Booking", active: false },
{ icon: "🔔", label: "Notifikasi", active: false },
{ icon: "👤", label: "Profil", active: false },
].map((nav) => (
))}
)}
);
}
/* ═══════════════════════════════════════════
TABLET ROOM MANAGEMENT DISPLAY
═══════════════════════════════════════════ */
function TabletRoomDisplay() {
const [currentTime, setCurrentTime] = useState(new Date());
const [selectedCourt, setSelectedCourt] = useState("all");
useEffect(() => {
const t = setInterval(() => setCurrentTime(new Date()), 1000);
return () => clearInterval(t);
}, []);
const courtStatuses = [
{
name: "Court A", status: "active", tenant: "BasketID Club", timeLeft: "1j 23m",
progress: 58, devices: { lights: true, ac: true, scoreboard: true, door: "locked" },
},
{
name: "Court B", status: "idle", tenant: null, timeLeft: null,
progress: 0, devices: { lights: false, ac: false, scoreboard: false, door: "locked" },
},
{
name: "Court C", status: "active", tenant: "SMA 5 Training", timeLeft: "47m",
progress: 72, devices: { lights: true, ac: true, scoreboard: false, door: "unlocked" },
},
];
const filteredSchedule = selectedCourt === "all" ? TABLET_SCHEDULE : TABLET_SCHEDULE.filter(s => s.court === selectedCourt);
const statusColor = (s) => s === "active" ? BRAND.accent : s === "upcoming" ? BRAND.primary : BRAND.textMuted;
const statusBg = (s) => s === "active" ? BRAND.accentPale : s === "upcoming" ? BRAND.primaryPale : "#F3F4F6";
const statusLabel = (s) => s === "active" ? "BERLANGSUNG" : s === "upcoming" ? "AKAN DATANG" : "SELESAI";
return (
{/* Top Bar */}
SIMPLO
Arena Basket — Room Management
Live Court Status & Schedule
{currentTime.toLocaleTimeString("id-ID", { hour: "2-digit", minute: "2-digit", second: "2-digit" })}
{currentTime.toLocaleDateString("id-ID", { weekday: "long", day: "numeric", month: "long", year: "numeric" })}
{/* Main Content */}
{/* Left: Court Status Cards */}
Status Lapangan
{courtStatuses.map((c) => (
{c.status === "active" && (
)}
{c.name}
{c.status === "active" ? "● AKTIF" : "○ IDLE"}
{c.status === "active" ? (
<>
{c.tenant} — {c.timeLeft} tersisa
{/* Progress bar */}
>
) : (
Tidak ada sesi aktif
)}
{/* IoT Device Status */}
{[
{ label: "Lampu", on: c.devices.lights, icon: "💡" },
{ label: "AC", on: c.devices.ac, icon: "❄️" },
{ label: "Score", on: c.devices.scoreboard, icon: "📊" },
{ label: "Pintu", on: c.devices.door === "unlocked", icon: c.devices.door === "unlocked" ? "🔓" : "🔒" },
].map((d) => (
))}
))}
{/* Quick Stats */}
Hari Ini
{[
{ label: "Total Booking", value: "8", color: BRAND.primary },
{ label: "Pendapatan", value: "Rp 2.1jt", color: BRAND.accent },
{ label: "Occupancy", value: "73%", color: BRAND.orange },
{ label: "Check-in Rate", value: "100%", color: BRAND.accent },
].map((s) => (
))}
{/* Right: Schedule Timeline */}
Jadwal Hari Ini
{["all", "Court A", "Court B", "Court C"].map((f) => (
))}
{/* Schedule Cards */}
{filteredSchedule.map((s, i) => (
{s.status === "active" && (
)}
{/* Time */}
{s.time.split(" - ")[0]}
sampai
{s.time.split(" - ")[1]}
{/* Info */}
{s.tenant}
{statusLabel(s.status)}
{s.court} · {s.players} pemain
{/* Actions */}
{s.status === "active" && (
)}
{s.status === "upcoming" && (
)}
{s.status === "completed" && (
✓ Done
)}
))}
{/* Timeline Bar Visual */}
Timeline Visual — Occupancy
{["Court A", "Court B", "Court C"].map((court) => {
const bookings = TABLET_SCHEDULE.filter(s => s.court === court);
return (
{court}
{bookings.map((b, i) => {
const startH = parseInt(b.time.split(" - ")[0]);
const endH = parseInt(b.time.split(" - ")[1]);
const left = ((startH - 6) / 18) * 100;
const width = ((endH - startH) / 18) * 100;
return (
{b.tenant}
);
})}
{/* Current time indicator */}
{(() => {
const nowH = 14.5; // simulated
const pos = ((nowH - 6) / 18) * 100;
return (
);
})()}
);
})}
{/* Time labels */}
{["06", "09", "12", "15", "18", "21", "00"].map((h, i) => (
{h}:00
))}
);
}
/* ═══════════════════════════════════════════
MAIN: DUAL INTERFACE VIEWER
═══════════════════════════════════════════ */
export default function SimploCourtDualUI() {
const [activeView, setActiveView] = useState("both");
return (
{/* Title */}
SIMPLO COURT
UI/UX PROTOTYPE
Dual Interface Design
Mobile Booking App (Penyewa) + Tablet Room Management (Venue)
{/* View Toggle */}
{[
{ id: "both", label: "📱 + 🖥 Both" },
{ id: "mobile", label: "📱 Mobile Only" },
{ id: "tablet", label: "🖥 Tablet Only" },
].map((v) => (
))}
{/* Devices */}
{(activeView === "both" || activeView === "mobile") && (
📱 Mobile App — Penyewa
Klik untuk navigate antar halaman
)}
{(activeView === "both" || activeView === "tablet") && (
🖥 Tablet Display — Di Venue
Tampilan real-time di samping court / resepsionis
)}
);
}