Revert to version c6c300f

This commit is contained in:
Flatlogic Bot 2026-02-23 19:53:41 +00:00
parent 171664feb3
commit 8f996b6408
8 changed files with 224 additions and 384 deletions

View File

@ -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>

View File

@ -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 $pdo->beginTransaction();
try {
$sql = "UPDATE users SET full_name = ?, email = ? WHERE id = ?";
$params = [$full_name, $email, $id];
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
// Update password if provided
if (!empty($_POST['password'])) {
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
$pdo->prepare("UPDATE users SET password = ? WHERE id = ?")->execute([$password, $id]);
}
// Handle Profile Picture Upload
if (isset($_FILES['profile_pic']) && $_FILES['profile_pic']['error'] === UPLOAD_ERR_OK) { if (isset($_FILES['profile_pic']) && $_FILES['profile_pic']['error'] === UPLOAD_ERR_OK) {
$upload_dir = __DIR__ . '/../assets/images/users/'; $upload_dir = __DIR__ . '/../assets/images/users/';
if (!is_dir($upload_dir)) mkdir($upload_dir, 0777, true); if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0775, true);
}
$ext = pathinfo($_FILES['profile_pic']['name'], PATHINFO_EXTENSION); $file_tmp = $_FILES['profile_pic']['tmp_name'];
$filename = 'user_' . $user['id'] . '_' . uniqid() . '.' . $ext; $file_name = $_FILES['profile_pic']['name'];
$target = $upload_dir . $filename; $file_ext = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));
$allowed_exts = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
if (move_uploaded_file($_FILES['profile_pic']['tmp_name'], $target)) { if (in_array($file_ext, $allowed_exts)) {
$profile_pic = 'assets/images/users/' . $filename; $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]);
}
} }
} }
try { $pdo->commit();
if (!empty($new_password)) { $message = '<div class="alert alert-success">Profile updated successfully!</div>';
if ($new_password !== $confirm_password) {
$error = 'Passwords do not match.'; // Refresh user data and update session
} else { $stmt->execute([$id]);
$hashed_pass = password_hash($new_password, PASSWORD_DEFAULT); $user = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt = $pdo->prepare("UPDATE users SET full_name = ?, email = ?, password = ?, profile_pic = ? WHERE id = ?"); $_SESSION['user'] = $user;
$stmt->execute([$full_name, $email, $hashed_pass, $profile_pic, $user['id']]); unset($_SESSION['user']['password']);
$success = 'Profile and password updated successfully.';
}
} else {
$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)) {
// Update session data
$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 = ?");
$stmt->execute([$user['id']]);
$_SESSION['user'] = $stmt->fetch(PDO::FETCH_ASSOC);
$user = $_SESSION['user'];
}
} 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="row">
<div class="col-md-8">
<div class="card border-0 shadow-sm rounded-4 mb-4">
<div class="card-body p-4"> <div class="card-body p-4">
<?php if ($success): ?>
<div class="alert alert-success alert-dismissible fade show" role="alert">
<?= $success ?>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
<?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"> <form method="POST" enctype="multipart/form-data">
<div class="row mb-4"> <div class="row mb-4">
<div class="col-md-4 text-center"> <div class="col-md-6">
<div class="mb-3"> <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']): ?> <?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;"> <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: ?> <?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;"> <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['username'], 0, 1)) ?> <?= strtoupper(substr($user['full_name'] ?: $user['username'], 0, 1)) ?>
</div> </div>
<?php endif; ?> <?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 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> </div>
</div> </div>
<hr class="my-4 text-muted opacity-25"> <div class="mb-4">
<label class="form-label small fw-bold text-muted">NEW PASSWORD (LEAVE BLANK TO KEEP CURRENT)</label>
<h6 class="fw-bold mb-3">Change Password</h6> <input type="password" name="password" class="form-control" placeholder="******">
<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>
<div class="mt-4 pt-2"> <hr class="my-4">
<button type="submit" class="btn btn-primary px-4 py-2 fw-bold rounded-pill">Save Profile Changes</button>
<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> </div>
</form> </form>
</div> </div>
</div> </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>
<?php endif; ?>
</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>
<?php include __DIR__ . '/includes/footer.php'; ?> <?php include 'includes/footer.php'; ?>

View File

@ -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();

View File

@ -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
); );

View File

@ -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 . '/';
} }

View File

@ -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%);

View File

@ -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
View 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";
}