import React, { useState, useEffect, useMemo } from 'react'; import { LayoutDashboard, Utensils, Truck, Package, Users, Settings, Sun, Moon, Brain, Plus, Bell, ChevronRight, ArrowRight, MapPin, CheckCircle, LogOut, Activity, PieChart, ClipboardList, Filter, TrendingUp, AlertCircle, Edit3, Save, X, Trash2, Search, ShieldCheck, Clock, Calendar, Camera, Globe, Building, Upload, ImageIcon, Key, User as UserIcon, Eye, EyeOff, PlusCircle, PackagePlus, Layers, Thermometer, Box, Timer, MoreVertical, ChefHat, Users2, LocateFixed, Play, Check, RotateCcw, MinusCircle, History, Mail, Shield, UserPlus, Phone, HelpCircle, Languages, BellRing, Smartphone, HardDrive, Info, Map, Navigation, CheckSquare, FastForward, UserCog, ToggleLeft, ToggleRight, Menu, Flame, Droplets, Wheat, Printer, ShoppingCart, Tag, Download, Stepper, Zap, Navigation2, Flag, ShieldAlert, Award, Briefcase, Lock } from 'lucide-react'; const App = () => { // --- Auth State --- const [isAuthenticated, setIsAuthenticated] = useState(false); const [currentUser, setCurrentUser] = useState(null); // --- UI State --- const [isDarkMode, setIsDarkMode] = useState(false); const [activeTab, setActiveTab] = useState('dashboard'); const [notification, setNotification] = useState(null); const [isSidebarOpen, setIsSidebarOpen] = useState(false); // --- Master Data --- const [kitchenSettings, setKitchenSettings] = useState({ name: "MBG SMART", tagline: "Sistem Manajemen Gizi Cerdas", address: "Jl. Merdeka No. 123, Jakarta Pusat", logo: null, phone: "021-555-0123", email: "admin@mbgsmart.id", capacity: 1500, timezone: "WIB (UTC+7)", language: "Bahasa Indonesia", }); const [inventory, setInventory] = useState([ { id: 1, name: "Beras Pandan Wangi", stock: 450, unit: "Kg", status: "Aman", category: "Pokok", lastUpdate: "2026-01-17" }, { id: 2, name: "Daging Ayam", stock: 15, unit: "Kg", status: "Kritis", category: "Protein", lastUpdate: "2026-01-18" }, { id: 3, name: "Minyak Goreng", stock: 120, unit: "Liter", status: "Aman", category: "Lainnya", lastUpdate: "2026-01-15" }, { id: 4, name: "Sayur Bayam", stock: 45, unit: "Kg", status: "Menipis", category: "Sayuran", lastUpdate: "2026-01-18" }, ]); const [menus, setMenus] = useState([ { id: 1, day: "Senin", date: "2026-01-12", name: "Nasi Ayam Bakar Madu", calories: 580, protein: 32, carbs: 65, fats: 18, ingredients: "Ayam, Nasi, Sayur, Madu", target: "SD/SMP", code: "MBG-001" }, { id: 2, day: "Selasa", date: "2026-01-13", name: "Ikan Pesmol & Tumis Labu", calories: 520, protein: 28, carbs: 55, fats: 14, ingredients: "Ikan, Labu Siam, Nasi, Bumbu Kuning", target: "SD/SMP", code: "MBG-002" }, ]); const [deliveries, setDeliveries] = useState([ { id: 1, courier: "Rizky Kurir", target: "SDN 02 Pagi", status: "Dalam Perjalanan", eta: "12 Menit", address: "Jl. Diponegoro No. 10", items: 450, phone: "0812-3456-7890", progress: 65 }, { id: 2, courier: "Andi Logistik", target: "MIN 1 Jakarta", status: "Tiba di Lokasi", eta: "0 Menit", address: "Jl. Sudirman Kav 22", items: 320, phone: "0812-9988-7766", progress: 100 }, ]); const [users, setUsers] = useState([ { id: 1, name: "Budi Santoso", username: "admin", role: "Super Admin", email: "budi@mbg.id", status: "Aktif", phone: "0811223344", joinDate: "2025-10-01", shift: "Full Time" }, { id: 2, name: "Siti Aminah", username: "siti_chef", role: "Kepala Dapur", email: "siti@mbg.id", status: "Aktif", phone: "0812998877", joinDate: "2025-11-15", shift: "Pagi" }, { id: 3, name: "SDN 01 Merdeka", username: "sdn01", role: "Akun Sekolah", email: "info@sdn01.sch.id", status: "Aktif", phone: "021-998877", joinDate: "2026-01-18", shift: "Monitoring" }, ]); // --- Global Handlers --- const showToast = (msg) => { setNotification(msg); setTimeout(() => setNotification(null), 3000); }; const handleLogin = (userCredentials) => { // Mock authentication logic const user = users.find(u => u.username === userCredentials.username); if (user) { setCurrentUser(user); setIsAuthenticated(true); // Auto-set tab based on role if (user.role === 'Akun Sekolah') { setActiveTab('menu'); } else { setActiveTab('dashboard'); } showToast(`Selamat datang, ${user.name}`); } else { showToast("Username tidak ditemukan"); } }; const handleLogout = () => { setIsAuthenticated(false); setCurrentUser(null); setActiveTab('dashboard'); }; useEffect(() => { if (isDarkMode) document.documentElement.classList.add('dark'); else document.documentElement.classList.remove('dark'); }, [isDarkMode]); // --- RBAC Navigation Logic --- const navItems = useMemo(() => { const allItems = [ { id: 'dashboard', label: 'Dashboard', icon: LayoutDashboard, roles: ['Super Admin', 'Kepala Dapur'] }, { id: 'inventory', label: 'Stok', icon: Package, roles: ['Super Admin', 'Kepala Dapur'] }, { id: 'menu', label: 'Menu Gizi', icon: Utensils, roles: ['Super Admin', 'Kepala Dapur', 'Akun Sekolah'] }, { id: 'logistics', label: 'Logistik', icon: Truck, roles: ['Super Admin', 'Kepala Dapur', 'Akun Sekolah'] }, { id: 'users', label: 'Staf', icon: Users, roles: ['Super Admin'] }, { id: 'settings', label: 'Sistem', icon: Settings, roles: ['Super Admin'] }, ]; if (!currentUser) return []; return allItems.filter(item => item.roles.includes(currentUser.role)); }, [currentUser]); if (!isAuthenticated) { return ; } return (
{isSidebarOpen && (
setIsSidebarOpen(false)} /> )}

