'Affogato', option: '', qty: 1, notes: 'Kahvesi bol dökülsün', status: 'Hazırlanıyor', time: Date.now() - 1200000, category: 'Bar' } ]); // Güvenlik & İşlem Logları const [auditLogs, setAuditLogs] = useState([ { id: 'log1', time: '12:30', user: 'Yönetici', action: 'Sistem yerel sunucu ağında başlatıldı.' }, { id: 'log2', time: '12:35', user: 'Garson Ahmet', action: 'Masa 2 için yeni sipariş girdi.' } ]); // Muhasebe & Depo Yönetim State'leri const [inventoryItems, setInventoryItems] = useState(INITIAL_INVENTORY_ITEMS); const [inventoryLogs, setInventoryLogs] = useState(INITIAL_INVENTORY_LOGS); const [expenses, setExpenses] = useState(INITIAL_EXPENSES); const [completedSales, setCompletedSales] = useState(INITIAL_COMPLETED_SALES); // Envanter Tanımlama State'i const [newInvItem, setNewInvItem] = useState({ name: '', category: 'Kahveler', initialStock: '', costPrice: '' }); // Envanter Hareket State'i (Sürekli Hızlı Stok Giriş/Çıkış Formu) const [stockMovement, setStockMovement] = useState({ itemId: '', type: 'Stok Girişi', qty: '', price: '', note: '' }); // Gider Ekleme State'i const [newExpense, setNewExpense] = useState({ title: '', amount: '', category: 'Günlük Alışveriş' }); // Admin PIN Onay Modalı (Hassas işlemler için) const [adminVerifyAction, setAdminVerifyAction] = useState(null); const [adminPinInput, setAdminPinInput] = useState(''); const [adminPinError, setAdminPinError] = useState(''); // Yönetici Paneli Görünümü Aktifliği const [showAdminPanel, setShowAdminPanel] = useState(false); const [adminTab, setAdminTab] = useState('menu'); // 'menu' | 'tables' | 'inventory' | 'expenses' | 'users' | 'logs' // Yeni Ürün Ekleme Form State'leri const [newProduct, setNewProduct] = useState({ name: '', category: 'Sıcak İçecekler', price: '', stock: '', options: '' }); // Yeni Masa Ekleme Form State'leri const [newTable, setNewTable] = useState({ name: '', section: 'İç Mekan' }); // Fiyat Hızlı Düzenleme State const [editingProductId, setEditingProductId] = useState(null); const [editingProductPrice, setEditingProductPrice] = useState(''); // Yeni Şifre Düzenleme State'leri const [editingRoleKey, setEditingRoleKey] = useState(null); const [newRolePinInput, setNewRolePinInput] = useState(''); const [rolePinError, setRolePinError] = useState(''); // Yeni Garson Ekleme State'i const [newWaiterName, setNewWaiterName] = useState(''); // Termal Fiş Yazıcı Simülatörü Modalı const [receiptData, setReceiptData] = useState(null); // İnaktivite Sayacı (30 Saniye) const [secondsRemaining, setSecondsRemaining] = useState(30); const timerRef = useRef(null); useEffect(() => { if (currentUser && currentUser.code !== roles.KITCHEN.code) { resetTimer(); timerRef.current = setInterval(() => { setSecondsRemaining(prev => { if (prev <= 1) { handleLogout(); return 30; } return prev - 1; }); }, 1000); } return () => { if (timerRef.current) clearInterval(timerRef.current); }; }, [currentUser, roles]); // Ekran tıklandığında süreyi sıfırla const handleUserActivity = () => { resetTimer(); }; const resetTimer = () => { setSecondsRemaining(30); }; const handleLogout = () => { setCurrentUser(null); setPinInput(''); setPinError(''); setSelectedTable(null); setPaymentTable(null); setShowAdminPanel(false); if (timerRef.current) clearInterval(timerRef.current); }; const handlePinSubmit = (e) => { e?.preventDefault(); const matchedRole = Object.values(roles).find(r => r.code === pinInput); if (matchedRole) { setCurrentUser(matchedRole); setPinInput(''); setPinError(''); addLog(matchedRole.name, 'Sistem oturumu açtı.'); } else { setPinError('Hatalı PIN Kodu! Lütfen tekrar deneyin.'); setPinInput(''); } }; const handlePinKeyPress = (num) => { setPinError(''); if (pinInput.length < 4) { setPinInput(prev => prev + num); } }; const addLog = (user, action) => { const timeString = new Date().toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit', second: '2-digit' }); setAuditLogs(prev => [{ id: 'log_' + Date.now(), time: timeString, user, action }, ...prev]); }; const handleTableClick = (table) => { resetTimer(); if (currentUser.code === roles.KITCHEN.code) return; // Mutfak masalara müdahale edemez. setSelectedTable(table); setCurrentOrderCart(table.orders || []); }; const handleAddToOrderCart = (item) => { resetTimer(); if (item.stock <= 0) { alert("Bu ürünün stoğu kalmamıştır!"); return; } if (item.options && item.options.length > 0) { setSelectedItemForOptions(item); setSelectedOption(item.options[0]); } else { addToCartWithOption(item, ''); } }; const addToCartWithOption = (item, option) => { const optionExtraPrice = option.includes('+') ? parseInt(option.split('+')[1]) : 0; const finalPrice = item.price + optionExtraPrice; setCurrentOrderCart(prev => { const existing = prev.find(x => x.name === item.name && x.option === option); if (existing) { return prev.map(x => (x.name === item.name && x.option === option) ? { ...x, qty: x.qty + 1 } : x); } else { return [...prev, { name: item.name, price: finalPrice, option, qty: 1, notes: orderNotes }]; } }); setMenuItems(prev => prev.map(m => m.id === item.id ? { ...m, stock: m.stock - 1 } : m)); setOrderNotes(''); setSelectedItemForOptions(null); }; const handleUpdateCartQty = (index, delta) => { resetTimer(); const cartItem = currentOrderCart[index]; const menuItem = menuItems.find(m => m.name === cartItem.name); if (delta > 0 && menuItem && menuItem.stock <= 0) { alert("Yeterli stok yok!"); return; } setCurrentOrderCart(prev => { const updated = [...prev]; updated[index].qty += delta; if (updated[index].qty <= 0) { if (currentUser.code !== roles.ADMIN.code) { // Garson silmeye çalışırsa şifre isteyeceğiz setAdminVerifyAction({ actionType: 'DELETE_CART_ITEM', data: { index, cartItem } }); return prev; } else { addLog(currentUser.name, `${selectedTable.name} masasından ${cartItem.name} siparişi silindi.`); updated.splice(index, 1); } } return updated; }); if (menuItem) { setMenuItems(prev => prev.map(m => m.id === menuItem.id ? { ...m, stock: m.stock - delta } : m)); } }; const handleConfirmOrder = () => { resetTimer(); if (currentOrderCart.length === 0) return; const updatedTables = tables.map(t => { if (t.id === selectedTable.id) { const totalAmount = currentOrderCart.reduce((acc, curr) => acc + (curr.price * curr.qty), 0); return { ...t, status: 'Dolu', orders: currentOrderCart, waiter: selectedTable.waiter || currentUser.name, total: totalAmount }; } return t; }); const newKdsOrders = currentOrderCart.map(cart => ({ id: 'kds_' + Date.now() + '_' + Math.random(), tableName: selectedTable.name, item: cart.name, option: cart.option, qty: cart.qty, notes: cart.notes, status: 'Hazırlanıyor', time: Date.now(), category: ['Sıcak İçecekler', 'Türk Kahvesi', 'Çay'].some(k => cart.name.includes(k)) ? 'Mutfak' : 'Bar' })); setKdsOrders(prev => [...prev, ...newKdsOrders]); setTables(updatedTables); addLog(currentUser.name, `${selectedTable.name} için siparişleri onayladı. Atanan Garson: ${selectedTable.waiter || currentUser.name}, Toplam: ${currentOrderCart.reduce((a, b) => a + (b.price * b.qty), 0)}₺`); setSelectedTable(null); }; const handleAdminVerify = (e) => { e.preventDefault(); if (adminPinInput === roles.ADMIN.code) { if (adminVerifyAction.actionType === 'DELETE_CART_ITEM') { const { index, cartItem } = adminVerifyAction.data; setCurrentOrderCart(prev => prev.filter((_, idx) => idx !== index)); addLog('Yönetici Onaylı', `${selectedTable.name} masasından ${cartItem.name} iptal edildi.`); } else if (adminVerifyAction.actionType === 'APPLY_CUSTOM_DISCOUNT') { setDiscountPercent(adminVerifyAction.data); addLog('Yönetici Onaylı', `İndirim uygulandı: %${adminVerifyAction.data}`); } setAdminVerifyAction(null); setAdminPinInput(''); setAdminPinError(''); } else { setAdminPinError('Yönetici PIN hatalı!'); setAdminPinInput(''); } }; const handleOpenPayment = (table) => { resetTimer(); setPaymentTable(table); setDiscountPercent(0); setNumSplits(1); setRemainingSplitAmount(table.total); setPaymentHistory([]); }; const handlePayPartial = (type, amount) => { resetTimer(); const nextRemaining = remainingSplitAmount - amount; const historyEntry = { id: 'p_' + Date.now(), type, amount }; setPaymentHistory(prev => [...prev, historyEntry]); setRemainingSplitAmount(nextRemaining); if (nextRemaining <= 1) { finalizeReceipt(paymentTable, paymentHistory.concat(historyEntry), discountPercent); } }; const finalizeReceipt = (table, payments, discount) => { const discountedTotal = table.total * (1 - discount / 100); // Satış Detayını Raporlama İçin Kaydet const paymentMethodsText = payments.map(p => `${p.type} (${p.amount}₺)`).join(', '); const newSale = { id: 'sale_' + Date.now(), tableName: table.name, total: discountedTotal, paymentMethod: paymentMethodsText, date: new Date().toLocaleDateString('tr-TR') }; setCompletedSales(prev => [newSale, ...prev]); setReceiptData({ tableName: table.name, waiter: table.waiter, orders: table.orders, discount, total: table.total, finalTotal: discountedTotal, payments, date: new Date().toLocaleString('tr-TR') }); setTables(prev => prev.map(t => t.id === table.id ? { ...t, status: 'Boş', orders: [], waiter: '', total: 0 } : t)); addLog(currentUser.name, `${table.name} hesabı kapatıldı. Toplam tahsilat: ${discountedTotal}₺`); setPaymentTable(null); }; const handleKdsStatusChange = (orderId, newStatus) => { setKdsOrders(prev => prev.map(o => o.id === orderId ? { ...o, status: newStatus } : o)); const target = kdsOrders.find(o => o.id === orderId); if (target) { addLog('Mutfak/Bar', `${target.tableName} için ${target.item} ${newStatus === 'Hazır' ? 'Hazırlandı' : 'Teslim Edildi'}`); } }; // --- YÖNETİCİ GEREKSİNİMLERİ (Menü & Masa & Şifre İşlemleri) --- // 1. Yeni Ürün Ekleme const handleAddProductSubmit = (e) => { e.preventDefault(); if (!newProduct.name || !newProduct.price) { alert("Lütfen ürün adı ve fiyatını giriniz."); return; } const newId = 'm_' + Date.now(); const formattedOptions = newProduct.options ? newProduct.options.split(',').map(o => o.trim()) : []; const newItem = { id: newId, category: newProduct.category, name: newProduct.name, price: parseFloat(newProduct.price), stock: parseInt(newProduct.stock) || 100, options: formattedOptions }; setMenuItems(prev => [...prev, newItem]); addLog(currentUser.name, `YENİ ÜRÜN EKLEDİ: ${newProduct.name} - ${newProduct.price}₺`); setNewProduct({ name: '', category: 'Sıcak İçecekler', price: '', stock: '', options: '' }); }; // 2. Ürün Fiyatı Inline Düzenleme const startEditingPrice = (item) => { setEditingProductId(item.id); setEditingProductPrice(item.price.toString()); }; const saveProductPrice = (id) => { const priceNum = parseFloat(editingProductPrice); if (isNaN(priceNum) || priceNum <= 0) { alert("Geçersiz fiyat girdiniz!"); return; } setMenuItems(prev => prev.map(item => { if (item.id === id) { addLog(currentUser.name, `${item.name} ürününün fiyatını ${item.price}₺ -> ${priceNum}₺ olarak güncelledi.`); return { ...item, price: priceNum }; } return item; })); setEditingProductId(null); }; // 3. Ürün Silme const handleDeleteProduct = (id, name) => { if (window.confirm(`${name} ürününü menüden tamamen silmek istediğinizden emin misiniz?`)) { setMenuItems(prev => prev.filter(item => item.id !== id)); addLog(currentUser.name, `ÜRÜN SİLİNDİ: ${name}`); } }; // 4. Yeni Masa Ekleme const handleAddTableSubmit = (e) => { e.preventDefault(); if (!newTable.name) { alert("Lütfen bir masa ismi yazın."); return; } const newId = 't_' + Date.now(); const tableItem = { id: newId, name: newTable.name, section: newTable.section, status: 'Boş', orders: [], waiter: '', total: 0 }; setTables(prev => [...prev, tableItem]); addLog(currentUser.name, `YENİ MASA EKLEDİ: ${newTable.name} (${newTable.section})`); setNewTable({ name: '', section: 'İç Mekan' }); }; // 5. Masa Silme const handleDeleteTable = (id, name) => { if (window.confirm(`${name} masasını sistemden kaldırmak istiyor musunuz?`)) { setTables(prev => prev.filter(t => t.id !== id)); addLog(currentUser.name, `MASA SİLİNDİ: ${name}`); } }; // 6. Masa Garsonu Değiştirme const handleTableWaiterChange = (tableId, waiterName) => { setTables(prev => prev.map(t => { if (t.id === tableId) { addLog(currentUser.name, `${t.name} masasına yeni sorumlu garson atandı: ${waiterName || 'YOK'}`); return { ...t, waiter: waiterName }; } return t; })); }; // 7. PIN Güncelleme İşlemi const startEditingRolePin = (roleKey, currentPin) => { setEditingRoleKey(roleKey); setNewRolePinInput(currentPin); setRolePinError(''); }; const saveRolePin = (roleKey) => { // 4 Haneli Sayı Kontrolü const pinRegex = /^[0-9]{4}$/; if (!pinRegex.test(newRolePinInput)) { setRolePinError('Şifre tam olarak 4 haneli rakamdan oluşmalıdır!'); return; } // Şifre Çakışması Kontrolü const otherRolesUsingPin = Object.entries(roles).filter(([k, r]) => k !== roleKey && r.code === newRolePinInput); if (otherRolesUsingPin.length > 0) { setRolePinError('Bu şifre başka bir rol tarafından kullanılıyor!'); return; } setRoles(prev => { const updated = { ...prev }; updated[roleKey] = { ...updated[roleKey], code: newRolePinInput }; addLog(currentUser.name, `${updated[roleKey].name} rolünün terminal giriş şifresini güncelledi.`); // Eğer kendi şifresini güncellediyse aktif kullanıcın
import React, { useState, useEffect, useRef } from 'react'; import { LogIn, LogOut, Utensils, ClipboardList, Layers, Settings, Users, CheckCircle, RefreshCw, Trash2, Plus, Minus, DollarSign, Clock, ShieldAlert, Award, FileText, ShoppingBag, Coffee, ChevronRight, AlertTriangle, Printer, Edit, Save, PlusCircle, X, UserCheck, TrendingUp, TrendingDown, Box, FileSpreadsheet, Calendar } from 'lucide-react'; // Orijinal "noc" Geometrisine Sahip Bağımsız, Kusursuz Yan Yana Logo Tasarımı (Overlapping Yoktur) function NocLogo({ className = "w-28 h-10", color = "currentColor", subtext = true }) { return (