- = $success ?>
-
+
+
My Profile
+
Manage your personal information and account settings.
+
+
+= $message ?>
+
+
+
+
+
+
+
+
+
+
 ?>?v=<?= time() ?>)
+
+
+ = strtoupper(substr($user['full_name'] ?: $user['username'], 0, 1)) ?>
-
-
-
- = $error ?>
-
-
-
-
-
+
= htmlspecialchars($user['full_name']) ?>
+
@= htmlspecialchars($user['username']) ?> • = htmlspecialchars($user['group_name']) ?>
+
+
+ Active Account
+
+
+
+ Member since = date('F d, Y', strtotime($user['created_at'])) ?>
+
+
+
+
+
+
+
Account Security
+
+ - Password is encrypted
+ - Role-based access control
+ - Session-based authentication
+
-
+
\ No newline at end of file
diff --git a/db/init.php b/db/init.php
index 6f9d174..9375db9 100644
--- a/db/init.php
+++ b/db/init.php
@@ -4,64 +4,36 @@ require_once __DIR__ . '/config.php';
try {
$pdo = db();
- // 1. Run Consolidated Schema
- $schema = file_get_contents(__DIR__ . '/schema.sql');
- // Split by ; to handle multiple statements if PDO::exec has issues,
- // 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";
- }
- }
- }
- }
+ // Execute schema
+ $sql = file_get_contents(__DIR__ . '/schema.sql');
+ $pdo->exec($sql);
- // 2. Run Company Settings if table 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
+ // Check if data exists
$stmt = $pdo->query("SELECT COUNT(*) FROM outlets");
if ($stmt->fetchColumn() == 0) {
+ // Seed Outlets
$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)");
+
+ // Seed Products
$pdo->exec("INSERT INTO products (category_id, name, description, price) VALUES
(1, 'Signature Burger', 'Juicy beef patty with special sauce', 12.99),
(1, 'Veggie Delight', 'Plant-based patty with fresh avocado', 11.50),
(2, 'Truffle Fries', 'Crispy fries with truffle oil and parmesan', 5.99),
(3, 'Craft Cola', 'House-made sparkling cola', 3.50)");
- // Ensure Admin group and user exist
- $pdo->exec("INSERT IGNORE INTO user_groups (id, name, permissions) VALUES (1, 'Administrator', 'all')");
-
- // admin123 hash
- $hashed_pass = password_hash('admin123', PASSWORD_DEFAULT);
- $pdo->exec("INSERT IGNORE INTO users (group_id, username, password, full_name, is_active)
- VALUES (1, 'admin', '$hashed_pass', 'Super Admin', 1)");
+ // Seed Variants
+ $pdo->exec("INSERT INTO product_variants (product_id, name, price_adjustment) VALUES
+ (1, 'Double Patty', 4.00),
+ (1, 'Extra Cheese', 1.00),
+ (3, 'Large Portion', 2.00)");
echo "Database initialized and seeded successfully.";
} else {
- echo "Database structure updated. Data already exists.";
+ echo "Database already initialized.";
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
-}
\ No newline at end of file
+}
diff --git a/db/schema.sql b/db/schema.sql
index 439f88e..78d2335 100644
--- a/db/schema.sql
+++ b/db/schema.sql
@@ -17,12 +17,7 @@ CREATE TABLE IF NOT EXISTS products (
name VARCHAR(255) NOT NULL,
description TEXT,
price DECIMAL(10, 2) NOT NULL,
- cost_price DECIMAL(10, 2) DEFAULT 0.00,
- stock_quantity INT DEFAULT 0,
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)
);
@@ -51,72 +46,18 @@ CREATE TABLE IF NOT EXISTS tables (
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 (
id INT AUTO_INCREMENT PRIMARY KEY,
outlet_id INT,
- user_id INT(11) NULL,
- customer_id INT DEFAULT NULL,
table_id INT,
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',
- payment_type_id INT DEFAULT NULL,
total_amount DECIMAL(10, 2) NOT NULL,
customer_name VARCHAR(255),
customer_phone VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
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)
);
@@ -131,123 +72,11 @@ CREATE TABLE IF NOT EXISTS order_items (
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,
- company_name VARCHAR(255) NOT NULL DEFAULT 'My Restaurant',
- address TEXT,
- phone VARCHAR(50),
- 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,
+ name VARCHAR(255),
+ email VARCHAR(255) UNIQUE,
+ points INT DEFAULT 0,
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
-);
+);
\ No newline at end of file
diff --git a/includes/functions.php b/includes/functions.php
index 9265677..70110bd 100644
--- a/includes/functions.php
+++ b/includes/functions.php
@@ -239,7 +239,7 @@ function login_user($username, $password) {
LEFT JOIN user_groups g ON u.group_id = g.id
WHERE u.username = ? AND u.is_active = 1
LIMIT 1");
- $stmt->execute([username]);
+ $stmt->execute([$username]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && password_verify($password, $user['password'])) {
@@ -264,7 +264,7 @@ function get_logged_user() {
function require_login() {
if (!get_logged_user()) {
- header('Location: ' . url('login.php'));
+ header('Location: /login.php');
exit;
}
}
@@ -291,35 +291,17 @@ function require_permission($permission) {
}
/**
- * Get the base path of the application.
- */
-function get_base_path() {
- 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.
+ * Get the base URL of the application.
+ *
+ * @return string The base URL.
*/
function get_base_url() {
- $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || ($_SERVER['SERVER_PORT'] ?? '') == 443) ? "https://" : "http://";
- $domainName = $_SERVER['HTTP_HOST'] ?? 'localhost';
+ $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
+ $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();
-}
\ No newline at end of file
+ return $protocol . $domainName . $script_dir . '/';
+}
diff --git a/login.php b/login.php
index 967bed7..a66e114 100644
--- a/login.php
+++ b/login.php
@@ -6,7 +6,7 @@ init_session();
// Redirect if already logged in
if (get_logged_user()) {
- header('Location: ' . url('admin/index.php'));
+ header('Location: /admin/index.php');
exit;
}
@@ -17,7 +17,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$password = $_POST['password'] ?? '';
if (login_user($username, $password)) {
- header('Location: ' . url('admin/index.php'));
+ header('Location: /admin/index.php');
exit;
} else {
$error = 'Invalid username or password.';
@@ -34,7 +34,7 @@ $settings = get_company_settings();
Login - = htmlspecialchars($settings['company_name']) ?>
-
+