{activeTab} {currentUser?.role} Panel

{currentUser?.name.charAt(0)}
{activeTab === 'dashboard' && } {activeTab === 'inventory' && } {activeTab === 'menu' && } {activeTab === 'logistics' && } {activeTab === 'users' && } {activeTab === 'settings' && }
{/* Mobile Nav */}
{notification && }
); }; // --- LOGIN VIEW --- const LoginView = ({ onLogin, isDark, setIsDark }) => { const [loading, setLoading] = useState(false); const [username, setUsername] = useState('admin'); const handleSubmit = (e) => { e.preventDefault(); setLoading(true); setTimeout(() => { onLogin({ username }); setLoading(false); }, 800); }; return (

MBG SMART AI

Masuk ke Panel Kontrol

setUsername(e.target.value)} className={`w-full px-6 py-4 rounded-xl border outline-none font-bold text-sm ${isDark ? 'bg-slate-800 border-slate-700' : 'bg-slate-50'}`} required />
); }; // --- STAF / USERS VIEW --- const UsersView = ({ users, setUsers, isDark, showToast }) => { const [isModalOpen, setIsModalOpen] = useState(false); const [editingUser, setEditingUser] = useState(null); const [searchQuery, setSearchQuery] = useState(''); const [filterRole, setFilterRole] = useState('Semua'); const [formData, setFormData] = useState({ name: '', username: '', role: 'Staf Dapur', email: '', phone: '', status: 'Aktif', shift: 'Pagi', joinDate: new Date().toISOString().split('T')[0] }); const roles = ['Semua', 'Super Admin', 'Kepala Dapur', 'Kurir', 'Staf Dapur', 'Akun Sekolah']; const handleOpen = (user = null) => { if (user) { setEditingUser(user); setFormData({...user}); } else { setEditingUser(null); setFormData({ name: '', username: '', role: 'Staf Dapur', email: '', phone: '', status: 'Aktif', shift: 'Pagi', joinDate: new Date().toISOString().split('T')[0] }); } setIsModalOpen(true); }; const handleClose = () => { setIsModalOpen(false); setEditingUser(null); }; const handleSubmit = (e) => { e.preventDefault(); if (editingUser) { setUsers(users.map(u => u.id === editingUser.id ? { ...formData, id: u.id } : u)); showToast(`Data ${formData.name} berhasil diperbarui`); } else { setUsers([...users, { ...formData, id: Date.now() }]); showToast(`Staf baru ${formData.name} telah terdaftar`); } handleClose(); }; const toggleUserStatus = (id) => { setUsers(users.map(u => { if (u.id === id) { const newStatus = u.status === 'Aktif' ? 'Non-Aktif' : 'Aktif'; showToast(`Status ${u.name} diubah menjadi ${newStatus}`); return { ...u, status: newStatus }; } return u; })); }; const deleteUser = (id, name) => { if (window.confirm(`Hapus data staf ${name}? Tindakan ini tidak dapat dibatalkan.`)) { setUsers(users.filter(u => u.id !== id)); showToast(`Data staf ${name} telah dihapus`); } }; const filteredUsers = users.filter(u => { const matchesSearch = u.name.toLowerCase().includes(searchQuery.toLowerCase()) || u.username.toLowerCase().includes(searchQuery.toLowerCase()); const matchesRole = filterRole === 'Semua' || u.role === filterRole; return matchesSearch && matchesRole; }); return (

