From 0fcbb065ea98c2ba11a8fe7bd5858eb299f21c56 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Tue, 14 Oct 2025 03:03:16 +0000 Subject: [PATCH] 0001 --- add_contact.php | 57 +++ add_menu_item.php | 48 ++ assets/css/custom.css | 477 ++++++++++++++++++ assets/images/placeholder.jpg | 0 assets/js/main.js | 100 ++++ cart.php | 218 ++++++++ checkout.php | 162 ++++++ customer_dashboard.php | 100 ++++ customer_signup.php | 99 ++++ customer_signup_handler.php | 49 ++ dashboard.php | 184 +++++++ db/migrate.php | 34 ++ .../002_create_restaurants_and_menu_items.sql | 39 ++ ...ate_users_table_and_update_restaurants.sql | 14 + db/migrations/004_create_orders_tables.sql | 22 + db/migrations/005_add_user_id_to_orders.sql | 5 + index.php | 272 +++++----- login.php | 139 +++++ login_handler.php | 47 ++ logout.php | 22 + menu.php | 120 +++++ order_confirmation.php | 97 ++++ place_order.php | 117 +++++ restaurant_signup.php | 111 ++++ restaurants.php | 73 +++ signup.php | 59 +++ update_order_status.php | 33 ++ 27 files changed, 2553 insertions(+), 145 deletions(-) create mode 100644 add_contact.php create mode 100644 add_menu_item.php create mode 100644 assets/css/custom.css create mode 100644 assets/images/placeholder.jpg create mode 100644 assets/js/main.js create mode 100644 cart.php create mode 100644 checkout.php create mode 100644 customer_dashboard.php create mode 100644 customer_signup.php create mode 100644 customer_signup_handler.php create mode 100644 dashboard.php create mode 100644 db/migrate.php create mode 100644 db/migrations/002_create_restaurants_and_menu_items.sql create mode 100644 db/migrations/003_create_users_table_and_update_restaurants.sql create mode 100644 db/migrations/004_create_orders_tables.sql create mode 100644 db/migrations/005_add_user_id_to_orders.sql create mode 100644 login.php create mode 100644 login_handler.php create mode 100644 logout.php create mode 100644 menu.php create mode 100644 order_confirmation.php create mode 100644 place_order.php create mode 100644 restaurant_signup.php create mode 100644 restaurants.php create mode 100644 signup.php create mode 100644 update_order_status.php diff --git a/add_contact.php b/add_contact.php new file mode 100644 index 0000000..5ef578a --- /dev/null +++ b/add_contact.php @@ -0,0 +1,57 @@ + false, 'message' => 'An unexpected error occurred.']; + +if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + $response['message'] = 'Invalid request method.'; + echo json_encode($response); + exit; +} + +$name = trim($_POST['name'] ?? ''); +$email = trim($_POST['email'] ?? ''); +$message = trim($_POST['message'] ?? ''); + +if (empty($name) || empty($email) || empty($message)) { + $response['message'] = 'Please fill out all fields.'; + echo json_encode($response); + exit; +} + +if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + $response['message'] = 'Invalid email format.'; + echo json_encode($response); + exit; +} + +try { + $pdo = db(); + $stmt = $pdo->prepare("INSERT INTO contact_submissions (name, email, message) VALUES (?, ?, ?)"); + $stmt->execute([$name, $email, $message]); + + // Send email notification + $mailResult = MailService::sendContactMessage($name, $email, $message); + + if ($mailResult['success']) { + $response['success'] = true; + $response['message'] = 'Message sent successfully!'; + } else { + // Still a success for the user, but log the email error + error_log("MailService Error: " . ($mailResult['error'] ?? 'Unknown error')); + $response['success'] = true; + $response['message'] = 'Message saved, but could not send notification email.'; + } + +} catch (PDOException $e) { + error_log("Database Error: " . $e->getMessage()); + $response['message'] = 'Error saving your message to the database.'; +} catch (Exception $e) { + error_log("General Error: " . $e->getMessage()); + $response['message'] = 'An unexpected error occurred.'; +} + +echo json_encode($response); \ No newline at end of file diff --git a/add_menu_item.php b/add_menu_item.php new file mode 100644 index 0000000..ce5634f --- /dev/null +++ b/add_menu_item.php @@ -0,0 +1,48 @@ +prepare("SELECT id FROM restaurants WHERE id = ? AND user_id = ?"); +$stmt->execute([$restaurant_id, $_SESSION['user_id']]); +if (!$stmt->fetch()) { + die('You do not have permission to add items to this restaurant.'); +} + +try { + $stmt_insert = $pdo->prepare( + "INSERT INTO menu_items (restaurant_id, name, description, price) VALUES (?, ?, ?, ?)" + ); + $stmt_insert->execute([$restaurant_id, $item_name, $item_description, $item_price]); + + // Redirect back to the dashboard + header("Location: dashboard.php?item_added=success"); + exit(); + +} catch (PDOException $e) { + // In a real app, you would log this error + die("Error adding menu item: " . $e->getMessage()); +} diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..e7164a5 --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,477 @@ +:root { + --primary-color: #40E0D0; /* Turquoise */ + --primary-hover-color: #36c4b5; + --secondary-color: #2E8B57; /* SeaGreen */ + --background-color: #F8F9FA; + --surface-color: #FFFFFF; + --text-color: #212529; + --text-muted-color: #6c757d; + --base-spacing: 1rem; + --border-radius: 0.5rem; + --box-shadow: 0 4px 6px rgba(0,0,0,0.1); +} + +body { + font-family: 'Poppins', sans-serif; + margin: 0; + background-color: var(--background-color); + color: var(--text-color); + line-height: 1.6; +} + +.container { + max-width: 1140px; + margin: 0 auto; + padding: 0 var(--base-spacing); +} + +.btn { + display: inline-block; + padding: 0.75rem 1.5rem; + border-radius: var(--border-radius); + text-decoration: none; + font-weight: 600; + transition: transform 0.2s, background-color 0.2s; + border: none; + cursor: pointer; +} + +.btn:hover { + transform: translateY(-2px); +} + +.btn-primary { + background-color: var(--primary-color); + color: var(--text-color); +} + +.btn-primary:hover { + background-color: var(--primary-hover-color); +} + +.navbar { + padding: var(--base-spacing) 0; + background-color: var(--surface-color); + box-shadow: var(--box-shadow); + position: sticky; + top: 0; + z-index: 1000; +} + +.navbar-inner { + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo { + font-size: 1.5rem; + font-weight: 700; + color: var(--secondary-color); + text-decoration: none; +} + +.nav-links a { + margin-left: 1.5rem; + text-decoration: none; + color: var(--text-color); + font-weight: 600; +} + +.hero { + padding: 6rem 0; + text-align: center; + background: linear-gradient(45deg, var(--primary-color), var(--secondary-color)); + color: white; +} + +.hero-content h1 { + font-size: 3rem; + margin-bottom: var(--base-spacing); + font-weight: 700; +} + +.hero-content p { + font-size: 1.2rem; + margin-bottom: 2rem; +} + +.section { + padding: 4rem 0; +} + +.section-alt { + background-color: var(--surface-color); + padding: 4rem 0; +} + +h2 { + text-align: center; + font-size: 2.5rem; + margin-bottom: 3rem; + color: var(--secondary-color); + font-weight: 700; +} + +.steps { + display: flex; + justify-content: space-around; + text-align: center; + gap: 2rem; +} + +.step { + max-width: 300px; +} + +.step i { + width: 48px; + height: 48px; + color: var(--primary-color); + margin-bottom: 1rem; +} + +.step h3 { + font-size: 1.5rem; + margin-bottom: 0.5rem; +} + +.contact-content { + max-width: 600px; + margin: 0 auto; + text-align: center; +} + +.contact-form { + margin-top: 2rem; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.form-group input, .form-group textarea { + width: 100%; + padding: 0.75rem; + border: 1px solid #ccc; + border-radius: var(--border-radius); + font-family: 'Poppins', sans-serif; + box-sizing: border-box; +} + +footer { + padding: 2rem 0; + text-align: center; + background-color: var(--surface-color); + margin-top: 4rem; +} + +.form-message { + margin-top: 1rem; + padding: 1rem; + border-radius: var(--border-radius); + display: none; +} + +.form-message.success { + background-color: #d4edda; + color: #155724; + display: block; +} + +.form-message.error { + background-color: #f8d7da; + color: #721c24; + display: block; +} + +.page-title { + font-size: 2.5rem; + font-weight: 700; + color: var(--secondary-color); + text-align: center; + margin-bottom: 3rem; +} + +.restaurants-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 2rem; +} + +.restaurant-card { + background-color: var(--surface-color); + border-radius: var(--border-radius); + box-shadow: var(--box-shadow); + overflow: hidden; + transition: transform 0.3s ease, box-shadow 0.3s ease; + text-decoration: none; + color: inherit; + display: flex; + flex-direction: column; +} + +.restaurant-card:hover { + transform: translateY(-5px); + box-shadow: 0 8px 12px rgba(0,0,0,0.15); +} + +.restaurant-card-image { + width: 100%; + height: 200px; + background-size: cover; + background-position: center; +} + +.restaurant-card-content { + padding: 1.5rem; + flex-grow: 1; +} + +.restaurant-card-content h3 { + margin-top: 0; + font-size: 1.5rem; + font-weight: 600; + color: var(--secondary-color); +} + +.restaurant-card-content p { + color: var(--text-muted-color); + line-height: 1.6; +} + +.menu-header { + padding: 4rem 0; + background-size: cover; + background-position: center; + color: white; + text-align: center; +} + +.menu-header h1 { + font-size: 3rem; + font-weight: 700; + margin: 0; +} + +.menu-header p { + font-size: 1.2rem; + max-width: 600px; + margin: 0.5rem auto 0; +} + +.section-title { + font-size: 2rem; + font-weight: 700; + color: var(--secondary-color); + text-align: center; + margin-bottom: 2rem; +} + +.menu-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 2rem; +} + +.menu-item-card { + background-color: var(--surface-color); + border-radius: var(--border-radius); + box-shadow: var(--box-shadow); + overflow: hidden; + display: flex; + flex-direction: column; +} + +.menu-item-image { + width: 100%; + height: 180px; + background-size: cover; + background-position: center; +} + +.menu-item-content { + padding: 1.5rem; + flex-grow: 1; + display: flex; + flex-direction: column; +} + +.menu-item-content h3 { + margin-top: 0; + font-size: 1.3rem; + font-weight: 600; + color: var(--secondary-color); +} + +.menu-item-content p { + color: var(--text-muted-color); + line-height: 1.6; + flex-grow: 1; + margin-bottom: 1rem; +} + +.menu-item-footer { + display: flex; + justify-content: space-between; + align-items: center; +} + +.menu-item-price { + font-size: 1.2rem; + font-weight: 700; + color: var(--primary-color); +} + +.cart-icon { + position: relative; +} + +#cart-count { + position: absolute; + top: -8px; + right: -8px; + background-color: #E04050; + color: white; + border-radius: 50%; + padding: 2px 6px; + font-size: 0.75rem; + font-weight: 700; + display: none; /* Hidden by default */ +} + +.dashboard-content { + background-color: var(--surface-color); + padding: 2rem; + border-radius: var(--border-radius); + box-shadow: var(--box-shadow); +} + +.table { + width: 100%; + border-collapse: collapse; +} + +.table th, .table td { + padding: 1rem; + text-align: left; + border-bottom: 1px solid #eee; +} + +.table th { + background-color: #f7f7f7; + font-weight: 600; +} + +.status { + padding: 0.25rem 0.5rem; + border-radius: 0.25rem; + font-weight: 600; + font-size: 0.9rem; +} + +.status-pending { + background-color: #fff3cd; + color: #856404; +} + +.status-confirmed { + background-color: #d4edda; + color: #155724; +} + +.status-delivered { + background-color: #d1ecf1; + color: #0c5460; +} + +.status-cancelled { + background-color: #f8d7da; + color: #721c24; +} + +.dashboard-header { + text-align: center; + margin-bottom: 2rem; +} + +.tabs { + display: flex; + justify-content: center; + margin-bottom: 2rem; +} + +.tab-link { + padding: 0.75rem 1.5rem; + border: none; + background-color: transparent; + cursor: pointer; + font-size: 1.1rem; + font-weight: 600; + color: var(--text-muted-color); + border-bottom: 2px solid transparent; + transition: color 0.2s, border-bottom-color 0.2s; +} + +.tab-link.active, .tab-link:hover { + color: var(--secondary-color); + border-bottom-color: var(--secondary-color); +} + +.tab-content { + display: none; +} + +.tab-content.active { + display: block; +} + +.order-list { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); + gap: 1.5rem; +} + +.order-card { + background-color: var(--surface-color); + border-radius: var(--border-radius); + box-shadow: var(--box-shadow); + display: flex; + flex-direction: column; +} + +.order-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem 1.5rem; + border-bottom: 1px solid #eee; +} + +.order-id { + font-weight: 700; + font-size: 1.1rem; +} + +.order-body { + padding: 1rem 1.5rem; + flex-grow: 1; +} + +.order-footer { + padding: 1rem 1.5rem; + background-color: #f7f7f7; + border-top: 1px solid #eee; +} + +.order-footer form { + display: flex; + justify-content: space-between; + align-items: center; +} + +.form-card { + background-color: var(--surface-color); + padding: 2rem; + border-radius: var(--border-radius); + box-shadow: var(--box-shadow); +} \ No newline at end of file diff --git a/assets/images/placeholder.jpg b/assets/images/placeholder.jpg new file mode 100644 index 0000000..e69de29 diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..585c7fd --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,100 @@ +document.addEventListener('DOMContentLoaded', function() { + const contactForm = document.getElementById('contactForm'); + const formMessage = document.getElementById('form-message'); + + if (contactForm) { + contactForm.addEventListener('submit', function(event) { + event.preventDefault(); + + const name = document.getElementById('name').value.trim(); + const email = document.getElementById('email').value.trim(); + const message = document.getElementById('message').value.trim(); + + if (!name || !email || !message) { + showMessage('Please fill out all fields.', 'error'); + return; + } + + if (!validateEmail(email)) { + showMessage('Please enter a valid email address.', 'error'); + return; + } + + const formData = new FormData(contactForm); + + fetch('add_contact.php', { + method: 'POST', + body: formData + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + showMessage('Thank you for your message! We will get back to you soon.', 'success'); + contactForm.reset(); + } else { + showMessage(data.message || 'An error occurred. Please try again.', 'error'); + } + }) + .catch(error => { + console.error('Error:', error); + showMessage('An error occurred. Please try again.', 'error'); + }); + }); + } + + function showMessage(message, type) { + formMessage.textContent = message; + formMessage.className = type; + } + + function validateEmail(email) { + const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return re.test(String(email).toLowerCase()); + } +}); + +// --- Shopping Cart --- +const shoppingCart = { + getCart: () => { + const cart = localStorage.getItem('shoppingCart'); + return cart ? JSON.parse(cart) : { items: [], restaurantId: null }; + }, + saveCart: (cart) => { + localStorage.setItem('shoppingCart', JSON.stringify(cart)); + }, + addItem: (item, restaurantId) => { + const cart = shoppingCart.getCart(); + if (cart.restaurantId && cart.restaurantId !== restaurantId) { + // If the item is from a different restaurant, ask for confirmation to clear the cart + if (!confirm('You have items from another restaurant in your cart. Do you want to clear your cart and add this item?')) { + return; + } + // Clear the cart + cart.items = []; + } + + cart.restaurantId = restaurantId; + const existingItem = cart.items.find(i => i.id === item.id); + + if (existingItem) { + existingItem.quantity++; + } else { + cart.items.push({ ...item, quantity: 1 }); + } + shoppingCart.saveCart(cart); + shoppingCart.updateCartCount(); + alert(`${item.name} has been added to your cart.`); + }, + updateCartCount: () => { + const cart = shoppingCart.getCart(); + const cartCount = cart.items.reduce((total, item) => total + item.quantity, 0); + const cartCountElement = document.getElementById('cart-count'); + if (cartCountElement) { + cartCountElement.textContent = cartCount; + } + } +}; + +document.addEventListener('DOMContentLoaded', () => { + shoppingCart.updateCartCount(); +}); diff --git a/cart.php b/cart.php new file mode 100644 index 0000000..1eb1000 --- /dev/null +++ b/cart.php @@ -0,0 +1,218 @@ + + + + + + Your Cart - MajuroEats + + + + + + + + +
+

Your Shopping Cart

+
+ +
+
+ + + + + diff --git a/checkout.php b/checkout.php new file mode 100644 index 0000000..716d378 --- /dev/null +++ b/checkout.php @@ -0,0 +1,162 @@ + + + + + + + Checkout - MajuroEats + + + + + + + +
+

Checkout

+
+
+

Delivery Information

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+

Your Order

+
+ +
+

+
+
+
+ + + + + diff --git a/customer_dashboard.php b/customer_dashboard.php new file mode 100644 index 0000000..3c3a644 --- /dev/null +++ b/customer_dashboard.php @@ -0,0 +1,100 @@ +prepare( + "SELECT o.id, o.created_at, o.total_price, o.status, r.name as restaurant_name + FROM orders o + JOIN restaurants r ON o.restaurant_id = r.id + WHERE o.user_id = ? + ORDER BY o.created_at DESC" +); +$stmt->execute([$_SESSION['user_id']]); +$orders = $stmt->fetchAll(PDO::FETCH_ASSOC); + +?> + + + + + + My Account - MajuroEats + + + + + + +
+

Your Order History

+
+ 0): ?> + + + + + + + + + + + + + + + + + + + + + +
Order IDDateRestaurantTotalStatus
#$
+ +

