diff --git a/app/(tabs)/_layout.js b/app/(tabs)/_layout.js index 2aa2318..fad66f4 100644 --- a/app/(tabs)/_layout.js +++ b/app/(tabs)/_layout.js @@ -26,6 +26,18 @@ export default function TabLayout() { ), }} /> + ( + + ), + }} + /> - + ); } diff --git a/app/(tabs)/tables.tsx b/app/(tabs)/tables.tsx new file mode 100644 index 0000000..ee14dd6 --- /dev/null +++ b/app/(tabs)/tables.tsx @@ -0,0 +1,11 @@ +import React from "react"; +import RestaurantTablesScreen from "@/screens/TablesScreen/RestaurantTablesScreen"; +import { GestureHandlerRootView } from "react-native-gesture-handler"; + +export default function MenuScreen() { + return ( + + + + ); +} diff --git a/components/OrderTaking/OrderDetails.js b/components/OrderTaking/OrderDetails.js index 8364cfe..51bc0a1 100644 --- a/components/OrderTaking/OrderDetails.js +++ b/components/OrderTaking/OrderDetails.js @@ -1,49 +1,53 @@ -import React, { useState, useEffect } from "react"; import { StyleSheet, FlatList, Platform } from "react-native"; import Icon from "react-native-vector-icons/MaterialIcons"; import { ThemedView } from "../common/ThemedView"; import { ThemedText } from "../common/ThemedText"; import ThemedButton from "../common/ThemedButton"; +import { generateUUID } from "@/utils/uuidGenerator"; const OrderDetails = ({ style, orders, increaseQuantity, decreaseQuantity, - removeItem, }) => { - const [orderItems, setOrderItems] = useState(orders); + const handleIncreaseQuantityClick = (item) => { + const orderItem = { + id: generateUUID(), + menuItemId: item.menuItemId, + name: item.name, + category: item.category, + cuisine: item.cuisine, + price: item.price, + searchableKey: item.searchableKey, + dietaryPreference: item.dietaryPreference, + image: item.image, + quantity: 1, + status: "ACTIVE", + itemValue: parseFloat(item.price) || 0, // Convert price to a number, + orderTimestamp: Date.now(), + }; + increaseQuantity(orderItem); + }; - useEffect(() => { - setOrderItems(orders); - }, [orders]); - - const renderOrderItem = ({ item, index }) => ( + const renderOrderItem = ({ item }) => ( - removeItem(index)} - style={styles.removeItemButton} - > - - {item.name} - - @ ₹ {item.price.toFixed(2)} - + @ ₹ {item.price} decreaseQuantity(index)} + onPress={() => decreaseQuantity(item)} style={styles.quantityButton} > {item.quantity} increaseQuantity(index)} + onPress={() => handleIncreaseQuantityClick(item)} style={styles.quantityButton} > @@ -59,7 +63,7 @@ const OrderDetails = ({ return ( - {orders.length === 0 ? ( + {!orders || orders.length === 0 ? ( <> No item selected Please select item from left menu item diff --git a/components/OrderTaking/OrderManagement.js b/components/OrderTaking/OrderManagement.js index eef4dc9..e4ee899 100644 --- a/components/OrderTaking/OrderManagement.js +++ b/components/OrderTaking/OrderManagement.js @@ -9,14 +9,28 @@ import FloatingCloseButton from "@/components/FloatingCloseButton/FloatingCloseB import { ThemedView } from "../common/ThemedView"; import { collection, getDocs } from "firebase/firestore"; import { db } from "@/firebase/firebaseConfig"; +import { aggregateOrders } from "@/utils/orderManagement"; +import { generateUUID } from "@/utils/uuidGenerator"; const OrderManagement = ({ items, onClose, updateOrder }) => { - const [selectedCategory, setSelectedCategory] = useState(0); + const [menuItems, setMenuItems] = useState([]); const [selectedMenu, setSelectedMenu] = useState(); - const [orders, setOrders] = useState(items ? items : []); - const [menuItems, setMenuItems] = useState([]); const [categories, setCategories] = useState([]); + const [selectedCategory, setSelectedCategory] = useState(0); + + const [rawOrders, setRawOrders] = useState(items ? items : []); + const [orders, setOrders] = useState([]); + + const { width } = useWindowDimensions(); + const isLargeScreen = width > 768; // Adjust the breakpoint as needed + + useEffect(() => { + if (selectedCategory == 0) return setSelectedMenu(getFavoriteItems()); + setSelectedMenu( + menuItems.filter((item) => item.category === categories[selectedCategory]) + ); + }, [selectedCategory, menuItems]); useEffect(() => { const fetchMenuItems = async () => { @@ -42,58 +56,60 @@ const OrderManagement = ({ items, onClose, updateOrder }) => { fetchMenuItems(); }, []); + // Update orders whenever rawOrders changes useEffect(() => { - if (selectedCategory == 0) return setSelectedMenu(getFavoriteItems()); - setSelectedMenu( - menuItems.filter((item) => item.category === categories[selectedCategory]) - ); - }, [selectedCategory, menuItems]); + const aggOrders = aggregateOrders(rawOrders); + setOrders(aggOrders); + }, [rawOrders]); - useEffect(() => updateOrder(orders), [orders]); + // Update Firebase whenever orders change + useEffect(() => { + updateOrder(rawOrders); + }, [rawOrders]); const onSidebarItemClicked = (item, idx) => { setSelectedCategory(idx); }; - const onMenuItemClick = (item, index) => { - const idx = getItemIndexInOrderBook(item); - if (idx === -1) { - // Add new item to the order - item.quantity = 1; - item.itemValue = item.price * item.quantity; - setOrders([...orders, item]); - } else increaseQuantity(idx); - }; - - const getItemIndexInOrderBook = (item) => { - return orders.findIndex( - (orderItem) => - orderItem.name === item.name && orderItem.category === item.category - ); + const onMenuItemClick = (item) => { + const orderItem = { + id: generateUUID(), + menuItemId: item.id, + name: item.name, + category: item.category, + cuisine: item.cuisine, + price: item.price, + searchableKey: item.searchableKey, + dietaryPreference: item.type, + image: item.image, + quantity: 1, + status: "ACTIVE", + itemValue: parseFloat(item.price) || 0, // Convert price to a number, + orderTimestamp: Date.now(), + }; + addItem(orderItem); }; - const increaseQuantity = (index) => { - const newOrderItems = [...orders]; - newOrderItems[index].quantity += 1; - newOrderItems[index].itemValue = - newOrderItems[index].price * newOrderItems[index].quantity; - setOrders(newOrderItems); + const addItem = (item) => { + setRawOrders([...rawOrders, item]); }; - const decreaseQuantity = (index) => { - const newOrderItems = [...orders]; - if (newOrderItems[index].quantity > 1) { - newOrderItems[index].quantity -= 1; - newOrderItems[index].itemValue = - newOrderItems[index].price * newOrderItems[index].quantity; - setOrders(newOrderItems); - } else removeItem(index); - }; + const removeItem = (item) => { + // Find the first item with the same name, category, and status ACTIVE + const index = rawOrders.findIndex( + (orderItem) => + orderItem.name === item.name && + orderItem.category === item.category && + orderItem.status === "ACTIVE" + ); - const removeItem = (index) => { - const newOrders = [...orders]; // Create a copy of the orders array - newOrders.splice(index, 1); // Remove the item at the specified index - setOrders(newOrders); // Update the state with the new array + if (index !== -1) { + const newOrders = [...rawOrders]; // Create a copy of the orders array + newOrders.splice(index, 1); // Remove the item at the specified index + setRawOrders(newOrders); // Update the state with the new array + } else { + console.log("No active item found to decrease quantity."); + } }; const getFavoriteItems = () => { @@ -101,9 +117,6 @@ const OrderManagement = ({ items, onClose, updateOrder }) => { // return menuItems.filter((item) => item.orderCountPercentile > 70); }; - const { width } = useWindowDimensions(); - const isLargeScreen = width > 768; // Adjust the breakpoint as needed - return ( {/* */} @@ -137,9 +150,8 @@ const OrderManagement = ({ items, onClose, updateOrder }) => { diff --git a/package-lock.json b/package-lock.json index 8f983c4..f48c231 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,8 @@ "@react-native-firebase/app": "^20.1.0", "@react-native-firebase/firestore": "^20.1.0", "@react-native-picker/picker": "^2.7.7", - "@react-navigation/native": "^6.0.2", + "@react-navigation/drawer": "^6.7.2", + "@react-navigation/native": "^6.1.18", "expo": "~51.0.20", "expo-constants": "~16.0.2", "expo-font": "~12.0.8", @@ -35,8 +36,8 @@ "react-native-parallax-scroll-view": "^0.21.3", "react-native-reanimated": "~3.10.1", "react-native-responsive-screen": "^1.4.2", - "react-native-safe-area-context": "4.10.1", - "react-native-screens": "3.31.1", + "react-native-safe-area-context": "^4.10.1", + "react-native-screens": "^3.31.1", "react-native-super-grid": "^6.0.1", "react-native-vector-icons": "^10.1.0", "react-native-web": "^0.19.12" @@ -7615,25 +7616,56 @@ } }, "node_modules/@react-navigation/core": { - "version": "6.4.16", - "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-6.4.16.tgz", - "integrity": "sha512-UDTJBsHxnzgFETR3ZxhctP+RWr4SkyeZpbhpkQoIGOuwSCkt1SE0qjU48/u6r6w6XlX8OqVudn1Ab0QFXTHxuQ==", + "version": "6.4.17", + "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-6.4.17.tgz", + "integrity": "sha512-Nd76EpomzChWAosGqWOYE3ItayhDzIEzzZsT7PfGcRFDgW5miHV2t4MZcq9YIK4tzxZjVVpYbIynOOQQd1e0Cg==", + "license": "MIT", "dependencies": { "@react-navigation/routers": "^6.1.9", "escape-string-regexp": "^4.0.0", "nanoid": "^3.1.23", "query-string": "^7.1.3", "react-is": "^16.13.0", - "use-latest-callback": "^0.1.9" + "use-latest-callback": "^0.2.1" }, "peerDependencies": { "react": "*" } }, + "node_modules/@react-navigation/core/node_modules/use-latest-callback": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.1.tgz", + "integrity": "sha512-QWlq8Is8BGWBf883QOEQP5HWYX/kMI+JTbJ5rdtvJLmXTIh9XoHIO3PQcmQl8BU44VKxow1kbQUHa6mQSMALDQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/@react-navigation/drawer": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-6.7.2.tgz", + "integrity": "sha512-o4g2zgTZa2+oLd+8V33etrSM38KIqu8S/zCBTsdsHUoQyVE7JNRiv3Qgq/jMvEb8PZCqWmm7jHItcgzrBuwyOQ==", + "license": "MIT", + "dependencies": { + "@react-navigation/elements": "^1.3.31", + "color": "^4.2.3", + "warn-once": "^0.1.0" + }, + "peerDependencies": { + "@react-navigation/native": "^6.0.0", + "react": "*", + "react-native": "*", + "react-native-gesture-handler": ">= 1.0.0", + "react-native-reanimated": ">= 1.0.0", + "react-native-safe-area-context": ">= 3.0.0", + "react-native-screens": ">= 3.0.0" + } + }, "node_modules/@react-navigation/elements": { - "version": "1.3.30", - "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.30.tgz", - "integrity": "sha512-plhc8UvCZs0UkV+sI+3bisIyn78wz9O/BiWZXpounu72k/R/Sj5PuZYFJ1fi6psvriUveMCGh4LeZckAZu2qiQ==", + "version": "1.3.31", + "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.31.tgz", + "integrity": "sha512-bUzP4Awlljx5RKEExw8WYtif8EuQni2glDaieYROKTnaxsu9kEIA515sXQgUDZU4Ob12VoL7+z70uO3qrlfXcQ==", + "license": "MIT", "peerDependencies": { "@react-navigation/native": "^6.0.0", "react": "*", @@ -7642,11 +7674,12 @@ } }, "node_modules/@react-navigation/native": { - "version": "6.1.17", - "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.17.tgz", - "integrity": "sha512-mer3OvfwWOHoUSMJyLa4vnBH3zpFmCwuzrBPlw7feXklurr/ZDiLjLxUScOot6jLRMz/67GyilEYMmP99LL0RQ==", + "version": "6.1.18", + "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.18.tgz", + "integrity": "sha512-mIT9MiL/vMm4eirLcmw2h6h/Nm5FICtnYSdohq4vTLA2FF/6PNhByM7s8ffqoVfE5L0uAa6Xda1B7oddolUiGg==", + "license": "MIT", "dependencies": { - "@react-navigation/core": "^6.4.16", + "@react-navigation/core": "^6.4.17", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.1.23" @@ -7676,6 +7709,7 @@ "version": "6.1.9", "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-6.1.9.tgz", "integrity": "sha512-lTM8gSFHSfkJvQkxacGM6VJtBt61ip2XO54aNfswD+KMw6eeZ4oehl7m0me3CR9hnDE4+60iAZR8sAhvCiI3NA==", + "license": "MIT", "dependencies": { "nanoid": "^3.1.23" } @@ -10054,6 +10088,7 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", "engines": { "node": ">=0.10" } @@ -11953,6 +11988,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -18153,6 +18189,7 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "license": "MIT", "dependencies": { "decode-uri-component": "^0.2.2", "filter-obj": "^1.1.0", @@ -18515,6 +18552,7 @@ "version": "4.10.1", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.10.1.tgz", "integrity": "sha512-w8tCuowDorUkPoWPXmhqosovBr33YsukkwYCDERZFHAxIkx6qBadYxfeoaJ91nCQKjkNzGrK5qhoNOeSIcYSpA==", + "license": "MIT", "peerDependencies": { "react": "*", "react-native": "*" @@ -18524,6 +18562,7 @@ "version": "3.31.1", "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-3.31.1.tgz", "integrity": "sha512-8fRW362pfZ9y4rS8KY5P3DFScrmwo/vu1RrRMMx0PNHbeC9TLq0Kw1ubD83591yz64gLNHFLTVkTJmWeWCXKtQ==", + "license": "MIT", "dependencies": { "react-freeze": "^1.0.0", "warn-once": "^0.1.0" @@ -19551,6 +19590,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", "engines": { "node": ">=6" } @@ -19689,6 +19729,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", "engines": { "node": ">=4" } diff --git a/package.json b/package.json index eb088f6..6b8aae7 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "@react-native-firebase/app": "^20.1.0", "@react-native-firebase/firestore": "^20.1.0", "@react-native-picker/picker": "^2.7.7", - "@react-navigation/native": "^6.0.2", + "@react-navigation/drawer": "^6.7.2", + "@react-navigation/native": "^6.1.18", "expo": "~51.0.20", "expo-constants": "~16.0.2", "expo-font": "~12.0.8", @@ -42,8 +43,8 @@ "react-native-parallax-scroll-view": "^0.21.3", "react-native-reanimated": "~3.10.1", "react-native-responsive-screen": "^1.4.2", - "react-native-safe-area-context": "4.10.1", - "react-native-screens": "3.31.1", + "react-native-safe-area-context": "^4.10.1", + "react-native-screens": "^3.31.1", "react-native-super-grid": "^6.0.1", "react-native-vector-icons": "^10.1.0", "react-native-web": "^0.19.12" diff --git a/screens/HomeScreen/HomeScreen.js b/screens/HomeScreen/HomeScreen.js index 3e696a6..4a8110d 100644 --- a/screens/HomeScreen/HomeScreen.js +++ b/screens/HomeScreen/HomeScreen.js @@ -32,10 +32,10 @@ export default function HomeScreen() { }, []); const navigationItems = [ + { title: "Staffs", onPress: () => navigation.navigate("staffs") }, + { title: "Tables", onPress: () => navigation.navigate("tables") }, { title: "Menu", onPress: () => navigation.navigate("menu") }, { title: "Orders", onPress: () => navigation.navigate("orders") }, - { title: "Staffs", onPress: () => navigation.navigate("staffs") }, - { title: "Inventory", onPress: () => navigation.navigate("inventory") }, ]; const renderHeader = () => ( diff --git a/screens/MenuScreen/MenuScreenContainer.js b/screens/MenuScreen/MenuScreenContainer.js index bf54f9b..6ebc482 100644 --- a/screens/MenuScreen/MenuScreenContainer.js +++ b/screens/MenuScreen/MenuScreenContainer.js @@ -116,7 +116,6 @@ const MenuScreenContainer = () => { }; const addMenuItem = (item) => { - console.log(item); addMenuItems([item]); }; diff --git a/screens/OrdersScreen/OrdersScreen.js b/screens/OrdersScreen/OrdersScreen.js new file mode 100644 index 0000000..1d97625 --- /dev/null +++ b/screens/OrdersScreen/OrdersScreen.js @@ -0,0 +1,126 @@ +import React, { useState, useEffect, useCallback } from "react"; +import { + View, + Text, + FlatList, + StyleSheet, + TouchableOpacity, + RefreshControl, + Dimensions, +} from "react-native"; + +const OrdersScreen = ({ orders, onCompleteOrder }) => { + const [refreshing, setRefreshing] = useState(false); + + const calculateNumColumns = () => { + const screenWidth = Dimensions.get("window").width; + return Math.floor(screenWidth / 250); // Adjust 250 to fit your design + }; + const [numColumns, setNumColumns] = useState(calculateNumColumns()); + + useEffect(() => { + const updateLayout = () => { + setNumColumns(calculateNumColumns()); + }; + + const subscription = Dimensions.addEventListener("change", updateLayout); + return () => subscription?.remove(); + }, []); + + const onRefresh = useCallback(() => { + setRefreshing(true); + setTimeout(() => setRefreshing(false), 2000); + }, []); + + const renderOrder = ({ item }) => ( + + + {item.name} + {item.tableNumber} + + Qty: {item.quantity} + Notes: {item.tableNote} + onCompleteOrder(item.id, item.tableId)} + > + Complete Order + + + ); + const filteredOrders = orders.filter( + (order) => !order.status || order.status === "ACTIVE" + ); + return ( + `order-${index}-${item.id}`} + contentContainerStyle={styles.listContent} + refreshControl={ + + } + numColumns={numColumns} + key={numColumns} // Force re-render when numColumns changes + /> + ); +}; + +const styles = StyleSheet.create({ + listContent: { + padding: 8, + }, + card: { + flex: 1, + backgroundColor: "white", + borderRadius: 8, + padding: 16, + margin: 8, + elevation: 4, + shadowColor: "#000", + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.1, + shadowRadius: 4, + maxWidth: "100%", + minWidth: 200, + }, + headerRow: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + marginBottom: 8, + }, + itemName: { + fontSize: 18, + fontWeight: "bold", + }, + quantity: { + fontSize: 18, + fontWeight: "bold", + color: "#4a90e2", + }, + tableNumber: { + fontSize: 16, + fontWeight: "500", + marginBottom: 4, + }, + notes: { + fontSize: 14, + fontStyle: "italic", + color: "#666", + marginBottom: 8, + }, + completeButton: { + backgroundColor: "#4a90e2", + padding: 10, + borderRadius: 5, + alignItems: "center", + marginTop: 8, + }, + completeButtonText: { + color: "white", + fontWeight: "bold", + }, +}); + +export default OrdersScreen; diff --git a/screens/OrdersScreen/OrdersScreenContainer.js b/screens/OrdersScreen/OrdersScreenContainer.js new file mode 100644 index 0000000..fca1544 --- /dev/null +++ b/screens/OrdersScreen/OrdersScreenContainer.js @@ -0,0 +1,110 @@ +import React, { useState, useEffect } from "react"; +import { + doc, + getDoc, + updateDoc, + collection, + query, + onSnapshot, +} from "firebase/firestore"; +import { db } from "@/firebase/firebaseConfig"; +import { ThemedText } from "@/components/common/ThemedText"; +import OrdersScreen from "./OrdersScreen"; + +const OrdersScreenContainer = () => { + const [orders, setOrders] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchOrders = async () => { + try { + const tablesRef = collection( + db, + "hotel-details/seating-arrangement/tables" + ); + const q = query(tablesRef); + + // Set up real-time listener + const unsubscribe = onSnapshot(q, (querySnapshot) => { + const allOrders = []; + querySnapshot.docs.forEach((doc) => { + const tableData = doc.data(); + const tableNumber = tableData.number; + const tableNote = tableData.notes; + if (tableData.orders && Array.isArray(tableData.orders)) { + // Add table ID to each order for reference + const tableOrders = tableData.orders.map((order) => ({ + ...order, + tableId: doc.id, + tableNumber: tableNumber, + tableNote: tableNote, + })); + allOrders.push(...tableOrders); + } + }); + + // Sort orders by timestamp if available + allOrders.sort( + (a, b) => + (b.timestamp?.toMillis() || 0) - (a.timestamp?.toMillis() || 0) + ); + + setOrders(allOrders); + setLoading(false); + }); + + // Clean up the listener on component unmount + return () => unsubscribe(); + } catch (error) { + console.error("Error fetching orders:", error); + setLoading(false); + } + }; + + fetchOrders(); + }, []); + + const handleCompleteOrder = async (orderId, tableId) => { + try { + // Find the table document + const tableDocRef = doc( + db, + "hotel-details/seating-arrangement/tables", + tableId + ); + + // Get the current orders + const tableSnapshot = await getDoc(tableDocRef); + const currentOrders = tableSnapshot.data().orders; + + // Find the order to update + const updatedOrders = currentOrders.map((order) => + order.id === orderId ? { ...order, status: "COMPLETE" } : order + ); + + // Update the orders array in Firestore + await updateDoc(tableDocRef, { orders: updatedOrders }); + + // Update local state + setOrders((prevOrders) => + prevOrders.map((order) => + order.id === orderId ? { ...order, status: "COMPLETE" } : order + ) + ); + } catch (error) { + console.error("Error updating order status:", error); + } + }; + + if (loading) { + return Loading orders...; + } + + return ( + <> + + + ); +}; + +export default OrdersScreenContainer; diff --git a/screens/OrdersScreen/AddTable.js b/screens/TablesScreen/AddTable.js similarity index 100% rename from screens/OrdersScreen/AddTable.js rename to screens/TablesScreen/AddTable.js diff --git a/screens/OrdersScreen/OrderDetails.js b/screens/TablesScreen/OrderDetails.js similarity index 83% rename from screens/OrdersScreen/OrderDetails.js rename to screens/TablesScreen/OrderDetails.js index 77204bc..3900497 100644 --- a/screens/OrdersScreen/OrderDetails.js +++ b/screens/TablesScreen/OrderDetails.js @@ -1,9 +1,16 @@ import { ThemedText } from "@/components/common/ThemedText"; import { ThemedView } from "@/components/common/ThemedView"; -import React from "react"; +import React, { useEffect } from "react"; import { StyleSheet, FlatList } from "react-native"; +import { + aggregateOrders, + calculateOrderValue, +} from "../../utils/orderManagement"; + +const OrderDetails = ({ rawOrders }) => { + const orders = aggregateOrders(rawOrders); + const orderValue = calculateOrderValue(orders); -const OrderDetails = ({ order }) => { const renderOrderItem = ({ item }) => ( {item.name} @@ -19,15 +26,13 @@ const OrderDetails = ({ order }) => { Order Details item.name.toString() + item.category.toString()} /> Total: - - ₹{order.orderValue?.toFixed(2)} - + ₹{orderValue} ); diff --git a/screens/OrdersScreen/RestaurantTablesScreen.js b/screens/TablesScreen/RestaurantTablesScreen.js similarity index 91% rename from screens/OrdersScreen/RestaurantTablesScreen.js rename to screens/TablesScreen/RestaurantTablesScreen.js index 2033be1..3706403 100644 --- a/screens/OrdersScreen/RestaurantTablesScreen.js +++ b/screens/TablesScreen/RestaurantTablesScreen.js @@ -33,7 +33,9 @@ const RestaurantTablesScreen = () => { useEffect(() => { const fetchAllTables = async () => { try { - const querySnapshot = await getDocs(collection(db, "tables/")); + const querySnapshot = await getDocs( + collection(db, "hotel-details/seating-arrangement/tables/") + ); const items = querySnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), @@ -59,9 +61,9 @@ const RestaurantTablesScreen = () => { item.number = item.number ? item.number : tables.length + 1; item.searchableKey = item.number; item.status = "Available"; - item.orderCount = 0; - item.totalOrders = 0; - const docRef = doc(collection(db, "tables/")); + const docRef = doc( + collection(db, "hotel-details/seating-arrangement/tables/") + ); batch.set(docRef, item); newItems.push({ ...item, id: docRef.id }); }); @@ -87,7 +89,7 @@ const RestaurantTablesScreen = () => { const deleteTableDetails = async (id) => { try { - await deleteDoc(doc(db, "tables/", id)); + await deleteDoc(doc(db, "hotel-details/seating-arrangement/tables/", id)); setTables(tables.filter((item) => item.id !== id)); console.log("Document successfully deleted!"); } catch (error) { @@ -97,7 +99,7 @@ const RestaurantTablesScreen = () => { const updateTableDetails = async (id, updatedItem) => { try { - const itemRef = doc(db, "tables/", id); + const itemRef = doc(db, "hotel-details/seating-arrangement/tables/", id); await updateDoc(itemRef, updatedItem); setTables( tables.map((item) => @@ -128,25 +130,16 @@ const RestaurantTablesScreen = () => { setTableInfoOptionClicked(false); }; - const calculateOrderValue = (orders) => { - return orders.reduce((total, order) => total + order.itemValue, 0); - }; - - const calculateTotalOrderCount = (orders) => { - return orders.reduce((total, order) => total + order.quantity, 0); - }; - const handleTableOrderUpdate = (orders) => { const tableIndex = tables.findIndex((t) => t.id === selectedTable.id); if (tableIndex !== -1) { const updatedTable = { ...tables[tableIndex], orders }; - updatedTable.orderValue = calculateOrderValue(orders); - updatedTable.totalOrders = calculateTotalOrderCount(orders); const newTables = [...tables]; newTables[tableIndex] = updatedTable; + console.log(updatedTable); setTables(newTables); updateTableDetails(selectedTable.id, updatedTable); - } + } else console.error("Couldn't find table"); }; const updateSearch = (searchText) => { diff --git a/screens/OrdersScreen/TableList.js b/screens/TablesScreen/TableList.js similarity index 92% rename from screens/OrdersScreen/TableList.js rename to screens/TablesScreen/TableList.js index 2edac86..515b90c 100644 --- a/screens/OrdersScreen/TableList.js +++ b/screens/TablesScreen/TableList.js @@ -4,6 +4,10 @@ import { ThemedView } from "@/components/common/ThemedView"; import React, { useMemo, useState, useCallback, useEffect } from "react"; import { useWindowDimensions, FlatList, StyleSheet, View } from "react-native"; import Icon from "react-native-vector-icons/MaterialIcons"; +import { + calculateTotalOrderCount, + completedOrdersCount, +} from "@/utils/orderManagement"; const TableList = ({ tables, onTablePress, onOrderDetailsPress }) => { const { width } = useWindowDimensions(); @@ -91,13 +95,13 @@ const TableList = ({ tables, onTablePress, onOrderDetailsPress }) => { onPress={() => onTablePress(item)} lightBackgroundColor={getLightBackgroundColor( item.status, - item.orderCount, - item.totalOrders + completedOrdersCount(item.orders), + calculateTotalOrderCount(item.orders) )} darkBackgroundColor={getDarkBackgroundColor( item.status, - item.orderCount, - item.totalOrders + completedOrdersCount(item.orders), + calculateTotalOrderCount(item.orders) )} > Table - {item.number} @@ -118,13 +122,15 @@ const TableList = ({ tables, onTablePress, onOrderDetailsPress }) => { - Orders: {item.orderCount || 0}/{item.totalOrders || 0} + Orders: {completedOrdersCount(item.orders) || 0}/ + {calculateTotalOrderCount(item.orders) || 0} {item.status === "Occupied" && - item.orderCount && - item.totalOrders && - item.orderCount < item.totalOrders + completedOrdersCount(item.orders) && + calculateTotalOrderCount(item.orders) && + completedOrdersCount(item.orders) < + calculateTotalOrderCount(item.orders) ? `ETA: ${item.eta ? Math.floor(item.eta / 60) : 0}${ item.eta ? ":" : "" }${item.eta ? String(item.eta % 60).padStart(2, "0") : ""} min` diff --git a/screens/OrdersScreen/TableManagement.js b/screens/TablesScreen/TableManagement.js similarity index 99% rename from screens/OrdersScreen/TableManagement.js rename to screens/TablesScreen/TableManagement.js index ed16d4f..364d9dc 100644 --- a/screens/OrdersScreen/TableManagement.js +++ b/screens/TablesScreen/TableManagement.js @@ -149,7 +149,7 @@ const TableManagement = ({ table, onUpdateTable, onClose }) => { > Book Table - + ); diff --git a/utils/orderManagement.js b/utils/orderManagement.js new file mode 100644 index 0000000..cb4166e --- /dev/null +++ b/utils/orderManagement.js @@ -0,0 +1,50 @@ +export function aggregateOrders(rawOrders) { + if (!rawOrders || rawOrders.length === 0) return []; + + const aggregatedOrders = rawOrders.reduce((acc, rawOrder) => { + const existingOrder = acc.find( + (order) => + order.name === rawOrder.name && order.category === rawOrder.category + ); + + if (existingOrder) { + existingOrder.quantity += 1; + existingOrder.itemValue += rawOrder.price; + } else { + acc.push({ + name: rawOrder.name, + category: rawOrder.category, + cuisine: rawOrder.cuisine, + menuItemId: rawOrder.menuItemId, + image: rawOrder.image, + searchableKey: rawOrder.searchableKey, + dietaryPreference: rawOrder.type || rawOrder.dietaryPreference, + price: rawOrder.price, + quantity: 1, + itemValue: rawOrder.price, + }); + } + + return acc; + }, []); + + return aggregatedOrders; +} + +export function calculateOrderValue(orders) { + return !orders + ? 0 + : orders.reduce((total, order) => total + order.price * order.quantity, 0); +} + +export function calculateTotalOrderCount(orders) { + return !orders + ? 0 + : orders.reduce((total, order) => total + order.quantity, 0); +} + +export function completedOrdersCount(orders) { + return !orders + ? 0 + : orders.filter((order) => order.status !== "ACTIVE").length; +} diff --git a/utils/uuidGenerator.js b/utils/uuidGenerator.js new file mode 100644 index 0000000..7cc18f5 --- /dev/null +++ b/utils/uuidGenerator.js @@ -0,0 +1,7 @@ +export function generateUUID() { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) { + const r = (Math.random() * 16) | 0; + const v = c === "x" ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); +}