Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fcbb065ea |
57
add_contact.php
Normal file
57
add_contact.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
require_once __DIR__ . '/mail/MailService.php';
|
||||||
|
|
||||||
|
$response = ['success' => 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);
|
||||||
48
add_menu_item.php
Normal file
48
add_menu_item.php
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Protect the page: check if user is logged in and is a restaurant owner
|
||||||
|
if (!isset($_SESSION['user_id'])) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
// Basic validation
|
||||||
|
if (empty($_POST['item_name']) || !isset($_POST['item_price']) || empty($_POST['restaurant_id'])) {
|
||||||
|
die('Please fill all required fields.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$item_name = $_POST['item_name'];
|
||||||
|
$item_description = $_POST['item_description'] ?? '';
|
||||||
|
$item_price = filter_var($_POST['item_price'], FILTER_VALIDATE_FLOAT);
|
||||||
|
$restaurant_id = $_POST['restaurant_id'];
|
||||||
|
|
||||||
|
if ($item_price === false || $item_price < 0) {
|
||||||
|
die('Invalid price format.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// Verify that the current user owns the restaurant they are trying to add to
|
||||||
|
$stmt = $pdo->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());
|
||||||
|
}
|
||||||
477
assets/css/custom.css
Normal file
477
assets/css/custom.css
Normal file
@ -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);
|
||||||
|
}
|
||||||
0
assets/images/placeholder.jpg
Normal file
0
assets/images/placeholder.jpg
Normal file
100
assets/js/main.js
Normal file
100
assets/js/main.js
Normal file
@ -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();
|
||||||
|
});
|
||||||
218
cart.php
Normal file
218
cart.php
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Your Cart - MajuroEats</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/feather-icons/4.28.0/feather.min.css">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
background-color: #F8F9FA;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.navbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
.navbar-brand {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #40E0D0;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.nav-links {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
.nav-links a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #2E8B57;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
padding: 4rem 2rem;
|
||||||
|
max-width: 900px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
.cart-container h1 {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #2E8B57;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.cart-items {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 2rem;
|
||||||
|
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
.cart-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1rem 0;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
}
|
||||||
|
.cart-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.item-details {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.item-details h3 {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.item-quantity {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
.item-quantity input {
|
||||||
|
width: 50px;
|
||||||
|
text-align: center;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
padding: 0.25rem;
|
||||||
|
}
|
||||||
|
.item-price {
|
||||||
|
font-weight: 600;
|
||||||
|
min-width: 80px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.remove-item {
|
||||||
|
color: #E04050;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
.cart-summary {
|
||||||
|
margin-top: 2rem;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.total-price {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #2E8B57;
|
||||||
|
}
|
||||||
|
.checkout-btn {
|
||||||
|
background-color: #40E0D0;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
padding: 0.8rem 2rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1rem;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 1rem;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
.checkout-btn:hover {
|
||||||
|
background-color: #2E8B57;
|
||||||
|
}
|
||||||
|
.empty-cart {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar">
|
||||||
|
<a href="index.php" class="navbar-brand">MajuroEats</a>
|
||||||
|
<div class="nav-links">
|
||||||
|
<a href="restaurants.php">All Restaurants</a>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container cart-container">
|
||||||
|
<h1>Your Shopping Cart</h1>
|
||||||
|
<div id="cart-content">
|
||||||
|
<!-- Cart items will be rendered here by JavaScript -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const cartContent = document.getElementById('cart-content');
|
||||||
|
|
||||||
|
function renderCart() {
|
||||||
|
const cart = shoppingCart.getCart();
|
||||||
|
if (!cart.items || cart.items.length === 0) {
|
||||||
|
cartContent.innerHTML = '<div class="empty-cart"><p>Your cart is empty.</p><a href="restaurants.php" class="checkout-btn">Browse Restaurants</a></div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let total = 0;
|
||||||
|
const itemsHtml = cart.items.map(item => {
|
||||||
|
const itemTotal = item.price * item.quantity;
|
||||||
|
total += itemTotal;
|
||||||
|
return `
|
||||||
|
<div class="cart-item" data-id="${item.id}">
|
||||||
|
<div class="item-details">
|
||||||
|
<h3>${item.name}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="item-quantity">
|
||||||
|
<input type="number" value="${item.quantity}" min="1" onchange="updateQuantity(${item.id}, this.value)">
|
||||||
|
</div>
|
||||||
|
<div class="item-price">$${itemTotal.toFixed(2)}</div>
|
||||||
|
<div class="remove-item" onclick="removeItem(${item.id})">×</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
cartContent.innerHTML = `
|
||||||
|
<div class="cart-items">
|
||||||
|
${itemsHtml}
|
||||||
|
</div>
|
||||||
|
<div class="cart-summary">
|
||||||
|
<div class="total-price">Total: $${total.toFixed(2)}</div>
|
||||||
|
<a href="checkout.php" class="checkout-btn">Proceed to Checkout</a>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.updateQuantity = (itemId, quantity) => {
|
||||||
|
const cart = shoppingCart.getCart();
|
||||||
|
const item = cart.items.find(i => i.id === itemId);
|
||||||
|
if (item) {
|
||||||
|
item.quantity = parseInt(quantity, 10);
|
||||||
|
if (item.quantity < 1) item.quantity = 1;
|
||||||
|
shoppingCart.saveCart(cart);
|
||||||
|
renderCart();
|
||||||
|
shoppingCart.updateCartCount();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.removeItem = (itemId) => {
|
||||||
|
if (confirm('Are you sure you want to remove this item?')) {
|
||||||
|
let cart = shoppingCart.getCart();
|
||||||
|
cart.items = cart.items.filter(i => i.id !== itemId);
|
||||||
|
if(cart.items.length === 0) {
|
||||||
|
cart.restaurantId = null;
|
||||||
|
}
|
||||||
|
shoppingCart.saveCart(cart);
|
||||||
|
renderCart();
|
||||||
|
shoppingCart.updateCartCount();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
renderCart();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
162
checkout.php
Normal file
162
checkout.php
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<?php session_start(); ?>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Checkout - MajuroEats</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
background-color: #F8F9FA;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.navbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.navbar-brand {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #40E0D0;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
padding: 4rem 2rem;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
.checkout-container h1 {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #2E8B57;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.checkout-form {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.form-group input, .form-group textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
.order-summary {
|
||||||
|
margin-top: 2rem;
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 2rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
}
|
||||||
|
.order-summary h2 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: #2E8B57;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.place-order-btn {
|
||||||
|
background-color: #40E0D0;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
padding: 0.8rem 2rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1rem;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
.place-order-btn:hover {
|
||||||
|
background-color: #2E8B57;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar">
|
||||||
|
<a href="index.php" class="navbar-brand">MajuroEats</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container checkout-container">
|
||||||
|
<h1>Checkout</h1>
|
||||||
|
<div class="checkout-grid">
|
||||||
|
<form id="checkout-form" class="checkout-form" action="place_order.php" method="POST">
|
||||||
|
<h2>Delivery Information</h2>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="customer_name">Full Name</label>
|
||||||
|
<input type="text" id="customer_name" name="customer_name" value="<?php echo isset($_SESSION['user_name']) ? htmlspecialchars($_SESSION['user_name']) : ''; ?>" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="customer_email">Email Address</label>
|
||||||
|
<input type="email" id="customer_email" name="customer_email" value="<?php echo isset($_SESSION['user_email']) ? htmlspecialchars($_SESSION['user_email']) : ''; ?>" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="customer_phone">Phone Number</label>
|
||||||
|
<input type="tel" id="customer_phone" name="customer_phone">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="delivery_address">Delivery Address</label>
|
||||||
|
<textarea id="delivery_address" name="delivery_address" rows="3" required></textarea>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="cart_data" id="cart_data">
|
||||||
|
<button type="submit" class="place-order-btn">Place Order</button>
|
||||||
|
</form>
|
||||||
|
<div class="order-summary">
|
||||||
|
<h2>Your Order</h2>
|
||||||
|
<div id="summary-items">
|
||||||
|
<!-- Summary items will be rendered here -->
|
||||||
|
</div>
|
||||||
|
<h3 id="summary-total"></h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const summaryItems = document.getElementById('summary-items');
|
||||||
|
const summaryTotal = document.getElementById('summary-total');
|
||||||
|
const cartDataInput = document.getElementById('cart_data');
|
||||||
|
const checkoutForm = document.getElementById('checkout-form');
|
||||||
|
|
||||||
|
const cart = shoppingCart.getCart();
|
||||||
|
|
||||||
|
if (!cart.items || cart.items.length === 0) {
|
||||||
|
window.location.href = 'cart.php';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let total = 0;
|
||||||
|
const itemsHtml = cart.items.map(item => {
|
||||||
|
const itemTotal = item.price * item.quantity;
|
||||||
|
total += itemTotal;
|
||||||
|
return `<p>${item.name} <span>(x${item.quantity})</span> - <strong>$${itemTotal.toFixed(2)}</strong></p>`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
summaryItems.innerHTML = itemsHtml;
|
||||||
|
summaryTotal.textContent = `Total: $${total.toFixed(2)}`;
|
||||||
|
|
||||||
|
checkoutForm.addEventListener('submit', () => {
|
||||||
|
cartDataInput.value = JSON.stringify(cart);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
100
customer_dashboard.php
Normal file
100
customer_dashboard.php
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'customer') {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'db/config.php';
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
$stmt = $pdo->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);
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>My Account - MajuroEats</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="navbar">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-inner">
|
||||||
|
<a href="/" class="logo">MajuroEats</a>
|
||||||
|
<nav class="nav-links">
|
||||||
|
<a href="index.php#how-it-works">How It Works</a>
|
||||||
|
<a href="restaurants.php">Restaurants</a>
|
||||||
|
<a href="index.php#contact">Contact</a>
|
||||||
|
<a href="cart.php" class="cart-icon">
|
||||||
|
<i data-feather="shopping-cart"></i>
|
||||||
|
<span id="cart-count">0</span>
|
||||||
|
</a>
|
||||||
|
<?php if (isset($_SESSION['user_id'])): ?>
|
||||||
|
<a href="customer_dashboard.php">My Account</a>
|
||||||
|
<a href="logout.php">Log Out</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="login.php">Login</a>
|
||||||
|
<a href="customer_signup.php" class="btn btn-primary">Sign Up</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container section">
|
||||||
|
<h1 class="page-title">Your Order History</h1>
|
||||||
|
<div class="dashboard-content">
|
||||||
|
<?php if (count($orders) > 0): ?>
|
||||||
|
<table class="table order-history-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Order ID</th>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Restaurant</th>
|
||||||
|
<th>Total</th>
|
||||||
|
<th>Status</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($orders as $order): ?>
|
||||||
|
<tr>
|
||||||
|
<td>#<?php echo $order['id']; ?></td>
|
||||||
|
<td><?php echo date('M d, Y', strtotime($order['created_at'])); ?></td>
|
||||||
|
<td><?php echo htmlspecialchars($order['restaurant_name']); ?></td>
|
||||||
|
<td>$<?php echo htmlspecialchars(number_format($order['total_price'], 2)); ?></td>
|
||||||
|
<td><span class="status status-<?php echo strtolower(htmlspecialchars($order['status'])); ?>"><?php echo ucfirst(htmlspecialchars($order['status'])); ?></span></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<?php else: ?>
|
||||||
|
<p>You have not placed any orders yet. <a href="restaurants.php">Start your first order!</a></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div class="container">
|
||||||
|
<p>© 2025 MajuroEats. All Rights Reserved.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
<script src="https://unpkg.com/feather-icons"></script>
|
||||||
|
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||||
|
<script>
|
||||||
|
feather.replace();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
99
customer_signup.php
Normal file
99
customer_signup.php
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Sign Up - MajuroEats</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
background-color: #F8F9FA;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
.signup-container {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
padding: 3rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 450px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.signup-container h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #2E8B57;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.form-group input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.btn-submit {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background-color: #40E0D0;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
.btn-submit:hover {
|
||||||
|
background-color: #2E8B57;
|
||||||
|
}
|
||||||
|
.login-link {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
.login-link a {
|
||||||
|
color: #2E8B57;
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="signup-container">
|
||||||
|
<h1>Create Your Account</h1>
|
||||||
|
<form action="customer_signup_handler.php" method="POST">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name">Full Name</label>
|
||||||
|
<input type="text" id="name" name="name" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email">Email Address</label>
|
||||||
|
<input type="email" id="email" name="email" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input type="password" id="password" name="password" minlength="8" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn-submit">Sign Up</button>
|
||||||
|
</form>
|
||||||
|
<div class="login-link">
|
||||||
|
<p>Already have an account? <a href="login.php">Log In</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
49
customer_signup_handler.php
Normal file
49
customer_signup_handler.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: customer_signup.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic validation
|
||||||
|
if (empty($_POST['name']) || empty($_POST['email']) || empty($_POST['password'])) {
|
||||||
|
die('Please fill all required fields.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($_POST['password']) < 8) {
|
||||||
|
die('Password must be at least 8 characters long.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_STRING);
|
||||||
|
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
|
||||||
|
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
|
||||||
|
|
||||||
|
if (!$name || !$email) {
|
||||||
|
die('Invalid input.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// Check if email already exists
|
||||||
|
$stmt = $pdo->prepare("SELECT id FROM users WHERE email = ?");
|
||||||
|
$stmt->execute([$email]);
|
||||||
|
if ($stmt->fetch()) {
|
||||||
|
die('An account with this email already exists. <a href="login.php">Log in here</a>.');
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
184
dashboard.php
Normal file
184
dashboard.php
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'restaurant_owner') {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'db/config.php';
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
$stmt = $pdo->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 '<br>') 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);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Dashboard - MajuroEats</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
<script src="https://unpkg.com/feather-icons"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="navbar">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-inner">
|
||||||
|
<a href="/" class="logo">MajuroEats</a>
|
||||||
|
<nav class="nav-links">
|
||||||
|
<a href="logout.php">Log Out</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container section">
|
||||||
|
<h1 class="page-title">Restaurant Dashboard</h1>
|
||||||
|
|
||||||
|
<?php if ($restaurant): ?>
|
||||||
|
<div class="dashboard-header">
|
||||||
|
<h2><?php echo htmlspecialchars($restaurant['name']); ?></h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tabs">
|
||||||
|
<button class="tab-link active" onclick="openTab(event, 'orders')">Orders</button>
|
||||||
|
<button class="tab-link" onclick="openTab(event, 'menu')">Menu</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="orders" class="tab-content active">
|
||||||
|
<div class="dashboard-content">
|
||||||
|
<h3>Recent Orders</h3>
|
||||||
|
<?php if (count($orders) > 0): ?>
|
||||||
|
<div class="order-list">
|
||||||
|
<?php foreach ($orders as $order): ?>
|
||||||
|
<div class="order-card">
|
||||||
|
<div class="order-header">
|
||||||
|
<span class="order-id">Order #<?php echo $order['id']; ?></span>
|
||||||
|
<span class="status status-<?php echo strtolower(htmlspecialchars($order['status'])); ?>"><?php echo ucfirst(htmlspecialchars($order['status'])); ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="order-body">
|
||||||
|
<p><strong>Customer:</strong> <?php echo htmlspecialchars($order['customer_name']); ?></p>
|
||||||
|
<p><strong>Items:</strong><br><?php echo $order['items']; ?></p>
|
||||||
|
</div>
|
||||||
|
<div class="order-footer">
|
||||||
|
<form action="update_order_status.php" method="POST">
|
||||||
|
<input type="hidden" name="order_id" value="<?php echo $order['id']; ?>">
|
||||||
|
<select name="status">
|
||||||
|
<option value="pending" <?php if($order['status'] == 'pending') echo 'selected'; ?>>Pending</option>
|
||||||
|
<option value="confirmed" <?php if($order['status'] == 'confirmed') echo 'selected'; ?>>Confirmed</option>
|
||||||
|
<option value="delivered" <?php if($order['status'] == 'delivered') echo 'selected'; ?>>Delivered</option>
|
||||||
|
<option value="cancelled" <?php if($order['status'] == 'cancelled') echo 'selected'; ?>>Cancelled</option>
|
||||||
|
</select>
|
||||||
|
<button type="submit" class="btn btn-primary">Update</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<p>You have no recent orders.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="menu" class="tab-content">
|
||||||
|
<div class="dashboard-content">
|
||||||
|
<h3>Your Menu Items</h3>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Price</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($menu_items as $item): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?php echo htmlspecialchars($item['name']); ?></td>
|
||||||
|
<td>$<?php echo htmlspecialchars(number_format($item['price'], 2)); ?></td>
|
||||||
|
<td><a href="#">Edit</a></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="dashboard-content" style="margin-top: 2rem;">
|
||||||
|
<h3>Add New Item</h3>
|
||||||
|
<form action="add_menu_item.php" method="POST" class="form-card">
|
||||||
|
<input type="hidden" name="restaurant_id" value="<?php echo $restaurant['id']; ?>">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="item_name">Item Name</label>
|
||||||
|
<input type="text" id="item_name" name="item_name" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="item_description">Description</label>
|
||||||
|
<textarea id="item_description" name="item_description" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="item_price">Price</label>
|
||||||
|
<input type="number" id="item_price" name="item_price" step="0.01" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Add Item</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php else: ?>
|
||||||
|
<p>You do not have a restaurant associated with your account.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div class="container">
|
||||||
|
<p>© 2025 MajuroEats. All Rights Reserved.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
feather.replace();
|
||||||
|
function openTab(evt, tabName) {
|
||||||
|
var i, tabcontent, tablinks;
|
||||||
|
tabcontent = document.getElementsByClassName("tab-content");
|
||||||
|
for (i = 0; i < tabcontent.length; i++) {
|
||||||
|
tabcontent[i].style.display = "none";
|
||||||
|
}
|
||||||
|
tablinks = document.getElementsByClassName("tab-link");
|
||||||
|
for (i = 0; i < tablinks.length; i++) {
|
||||||
|
tablinks[i].className = tablinks[i].className.replace(" active", "");
|
||||||
|
}
|
||||||
|
document.getElementById(tabName).style.display = "block";
|
||||||
|
evt.currentTarget.className += " active";
|
||||||
|
}
|
||||||
|
// Set default open tab
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
document.querySelector('.tab-link.active').click();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
34
db/migrate.php
Normal file
34
db/migrate.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/config.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Connect without specifying a DB to create it if it doesn't exist
|
||||||
|
$pdo_admin = new PDO('mysql:host='.DB_HOST, DB_USER, DB_PASS, [
|
||||||
|
PDO::ATTR_ERRMODE => 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());
|
||||||
|
}
|
||||||
|
|
||||||
39
db/migrations/002_create_restaurants_and_menu_items.sql
Normal file
39
db/migrations/002_create_restaurants_and_menu_items.sql
Normal file
@ -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');
|
||||||
@ -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;
|
||||||
22
db/migrations/004_create_orders_tables.sql
Normal file
22
db/migrations/004_create_orders_tables.sql
Normal file
@ -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)
|
||||||
|
);
|
||||||
5
db/migrations/005_add_user_id_to_orders.sql
Normal file
5
db/migrations/005_add_user_id_to_orders.sql
Normal file
@ -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;
|
||||||
258
index.php
258
index.php
@ -1,150 +1,132 @@
|
|||||||
<?php
|
<!DOCTYPE html>
|
||||||
declare(strict_types=1);
|
|
||||||
@ini_set('display_errors', '1');
|
|
||||||
@error_reporting(E_ALL);
|
|
||||||
@date_default_timezone_set('UTC');
|
|
||||||
|
|
||||||
$phpVersion = PHP_VERSION;
|
|
||||||
$now = date('Y-m-d H:i:s');
|
|
||||||
?>
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>New Style</title>
|
<title>MajuroEats - Food Delivery in Majuro</title>
|
||||||
<?php
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
||||||
// Read project preview data from environment
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
<script src="https://unpkg.com/feather-icons"></script>
|
||||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
|
||||||
?>
|
|
||||||
<?php if ($projectDescription): ?>
|
|
||||||
<!-- Meta description -->
|
|
||||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
|
||||||
<!-- Open Graph meta tags -->
|
|
||||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
|
||||||
<!-- Twitter meta tags -->
|
|
||||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if ($projectImageUrl): ?>
|
|
||||||
<!-- Open Graph image -->
|
|
||||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
|
||||||
<!-- Twitter image -->
|
|
||||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
|
||||||
<?php endif; ?>
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--bg-color-start: #6a11cb;
|
|
||||||
--bg-color-end: #2575fc;
|
|
||||||
--text-color: #ffffff;
|
|
||||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
|
||||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
|
||||||
color: var(--text-color);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
min-height: 100vh;
|
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
body::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
|
|
||||||
animation: bg-pan 20s linear infinite;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
@keyframes bg-pan {
|
|
||||||
0% { background-position: 0% 0%; }
|
|
||||||
100% { background-position: 100% 100%; }
|
|
||||||
}
|
|
||||||
main {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
.card {
|
|
||||||
background: var(--card-bg-color);
|
|
||||||
border: 1px solid var(--card-border-color);
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 2rem;
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
.loader {
|
|
||||||
margin: 1.25rem auto 1.25rem;
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
border: 3px solid rgba(255, 255, 255, 0.25);
|
|
||||||
border-top-color: #fff;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
}
|
|
||||||
@keyframes spin {
|
|
||||||
from { transform: rotate(0deg); }
|
|
||||||
to { transform: rotate(360deg); }
|
|
||||||
}
|
|
||||||
.hint {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
.sr-only {
|
|
||||||
position: absolute;
|
|
||||||
width: 1px; height: 1px;
|
|
||||||
padding: 0; margin: -1px;
|
|
||||||
overflow: hidden;
|
|
||||||
clip: rect(0, 0, 0, 0);
|
|
||||||
white-space: nowrap; border: 0;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-size: 3rem;
|
|
||||||
font-weight: 700;
|
|
||||||
margin: 0 0 1rem;
|
|
||||||
letter-spacing: -1px;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
background: rgba(0,0,0,0.2);
|
|
||||||
padding: 2px 6px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
||||||
}
|
|
||||||
footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 1rem;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<?php session_start(); ?>
|
||||||
|
<header class="navbar">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-inner">
|
||||||
|
<a href="/" class="logo">MajuroEats</a>
|
||||||
|
<nav class="nav-links">
|
||||||
|
<a href="#how-it-works">How It Works</a>
|
||||||
|
<a href="restaurants.php">Restaurants</a>
|
||||||
|
<a href="#contact">Contact</a>
|
||||||
|
<?php if (isset($_SESSION['user_id'])): ?>
|
||||||
|
<?php if ($_SESSION['user_role'] === 'customer'): ?>
|
||||||
|
<a href="customer_dashboard.php">My Account</a>
|
||||||
|
<?php elseif ($_SESSION['user_role'] === 'restaurant_owner'): ?>
|
||||||
|
<a href="dashboard.php">Dashboard</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<a href="logout.php">Log Out</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="login.php">Login</a>
|
||||||
|
<a href="customer_signup.php" class="btn btn-primary">Sign Up</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<div class="card">
|
<section class="hero">
|
||||||
<h1>Analyzing your requirements and generating your website…</h1>
|
<div class="container">
|
||||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
<div class="hero-content">
|
||||||
<span class="sr-only">Loading…</span>
|
<h1>Your Favorite Food, Delivered in Majuro</h1>
|
||||||
|
<p>Fresh, fast, and reliable. The best local restaurants at your fingertips.</p>
|
||||||
|
<a href="restaurants.php" class="btn btn-primary">Browse Restaurants</a>
|
||||||
</div>
|
</div>
|
||||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
|
||||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
|
||||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="how-it-works" class="container section">
|
||||||
|
<h2>How It Works</h2>
|
||||||
|
<div class="steps">
|
||||||
|
<div class="step">
|
||||||
|
<i data-feather="map-pin"></i>
|
||||||
|
<h3>1. Discover</h3>
|
||||||
|
<p>Browse menus from the best local restaurants.</p>
|
||||||
|
</div>
|
||||||
|
<div class="step">
|
||||||
|
<i data-feather="shopping-cart"></i>
|
||||||
|
<h3>2. Order</h3>
|
||||||
|
<p>Select your favorite dishes and place your order in seconds.</p>
|
||||||
|
</div>
|
||||||
|
<div class="step">
|
||||||
|
<i data-feather="gift"></i>
|
||||||
|
<h3>3. Enjoy</h3>
|
||||||
|
<p>Get your food delivered hot and fresh to your doorstep.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="contact" class="section-alt">
|
||||||
|
<div class="container">
|
||||||
|
<h2>Contact Us</h2>
|
||||||
|
<div class="contact-content">
|
||||||
|
<p>Have a question or feedback? Get in touch with us!</p>
|
||||||
|
<form id="contactForm" class="contact-form">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" id="name" name="name" required placeholder="Your Name">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="email" id="email" name="email" required placeholder="Your Email">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<textarea id="message" name="message" rows="5" required placeholder="Your Message"></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Send Message</button>
|
||||||
|
</form>
|
||||||
|
<div id="form-message"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
<div class="container">
|
||||||
|
<p>© 2025 MajuroEats. All Rights Reserved.</p>
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||||
|
<script>
|
||||||
|
feather.replace();
|
||||||
|
|
||||||
|
document.getElementById('contactForm').addEventListener('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const form = e.target;
|
||||||
|
const messageDiv = document.getElementById('form-message');
|
||||||
|
const formData = new FormData(form);
|
||||||
|
|
||||||
|
fetch('mail/contact_form_handler.php', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
messageDiv.className = 'form-message success';
|
||||||
|
messageDiv.textContent = 'Thank you for your message! We will get back to you shortly.';
|
||||||
|
form.reset();
|
||||||
|
} else {
|
||||||
|
messageDiv.className = 'form-message error';
|
||||||
|
messageDiv.textContent = 'An error occurred: ' + (data.error || 'Please try again.');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
messageDiv.className = 'form-message error';
|
||||||
|
messageDiv.textContent = 'A network error occurred. Please try again later.';
|
||||||
|
console.error('Error:', error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
139
login.php
Normal file
139
login.php
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// If user is already logged in, redirect to dashboard
|
||||||
|
if (isset($_SESSION['user_id'])) {
|
||||||
|
header("Location: dashboard.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Login - MajuroEats</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
background-color: #F8F9FA;
|
||||||
|
color: #333;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.login-container {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
padding: 3rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
.login-container h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #2E8B57;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.form-group input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.btn-submit {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background-color: #40E0D0;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
.btn-submit:hover {
|
||||||
|
background-color: #2E8B57;
|
||||||
|
}
|
||||||
|
.signup-link {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
.signup-link a {
|
||||||
|
color: #2E8B57;
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.message {
|
||||||
|
padding: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.message.success {
|
||||||
|
background-color: #d4edda;
|
||||||
|
color: #155724;
|
||||||
|
}
|
||||||
|
.message.error {
|
||||||
|
background-color: #f8d7da;
|
||||||
|
color: #721c24;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="login-container">
|
||||||
|
<h1>Partner Login</h1>
|
||||||
|
|
||||||
|
<?php if (isset($_GET['signup']) && $_GET['signup'] == 'success'): ?>
|
||||||
|
<div class="message success">
|
||||||
|
Your account has been created successfully! Please log in.
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (isset($_GET['error'])):
|
||||||
|
// Corrected: Changed " to ' for the $_GET['error'] comparison
|
||||||
|
?>
|
||||||
|
<div class="message error">
|
||||||
|
<?php
|
||||||
|
if ($_GET['error'] == 'invalid') echo "Invalid email or password.";
|
||||||
|
if ($_GET['error'] == 'not_owner') echo "Access denied. Only restaurant owners can log in here.";
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form action="login_handler.php" method="POST">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email">Email</label>
|
||||||
|
<input type="email" id="email" name="email" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input type="password" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn-submit">Log In</button>
|
||||||
|
</form>
|
||||||
|
<div class="signup-link">
|
||||||
|
<p>Don't have an account? <a href="restaurant_signup.php">Sign Up</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
47
login_handler.php
Normal file
47
login_handler.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
|
||||||
|
if (!$email || empty($password)) {
|
||||||
|
header('Location: login.php?error=invalid');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
$stmt = $pdo->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();
|
||||||
|
}
|
||||||
22
logout.php
Normal file
22
logout.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Unset all of the session variables
|
||||||
|
$_SESSION = [];
|
||||||
|
|
||||||
|
// If it's desired to kill the session, also delete the session cookie.
|
||||||
|
// Note: This will destroy the session, and not just the session data!
|
||||||
|
if (ini_get("session.use_cookies")) {
|
||||||
|
$params = session_get_cookie_params();
|
||||||
|
setcookie(session_name(), '', time() - 42000,
|
||||||
|
$params["path"], $params["domain"],
|
||||||
|
$params["secure"], $params["httponly"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, destroy the session.
|
||||||
|
session_destroy();
|
||||||
|
|
||||||
|
// Redirect to login page
|
||||||
|
header("Location: login.php");
|
||||||
|
exit();
|
||||||
120
menu.php
Normal file
120
menu.php
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
$restaurant_id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
|
||||||
|
$restaurant = null;
|
||||||
|
$menu_items = [];
|
||||||
|
|
||||||
|
if ($restaurant_id) {
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt_restaurant = $pdo->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.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title><?php echo $restaurant ? htmlspecialchars($restaurant['name']) : 'Menu'; ?> - MajuroEats</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
<script src="https://unpkg.com/feather-icons"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<?php session_start(); ?>
|
||||||
|
<header class="navbar">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-inner">
|
||||||
|
<a href="/" class="logo">MajuroEats</a>
|
||||||
|
<nav class="nav-links">
|
||||||
|
<a href="index.php#how-it-works">How It Works</a>
|
||||||
|
<a href="restaurants.php">Restaurants</a>
|
||||||
|
<a href="index.php#contact">Contact</a>
|
||||||
|
<a href="cart.php" class="cart-icon">
|
||||||
|
<i data-feather="shopping-cart"></i>
|
||||||
|
<span id="cart-count">0</span>
|
||||||
|
</a>
|
||||||
|
<?php if (isset($_SESSION['user_id'])): ?>
|
||||||
|
<?php if ($_SESSION['user_role'] === 'customer'): ?>
|
||||||
|
<a href="customer_dashboard.php">My Account</a>
|
||||||
|
<?php elseif ($_SESSION['user_role'] === 'restaurant_owner'): ?>
|
||||||
|
<a href="dashboard.php">Dashboard</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<a href="logout.php">Log Out</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="login.php">Login</a>
|
||||||
|
<a href="customer_signup.php" class="btn btn-primary">Sign Up</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<?php if ($restaurant): ?>
|
||||||
|
<section class="menu-header" style="background-image: linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.5)), url('<?php echo htmlspecialchars($restaurant['image_url'] ? $restaurant['image_url'] : 'assets/images/placeholder.jpg'); ?>');">
|
||||||
|
<div class="container">
|
||||||
|
<h1><?php echo htmlspecialchars($restaurant['name']); ?></h1>
|
||||||
|
<p><?php echo htmlspecialchars($restaurant['description']); ?></p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="container section">
|
||||||
|
<h2 class="section-title">Menu</h2>
|
||||||
|
<?php if (count($menu_items) > 0): ?>
|
||||||
|
<div class="menu-grid">
|
||||||
|
<?php foreach ($menu_items as $item): ?>
|
||||||
|
<div class="menu-item-card">
|
||||||
|
<div class="menu-item-image" style="background-image: url('<?php echo htmlspecialchars($item['image_url'] ? $item['image_url'] : 'assets/images/placeholder.jpg'); ?>');"></div>
|
||||||
|
<div class="menu-item-content">
|
||||||
|
<h3><?php echo htmlspecialchars($item['name']); ?></h3>
|
||||||
|
<p><?php echo htmlspecialchars($item['description']); ?></p>
|
||||||
|
<div class="menu-item-footer">
|
||||||
|
<span class="menu-item-price">$<?php echo htmlspecialchars(number_format($item['price'], 2)); ?></span>
|
||||||
|
<?php
|
||||||
|
$item_json = htmlspecialchars(json_encode(['id' => $item['id'], 'name' => $item['name'], 'price' => $item['price']]), ENT_QUOTES, 'UTF-8');
|
||||||
|
?>
|
||||||
|
<button class="btn btn-primary add-to-cart-btn" onclick="shoppingCart.addItem(<?php echo $item_json; ?>, <?php echo $restaurant_id; ?>)">Add to Cart</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<p>This restaurant currently has no menu items.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="container section">
|
||||||
|
<h1 class="page-title">Restaurant not found</h1>
|
||||||
|
<p>Sorry, the restaurant you are looking for does not exist.</p>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div class="container">
|
||||||
|
<p>© 2025 MajuroEats. All Rights Reserved.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
|
||||||
|
<script>
|
||||||
|
feather.replace();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
97
order_confirmation.php
Normal file
97
order_confirmation.php
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// Check if the user has just placed an order
|
||||||
|
if (!isset($_SESSION['last_order_id'])) {
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$order_id = $_SESSION['last_order_id'];
|
||||||
|
|
||||||
|
// Clear the session variable to prevent users from re-visiting this page for the same order
|
||||||
|
unset($_SESSION['last_order_id']);
|
||||||
|
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Order Confirmed - MajuroEats</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
background-color: #F8F9FA;
|
||||||
|
color: #333;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
padding: 4rem 2rem;
|
||||||
|
max-width: 700px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
.confirmation-box {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 3rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.confirmation-box h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #2E8B57;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
.confirmation-box p {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
line-height: 1.7;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.order-id {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #40E0D0;
|
||||||
|
background-color: #f0fdfa;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.home-btn {
|
||||||
|
background-color: #40E0D0;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
padding: 0.8rem 2rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 1rem;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 1rem;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
.home-btn:hover {
|
||||||
|
background-color: #2E8B57;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="confirmation-box">
|
||||||
|
<h1>Thank You For Your Order!</h1>
|
||||||
|
<p>Your order has been successfully placed. You will receive an email confirmation shortly. The restaurant will begin preparing your food soon!</p>
|
||||||
|
<p>Your Order ID is:</p>
|
||||||
|
<div class="order-id">#<?php echo htmlspecialchars($order_id); ?></div>
|
||||||
|
<br>
|
||||||
|
<a href="index.php" class="home-btn">Back to Homepage</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
// Clear the cart from local storage after a successful order
|
||||||
|
localStorage.removeItem('majuroEatsCart');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
117
place_order.php
Normal file
117
place_order.php
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
require_once __DIR__ . '/mail/MailService.php';
|
||||||
|
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Validation ---
|
||||||
|
$customer_name = filter_input(INPUT_POST, 'customer_name', FILTER_SANITIZE_STRING);
|
||||||
|
$customer_email = filter_input(INPUT_POST, 'customer_email', FILTER_VALIDATE_EMAIL);
|
||||||
|
$customer_phone = filter_input(INPUT_POST, 'customer_phone', FILTER_SANITIZE_STRING);
|
||||||
|
$delivery_address = filter_input(INPUT_POST, 'delivery_address', FILTER_SANITIZE_STRING);
|
||||||
|
$cart_data_json = $_POST['cart_data'];
|
||||||
|
|
||||||
|
if (!$customer_name || !$customer_email || !$delivery_address || !$cart_data_json) {
|
||||||
|
$_SESSION['error_message'] = 'Please fill in all required fields.';
|
||||||
|
header('Location: checkout.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cart = json_decode($cart_data_json, true);
|
||||||
|
|
||||||
|
if (!$cart || empty($cart['items']) || !$cart['restaurantId']) {
|
||||||
|
$_SESSION['error_message'] = 'Your cart is invalid. Please try again.';
|
||||||
|
header('Location: cart.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// --- Calculate Total and Verify Prices ---
|
||||||
|
$total_price = 0;
|
||||||
|
$item_ids = array_map(function($item) { return $item['id']; }, $cart['items']);
|
||||||
|
$placeholders = implode(',', array_fill(0, count($item_ids), '?'));
|
||||||
|
|
||||||
|
$stmt = $pdo->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 = "<h1>Thank you for your order!</h1><p>Your order with ID <strong>#{$order_id}</strong> has been placed.</p><p>We will notify you once the restaurant confirms it.</p>";
|
||||||
|
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 = "<h1>You have a new order!</h1><p>Order ID: <strong>#{$order_id}</strong></p><p>Please log in to your dashboard to view the details and confirm the order.</p>";
|
||||||
|
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;
|
||||||
|
}
|
||||||
111
restaurant_signup.php
Normal file
111
restaurant_signup.php
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Restaurant Signup - MajuroEats</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Poppins', sans-serif;
|
||||||
|
background-color: #F8F9FA;
|
||||||
|
color: #333;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.signup-container {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
padding: 3rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
.signup-container h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #2E8B57;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.form-group input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.btn-submit {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background-color: #40E0D0;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
.btn-submit:hover {
|
||||||
|
background-color: #2E8B57;
|
||||||
|
}
|
||||||
|
.login-link {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
.login-link a {
|
||||||
|
color: #2E8B57;
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="signup-container">
|
||||||
|
<h1>Become a Partner</h1>
|
||||||
|
<form action="signup.php" method="POST">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="owner_name">Your Full Name</label>
|
||||||
|
<input type="text" id="owner_name" name="owner_name" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email">Your Email</label>
|
||||||
|
<input type="email" id="email" name="email" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input type="password" id="password" name="password" required minlength="8">
|
||||||
|
</div>
|
||||||
|
<hr style="margin: 2rem 0; border-color: #eee;">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="restaurant_name">Restaurant Name</label>
|
||||||
|
<input type="text" id="restaurant_name" name="restaurant_name" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="restaurant_description">Restaurant Description</label>
|
||||||
|
<textarea id="restaurant_description" name="restaurant_description" rows="3" style="width: 100%; padding: 0.75rem; border: 1px solid #ccc; border-radius: 0.5rem; box-sizing: border-box;"></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn-submit">Sign Up</button>
|
||||||
|
</form>
|
||||||
|
<div class="login-link">
|
||||||
|
<p>Already have an account? <a href="login.php">Log In</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
73
restaurants.php
Normal file
73
restaurants.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Restaurants - MajuroEats</title>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<?php session_start(); ?>
|
||||||
|
<header class="navbar">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-inner">
|
||||||
|
<a href="/" class="logo">MajuroEats</a>
|
||||||
|
<nav class="nav-links">
|
||||||
|
<a href="index.php#how-it-works">How It Works</a>
|
||||||
|
<a href="restaurants.php">Restaurants</a>
|
||||||
|
<a href="index.php#contact">Contact</a>
|
||||||
|
<?php if (isset($_SESSION['user_id'])): ?>
|
||||||
|
<?php if ($_SESSION['user_role'] === 'customer'): ?>
|
||||||
|
<a href="customer_dashboard.php">My Account</a>
|
||||||
|
<?php elseif ($_SESSION['user_role'] === 'restaurant_owner'): ?>
|
||||||
|
<a href="dashboard.php">Dashboard</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<a href="logout.php">Log Out</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="login.php">Login</a>
|
||||||
|
<a href="customer_signup.php" class="btn btn-primary">Sign Up</a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container section">
|
||||||
|
<h1 class="page-title">Explore Restaurants</h1>
|
||||||
|
<div class="restaurants-grid">
|
||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->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 '<a href="menu.php?id=' . htmlspecialchars($restaurant['id']) . '" class="restaurant-card">';
|
||||||
|
echo '<div class="restaurant-card-image" style="background-image: url('' . htmlspecialchars($restaurant['image_url'] ? $restaurant['image_url'] : 'assets/images/placeholder.jpg') . '');"></div>';
|
||||||
|
echo '<div class="restaurant-card-content">';
|
||||||
|
echo '<h3>' . htmlspecialchars($restaurant['name']) . '</h3>';
|
||||||
|
echo '<p>' . htmlspecialchars($restaurant['description']) . '</p>';
|
||||||
|
echo '</div>';
|
||||||
|
echo '</a>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
echo '<p>No restaurants found.</p>';
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
echo '<p>Error fetching restaurants. Please try again later.</p>';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<div class="container">
|
||||||
|
<p>© 2025 MajuroEats. All Rights Reserved.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
59
signup.php
Normal file
59
signup.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
// Basic validation
|
||||||
|
if (empty($_POST['owner_name']) || empty($_POST['email']) || empty($_POST['password']) || empty($_POST['restaurant_name'])) {
|
||||||
|
die('Please fill all required fields.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($_POST['password']) < 8) {
|
||||||
|
die('Password must be at least 8 characters long.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$owner_name = $_POST['owner_name'];
|
||||||
|
$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
|
||||||
|
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
|
||||||
|
$restaurant_name = $_POST['restaurant_name'];
|
||||||
|
$restaurant_description = $_POST['restaurant_description'] ?? '';
|
||||||
|
|
||||||
|
if (!$email) {
|
||||||
|
die('Invalid email format.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
|
||||||
|
// Check if email already exists
|
||||||
|
$stmt = $pdo->prepare("SELECT id FROM users WHERE email = ?");
|
||||||
|
$stmt->execute([$email]);
|
||||||
|
if ($stmt->fetch()) {
|
||||||
|
die('An account with this email already exists. <a href="login.php">Log in here</a>.');
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
33
update_order_status.php
Normal file
33
update_order_status.php
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'restaurant_owner') {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once 'db/config.php';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$order_id = filter_input(INPUT_POST, 'order_id', FILTER_VALIDATE_INT);
|
||||||
|
$status = filter_input(INPUT_POST, 'status', FILTER_SANITIZE_STRING);
|
||||||
|
$allowed_statuses = ['pending', 'confirmed', 'delivered', 'cancelled'];
|
||||||
|
|
||||||
|
if ($order_id && $status && in_array($status, $allowed_statuses)) {
|
||||||
|
$pdo = db();
|
||||||
|
// Verify the order belongs to the logged-in user's restaurant
|
||||||
|
$stmt = $pdo->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();
|
||||||
Loading…
x
Reference in New Issue
Block a user