Cloud Kitchen Mobile App
1 Architecture Overview
The Tech Stack
- ✅ FastAPI – High-performance Async API
- ✅ React Native
- ✅ Firebase (for authentication)
- ✅ MySQL (database)
Project Structure
frontend/ ├── app │ └── UserDashboard.tsx└── ...├── assets/images└── components└── README.md└── constants└── hooks└── scripts└── app.json└── eslint.config.js└── expo-env.d.ts└── app.json└── package-lock.json└── package.json└── tsconfig.json
Frontend Implementation
frontend/index.html
import React, { useState, useEffect } from "react";
import { View, Text, TouchableOpacity, ScrollView, StyleSheet, TextInput } from "react-native";
import PizzaList from "./PizzaList";
import Cart from "./Cart";
import { signOut } from "firebase/auth";
import { auth } from "@/components/firebase";
import { useRouter } from "expo-router";
const UserDashboard = ({ user = { email: "" } }) => {
const [cart, setCart] = useState<any[]>([]);
const [orders, setOrders] = useState<any[]>([]);
const [fromDate, setFromDate] = useState("");
const [toDate, setToDate] = useState("");
const [quickFilter, setQuickFilter] = useState("all");
const router = useRouter();
const getToken = async () => auth.currentUser?.getIdToken();
const fetchOrders = async (f = fromDate, t = toDate) => {
try {
const token = await getToken();
if (!token) return;
const res = await fetch(`http://localhost:8000/api/orders/me?from_date=${f}&to_date=${t}`, {
headers: { Authorization: `Bearer ${token}` },
});
setOrders(await res.json());
} catch (err: any) { alert(err.message); }
};
useEffect(() => { fetchOrders(); }, []);
const applyQuickFilter = (type: string) => {
setQuickFilter(type);
let f = "", t = "";
const today = new Date().toISOString().split("T")[0];
if (type === "today") { f = today; t = today; }
else if (type === "week") {
const d = new Date(); d.setDate(d.getDate() - 7);
f = d.toISOString().split("T")[0]; t = today;
}
setFromDate(f); setToDate(t);
fetchOrders(f, t);
};
const placeOrder = async () => {
if (cart.length === 0) return alert("Cart empty");
try {
const token = await getToken();
const res = await fetch("http://localhost:8000/api/orders", {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
body: JSON.stringify({ userEmail: user.email, items: cart, total: cart.reduce((s, p) => s + p.price, 0) }),
});
if (res.ok) { alert("Success!"); setCart([]); fetchOrders(); }
} catch (err: any) { alert(err.message); }
};
return (
<ScrollView style={styles.container}>
<View style={styles.header}>
<Text>Welcome, <Text style={styles.bold}>{user?.email}</Text></Text>
<TouchableOpacity style={styles.logoutButton} onPress={() => signOut(auth).then(() => router.replace("/"))}>
<Text>Logout</Text>
</TouchableOpacity>
</View>
{/* COMPACT FILTER SECTION */}
<View style={styles.filterBox}>
<View style={styles.row}>
<TextInput style={styles.input} placeholder="From: YYYY-MM-DD" value={fromDate} onChangeText={setFromDate} />
<TextInput style={styles.input} placeholder="To: YYYY-MM-DD" value={toDate} onChangeText={setToDate} />
<TouchableOpacity style={styles.goBtn} onPress={() => fetchOrders()}>
<Text style={{ color: "#fff", fontSize: 12 }}>Go</Text>
</TouchableOpacity>
</View>
<View style={[styles.row, { marginTop: 8 }]}>
{["all", "today", "week"].map((f) => (
<TouchableOpacity key={f} onPress={() => applyQuickFilter(f)} style={[styles.chip, quickFilter === f && styles.activeChip]}>
<Text style={{ fontSize: 10 }}>{f.toUpperCase()}</Text>
</TouchableOpacity>
))}
</View>
</View>
<PizzaList addToCart={(p: any) => setCart([...cart, p])} />
<Cart cart={cart} placeOrder={placeOrder} />
<View style={styles.ordersContainer}>
<Text style={styles.bold}>Previous Orders</Text>
{orders.length === 0 ? <Text>No orders yet</Text> : orders.map((o) => (
<View key={o.id} style={styles.orderCard}>
<View style={styles.orderHeader}>
<Text style={styles.bold}>{o.status}</Text>
<Text>₹{o.total}</Text>
</View>
<Text style={{fontSize: 11, color: '#666'}}>{o.created_at?.split('T')[0]}</Text>
{o.items.map((it: any, i: number) => <Text key={i} style={{fontSize: 12}}>• {it.name}</Text>)}
</View>
))}
</View>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: { flex: 1, padding: 16 },
header: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", marginBottom: 16 },
bold: { fontWeight: "bold" },
logoutButton: { padding: 6, backgroundColor: "#ddd", borderRadius: 4 },
filterBox: { marginBottom: 16, padding: 10, backgroundColor: "#f0f0f0", borderRadius: 8 },
row: { flexDirection: "row", gap: 6 },
input: { flex: 1, borderWidth: 1, borderColor: "#ccc", padding: 6, borderRadius: 4, fontSize: 11, backgroundColor: "#fff" },
goBtn: { backgroundColor: "#000", paddingHorizontal: 12, justifyContent: "center", borderRadius: 4 },
chip: { paddingVertical: 4, paddingHorizontal: 8, borderWidth: 1, borderColor: "#ccc", borderRadius: 12 },
activeChip: { backgroundColor: "#ddd" },
ordersContainer: { marginTop: 16 },
orderCard: { padding: 10, borderWidth: 1, borderColor: "#ccc", borderRadius: 6, marginBottom: 8 },
orderHeader: { flexDirection: "row", justifyContent: "space-between" },
});
export default UserDashboard;