Revert to version c6c300f
This commit is contained in:
parent
171664feb3
commit
8f996b6408
@ -579,7 +579,7 @@ function can_view($module) {
|
|||||||
<li><a class="dropdown-item" href="profile.php"><i class="bi bi-person me-2"></i> My Profile</a></li>
|
<li><a class="dropdown-item" href="profile.php"><i class="bi bi-person me-2"></i> My Profile</a></li>
|
||||||
<li><a class="dropdown-item" href="company.php"><i class="bi bi-building me-2"></i> Company Settings</a></li>
|
<li><a class="dropdown-item" href="company.php"><i class="bi bi-building me-2"></i> Company Settings</a></li>
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li><hr class="dropdown-divider"></li>
|
||||||
<li><a class="dropdown-item text-danger" href="<?= url("logout.php") ?>"><i class="bi bi-box-arrow-right me-2"></i> Logout</a></li>
|
<li><a class="dropdown-item text-danger" href="/logout.php"><i class="bi bi-box-arrow-right me-2"></i> Logout</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,151 +2,197 @@
|
|||||||
require_once __DIR__ . '/../db/config.php';
|
require_once __DIR__ . '/../db/config.php';
|
||||||
require_once __DIR__ . '/../includes/functions.php';
|
require_once __DIR__ . '/../includes/functions.php';
|
||||||
|
|
||||||
init_session();
|
require_login();
|
||||||
|
$pdo = db();
|
||||||
|
$currentUser = get_logged_user();
|
||||||
|
$id = $currentUser['id'];
|
||||||
|
|
||||||
// Ensure login
|
// Always fetch fresh data from DB
|
||||||
if (!get_logged_user()) {
|
$stmt = $pdo->prepare("SELECT u.*, g.name as group_name, g.permissions
|
||||||
header('Location: ' . url('login.php'));
|
FROM users u
|
||||||
|
LEFT JOIN user_groups g ON u.group_id = g.id
|
||||||
|
WHERE u.id = ?");
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$user) {
|
||||||
|
logout_user();
|
||||||
|
header('Location: /login.php');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = get_logged_user();
|
$message = '';
|
||||||
$pdo = db();
|
|
||||||
$error = '';
|
|
||||||
$success = '';
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
$full_name = $_POST['full_name'] ?? '';
|
$full_name = $_POST['full_name'];
|
||||||
$email = $_POST['email'] ?? '';
|
$email = $_POST['email'];
|
||||||
$new_password = $_POST['new_password'] ?? '';
|
|
||||||
$confirm_password = $_POST['confirm_password'] ?? '';
|
|
||||||
$profile_pic = $user['profile_pic'] ?? null;
|
|
||||||
|
|
||||||
// Profile Pic Upload
|
|
||||||
if (isset($_FILES['profile_pic']) && $_FILES['profile_pic']['error'] === UPLOAD_ERR_OK) {
|
|
||||||
$upload_dir = __DIR__ . '/../assets/images/users/';
|
|
||||||
if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true);
|
|
||||||
|
|
||||||
$ext = pathinfo($_FILES['profile_pic']['name'], PATHINFO_EXTENSION);
|
|
||||||
$filename = 'user_' . $user['id'] . '_' . uniqid() . '.' . $ext;
|
|
||||||
$target = $upload_dir . $filename;
|
|
||||||
|
|
||||||
if (move_uploaded_file($_FILES['profile_pic']['tmp_name'], $target)) {
|
|
||||||
$profile_pic = 'assets/images/users/' . $filename;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$pdo->beginTransaction();
|
||||||
try {
|
try {
|
||||||
if (!empty($new_password)) {
|
$sql = "UPDATE users SET full_name = ?, email = ? WHERE id = ?";
|
||||||
if ($new_password !== $confirm_password) {
|
$params = [$full_name, $email, $id];
|
||||||
$error = 'Passwords do not match.';
|
|
||||||
} else {
|
$stmt = $pdo->prepare($sql);
|
||||||
$hashed_pass = password_hash($new_password, PASSWORD_DEFAULT);
|
$stmt->execute($params);
|
||||||
$stmt = $pdo->prepare("UPDATE users SET full_name = ?, email = ?, password = ?, profile_pic = ? WHERE id = ?");
|
|
||||||
$stmt->execute([$full_name, $email, $hashed_pass, $profile_pic, $user['id']]);
|
// Update password if provided
|
||||||
$success = 'Profile and password updated successfully.';
|
if (!empty($_POST['password'])) {
|
||||||
}
|
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
|
||||||
} else {
|
$pdo->prepare("UPDATE users SET password = ? WHERE id = ?")->execute([$password, $id]);
|
||||||
$stmt = $pdo->prepare("UPDATE users SET full_name = ?, email = ? , profile_pic = ? WHERE id = ?");
|
|
||||||
$stmt->execute([$full_name, $email, $profile_pic, $user['id']]);
|
|
||||||
$success = 'Profile updated successfully.';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($error)) {
|
// Handle Profile Picture Upload
|
||||||
// Update session data
|
if (isset($_FILES['profile_pic']) && $_FILES['profile_pic']['error'] === UPLOAD_ERR_OK) {
|
||||||
$stmt = $pdo->prepare("SELECT u.*, g.name as group_name, g.permissions FROM users u LEFT JOIN user_groups g ON u.group_id = g.id WHERE u.id = ?");
|
$upload_dir = __DIR__ . '/../assets/images/users/';
|
||||||
$stmt->execute([$user['id']]);
|
if (!is_dir($upload_dir)) {
|
||||||
$_SESSION['user'] = $stmt->fetch(PDO::FETCH_ASSOC);
|
mkdir($upload_dir, 0775, true);
|
||||||
$user = $_SESSION['user'];
|
}
|
||||||
|
|
||||||
|
$file_tmp = $_FILES['profile_pic']['tmp_name'];
|
||||||
|
$file_name = $_FILES['profile_pic']['name'];
|
||||||
|
$file_ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));
|
||||||
|
$allowed_exts = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
||||||
|
|
||||||
|
if (in_array($file_ext, $allowed_exts)) {
|
||||||
|
$new_file_name = 'user_' . $id . '_' . uniqid() . '.' . $file_ext;
|
||||||
|
$upload_path = $upload_dir . $new_file_name;
|
||||||
|
|
||||||
|
if (move_uploaded_file($file_tmp, $upload_path)) {
|
||||||
|
// Delete old profile pic if exists
|
||||||
|
if ($user['profile_pic'] && file_exists(__DIR__ . '/../' . $user['profile_pic'])) {
|
||||||
|
unlink(__DIR__ . '/../' . $user['profile_pic']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$profile_pic_path = 'assets/images/users/' . $new_file_name;
|
||||||
|
$pdo->prepare("UPDATE users SET profile_pic = ? WHERE id = ?")->execute([$profile_pic_path, $id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$pdo->commit();
|
||||||
|
$message = '<div class="alert alert-success">Profile updated successfully!</div>';
|
||||||
|
|
||||||
|
// Refresh user data and update session
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
$_SESSION['user'] = $user;
|
||||||
|
unset($_SESSION['user']['password']);
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$error = 'Error: ' . $e->getMessage();
|
$pdo->rollBack();
|
||||||
|
$message = '<div class="alert alert-danger">Error updating profile: ' . $e->getMessage() . '</div>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
include __DIR__ . '/includes/header.php';
|
include 'includes/header.php';
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="container-fluid">
|
<div class="mb-4">
|
||||||
<div class="row">
|
<h2 class="fw-bold">My Profile</h2>
|
||||||
<div class="col-md-8 mx-auto">
|
<p class="text-muted">Manage your personal information and account settings.</p>
|
||||||
<div class="card shadow-sm border-0 rounded-4 overflow-hidden mb-4">
|
</div>
|
||||||
<div class="card-header bg-white py-3 border-bottom-0">
|
|
||||||
<h5 class="mb-0 fw-bold">My Profile</h5>
|
<?= $message ?>
|
||||||
</div>
|
|
||||||
<div class="card-body p-4">
|
<div class="row">
|
||||||
<?php if ($success): ?>
|
<div class="col-md-8">
|
||||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
<div class="card border-0 shadow-sm rounded-4 mb-4">
|
||||||
<?= $success ?>
|
<div class="card-body p-4">
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
<form method="POST" enctype="multipart/form-data">
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label small fw-bold text-muted">FULL NAME</label>
|
||||||
|
<input type="text" name="full_name" class="form-control" value="<?= htmlspecialchars($user['full_name']) ?>" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label small fw-bold text-muted">USERNAME (READ-ONLY)</label>
|
||||||
|
<input type="text" class="form-control bg-light" value="<?= htmlspecialchars($user['username']) ?>" readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label small fw-bold text-muted">EMAIL</label>
|
||||||
|
<input type="email" name="email" class="form-control" value="<?= htmlspecialchars($user['email']) ?>" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label small fw-bold text-muted">ROLE / GROUP</label>
|
||||||
|
<input type="text" class="form-control bg-light" value="<?= htmlspecialchars($user['group_name']) ?>" readonly>
|
||||||
|
<label class="form-label small fw-bold text-muted mt-3">EMPLOYEE / BIOMETRIC ID</label>
|
||||||
|
<input type="text" class="form-control bg-light" value="<?= htmlspecialchars($user['employee_id'] ?? 'Not assigned') ?>" readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row mb-4">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<label class="form-label small fw-bold text-muted">PROFILE PICTURE</label>
|
||||||
|
<div class="d-flex align-items-center gap-3">
|
||||||
|
<?php if ($user['profile_pic']): ?>
|
||||||
|
<img src="../<?= htmlspecialchars($user['profile_pic']) ?>?v=<?= time() ?>" alt="Profile Picture" class="rounded-circle shadow-sm" style="width: 100px; height: 100px; object-fit: cover;">
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="bg-primary bg-gradient text-white rounded-circle d-flex align-items-center justify-content-center shadow-sm" style="width: 100px; height: 100px; font-weight: 700; font-size: 2rem;">
|
||||||
|
<?= strtoupper(substr($user['full_name'] ?: $user['username'], 0, 1)) ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<div class="flex-grow-1">
|
||||||
|
<input type="file" name="profile_pic" class="form-control" accept="image/*">
|
||||||
|
<div class="form-text mt-1">Allowed: JPG, PNG, GIF, WebP. Recommended: Square image.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="form-label small fw-bold text-muted">NEW PASSWORD (LEAVE BLANK TO KEEP CURRENT)</label>
|
||||||
|
<input type="password" name="password" class="form-control" placeholder="******">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr class="my-4">
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-end gap-2">
|
||||||
|
<button type="submit" class="btn btn-primary rounded-pill px-5 fw-bold">Save Changes</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div class="card border-0 shadow-sm rounded-4 bg-primary bg-gradient text-white shadow mb-4">
|
||||||
|
<div class="card-body p-4 text-center">
|
||||||
|
<div class="mb-3">
|
||||||
|
<?php if ($user['profile_pic']): ?>
|
||||||
|
<img src="../<?= htmlspecialchars($user['profile_pic']) ?>?v=<?= time() ?>" alt="Profile Picture" class="rounded-circle shadow-sm border border-3 border-white mx-auto" style="width: 120px; height: 120px; object-fit: cover;">
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="bg-white text-primary rounded-circle d-flex align-items-center justify-content-center mx-auto shadow-sm" style="width:120px;height:120px; font-weight:700; font-size:3rem;">
|
||||||
|
<?= strtoupper(substr($user['full_name'] ?: $user['username'], 0, 1)) ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if ($error): ?>
|
|
||||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
|
||||||
<?= $error ?>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
|
|
||||||
<form method="POST" enctype="multipart/form-data">
|
|
||||||
<div class="row mb-4">
|
|
||||||
<div class="col-md-4 text-center">
|
|
||||||
<div class="mb-3">
|
|
||||||
<?php if ($user['profile_pic']): ?>
|
|
||||||
<img src="../<?= htmlspecialchars($user['profile_pic']) ?>?v=<?= time() ?>" class="rounded-circle shadow-sm border" style="width: 150px; height: 150px; object-fit: cover;">
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="bg-primary bg-gradient rounded-circle d-flex align-items-center justify-content-center text-white mx-auto shadow-sm" style="width: 150px; height: 150px; font-size: 4rem;">
|
|
||||||
<?= strtoupper(substr($user['username'], 0, 1)) ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="btn btn-outline-primary btn-sm rounded-pill">
|
|
||||||
Change Photo
|
|
||||||
<input type="file" name="profile_pic" class="d-none" accept="image/*">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="small text-muted">Role: <?= htmlspecialchars($user['group_name']) ?></div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-8">
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label fw-medium small">Username</label>
|
|
||||||
<input type="text" class="form-control bg-light border-0" value="<?= htmlspecialchars($user['username']) ?>" readonly>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label fw-medium small">Full Name</label>
|
|
||||||
<input type="text" name="full_name" class="form-control bg-light border-0" value="<?= htmlspecialchars($user['full_name']) ?>" required>
|
|
||||||
</div>
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label fw-medium small">Email Address</label>
|
|
||||||
<input type="email" name="email" class="form-control bg-light border-0" value="<?= htmlspecialchars($user['email']) ?>">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<hr class="my-4 text-muted opacity-25">
|
|
||||||
|
|
||||||
<h6 class="fw-bold mb-3">Change Password</h6>
|
|
||||||
<div class="row mb-3">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<label class="form-label fw-medium small">New Password</label>
|
|
||||||
<input type="password" name="new_password" class="form-control bg-light border-0" placeholder="Leave blank to keep current">
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<label class="form-label fw-medium small">Confirm Password</label>
|
|
||||||
<input type="password" name="confirm_password" class="form-control bg-light border-0">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-4 pt-2">
|
|
||||||
<button type="submit" class="btn btn-primary px-4 py-2 fw-bold rounded-pill">Save Profile Changes</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
|
<h4 class="fw-bold mb-1"><?= htmlspecialchars($user['full_name']) ?></h4>
|
||||||
|
<div class="small opacity-75 mb-3">@<?= htmlspecialchars($user['username']) ?> • <?= htmlspecialchars($user['group_name']) ?></div>
|
||||||
|
|
||||||
|
<div class="badge bg-white text-primary rounded-pill px-3 py-2 mb-3">
|
||||||
|
Active Account
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="small opacity-75">
|
||||||
|
Member since <?= date('F d, Y', strtotime($user['created_at'])) ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card border-0 shadow-sm rounded-4">
|
||||||
|
<div class="card-body p-4">
|
||||||
|
<h6 class="fw-bold mb-3"><i class="bi bi-shield-check me-2 text-primary"></i> Account Security</h6>
|
||||||
|
<ul class="list-unstyled small mb-0">
|
||||||
|
<li class="mb-2"><i class="bi bi-check-circle-fill text-success me-2"></i> Password is encrypted</li>
|
||||||
|
<li class="mb-2"><i class="bi bi-check-circle-fill text-success me-2"></i> Role-based access control</li>
|
||||||
|
<li><i class="bi bi-check-circle-fill text-success me-2"></i> Session-based authentication</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php include __DIR__ . '/includes/footer.php'; ?>
|
<?php include 'includes/footer.php'; ?>
|
||||||
58
db/init.php
58
db/init.php
@ -4,63 +4,35 @@ require_once __DIR__ . '/config.php';
|
|||||||
try {
|
try {
|
||||||
$pdo = db();
|
$pdo = db();
|
||||||
|
|
||||||
// 1. Run Consolidated Schema
|
// Execute schema
|
||||||
$schema = file_get_contents(__DIR__ . '/schema.sql');
|
$sql = file_get_contents(__DIR__ . '/schema.sql');
|
||||||
// Split by ; to handle multiple statements if PDO::exec has issues,
|
$pdo->exec($sql);
|
||||||
// but schema.sql is designed to be executed as one block or via a loop.
|
|
||||||
// For simplicity and to handle the large schema:
|
|
||||||
$queries = array_filter(array_map('trim', explode(';', $schema)));
|
|
||||||
foreach ($queries as $query) {
|
|
||||||
if (!empty($query)) {
|
|
||||||
try {
|
|
||||||
$pdo->exec($query);
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
// Ignore errors like "Duplicate column" if re-running on existing DB
|
|
||||||
if (strpos($e->getMessage(), 'Duplicate column') === false &&
|
|
||||||
strpos($e->getMessage(), 'already exists') === false) {
|
|
||||||
echo "Notice (Schema): " . $e->getMessage() . "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Run Company Settings if table exists
|
// Check if data exists
|
||||||
if (file_exists(__DIR__ . '/company_settings.sql')) {
|
|
||||||
$cs_sql = file_get_contents(__DIR__ . '/company_settings.sql');
|
|
||||||
$cs_queries = array_filter(array_map('trim', explode(';', $cs_sql)));
|
|
||||||
foreach ($cs_queries as $query) {
|
|
||||||
if (!empty($query)) {
|
|
||||||
try {
|
|
||||||
$pdo->exec($query);
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Seed initial data if empty
|
|
||||||
$stmt = $pdo->query("SELECT COUNT(*) FROM outlets");
|
$stmt = $pdo->query("SELECT COUNT(*) FROM outlets");
|
||||||
if ($stmt->fetchColumn() == 0) {
|
if ($stmt->fetchColumn() == 0) {
|
||||||
|
// Seed Outlets
|
||||||
$pdo->exec("INSERT INTO outlets (name, address) VALUES ('Main Downtown', '123 Main St'), ('Westside Hub', '456 West Blvd')");
|
$pdo->exec("INSERT INTO outlets (name, address) VALUES ('Main Downtown', '123 Main St'), ('Westside Hub', '456 West Blvd')");
|
||||||
|
|
||||||
|
// Seed Categories
|
||||||
$pdo->exec("INSERT INTO categories (name, sort_order) VALUES ('Burgers', 1), ('Sides', 2), ('Drinks', 3)");
|
$pdo->exec("INSERT INTO categories (name, sort_order) VALUES ('Burgers', 1), ('Sides', 2), ('Drinks', 3)");
|
||||||
|
|
||||||
|
// Seed Products
|
||||||
$pdo->exec("INSERT INTO products (category_id, name, description, price) VALUES
|
$pdo->exec("INSERT INTO products (category_id, name, description, price) VALUES
|
||||||
(1, 'Signature Burger', 'Juicy beef patty with special sauce', 12.99),
|
(1, 'Signature Burger', 'Juicy beef patty with special sauce', 12.99),
|
||||||
(1, 'Veggie Delight', 'Plant-based patty with fresh avocado', 11.50),
|
(1, 'Veggie Delight', 'Plant-based patty with fresh avocado', 11.50),
|
||||||
(2, 'Truffle Fries', 'Crispy fries with truffle oil and parmesan', 5.99),
|
(2, 'Truffle Fries', 'Crispy fries with truffle oil and parmesan', 5.99),
|
||||||
(3, 'Craft Cola', 'House-made sparkling cola', 3.50)");
|
(3, 'Craft Cola', 'House-made sparkling cola', 3.50)");
|
||||||
|
|
||||||
// Ensure Admin group and user exist
|
// Seed Variants
|
||||||
$pdo->exec("INSERT IGNORE INTO user_groups (id, name, permissions) VALUES (1, 'Administrator', 'all')");
|
$pdo->exec("INSERT INTO product_variants (product_id, name, price_adjustment) VALUES
|
||||||
|
(1, 'Double Patty', 4.00),
|
||||||
// admin123 hash
|
(1, 'Extra Cheese', 1.00),
|
||||||
$hashed_pass = password_hash('admin123', PASSWORD_DEFAULT);
|
(3, 'Large Portion', 2.00)");
|
||||||
$pdo->exec("INSERT IGNORE INTO users (group_id, username, password, full_name, is_active)
|
|
||||||
VALUES (1, 'admin', '$hashed_pass', 'Super Admin', 1)");
|
|
||||||
|
|
||||||
echo "Database initialized and seeded successfully.";
|
echo "Database initialized and seeded successfully.";
|
||||||
} else {
|
} else {
|
||||||
echo "Database structure updated. Data already exists.";
|
echo "Database already initialized.";
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
echo "Error: " . $e->getMessage();
|
echo "Error: " . $e->getMessage();
|
||||||
|
|||||||
183
db/schema.sql
183
db/schema.sql
@ -17,12 +17,7 @@ CREATE TABLE IF NOT EXISTS products (
|
|||||||
name VARCHAR(255) NOT NULL,
|
name VARCHAR(255) NOT NULL,
|
||||||
description TEXT,
|
description TEXT,
|
||||||
price DECIMAL(10, 2) NOT NULL,
|
price DECIMAL(10, 2) NOT NULL,
|
||||||
cost_price DECIMAL(10, 2) DEFAULT 0.00,
|
|
||||||
stock_quantity INT DEFAULT 0,
|
|
||||||
image_url VARCHAR(255),
|
image_url VARCHAR(255),
|
||||||
promo_discount_percent DECIMAL(5, 2) DEFAULT NULL,
|
|
||||||
promo_date_from DATE DEFAULT NULL,
|
|
||||||
promo_date_to DATE DEFAULT NULL,
|
|
||||||
FOREIGN KEY (category_id) REFERENCES categories(id)
|
FOREIGN KEY (category_id) REFERENCES categories(id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -51,72 +46,18 @@ CREATE TABLE IF NOT EXISTS tables (
|
|||||||
FOREIGN KEY (area_id) REFERENCES areas(id)
|
FOREIGN KEY (area_id) REFERENCES areas(id)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS user_groups (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
permissions TEXT,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
group_id INT,
|
|
||||||
username VARCHAR(255) NOT NULL UNIQUE,
|
|
||||||
password VARCHAR(255) NOT NULL,
|
|
||||||
full_name VARCHAR(255),
|
|
||||||
employee_id VARCHAR(50) UNIQUE,
|
|
||||||
email VARCHAR(255) UNIQUE,
|
|
||||||
profile_pic VARCHAR(255) DEFAULT NULL,
|
|
||||||
is_active BOOLEAN DEFAULT TRUE,
|
|
||||||
is_ratable TINYINT(1) DEFAULT 0,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (group_id) REFERENCES user_groups(id) ON DELETE SET NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS user_outlets (
|
|
||||||
user_id INT(11) NOT NULL,
|
|
||||||
outlet_id INT(11) NOT NULL,
|
|
||||||
PRIMARY KEY (user_id, outlet_id),
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
||||||
FOREIGN KEY (outlet_id) REFERENCES outlets(id) ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS customers (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
phone VARCHAR(20) UNIQUE,
|
|
||||||
email VARCHAR(255),
|
|
||||||
points INT DEFAULT 0,
|
|
||||||
loyalty_redemptions_count INT DEFAULT 0,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS payment_types (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
type ENUM('cash', 'card', 'api') DEFAULT 'cash',
|
|
||||||
api_provider VARCHAR(50) DEFAULT NULL,
|
|
||||||
is_active BOOLEAN DEFAULT 1,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS orders (
|
CREATE TABLE IF NOT EXISTS orders (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
outlet_id INT,
|
outlet_id INT,
|
||||||
user_id INT(11) NULL,
|
|
||||||
customer_id INT DEFAULT NULL,
|
|
||||||
table_id INT,
|
table_id INT,
|
||||||
table_number VARCHAR(50),
|
table_number VARCHAR(50),
|
||||||
order_type ENUM('dine-in', 'delivery', 'drive-thru', 'takeaway') DEFAULT 'dine-in',
|
order_type ENUM('dine-in', 'delivery', 'drive-thru') DEFAULT 'dine-in',
|
||||||
status ENUM('pending', 'preparing', 'ready', 'completed', 'cancelled') DEFAULT 'pending',
|
status ENUM('pending', 'preparing', 'ready', 'completed', 'cancelled') DEFAULT 'pending',
|
||||||
payment_type_id INT DEFAULT NULL,
|
|
||||||
total_amount DECIMAL(10, 2) NOT NULL,
|
total_amount DECIMAL(10, 2) NOT NULL,
|
||||||
customer_name VARCHAR(255),
|
customer_name VARCHAR(255),
|
||||||
customer_phone VARCHAR(50),
|
customer_phone VARCHAR(50),
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (outlet_id) REFERENCES outlets(id),
|
FOREIGN KEY (outlet_id) REFERENCES outlets(id),
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL,
|
|
||||||
FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE SET NULL,
|
|
||||||
FOREIGN KEY (table_id) REFERENCES tables(id)
|
FOREIGN KEY (table_id) REFERENCES tables(id)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -131,123 +72,11 @@ CREATE TABLE IF NOT EXISTS order_items (
|
|||||||
FOREIGN KEY (product_id) REFERENCES products(id)
|
FOREIGN KEY (product_id) REFERENCES products(id)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS company_settings (
|
-- Loyalty
|
||||||
|
CREATE TABLE IF NOT EXISTS loyalty_customers (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
company_name VARCHAR(255) NOT NULL DEFAULT 'My Restaurant',
|
name VARCHAR(255),
|
||||||
address TEXT,
|
email VARCHAR(255) UNIQUE,
|
||||||
phone VARCHAR(50),
|
points INT DEFAULT 0,
|
||||||
email VARCHAR(255),
|
|
||||||
vat_rate DECIMAL(5, 2) DEFAULT 0.00,
|
|
||||||
currency_symbol VARCHAR(10) DEFAULT '$',
|
|
||||||
currency_decimals INT DEFAULT 2,
|
|
||||||
logo_url VARCHAR(255),
|
|
||||||
favicon_url VARCHAR(255),
|
|
||||||
ctr_number VARCHAR(50),
|
|
||||||
vat_number VARCHAR(50),
|
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS loyalty_settings (
|
|
||||||
id INT PRIMARY KEY,
|
|
||||||
points_per_order INT DEFAULT 10,
|
|
||||||
points_for_free_meal INT DEFAULT 70,
|
|
||||||
is_enabled TINYINT(1) DEFAULT 1
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS integration_settings (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
provider VARCHAR(50) NOT NULL,
|
|
||||||
setting_key VARCHAR(100) NOT NULL,
|
|
||||||
setting_value TEXT,
|
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
||||||
UNIQUE KEY unique_provider_key (provider, setting_key)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS expense_categories (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
description TEXT,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS expenses (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
category_id INT NOT NULL,
|
|
||||||
outlet_id INT NOT NULL,
|
|
||||||
amount DECIMAL(10, 2) NOT NULL,
|
|
||||||
description TEXT,
|
|
||||||
expense_date DATE NOT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (category_id) REFERENCES expense_categories(id),
|
|
||||||
FOREIGN KEY (outlet_id) REFERENCES outlets(id)
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS suppliers (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
name VARCHAR(255) NOT NULL,
|
|
||||||
contact_person VARCHAR(255),
|
|
||||||
email VARCHAR(255),
|
|
||||||
phone VARCHAR(50),
|
|
||||||
address TEXT,
|
|
||||||
vat_no VARCHAR(50),
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS purchases (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
supplier_id INT NULL,
|
|
||||||
purchase_date DATE NOT NULL,
|
|
||||||
total_amount DECIMAL(10, 2) DEFAULT 0.00,
|
|
||||||
status ENUM('pending', 'completed', 'cancelled') DEFAULT 'pending',
|
|
||||||
notes TEXT,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (supplier_id) REFERENCES suppliers(id) ON DELETE SET NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS purchase_items (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
purchase_id INT NOT NULL,
|
|
||||||
product_id INT NOT NULL,
|
|
||||||
quantity INT NOT NULL,
|
|
||||||
cost_price DECIMAL(10, 2) NOT NULL,
|
|
||||||
total_price DECIMAL(10, 2) NOT NULL,
|
|
||||||
FOREIGN KEY (purchase_id) REFERENCES purchases(id) ON DELETE CASCADE,
|
|
||||||
FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS ads_images (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
image_path VARCHAR(255) NOT NULL,
|
|
||||||
title VARCHAR(255) DEFAULT NULL,
|
|
||||||
sort_order INT DEFAULT 0,
|
|
||||||
is_active TINYINT(1) DEFAULT 1,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS attendance_logs (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
user_id INT,
|
|
||||||
employee_id VARCHAR(50),
|
|
||||||
log_timestamp DATETIME,
|
|
||||||
log_type ENUM('IN', 'OUT', 'OTHER') DEFAULT 'IN',
|
|
||||||
device_id VARCHAR(100),
|
|
||||||
ip_address VARCHAR(45),
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS staff_ratings (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
user_id INT NOT NULL,
|
|
||||||
rating INT NOT NULL CHECK (rating >= 1 AND rating <= 5),
|
|
||||||
comment TEXT,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS service_ratings (
|
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
||||||
rating INT NOT NULL CHECK (rating >= 1 AND rating <= 5),
|
|
||||||
comment TEXT,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
);
|
);
|
||||||
@ -239,7 +239,7 @@ function login_user($username, $password) {
|
|||||||
LEFT JOIN user_groups g ON u.group_id = g.id
|
LEFT JOIN user_groups g ON u.group_id = g.id
|
||||||
WHERE u.username = ? AND u.is_active = 1
|
WHERE u.username = ? AND u.is_active = 1
|
||||||
LIMIT 1");
|
LIMIT 1");
|
||||||
$stmt->execute([username]);
|
$stmt->execute([$username]);
|
||||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
if ($user && password_verify($password, $user['password'])) {
|
if ($user && password_verify($password, $user['password'])) {
|
||||||
@ -264,7 +264,7 @@ function get_logged_user() {
|
|||||||
|
|
||||||
function require_login() {
|
function require_login() {
|
||||||
if (!get_logged_user()) {
|
if (!get_logged_user()) {
|
||||||
header('Location: ' . url('login.php'));
|
header('Location: /login.php');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -291,35 +291,17 @@ function require_permission($permission) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the base path of the application.
|
* Get the base URL of the application.
|
||||||
*/
|
*
|
||||||
function get_base_path() {
|
* @return string The base URL.
|
||||||
static $base_path = null;
|
|
||||||
if ($base_path === null) {
|
|
||||||
$script_dir = dirname($_SERVER['SCRIPT_NAME'] ?? '');
|
|
||||||
// Replace known subfolders at the end of the path
|
|
||||||
$base_path = preg_replace('/(\/admin|\/api|\/includes)$|(\/admin\/.*|\/api\/.*|\/includes\/.*)$/', '', $script_dir);
|
|
||||||
if ($base_path === DIRECTORY_SEPARATOR || $base_path === '/') {
|
|
||||||
$base_path = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $base_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a URL relative to the application base.
|
|
||||||
*/
|
|
||||||
function url($path = '') {
|
|
||||||
$path = ltrim($path, '/');
|
|
||||||
return get_base_path() . '/' . $path;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the full base URL including domain and protocol.
|
|
||||||
*/
|
*/
|
||||||
function get_base_url() {
|
function get_base_url() {
|
||||||
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || ($_SERVER['SERVER_PORT'] ?? '') == 443) ? "https://" : "http://";
|
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
|
||||||
$domainName = $_SERVER['HTTP_HOST'] ?? 'localhost';
|
$domainName = $_SERVER['HTTP_HOST'];
|
||||||
|
// Remove admin/ if we are in it
|
||||||
|
$script_dir = dirname($_SERVER['SCRIPT_NAME']);
|
||||||
|
$script_dir = str_replace(['/admin', '/api'], '', $script_dir);
|
||||||
|
if ($script_dir === '/') $script_dir = '';
|
||||||
|
|
||||||
return $protocol . $domainName . url();
|
return $protocol . $domainName . $script_dir . '/';
|
||||||
}
|
}
|
||||||
@ -6,7 +6,7 @@ init_session();
|
|||||||
|
|
||||||
// Redirect if already logged in
|
// Redirect if already logged in
|
||||||
if (get_logged_user()) {
|
if (get_logged_user()) {
|
||||||
header('Location: ' . url('admin/index.php'));
|
header('Location: /admin/index.php');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$password = $_POST['password'] ?? '';
|
$password = $_POST['password'] ?? '';
|
||||||
|
|
||||||
if (login_user($username, $password)) {
|
if (login_user($username, $password)) {
|
||||||
header('Location: ' . url('admin/index.php'));
|
header('Location: /admin/index.php');
|
||||||
exit;
|
exit;
|
||||||
} else {
|
} else {
|
||||||
$error = 'Invalid username or password.';
|
$error = 'Invalid username or password.';
|
||||||
@ -34,7 +34,7 @@ $settings = get_company_settings();
|
|||||||
<title>Login - <?= htmlspecialchars($settings['company_name']) ?></title>
|
<title>Login - <?= htmlspecialchars($settings['company_name']) ?></title>
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
||||||
<link rel="stylesheet" href="<?= url('assets/css/custom.css') ?>">
|
<link rel="stylesheet" href="/assets/css/custom.css">
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/includes/functions.php';
|
require_once __DIR__ . '/includes/functions.php';
|
||||||
logout_user();
|
logout_user();
|
||||||
header('Location: ' . url('login.php'));
|
header('Location: /login.php');
|
||||||
exit;
|
exit;
|
||||||
11
test_url.php
Normal file
11
test_url.php
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
$protocol = "http://";
|
||||||
|
$host = "localhost";
|
||||||
|
$_SERVER['PHP_SELF'] = "/admin/tables.php";
|
||||||
|
try {
|
||||||
|
$baseUrl = $protocol . $host . rtrim(dirname($_SERVER['PHP_SELF'], 2), '/\') . '/qorder.php';
|
||||||
|
echo "Base URL: " . $baseUrl . "\n";
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
echo "Error: " . $e->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user