Manajemen Personel & Tim

Kelola hak akses, status keaktifan, dan profil staf dapur pusat.

setSearchQuery(e.target.value)} className={`w-full pl-11 pr-4 py-3.5 rounded-2xl border text-sm outline-none font-bold transition-all ${isDark ? 'bg-slate-800 border-slate-700 focus:border-blue-500' : 'bg-white border-slate-100 shadow-sm focus:border-blue-600'}`} />
{roles.map(r => ( ))}
{filteredUsers.length > 0 ? filteredUsers.map(u => (
{u.status}
{u.name.charAt(0)}

{u.name}

{u.role === 'Super Admin' && } {u.role === 'Kepala Dapur' && } {u.role === 'Akun Sekolah' && } {u.role}

{u.email}
{u.phone}
{u.shift}
)) : (

Tidak ada staf ditemukan

)}
{isModalOpen && (
{editingUser ? : }

{editingUser ? 'Perbarui Profil Personel' : 'Registrasi Personel Baru'}

Atur identitas, shift kerja, dan hak akses aplikasi

setFormData({...formData, name: e.target.value})} className={`w-full p-4 rounded-2xl border outline-none font-bold text-sm transition-all ${isDark ? 'bg-slate-800 border-slate-700 focus:border-blue-500' : 'bg-slate-50 border-slate-100 focus:bg-white focus:border-blue-600'}`} placeholder="Contoh: SDN 01 Merdeka" required />
setFormData({...formData, username: e.target.value})} className={`w-full p-4 rounded-2xl border outline-none font-bold text-sm transition-all ${isDark ? 'bg-slate-800 border-slate-700 focus:border-blue-500' : 'bg-slate-50 border-slate-100 focus:bg-white focus:border-blue-600'}`} placeholder="username_login" required />
setFormData({...formData, email: e.target.value})} className={`w-full p-4 rounded-2xl border outline-none font-bold text-sm transition-all ${isDark ? 'bg-slate-800 border-slate-700 focus:border-blue-500' : 'bg-slate-50 border-slate-100 focus:bg-white focus:border-blue-600'}`} placeholder="email@domain.com" required />
setFormData({...formData, phone: e.target.value})} className={`w-full p-4 rounded-2xl border outline-none font-bold text-sm transition-all ${isDark ? 'bg-slate-800 border-slate-700 focus:border-blue-500' : 'bg-slate-50 border-slate-100 focus:bg-white focus:border-blue-600'}`} placeholder="08xxxxxxxxxx" required />
)}
); }; // --- LOGISTICS VIEW --- const LogisticsView = ({ deliveries, setDeliveries, isDark, showToast, role }) => { const isSchool = role === 'Akun Sekolah'; const handleUpdateStatus = (id, newStatus) => { if (isSchool) return; setDeliveries(deliveries.map(d => { if (d.id === id) { let progress = 10; if (newStatus === 'Dalam Perjalanan') progress = 65; if (newStatus === 'Tiba di Lokasi') progress = 100; return { ...d, status: newStatus, progress }; } return d; })); showToast(`Status pengiriman diperbarui: ${newStatus}`); }; return (

