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) => (
{l.label}
))}
{/* 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) => (
{nav.icon}
{nav.label}
))}
)}
); } /* ═══════════════════════════════════════════ 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) => (
{d.icon}
{d.label}
))}
))} {/* 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) => (
{s.label}
{s.value}
))}
{/* 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
)}
); }