You have not placed any orders yet. Start your first order!

+ +
+
+ + + + + + + \ No newline at end of file diff --git a/customer_signup.php b/customer_signup.php new file mode 100644 index 0000000..755d595 --- /dev/null +++ b/customer_signup.php @@ -0,0 +1,99 @@ + + + + + + Sign Up - MajuroEats + + + + + +
+

Create Your Account

+
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ + diff --git a/customer_signup_handler.php b/customer_signup_handler.php new file mode 100644 index 0000000..d803b62 --- /dev/null +++ b/customer_signup_handler.php @@ -0,0 +1,49 @@ +prepare("SELECT id FROM users WHERE email = ?"); +$stmt->execute([$email]); +if ($stmt->fetch()) { + die('An account with this email already exists. Log in here.'); +} + +try { + // Create the user with the 'customer' role + $stmt_user = $pdo->prepare( + "INSERT INTO users (name, email, password, role) VALUES (?, ?, ?, 'customer')" + ); + $stmt_user->execute([$name, $email, $password]); + + // Redirect to the login page with a success message + header("Location: login.php?signup=success"); + exit(); + +} catch (PDOException $e) { + // In a real app, you would log this error + die("Error creating account: " . $e->getMessage()); +} diff --git a/dashboard.php b/dashboard.php new file mode 100644 index 0000000..ef8a91b --- /dev/null +++ b/dashboard.php @@ -0,0 +1,184 @@ +prepare("SELECT id, name FROM restaurants WHERE user_id = ?"); +$stmt->execute([$_SESSION['user_id']]); +$restaurant = $stmt->fetch(PDO::FETCH_ASSOC); + +$orders = []; +$menu_items = []; + +if ($restaurant) { + $stmt_orders = $pdo->prepare( + "SELECT o.*, GROUP_CONCAT(CONCAT(oi.quantity, ' x ', mi.name) SEPARATOR '
') as items + FROM orders o + JOIN order_items oi ON o.id = oi.order_id + JOIN menu_items mi ON oi.menu_item_id = mi.id + WHERE o.restaurant_id = ? + GROUP BY o.id + ORDER BY o.created_at DESC + LIMIT 20" + ); + $stmt_orders->execute([$restaurant['id']]); + $orders = $stmt_orders->fetchAll(PDO::FETCH_ASSOC); + + $stmt_menu = $pdo->prepare("SELECT id, name, description, price FROM menu_items WHERE restaurant_id = ? ORDER BY id DESC"); + $stmt_menu->execute([$restaurant['id']]); + $menu_items = $stmt_menu->fetchAll(PDO::FETCH_ASSOC); +} +?> + + + + + + Dashboard - MajuroEats + + + + + + + +
+

Restaurant Dashboard

+ + +
+

+
+ +
+ + +
+ +
+
+

Recent Orders

+ 0): ?> +
+ +
+
+ Order # + +
+
+