Pelacakan Logistik

{isSchool ? "Pantau status kedatangan paket gizi ke sekolah Anda." : "Pantau status pengantaran seluruh paket gizi."}

{!isSchool && ( )}
{deliveries.map(d => (

{d.target}

{d.courier}

{d.status}
{!isSchool && d.status === 'Dalam Perjalanan' && ( )} {isSchool && d.status === 'Tiba di Lokasi' && (
Paket Terverifikasi
)}
))}
); }; // --- MENU VIEW --- const MenuView = ({ menus, setMenus, kitchenSettings, isDark, showToast, role }) => { const isSchool = role === 'Akun Sekolah'; return (

Jadwal Menu Gizi

Informasi nutrisi harian untuk siswa.

{!isSchool && ( )}
{menus.map(menu => (

{menu.name}

{isSchool &&
Menu Hari Ini
}

{menu.day}, {menu.date} • {menu.target}

{menu.calories}

KCAL

{menu.protein}g

PROT

{menu.carbs}g

KARB

{menu.fats}g

LEMK

Komposisi Bahan

{menu.ingredients}

{isSchool && ( )}
))}
); }; // --- INVENTORY VIEW --- const InventoryView = ({ inventory, setInventory, isDark, showToast }) => { return (

Gudang Bahan

Pantau ketersediaan logistik.

{inventory.map(item => (
{item.status}

{item.name}

{item.category}

{item.stock} {item.unit}

))}
); }; // --- DASHBOARD COMPONENTS --- const DashboardView = ({ inventory, deliveries, users, isDark }) => (
{[ { label: 'Total Porsi', val: '1,240', icon: ClipboardList, color: 'text-blue-600', bg: 'bg-blue-50' }, { label: 'Stok Kritis', val: `${inventory.filter(i => i.status === 'Kritis').length}`, icon: AlertCircle, color: 'text-red-500', bg: 'bg-red-50' }, { label: 'Total Staf', val: `${users.length}`, icon: Users, color: 'text-emerald-500', bg: 'bg-emerald-50' }, { label: 'Logistik', val: `${deliveries.length}`, icon: Truck, color: 'text-orange-500', bg: 'bg-orange-50' }, ].map((s, i) => (

{s.label}

{s.val}

))}

Aktivitas Personel

{users.slice(0, 4).map(u => (
{u.name.charAt(0)}

{u.name}

{u.role} • Shift {u.shift}

{u.status}
))}

Status Armada

{deliveries.map(d => (

{d.target}

Kurir: {d.courier}

{d.progress}%

))}
); const SettingsView = ({ kitchenSettings, isDark }) => (

Konfigurasi Sistem

{['name', 'tagline', 'address', 'email'].map(f => (
))}
); const Toast = ({ message }) => (
{message}
); export default App;