import React, { useState, useEffect, useMemo } from 'react';
import { initializeApp } from 'firebase/app';
import {
getFirestore,
collection,
addDoc,
onSnapshot,
doc,
updateDoc,
serverTimestamp,
query,
where
} from 'firebase/firestore';
import {
getAuth,
signInAnonymously,
signInWithCustomToken,
onAuthStateChanged
} from 'firebase/auth';
import {
ShoppingBag,
Utensils,
Clock,
CheckCircle2,
ChevronRight,
Trash2,
Plus,
Minus,
Store,
X,
PackageCheck,
Truck,
Flame,
UserCheck
} from 'lucide-react';
// --- Firebase Configuration ---
const firebaseConfig = JSON.parse(__firebase_config);
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
const appId = typeof __app_id !== 'undefined' ? __app_id : 'kfc-clone-app';
// --- Sample Data: KFC Menu ---
const MENU_DATA = [
{
id: 'b1',
category: 'Buckets',
name: '10 pc. Family Bucket',
price: 29.99,
image: 'https://images.unsplash.com/photo-1626082927389-6cd097cdc6ec?auto=format&fit=crop&w=300&q=80',
description: '10 pieces of our Original Recipe chicken, 2 large sides, and 4 biscuits.'
},
{
id: 'b2',
category: 'Buckets',
name: '8 pc. Chicken & Tenders Box',
price: 24.99,
image: 'https://images.unsplash.com/photo-1513185041617-8ab03f83d6c5?auto=format&fit=crop&w=300&q=80',
description: '4 pieces of chicken and 4 Extra Crispy Tenders.'
},
{
id: 's1',
category: 'Sandwiches',
name: 'KFC Chicken Sandwich',
price: 5.49,
image: 'https://images.unsplash.com/photo-1606755962773-d324e0a13086?auto=format&fit=crop&w=300&q=80',
description: 'Double-breaded, premium chicken breast fillet with pickles and mayo.'
},
{
id: 's2',
category: 'Sandwiches',
name: 'Spicy Chicken Sandwich',
price: 5.49,
image: 'https://images.unsplash.com/photo-1521305916504-4a1121188589?auto=format&fit=crop&w=300&q=80',
description: 'Our signature spicy sauce adds the perfect kick.'
},
{
id: 't1',
category: 'Tenders',
name: '5 pc. Tenders Combo',
price: 10.99,
image: 'https://images.unsplash.com/photo-1562967914-608f82629710?auto=format&fit=crop&w=300&q=80',
description: '5 Extra Crispy Tenders, side, biscuit, and a medium drink.'
},
{
id: 'd1',
category: 'Sides',
name: 'Secret Recipe Fries',
price: 3.29,
image: 'https://images.unsplash.com/photo-1573080496219-bb080dd4f877?auto=format&fit=crop&w=300&q=80',
description: 'Seasoned with a secret blend of herbs and spices.'
}
];
const CATEGORIES = ['All', 'Buckets', 'Sandwiches', 'Tenders', 'Sides'];
export default function App() {
const [user, setUser] = useState(null);
const [view, setView] = useState('customer'); // 'customer' or 'kitchen'
const [cart, setCart] = useState([]);
const [orders, setOrders] = useState([]);
const [activeCategory, setActiveCategory] = useState('All');
const [isOrdering, setIsOrdering] = useState(false);
const [orderSuccess, setOrderSuccess] = useState(false);
// 1. Auth Logic
useEffect(() => {
const initAuth = async () => {
try {
if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) {
await signInWithCustomToken(auth, __initial_auth_token);
} else {
await signInAnonymously(auth);
}
} catch (err) {
console.error("Auth error:", err);
}
};
initAuth();
const unsubscribe = onAuthStateChanged(auth, setUser);
return () => unsubscribe();
}, []);
// 2. Data Logic (Kitchen Orders)
useEffect(() => {
if (!user) return;
const ordersRef = collection(db, 'artifacts', appId, 'public', 'data', 'orders');
// We listen to all orders for the kitchen view, but can filter in memory or locally for the customer
const unsubscribe = onSnapshot(ordersRef,
(snapshot) => {
const orderData = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data()
}));
setOrders(orderData.sort((a, b) => (b.createdAt?.seconds || 0) - (a.createdAt?.seconds || 0)));
},
(error) => console.error("Firestore error:", error)
);
return () => unsubscribe();
}, [user]);
// --- Customer Actions ---
const addToCart = (item) => {
setCart(prev => {
const existing = prev.find(i => i.id === item.id);
if (existing) {
return prev.map(i => i.id === item.id ? { ...i, qty: i.qty + 1 } : i);
}
return [...prev, { ...item, qty: 1 }];
});
};
const updateQty = (id, delta) => {
setCart(prev => prev.map(item => {
if (item.id === id) {
const newQty = Math.max(0, item.qty + delta);
return { ...item, qty: newQty };
}
return item;
}).filter(item => item.qty > 0));
};
const placeOrder = async () => {
if (cart.length === 0 || !user) return;
setIsOrdering(true);
try {
const orderPayload = {
userId: user.uid,
items: cart,
total: cart.reduce((sum, i) => sum + (i.price * i.qty), 0),
status: 'pending', // pending -> preparing -> ready -> received
createdAt: serverTimestamp(),
orderNumber: Math.floor(1000 + Math.random() * 9000)
};
await addDoc(collection(db, 'artifacts', appId, 'public', 'data', 'orders'), orderPayload);
setCart([]);
setOrderSuccess(true);
setTimeout(() => setOrderSuccess(false), 5000);
} catch (err) {
console.error("Order failed:", err);
} finally {
setIsOrdering(false);
}
};
// --- Status Helpers ---
const updateOrderStatus = async (orderId, newStatus) => {
try {
const orderRef = doc(db, 'artifacts', appId, 'public', 'data', 'orders', orderId);
await updateDoc(orderRef, { status: newStatus });
} catch (err) {
console.error("Update failed:", err);
}
};
// Filter orders for the current customer that aren't finalized yet
const myActiveOrders = useMemo(() => {
if (!user) return [];
return orders.filter(o => o.userId === user.uid && o.status !== 'received');
}, [orders, user]);
const filteredMenu = activeCategory === 'All'
? MENU_DATA
: MENU_DATA.filter(item => item.category === activeCategory);
const cartTotal = cart.reduce((sum, item) => sum + (item.price * item.qty), 0);
// Helper for status styling
const getStatusInfo = (status) => {
switch (status) {
case 'pending': return { label: 'Placed', icon: PackageCheck, color: 'text-amber-500', bg: 'bg-amber-50', step: 1 };
case 'preparing': return { label: 'Preparing', icon: Flame, color: 'text-blue-500', bg: 'bg-blue-50', step: 2 };
case 'ready': return { label: 'Ready for Pickup', icon: Truck, color: 'text-green-500', bg: 'bg-green-50', step: 3 };
default: return { label: 'Unknown', icon: Clock, color: 'text-gray-500', bg: 'bg-gray-50', step: 0 };
}
};
return (
{/* Navigation Header */}
{view === 'customer' ? (
{/* Customer Tracker - Only shows if there are active orders */}
{myActiveOrders.length > 0 && (
Track My Order
{myActiveOrders.map(order => {
const status = getStatusInfo(order.status);
const StatusIcon = status.icon;
return (
Order ID
#{order.orderNumber}
{status.label}
{/* Progress Tracker Bar */}
{[1, 2, 3].map((s) => (
= s ? 'border-red-600 bg-red-600' : 'border-gray-200'}`}>
))}
{order.items.length} items • ${order.total.toFixed(2)}
{order.status === 'ready' && (
)}
);
})}
)}
{/* Menu Section */}
{orderSuccess && (
Order placed! Track it at the top of the page.
)}
{/* Category Filter */}
{CATEGORIES.map(cat => (
))}
{filteredMenu.map(item => (
${item.price.toFixed(2)}
{item.name}
{item.description}
))}
{/* Cart Summary */}
My Bucket
{cart.reduce((s, i) => s + i.qty, 0)} items
{cart.length === 0 ? (
) : (
cart.map(item => (
{item.name}
${(item.price * item.qty).toFixed(2)}
{item.qty}
))
)}
{cart.length > 0 && (
Total
${cartTotal.toFixed(2)}
)}
) : (
/* Kitchen Display System (KDS) */
Kitchen Display
Manage incoming orders
Pending
{orders.filter(o => o.status === 'pending').length}
Preparing
{orders.filter(o => o.status === 'preparing').length}
{orders.filter(o => o.status !== 'received').map(order => (
Order
#{order.orderNumber}
{order.createdAt ? new Date(order.createdAt.seconds * 1000).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : '...'}
{order.items.map((item, idx) => (
))}
{order.status === 'pending' && (
)}
{order.status === 'preparing' && (
)}
{order.status === 'ready' && (
WAITING FOR CUSTOMER
)}
))}
{orders.filter(o => o.status !== 'received').length === 0 && (
)}
)}
{/* Mobile Footer Cart Hint */}
{view === 'customer' && cart.length > 0 && (
)}
);
}