Customer:

+

Items:

+
+ +
+ +
+ +

You have no recent orders.

+ +
+
+ + + + +

You do not have a restaurant associated with your account.

+ +
+ + + + + + \ No newline at end of file diff --git a/db/migrate.php b/db/migrate.php new file mode 100644 index 0000000..2153200 --- /dev/null +++ b/db/migrate.php @@ -0,0 +1,34 @@ + PDO::ERRMODE_EXCEPTION, + ]); + $pdo_admin->exec('CREATE DATABASE IF NOT EXISTS '.DB_NAME); + echo "Database '" . DB_NAME . "' created or already exists.\n"; + + // Now connect to the database to run migrations + $pdo = db(); + + $migration_files = glob(__DIR__ . '/migrations/*.sql'); + sort($migration_files); + + foreach ($migration_files as $file) { + echo "Running migration: " . basename($file) . "...\n"; + $sql = file_get_contents($file); + try { + $pdo->exec($sql); + echo "Migration " . basename($file) . " completed.\n"; + } catch (PDOException $e) { + echo "Error in migration " . basename($file) . ": " . $e->getMessage() . "\n"; + } + } + + echo "All migrations completed successfully.\n"; + +} catch (PDOException $e) { + die("DB ERROR: " . $e->getMessage()); +} + diff --git a/db/migrations/002_create_restaurants_and_menu_items.sql b/db/migrations/002_create_restaurants_and_menu_items.sql new file mode 100644 index 0000000..abdd6b1 --- /dev/null +++ b/db/migrations/002_create_restaurants_and_menu_items.sql @@ -0,0 +1,39 @@ +CREATE TABLE IF NOT EXISTS restaurants ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT, + image_url VARCHAR(255) +); + +CREATE TABLE IF NOT EXISTS menu_items ( + id INT AUTO_INCREMENT PRIMARY KEY, + restaurant_id INT NOT NULL, + name VARCHAR(255) NOT NULL, + description TEXT, + price DECIMAL(10, 2) NOT NULL, + image_url VARCHAR(255), + FOREIGN KEY (restaurant_id) REFERENCES restaurants(id) ON DELETE CASCADE +); + +-- Seed data for restaurants +INSERT INTO restaurants (name, description, image_url) VALUES +('Pizza Palace', 'The best pizza in town. Made with fresh, locally-sourced ingredients.', 'assets/images/placeholder.jpg'), +('Burger Barn', 'Home of the juiciest burgers and crispiest fries. A family favorite.', 'assets/images/placeholder.jpg'), +('Taco Town', 'Authentic Mexican street tacos, burritos, and more. A flavor fiesta!', 'assets/images/placeholder.jpg'); + +-- Seed data for menu_items +INSERT INTO menu_items (restaurant_id, name, description, price, image_url) VALUES +-- Pizza Palace +(1, 'Margherita Pizza', 'Classic cheese and tomato pizza with a touch of basil.', 12.99, 'assets/images/placeholder.jpg'), +(1, 'Pepperoni Pizza', 'A classic favorite, loaded with premium pepperoni.', 14.99, 'assets/images/placeholder.jpg'), +(1, 'Veggie Supreme', 'A delicious mix of fresh garden vegetables on our signature crust.', 15.99, 'assets/images/placeholder.jpg'), + +-- Burger Barn +(2, 'Classic Burger', 'A timeless 1/4 lb beef patty with lettuce, tomato, and our secret sauce.', 9.99, 'assets/images/placeholder.jpg'), +(2, 'Cheese Burger', 'Our classic burger with a slice of melted American cheese.', 10.99, 'assets/images/placeholder.jpg'), +(2, 'Bacon Deluxe', 'For the bacon lovers! Our cheese burger topped with crispy bacon.', 11.99, 'assets/images/placeholder.jpg'), + +-- Taco Town +(3, 'Carne Asada Taco', 'Grilled steak taco on a fresh corn tortilla with cilantro and onions.', 3.50, 'assets/images/placeholder.jpg'), +(3, 'Al Pastor Taco', 'Marinated pork taco with pineapple, cilantro, and onions.', 3.50, 'assets/images/placeholder.jpg'), +(3, 'Chicken Tinga Burrito', 'A large flour tortilla filled with shredded chicken, rice, beans, and cheese.', 8.99, 'assets/images/placeholder.jpg'); diff --git a/db/migrations/003_create_users_table_and_update_restaurants.sql b/db/migrations/003_create_users_table_and_update_restaurants.sql new file mode 100644 index 0000000..2d91c49 --- /dev/null +++ b/db/migrations/003_create_users_table_and_update_restaurants.sql @@ -0,0 +1,14 @@ +CREATE TABLE IF NOT EXISTS users ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL UNIQUE, + password VARCHAR(255) NOT NULL, + role ENUM('customer', 'restaurant_owner', 'admin') NOT NULL DEFAULT 'customer', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +ALTER TABLE restaurants +ADD COLUMN user_id INT, +ADD CONSTRAINT fk_user +FOREIGN KEY (user_id) REFERENCES users(id) +ON DELETE SET NULL; diff --git a/db/migrations/004_create_orders_tables.sql b/db/migrations/004_create_orders_tables.sql new file mode 100644 index 0000000..b4dc81c --- /dev/null +++ b/db/migrations/004_create_orders_tables.sql @@ -0,0 +1,22 @@ +CREATE TABLE IF NOT EXISTS orders ( + id INT AUTO_INCREMENT PRIMARY KEY, + restaurant_id INT NOT NULL, + customer_name VARCHAR(255) NOT NULL, + customer_email VARCHAR(255) NOT NULL, + customer_phone VARCHAR(50), + delivery_address TEXT NOT NULL, + total_price DECIMAL(10, 2) NOT NULL, + status VARCHAR(50) NOT NULL DEFAULT 'pending', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (restaurant_id) REFERENCES restaurants(id) +); + +CREATE TABLE IF NOT EXISTS order_items ( + id INT AUTO_INCREMENT PRIMARY KEY, + order_id INT NOT NULL, + menu_item_id INT NOT NULL, + quantity INT NOT NULL, + price DECIMAL(10, 2) NOT NULL, + FOREIGN KEY (order_id) REFERENCES orders(id), + FOREIGN KEY (menu_item_id) REFERENCES menu_items(id) +); diff --git a/db/migrations/005_add_user_id_to_orders.sql b/db/migrations/005_add_user_id_to_orders.sql new file mode 100644 index 0000000..a9bc086 --- /dev/null +++ b/db/migrations/005_add_user_id_to_orders.sql @@ -0,0 +1,5 @@ +ALTER TABLE orders +ADD COLUMN user_id INT NULL, +ADD CONSTRAINT fk_order_user +FOREIGN KEY (user_id) REFERENCES users(id) +ON DELETE SET NULL; \ No newline at end of file diff --git a/index.php b/index.php index 7205f3d..c8678eb 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,132 @@ - - + - - - New Style - - - - - - - - - - - - - - - - - - - + + + MajuroEats - Food Delivery in Majuro + + + -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

