import React, { useState, useMemo } from 'react';
import {
Utensils, Wallet, Users, Store, List,
ShoppingCart, Plus, Minus, Check, LogOut,
AlertCircle, ChevronRight, Receipt, Clock
} from 'lucide-react';
// --- INITIAL MOCK DATA (Simulasi Database) ---
const initialRestaurants = [
{ id: 1, name: 'Warteg Bahari Kharisma' },
{ id: 2, name: 'Ayam Geprek Bensu' },
{ id: 3, name: 'Soto Lamongan Cak Har' }
];
const initialMenus = [
{ id: 1, restaurant_id: 1, name: 'Nasi Telur Dadar + Orek', price: 15000 },
{ id: 2, restaurant_id: 1, name: 'Nasi Ayam Goreng', price: 18000 },
{ id: 3, restaurant_id: 1, name: 'Es Teh Manis', price: 4000 },
{ id: 4, restaurant_id: 2, name: 'Paket Geprek Leleh', price: 25000 },
{ id: 5, restaurant_id: 2, name: 'Jamur Crispy', price: 10000 },
{ id: 6, restaurant_id: 3, name: 'Soto Ayam Campur', price: 18000 },
{ id: 7, restaurant_id: 3, name: 'Soto Daging Pisah', price: 22000 },
];
const initialOrders = [
{
id: 1,
userName: "Mbak Rini",
total: 19000,
items: [
{ menuId: 1, name: 'Nasi Telur Dadar + Orek', price: 15000, qty: 1, notes: "Oreknya basah ya mas" },
{ menuId: 3, name: 'Es Teh Manis', price: 4000, qty: 1, notes: "" },
]
}
];
// Helper: Format Rupiah
const formatRp = (num) => 'Rp ' + num.toLocaleString('id-ID');
// ==========================================
// MAIN APP COMPONENT
// ==========================================
export default function LunchApp() {
// Global States
const [currentUser, setCurrentUser] = useState(null); // null = belum login
// "Database" States
const [restaurants, setRestaurants] = useState(initialRestaurants);
const [menus, setMenus] = useState(initialMenus);
const [orders, setOrders] = useState(initialOrders);
const [session, setSession] = useState({
isOpen: false,
openRestoIds: [1, 2], // Default resto yang dipilih Admin
endTime: '11:30',
bankAccount: 'BCA 123456789 a.n Bapak OB',
rejectMessage: 'Moon maaf, jam order udah lewat, silakan hubungi saya via wa... tapi please jangan order yang jauh2 ya ðŸ˜'
});
// Handle Login Logic
const handleLogin = (name, phone) => {
// Sesuai PRD: Role Admin/OB jika login khusus.
// Di sini kita buat trigger rahasia: Jika no HP '0000', maka dia OB.
const role = phone === '0000' ? 'admin' : 'user';
setCurrentUser({ name, phone, role });
};
const handleLogout = () => {
setCurrentUser(null);
};
const addOrder = (orderData) => {
setOrders([...orders, { id: Date.now(), ...orderData }]);
};
return (
{/* Mobile Wrapper */}
{!currentUser ? (
) : currentUser.role === 'admin' ? (
) : (
)}
);
}
// ==========================================
// LOGIN SCREEN
// ==========================================
function LoginScreen({ onLogin }) {
const [name, setName] = useState('');
const [phone, setPhone] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (name.trim() && phone.trim()) {
onLogin(name, phone);
}
};
return (
MakanBang!
Pesan makan siang kantor tanpa ribet, tanpa pusing ngitung.
Rahasia Testing: Masukkan No. HP 0000 untuk login sebagai OB (Admin). Selain itu, Anda masuk sebagai Karyawan.
);
}
// ==========================================
// ADMIN DASHBOARD & VIEWS
// ==========================================
function AdminDashboard({ onLogout, restaurants, setRestaurants, menus, setMenus, session, setSession, orders }) {
const [activeTab, setActiveTab] = useState('lapak'); // lapak | master | rekap
return (
<>
Panel OB
{activeTab === 'lapak' &&
}
{activeTab === 'master' &&
}
{activeTab === 'rekap' &&
}
{/* Admin Bottom Navigation */}
>
);
}
function AdminLapak({ session, setSession, restaurants }) {
const [formData, setFormData] = useState(session);
const handleToggleResto = (id) => {
setFormData(prev => ({
...prev,
openRestoIds: prev.openRestoIds.includes(id)
? prev.openRestoIds.filter(rId => rId !== id)
: [...prev.openRestoIds, id]
}));
};
const handleSave = () => {
if (formData.openRestoIds.length === 0) return alert('Pilih minimal 1 restoran!');
setSession({ ...formData, isOpen: true });
};
if (session.isOpen) {
return (
Lapak Sedang Buka!
Karyawan sekarang bisa melihat menu dan melakukan pemesanan.
);
}
return (
Setup Lapak Hari Ini
Pilih restoran dan atur batas waktu pesanan.
);
}
function AdminMaster({ restaurants, setRestaurants, menus, setMenus }) {
const [newRestoName, setNewRestoName] = useState('');
const [activeAddMenuRestoId, setActiveAddMenuRestoId] = useState(null);
const [newMenu, setNewMenu] = useState({ name: '', price: '' });
const handleAddResto = () => {
if (newRestoName.trim()) {
setRestaurants([...restaurants, { id: Date.now(), name: newRestoName }]);
setNewRestoName('');
}
};
const handleAddMenu = (restoId) => {
if (newMenu.name && newMenu.price) {
setMenus([...menus, { id: Date.now(), restaurant_id: restoId, name: newMenu.name, price: parseInt(newMenu.price) }]);
setNewMenu({ name: '', price: '' });
setActiveAddMenuRestoId(null);
}
};
return (
{restaurants.map(resto => {
const restoMenus = menus.filter(m => m.restaurant_id === resto.id);
return (
{resto.name}
{restoMenus.length === 0 ?
Belum ada menu
: null}
{restoMenus.map(menu => (
{menu.name}
{formatRp(menu.price)}
))}
{activeAddMenuRestoId === resto.id ? (
) : (
)}
);
})}
);
}
function AdminRekap({ orders, restaurants, menus }) {
const [view, setView] = useState('orang'); // orang | resto | menu
// Compute Data for Views
const rekapResto = useMemo(() => {
return restaurants.map(resto => {
const menuIds = menus.filter(m => m.restaurant_id === resto.id).map(m => m.id);
let totalQty = 0;
let totalUang = 0;
let items = [];
orders.forEach(order => {
order.items.forEach(item => {
if (menuIds.includes(item.menuId)) {
totalQty += item.qty;
totalUang += item.qty * item.price;
items.push({ ...item, userName: order.userName });
}
});
});
return { ...resto, totalQty, totalUang, items };
}).filter(r => r.totalQty > 0);
}, [orders, restaurants, menus]);
const rekapMenu = useMemo(() => {
return menus.map(menu => {
let totalQty = 0;
orders.forEach(order => {
order.items.forEach(item => {
if (item.menuId === menu.id) totalQty += item.qty;
});
});
return { ...menu, totalQty };
}).filter(m => m.totalQty > 0);
}, [orders, menus]);
const totalAllMoney = orders.reduce((sum, o) => sum + o.total, 0);
return (
Total Pemasukan Makanan
{formatRp(totalAllMoney)}
{['orang', 'resto', 'menu'].map(v => (
))}
{view === 'orang' && orders.length === 0 &&
}
{view === 'orang' && orders.map(order => (
{order.userName}
{formatRp(order.total)}
{order.items.map((item, i) => (
-
{item.qty}x {item.name}
{item.notes && "{item.notes}"
}
))}
))}
{view === 'resto' && rekapResto.length === 0 &&
}
{view === 'resto' && rekapResto.map(resto => (
{resto.name}
{resto.totalQty} porsi
Total tagihan ke resto: {formatRp(resto.totalUang)}
))}
{view === 'menu' && rekapMenu.length === 0 &&
}
{view === 'menu' && (
{rekapMenu.map((menu, i) => (
{menu.name}
{menu.totalQty}
))}
)}
);
}
function EmptyState({ message }) {
return (
);
}
// ==========================================
// USER DASHBOARD & VIEWS
// ==========================================
function UserDashboard({ user, onLogout, restaurants, menus, session, addOrder }) {
const [cart, setCart] = useState([]); // [{ menuId, name, price, qty, notes }]
const [showCart, setShowCart] = useState(false);
const [showSuccess, setShowSuccess] = useState(false);
// Jika lapak tutup
if (!session.isOpen) {
return (
Halo, {user.name}!
Lapak Belum Buka / Udah Tutup
"{session.rejectMessage}"
);
}
const handleUpdateCart = (menu, delta) => {
setCart(prev => {
const existing = prev.find(item => item.menuId === menu.id);
if (!existing) {
if (delta > 0) return [...prev, { menuId: menu.id, name: menu.name, price: menu.price, qty: 1, notes: '' }];
return prev;
}
const newQty = existing.qty + delta;
if (newQty <= 0) return prev.filter(item => item.menuId !== menu.id);
return prev.map(item => item.menuId === menu.id ? { ...item, qty: newQty } : item);
});
};
const handleUpdateNotes = (menuId, notes) => {
setCart(prev => prev.map(item => item.menuId === menuId ? { ...item, notes } : item));
};
const cartTotal = cart.reduce((sum, item) => sum + (item.price * item.qty), 0);
const cartItemsCount = cart.reduce((sum, item) => sum + item.qty, 0);
const handleCheckout = () => {
addOrder({
userName: user.name,
items: cart,
total: cartTotal
});
setCart([]);
setShowCart(false);
setShowSuccess(true);
};
const openRestaurants = restaurants.filter(r => session.openRestoIds.includes(r.id));
return (
<>
{/* Header */}
Halo, {user.name}! 👋
Order s/d {session.endTime}
{/* Main Content (Menu List) */}
{openRestaurants.map(resto => {
const restoMenus = menus.filter(m => m.restaurant_id === resto.id);
return (
{resto.name}
{restoMenus.map(menu => {
const cartItem = cart.find(c => c.menuId === menu.id);
const qty = cartItem ? cartItem.qty : 0;
return (
0 ? 'border-amber-400 ring-1 ring-amber-100' : 'border-gray-100'}`}>
{menu.name}
{formatRp(menu.price)}
{/* +/- Controls */}
{qty === 0 ? (
) : (
{qty}
)}
{/* Notes Input Field (Muncul jika item ada di keranjang) */}
{qty > 0 && (
handleUpdateNotes(menu.id, e.target.value)}
className="w-full bg-gray-50 border border-gray-200 p-2 rounded-lg text-sm text-gray-700 focus:outline-none focus:border-amber-400"
/>
)}
);
})}
)
})}
{/* Floating Cart Button */}
{cartItemsCount > 0 && !showCart && !showSuccess && (
)}
{/* Cart Modal */}
{showCart && (
Detail Pesanan
{cart.map(item => (
{item.name}
{item.qty} x {formatRp(item.price)}
{item.notes &&
"{item.notes}"
}
{formatRp(item.price * item.qty)}
))}
Total Harga
{formatRp(cartTotal)}
)}
{/* Success / Payment Modal */}
{showSuccess && (
Pesanan Masuk!
OB kita sedang mencatat pesananmu dengan sepenuh hati.
Info Transfer
Silakan transfer total {formatRp(cartTotal)} ke:
{session.bankAccount}
Tunjukkin bukti transfer ke OB kalau udah ya!
)}
>
);
}