-
-
- + + + +
+
+
+
+

Your Favorite Food, Delivered in Majuro

+

Fresh, fast, and reliable. The best local restaurants at your fingertips.

+ Browse Restaurants +
+
+
+ +
+

How It Works

+
+
+ +

1. Discover

+

Browse menus from the best local restaurants.

+
+
+ +

2. Order

+

Select your favorite dishes and place your order in seconds.

+
+
+ +

3. Enjoy

+

Get your food delivered hot and fresh to your doorstep.

+
+
+
+ +
+
+

Contact Us

+
+

Have a question or feedback? Get in touch with us!

+
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + + + + + - + \ No newline at end of file diff --git a/login.php b/login.php new file mode 100644 index 0000000..e749e93 --- /dev/null +++ b/login.php @@ -0,0 +1,139 @@ + + + + + + + Login - MajuroEats + + + + + + +
+

Partner Login

+ + +
+ Your account has been created successfully! Please log in. +
+ + + +
+ +
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ + + diff --git a/login_handler.php b/login_handler.php new file mode 100644 index 0000000..9b0bbf9 --- /dev/null +++ b/login_handler.php @@ -0,0 +1,47 @@ +prepare("SELECT id, name, email, password, role FROM users WHERE email = ?"); +$stmt->execute([$email]); +$user = $stmt->fetch(PDO::FETCH_ASSOC); + +if ($user && password_verify($password, $user['password'])) { + // Regenerate session ID to prevent session fixation + session_regenerate_id(true); + + // Store user info in session + $_SESSION['user_id'] = $user['id']; + $_SESSION['user_name'] = $user['name']; + $_SESSION['user_email'] = $user['email']; + $_SESSION['user_role'] = $user['role']; + + // Redirect based on role + if ($user['role'] === 'restaurant_owner') { + header("Location: dashboard.php"); + } elseif ($user['role'] === 'customer') { + header("Location: customer_dashboard.php"); + } else { + // For any other roles, or if role is not set, redirect to a generic page or show an error + header("Location: index.php"); + } + exit(); +} else { + header('Location: login.php?error=invalid'); + exit(); +} diff --git a/logout.php b/logout.php new file mode 100644 index 0000000..78b20aa --- /dev/null +++ b/logout.php @@ -0,0 +1,22 @@ +prepare('SELECT name, description, image_url FROM restaurants WHERE id = :id'); + $stmt_restaurant->bindParam(':id', $restaurant_id, PDO::PARAM_INT); + $stmt_restaurant->execute(); + $restaurant = $stmt_restaurant->fetch(PDO::FETCH_ASSOC); + + if ($restaurant) { + $stmt_menu = $pdo->prepare('SELECT id, name, description, price, image_url FROM menu_items WHERE restaurant_id = :id ORDER BY name'); + $stmt_menu->bindParam(':id', $restaurant_id, PDO::PARAM_INT); + $stmt_menu->execute(); + $menu_items = $stmt_menu->fetchAll(PDO::FETCH_ASSOC); + } + } catch (PDOException $e) { + // In a real app, log this error. + } +} +?> + + + + + + <?php echo $restaurant ? htmlspecialchars($restaurant['name']) : 'Menu'; ?> - MajuroEats + + + + + + + + +
+ + + +
+

Menu

+ 0): ?> + + +

This restaurant currently has no menu items.

+ +
+ +
+

Restaurant not found

+

Sorry, the restaurant you are looking for does not exist.

+
+ +
+ + + + + + + \ No newline at end of file diff --git a/order_confirmation.php b/order_confirmation.php new file mode 100644 index 0000000..e163f20 --- /dev/null +++ b/order_confirmation.php @@ -0,0 +1,97 @@ + + + + + + + Order Confirmed - MajuroEats + + + + + +
+
+

Thank You For Your Order!

+

Your order has been successfully placed. You will receive an email confirmation shortly. The restaurant will begin preparing your food soon!

+

Your Order ID is:

+
#
+
+ Back to Homepage +
+
+ + + diff --git a/place_order.php b/place_order.php new file mode 100644 index 0000000..de17805 --- /dev/null +++ b/place_order.php @@ -0,0 +1,117 @@ +prepare("SELECT id, price FROM menu_items WHERE id IN ($placeholders)"); +$stmt->execute($item_ids); +$db_items = $stmt->fetchAll(PDO::FETCH_KEY_PAIR); + +foreach ($cart['items'] as &$item) { + if (!isset($db_items[$item['id']])) { + die("Invalid item in cart."); // Or handle more gracefully + } + $item['price'] = $db_items[$item['id']]; // Use price from DB for security + $total_price += $item['price'] * $item['quantity']; +} +unset($item); + +// --- Database Transaction --- +try { + $pdo->beginTransaction(); + + $user_id = isset($_SESSION['user_id']) ? $_SESSION['user_id'] : null; + + // 1. Insert into orders table + $stmt = $pdo->prepare( + 'INSERT INTO orders (user_id, restaurant_id, customer_name, customer_email, customer_phone, delivery_address, total_price) VALUES (?, ?, ?, ?, ?, ?, ?)' + ); + $stmt->execute([ + $user_id, + $cart['restaurantId'], + $customer_name, + $customer_email, + $customer_phone, + $delivery_address, + $total_price + ]); + $order_id = $pdo->lastInsertId(); + + // 2. Insert into order_items table + $stmt = $pdo->prepare('INSERT INTO order_items (order_id, menu_item_id, quantity, price) VALUES (?, ?, ?, ?)'); + foreach ($cart['items'] as $item) { + $stmt->execute([ + $order_id, + $item['id'], + $item['quantity'], + $item['price'] + ]); + } + + $pdo->commit(); + + $_SESSION['last_order_id'] = $order_id; + + // --- Send Emails --- + // To Customer + $customer_subject = "Your MajuroEats Order #{$order_id} is Confirmed"; + $customer_html = "

Thank you for your order!

Your order with ID #{$order_id} has been placed.

We will notify you once the restaurant confirms it.

"; + MailService::sendMail($customer_email, $customer_subject, $customer_html, strip_tags($customer_html)); + + // To Restaurant + $stmt_restaurant = $pdo->prepare('SELECT u.email FROM users u JOIN restaurants r ON u.id = r.user_id WHERE r.id = ?'); + $stmt_restaurant->execute([$cart['restaurantId']]); + $restaurant_email = $stmt_restaurant->fetchColumn(); + + if ($restaurant_email) { + $restaurant_subject = "New Order Received (#{$order_id})"; + $restaurant_html = "

You have a new order!

Order ID: #{$order_id}

Please log in to your dashboard to view the details and confirm the order.

"; + MailService::sendMail($restaurant_email, $restaurant_subject, $restaurant_html, strip_tags($restaurant_html)); + } + + header('Location: order_confirmation.php'); + exit; + +} catch (Exception $e) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + // In a real app, log this error + $_SESSION['error_message'] = 'There was a problem placing your order. Please try again.'; + header('Location: checkout.php'); + exit; +} diff --git a/restaurant_signup.php b/restaurant_signup.php new file mode 100644 index 0000000..c29ce34 --- /dev/null +++ b/restaurant_signup.php @@ -0,0 +1,111 @@ + + + + + + Restaurant Signup - MajuroEats + + + + + + +
+

Become a Partner

+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+ +
+ +
+ + + diff --git a/restaurants.php b/restaurants.php new file mode 100644 index 0000000..f65665c --- /dev/null +++ b/restaurants.php @@ -0,0 +1,73 @@ + + + + + + Restaurants - MajuroEats + + + + + + + +
+

Explore Restaurants

+
+ query('SELECT id, name, description, image_url FROM restaurants ORDER BY name'); + $restaurants = $stmt->fetchAll(PDO::FETCH_ASSOC); + + if (count($restaurants) > 0) { + foreach ($restaurants as $restaurant) { + echo ''; + echo '
'; + echo '
'; + echo '

' . htmlspecialchars($restaurant['name']) . '

'; + echo '

' . htmlspecialchars($restaurant['description']) . '

'; + echo '
'; + echo '
'; + } + } else { + echo '

No restaurants found.

'; + } + } catch (PDOException $e) { + echo '

Error fetching restaurants. Please try again later.

'; + } + ?> +
+
+ + + + + \ No newline at end of file diff --git a/signup.php b/signup.php new file mode 100644 index 0000000..e8c9e03 --- /dev/null +++ b/signup.php @@ -0,0 +1,59 @@ +prepare("SELECT id FROM users WHERE email = ?"); +$stmt->execute([$email]); +if ($stmt->fetch()) { + die('An account with this email already exists. Log in here.'); +} + +try { + $pdo->beginTransaction(); + + // 1. Create the user + $stmt_user = $pdo->prepare( + "INSERT INTO users (name, email, password, role) VALUES (?, ?, ?, 'restaurant_owner')" + ); + $stmt_user->execute([$owner_name, $email, $password]); + $user_id = $pdo->lastInsertId(); + + // 2. Create the restaurant + $stmt_restaurant = $pdo->prepare( + "INSERT INTO restaurants (name, description, user_id, image_url) VALUES (?, ?, ?, ?)" + ); + // Using a placeholder image for now + $stmt_restaurant->execute([$restaurant_name, $restaurant_description, $user_id, 'assets/images/placeholder.jpg']); + + $pdo->commit(); + + // Redirect to a success page or login page + header("Location: login.php?signup=success"); + exit(); + +} catch (PDOException $e) { + $pdo->rollBack(); + // In a real app, you would log this error + die("Error creating account: " . $e->getMessage()); +} diff --git a/update_order_status.php b/update_order_status.php new file mode 100644 index 0000000..1c8d303 --- /dev/null +++ b/update_order_status.php @@ -0,0 +1,33 @@ +prepare( + "SELECT o.id FROM orders o JOIN restaurants r ON o.restaurant_id = r.id WHERE o.id = ? AND r.user_id = ?" + ); + $stmt->execute([$order_id, $_SESSION['user_id']]); + $order_exists = $stmt->fetch(); + + if ($order_exists) { + $stmt_update = $pdo->prepare("UPDATE orders SET status = ? WHERE id = ?"); + $stmt_update->execute([$status, $order_id]); + } + } +} + +header('Location: dashboard.php'); +exit();