Compare commits

..

5 Commits

Author SHA1 Message Date
Flatlogic Bot
2681ea74d4 2.2 Gemini error 2025-12-05 07:19:52 +00:00
Flatlogic Bot
d90a7739dc 2.1 db errors 2025-12-05 07:17:26 +00:00
Flatlogic Bot
929c1ba4df feat: Implement query and reply functionality for Dispatch and Sales teams 2025-12-05 07:11:51 +00:00
Flatlogic Bot
348f175175 Bug 1.0 2025-12-05 06:58:33 +00:00
Flatlogic Bot
496d2ec7e5 1.0 2025-12-05 06:45:47 +00:00
12 changed files with 1189 additions and 150 deletions

419
admin_users.php Normal file
View File

@ -0,0 +1,419 @@
<?php
session_start();
require_once __DIR__ . '/db/config.php';
// Check if user is logged in and is an Admin
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'Admin') {
header('Location: login.php');
exit();
}
// Handle delete action
if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) {
$user_id_to_delete = filter_var($_GET['id'], FILTER_VALIDATE_INT);
if ($user_id_to_delete && $user_id_to_delete !== $_SESSION['user_id']) { // Prevent admin from deleting themselves
try {
$stmt = db()->prepare("DELETE FROM users WHERE id = ?");
$stmt->execute([$user_id_to_delete]);
$_SESSION['success_message'] = 'User deleted successfully!';
} catch (PDOException $e) {
$_SESSION['error_message'] = 'Error deleting user: ' . htmlspecialchars($e->getMessage());
}
} else {
$_SESSION['error_message'] = 'Invalid user ID or cannot delete your own account.';
}
header('Location: admin_users.php');
exit();
}
$pageTitle = "Admin | User Management";
require_once __DIR__ . '/partials/header.php';
?>
<div class="container mt-5">
<h1 class="mb-4">User Management</h1>
<!-- Placeholder for user list and forms -->
<div class="card mb-4">
<div class="card-header">
Existing Users
</div>
<div class="card-body">
<?php
$users = [];
try {
$stmt = db()->query("SELECT id, name, email, role, created_at FROM users ORDER BY created_at DESC");
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
echo '<div class="alert alert-danger">Error fetching users: ' . htmlspecialchars($e->getMessage()) . '</div>';
}
?>
<?php if (empty($users)): ?>
<p>No users found.</p>
<?php else: ?>
<div class="table-responsive">
<table class="table table-hover table-striped">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Role</th>
<th>Created At</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($users as $user): ?>
<tr>
<td><?php echo htmlspecialchars($user['id']); ?></td>
<td><?php echo htmlspecialchars($user['name']); ?></td>
<td><?php echo htmlspecialchars($user['email']); ?></td>
<td><?php echo htmlspecialchars($user['role']); ?></td>
<td><?php echo htmlspecialchars($user['created_at']); ?></td>
<td>
<a href="?action=edit&id=<?php echo $user['id']; ?>" class="btn btn-sm btn-primary me-2">Edit</a>
<a href="?action=delete&id=<?php echo $user['id']; ?>" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure you want to delete this user?');">Delete</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div> </div>
<div class="card">
<div class="card-header">
<?php echo isset($edit_user) ? 'Edit User' : 'Add New User'; ?>
</div>
<div class="card-body">
<?php
$name = $email = $password = $role = '';
$errors = [];
$edit_user_id = null;
$edit_user = null;
// Handle edit action - fetch user data
if (isset($_GET['action']) && $_GET['action'] === 'edit' && isset($_GET['id'])) {
$edit_user_id = filter_var($_GET['id'], FILTER_VALIDATE_INT);
if ($edit_user_id) {
try {
$stmt = db()->prepare("SELECT id, name, email, role FROM users WHERE id = ?");
$stmt->execute([$edit_user_id]);
$edit_user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($edit_user) {
$name = $edit_user['name'];
$email = $edit_user['email'];
$role = $edit_user['role'];
} else {
$_SESSION['error_message'] = 'User not found.';
header('Location: admin_users.php');
exit();
}
} catch (PDOException $e) {
$_SESSION['error_message'] = 'Error fetching user for edit: ' . htmlspecialchars($e->getMessage());
header('Location: admin_users.php');
exit();
}
}
}
// Handle form submission for Add or Edit
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['add_user']) || isset($_POST['edit_user'])) {
$name = trim($_POST['name']);
$email = trim($_POST['email']);
$role = $_POST['role'];
$password = isset($_POST['password']) ? $_POST['password'] : '';
$confirm_password = isset($_POST['confirm_password']) ? $_POST['confirm_password'] : '';
$current_user_id = isset($_POST['user_id']) ? filter_var($_POST['user_id'], FILTER_VALIDATE_INT) : null;
if (empty($name)) {
$errors[] = 'Name is required.';
}
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Valid email is required.';
}
if (empty($role)) {
$errors[] = 'Role is required.';
}
if (isset($_POST['add_user'])) { // For adding new user
if (empty($password)) {
$errors[] = 'Password is required.';
}
if ($password !== $confirm_password) {
$errors[] = 'Passwords do not match.';
}
} else if (isset($_POST['edit_user'])) { // For editing existing user
if (!empty($password) && $password !== $confirm_password) {
$errors[] = 'Passwords do not match.';
}
}
if (empty($errors)) {
try {
if (isset($_POST['add_user'])) {
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$stmt = db()->prepare("INSERT INTO users (name, email, password, role) VALUES (?, ?, ?, ?)");
$stmt->execute([$name, $email, $hashed_password, $role]);
$_SESSION['success_message'] = 'User added successfully!';
} else if (isset($_POST['edit_user'])) {
$sql = "UPDATE users SET name = ?, email = ?, role = ?";
$params = [$name, $email, $role];
if (!empty($password)) {
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$sql .= ", password = ?";
$params[] = $hashed_password;
}
$sql .= " WHERE id = ?";
$params[] = $current_user_id;
$stmt = db()->prepare($sql);
$stmt->execute($params);
$_SESSION['success_message'] = 'User updated successfully!';
}
header('Location: admin_users.php');
exit();
} catch (PDOException $e) {
if ($e->getCode() === '23000') { // Duplicate entry
$errors[] = 'User with this email already exists.';
} else {
$errors[] = 'Error processing user: ' . htmlspecialchars($e->getMessage());
}
}
}
}
}
?>
<?php if (!empty($errors)): ?>
<div class="alert alert-danger">
<?php foreach ($errors as $error): ?>
<p class="mb-0"><?php echo htmlspecialchars($error); ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (isset($_SESSION['success_message'])): ?>
<div class="alert alert-success">
<?php echo $_SESSION['success_message']; ?>
</div>
<?php unset($_SESSION['success_message']); ?>
<?php endif; ?>
<?php if (isset($_SESSION['error_message'])): ?>
<div class="alert alert-danger">
<?php echo $_SESSION['error_message']; ?>
</div>
<?php unset($_SESSION['error_message']); ?>
<?php endif; ?>
<form action="admin_users.php" method="POST">
<?php if ($edit_user): ?>
<input type="hidden" name="user_id" value="<?php echo htmlspecialchars($edit_user['id']); ?>">
<?php endif; ?>
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" id="name" name="name" value="<?php echo htmlspecialchars($name); ?>" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" value="<?php echo htmlspecialchars($email); ?>" required>
</div>
<div class="mb-3">
<label for="password" class="form-label"><?php echo isset($edit_user) ? 'New Password (leave blank to keep current)' : 'Password'; ?></label>
<input type="password" class="form-control" id="password" name="password" <?php echo isset($edit_user) ? '' : 'required'; ?>>
</div>
<div class="mb-3">
<label for="confirm_password" class="form-label"><?php echo isset($edit_user) ? 'Confirm New Password' : 'Confirm Password'; ?></label>
<input type="password" class="form-control" id="confirm_password" name="confirm_password" <?php echo isset($edit_user) ? '' : 'required'; ?>>
</div>
<div class="mb-3">
<label for="role" class="form-label">Role</label>
<select class="form-select" id="role" name="role" required>
<option value="">Select Role</option>
<option value="Admin" <?php echo ($role === 'Admin') ? 'selected' : ''; ?>>Admin</option>
<option value="Sales Rep" <?php echo ($role === 'Sales Rep') ? 'selected' : ''; ?>>Sales Rep</option>
<option value="Dispatch" <?php echo ($role === 'Dispatch') ? 'selected' : ''; ?>>Dispatch</option>
</select>
</div>
<?php if ($edit_user): ?>
<button type="submit" name="edit_user" class="btn btn-primary">Update User</button>
<a href="admin_users.php" class="btn btn-secondary">Cancel</a>
<?php else: ?>
<button type="submit" name="add_user" class="btn btn-primary">Add User</button>
<?php endif; ?>
</form>
</div> </div>
</div>
<?php
require_once __DIR__ . '/partials/footer.php';
?>

31
assets/css/custom.css Normal file
View File

@ -0,0 +1,31 @@
body {
font-family: 'Inter', sans-serif;
}
.bg-light-gray {
background-color: #F8F9FA;
}
.btn-primary {
background-color: #F97316;
border-color: #F97316;
}
.btn-primary:hover, .btn-primary:focus, .btn-primary:active {
background-color: #EA580C;
border-color: #EA580C;
}
.text-primary {
color: #F97316 !important;
}
.form-control:focus {
border-color: #F97316;
box-shadow: 0 0 0 0.25rem rgba(249, 115, 22, 0.25);
}
.nav-link.active {
color: #F97316 !important;
border-bottom: 2px solid #F97316;
}

51
auth.php Normal file
View File

@ -0,0 +1,51 @@
<?php
ini_set('session.use_strict_mode', 1);
ini_set('session.cookie_lifetime', 0);
ini_set('session.cookie_secure', 1); // Should be 1 in production
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_samesite', 'Lax');
session_start();
require_once __DIR__ . '/db/config.php';
function is_logged_in() {
return isset($_SESSION['user_id']);
}
function require_login() {
if (!is_logged_in()) {
header('Location: login.php');
exit;
}
}
// Handle login
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['email'], $_POST['password'])) {
$email = $_POST['email'];
$password = $_POST['password'];
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
session_regenerate_id(true);
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_name'] = $user['name'];
$_SESSION['user_role'] = $user['role'];
header('Location: index.php');
exit;
} else {
$_SESSION['login_error'] = 'Invalid email or password.';
header('Location: login.php');
exit;
}
} catch (PDOException $e) {
// In a real app, log this error.
$_SESSION['login_error'] = 'An error occurred. Please try again later.';
header('Location: login.php');
exit;
}
}

119
create_order.php Normal file
View File

@ -0,0 +1,119 @@
<?php
session_start();
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/partials/header.php';
// Check if user is logged in and is a Sales Rep
if (!isset($_SESSION['user_id']) || $_SESSION['user_role'] !== 'Sales Rep') {
header('Location: login.php');
exit();
}
$errors = [];
$success_message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// CSRF protection
if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die('CSRF token validation failed.');
}
$order_text = trim($_POST['order_text'] ?? '');
$sales_rep_id = $_SESSION['user_id'];
$order_date = date('Y-m-d'); // Auto-set and locked
if (empty($order_text)) {
$errors[] = 'Order Text cannot be empty.';
}
if (empty($errors)) {
try {
$pdo = db();
$pdo->beginTransaction();
// Generate order number (simple placeholder for now, will enhance later)
// For now, let's just use a timestamp based simple one, we will improve later.
$order_number = 'FMO' . date('YmdHis');
$stmt = $pdo->prepare('INSERT INTO orders (order_number, order_date, order_text, status, sales_rep_id) VALUES (?, ?, ?, ?, ?)');
$stmt->execute([$order_number, $order_date, $order_text, 'Pending', $sales_rep_id]);
$pdo->commit();
$success_message = 'Order ' . $order_number . ' created successfully!';
// Clear the form
$order_text = '';
// Send email notification to Dispatch
require_once __DIR__ . '/mail/MailService.php';
$dispatch_email = 'info@focuzinternational.com'; // TODO: Make this configurable by Admin
$subject = 'New Order: ' . $order_number . ' (' . 'Pending' . ')';
$html_body = '<p>A new order has been created:</p>'
. '<p><strong>Order Number:</strong> ' . htmlspecialchars($order_number) . '</p>'
. '<p><strong>Order Date:</strong> ' . htmlspecialchars($order_date) . '</p>'
. '<p><strong>Order Text:</strong> ' . nl2br(htmlspecialchars($order_text)) . '</p>'
. '<p><strong>Status:</strong> Pending</p>';
$text_body = "A new order has been created:\n\n"
. "Order Number: {$order_number}\n"
. "Order Date: {$order_date}\n"
. "Order Text: {$order_text}\n"
. "Status: Pending";
$mail_result = MailService::sendMail($dispatch_email, $subject, $html_body, $text_body);
if (!empty($mail_result['error'])) {
// Log the email error, but don't fail the order creation
error_log('Email sending failed: ' . $mail_result['error']);
}
} catch (PDOException $e) {
$pdo->rollBack();
$errors[] = 'Database error: ' . $e->getMessage();
}
}
}
// Generate new CSRF token for the form
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
?>
<div class="container mt-5">
<h2>Create New Order</h2>
<?php if (!empty($errors)): ?>
<div class="alert alert-danger" role="alert">
<?php foreach ($errors as $error): ?>
<p><?php echo htmlspecialchars($error); ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (!empty($success_message)): ?>
<div class="alert alert-success" role="alert">
<?php echo htmlspecialchars($success_message); ?>
</div>
<?php endif; ?>
<form method="POST" action="create_order.php">
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
<div class="mb-3">
<label for="order_date" class="form-label">Order Date</label>
<input type="text" class="form-control" id="order_date" value="<?php echo date('Y-m-d'); ?>" readonly>
</div>
<div class="mb-3">
<label for="order_number" class="form-label">Order Number (Generated Automatically)</label>
<input type="text" class="form-control" id="order_number" value="Will be generated on save" readonly>
</div>
<div class="mb-3">
<label for="order_text" class="form-label">Order Text</label>
<textarea class="form-control" id="order_text" name="order_text" rows="5" required><?php echo htmlspecialchars($order_text); ?></textarea>
</div>
<div class="mb-3">
<label for="status" class="form-label">Status</label>
<input type="text" class="form-control" id="status" value="Pending" readonly>
</div>
<button type="submit" class="btn btn-primary">Create Order</button>
</form>
</div>
<?php require_once __DIR__ . '/partials/footer.php'; ?>

View File

@ -4,14 +4,21 @@ define('DB_HOST', '127.0.0.1');
define('DB_NAME', 'app_36675');
define('DB_USER', 'app_36675');
define('DB_PASS', '131b38b5-41c0-4253-a34e-9c92b4bb9911');
require_once __DIR__ . '/setup.php';
function db() {
static $pdo;
if (!$pdo) {
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
try {
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
// Run setup
setup_database($pdo);
} catch (PDOException $e) {
die("DB connection failed: " . $e->getMessage());
}
}
return $pdo;
}

46
db/setup.php Normal file
View File

@ -0,0 +1,46 @@
<?php
function setup_database($pdo) {
try {
$pdo->exec("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('Admin', 'Sales Rep', 'Dispatch') NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)");
$pdo->exec("CREATE TABLE IF NOT EXISTS orders (
id INT AUTO_INCREMENT PRIMARY KEY,
order_number VARCHAR(255) NOT NULL UNIQUE,
order_date DATE NOT NULL,
order_text TEXT NOT NULL,
status ENUM('Pending', 'Query', 'Query Replied', 'Shipped', 'Cancelled') NOT NULL DEFAULT 'Pending',
query_text TEXT DEFAULT NULL,
reply_text TEXT DEFAULT NULL,
sales_rep_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (sales_rep_id) REFERENCES users(id)
)");
$pdo->exec("ALTER TABLE orders ADD COLUMN IF NOT EXISTS query_text TEXT DEFAULT NULL;");
$pdo->exec("ALTER TABLE orders ADD COLUMN IF NOT EXISTS reply_text TEXT DEFAULT NULL;");
// Add a default admin user if one doesn't exist
$stmt = $pdo->query("SELECT COUNT(*) FROM users WHERE role = 'Admin'");
if ($stmt->fetchColumn() == 0) {
$admin_email = 'admin@example.com';
$admin_name = 'Admin';
// In a real app, use a more secure password policy
$admin_password = password_hash('password', PASSWORD_DEFAULT);
$admin_role = 'Admin';
$insert_stmt = $pdo->prepare("INSERT INTO users (name, email, password, role) VALUES (?, ?, ?, ?)");
$insert_stmt->execute([$admin_name, $admin_email, $admin_password, $admin_role]);
}
} catch (PDOException $e) {
// In a real app, you would log this error.
die("Database setup failed: " . $e->getMessage());
}
}

318
index.php
View File

@ -1,150 +1,176 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$page_title = 'Dashboard';
require_once __DIR__ . '/partials/header.php';
require_once __DIR__ . '/db/config.php';
$user_id = $_SESSION['user_id'];
$user_role = $_SESSION['user_role'];
$pdo = db();
// Initialize counts
$pending_orders_count = 0;
$pending_replies_count = 0;
$shipped_orders_month_count = 0;
try {
// --- Fetch Dashboard Counts ---
$base_conditions_arr = [];
$params = [];
if ($user_role === 'Sales Rep') {
$base_conditions_arr[] = "sales_rep_id = :user_id";
$params[':user_id'] = $user_id;
}
// Pending Orders Count
$pending_orders_conditions_arr = $base_conditions_arr;
$pending_orders_conditions_arr[] = "status = 'Pending'";
$pending_orders_where_clause = '';
if (!empty($pending_orders_conditions_arr)) {
$pending_orders_where_clause = " WHERE " . implode(" AND ", $pending_orders_conditions_arr);
}
$stmt = $pdo->prepare("SELECT COUNT(*) FROM orders " . $pending_orders_where_clause);
$stmt->execute($params);
$pending_orders_count = $stmt->fetchColumn();
// Pending Replies Count (Orders with status 'Query' or 'Query Replied')
$pending_replies_conditions_arr = $base_conditions_arr;
$pending_replies_conditions_arr[] = "(status = 'Query' OR status = 'Query Replied')";
$pending_replies_where_clause = '';
if (!empty($pending_replies_conditions_arr)) {
$pending_replies_where_clause = " WHERE " . implode(" AND ", $pending_replies_conditions_arr);
}
$stmt = $pdo->prepare("SELECT COUNT(*) FROM orders " . $pending_replies_where_clause);
$stmt->execute($params);
$pending_replies_count = $stmt->fetchColumn();
// Shipped Orders This Month Count
$current_month_start = date('Y-m-01 00:00:00');
$current_month_end = date('Y-m-t 23:59:59');
$shipped_conditions_arr = $base_conditions_arr;
$shipped_conditions_arr[] = "status = 'Shipped'";
$shipped_conditions_arr[] = "created_at >= :start_date";
$shipped_conditions_arr[] = "created_at <= :end_date";
$shipped_where_clause = '';
$shipped_params = $params;
$shipped_params[':start_date'] = $current_month_start;
$shipped_params[':end_date'] = $current_month_end;
if (!empty($shipped_conditions_arr)) {
$shipped_where_clause = " WHERE " . implode(" AND ", $shipped_conditions_arr);
}
$stmt = $pdo->prepare("SELECT COUNT(*) FROM orders " . $shipped_where_clause);
$stmt->execute($shipped_params);
$shipped_orders_month_count = $stmt->fetchColumn();
// --- Fetch Order List ---
$orders = [];
$sql = "SELECT o.id, o.order_number, o.order_date, o.order_text, o.status, o.sales_rep_id, u.name as sales_rep_name
FROM orders o
JOIN users u ON o.sales_rep_id = u.id";
if ($user_role === 'Sales Rep') {
$sql .= " WHERE o.sales_rep_id = :user_id";
$stmt = $pdo->prepare($sql);
$stmt->bindParam(':user_id', $user_id, PDO::PARAM_INT);
} else {
// Dispatch and Admin can see all orders
$stmt = $pdo->prepare($sql);
}
$stmt->execute();
$orders = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
error_log("Database error fetching dashboard data or orders: " . $e->getMessage());
echo '<div class="alert alert-danger" role="alert">Error loading dashboard data. Please try again later.</div>';
}
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</title>
<?php
// Read project preview data from environment
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
$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>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</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 class="row mb-4">
<div class="col-md-4">
<div class="card border-0 shadow-sm text-primary-emphasis bg-primary-subtle">
<div class="card-body">
<h5 class="card-title">Pending Orders</h5>
<p class="card-text h2"><?php echo htmlspecialchars($pending_orders_count); ?></p>
</div>
</div>
</div>
</main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer>
</body>
</html>
<div class="col-md-4">
<div class="card border-0 shadow-sm text-warning-emphasis bg-warning-subtle">
<div class="card-body">
<h5 class="card-title">Pending Replies</h5>
<p class="card-text h2"><?php echo htmlspecialchars($pending_replies_count); ?></p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card border-0 shadow-sm text-success-emphasis bg-success-subtle">
<div class="card-body">
<h5 class="card-title">Shipped This Month</h5>
<p class="card-text h2"><?php echo htmlspecialchars($shipped_orders_month_count); ?></p>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12 mb-4">
<div class="card border-0 shadow-sm">
<div class="card-body">
<h1 class="h4 mb-0">Welcome, <?php echo htmlspecialchars($_SESSION['user_name']); ?>!</h1>
<p class="text-muted">Your current role: <?php echo htmlspecialchars($user_role); ?></p>
</div>
</div>
</div>
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card-body">
<h5 class="card-title">Order List</h5>
<?php if (empty($orders)): ?>
<p>No orders found.</p>
<?php else: ?>
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Order Number</th>
<th>Date</th>
<th>Sales Rep</th>
<th>Text</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($orders as $order): ?>
<tr>
<td><?php echo htmlspecialchars($order['order_number']); ?></td>
<td><?php echo htmlspecialchars($order['order_date']); ?></td>
<td><?php echo htmlspecialchars($order['sales_rep_name']); ?></td>
<td><?php echo htmlspecialchars(substr($order['order_text'], 0, 50)); ?>...</td>
<td><span class="badge bg-secondary"><?php echo htmlspecialchars($order['status']); ?></span></td>
<td>
<a href="view_order.php?id=<?php echo $order['id']; ?>" class="btn btn-sm btn-outline-primary">View</a>
<?php if ($user_role === 'Sales Rep' && $order['sales_rep_id'] == $user_id): ?>
<a href="view_order.php?id=<?php echo $order['id']; ?>" class="btn btn-sm btn-outline-secondary">Edit</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php require_once __DIR__ . '/partials/footer.php'; ?>

63
login.php Normal file
View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - Focuz Order Management</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<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;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="assets/css/custom.css">
</head>
<body class="bg-light-gray">
<div class="container-fluid">
<div class="row vh-100 justify-content-center align-items-center">
<div class="col-11 col-sm-8 col-md-6 col-lg-5 col-xl-4">
<div class="card border-0 shadow-sm p-4" style="border-radius: 0.5rem;">
<div class="card-body">
<div class="text-center mb-4">
<i class="bi bi-box-seam-fill text-primary" style="font-size: 3rem;"></i>
<h1 class="h3 mb-1 fw-bold">Focuz Order Management</h1>
<p class="text-muted">Please sign in to continue</p>
</div>
<?php
session_start();
if (isset($_SESSION['login_error'])):
?>
<div class="alert alert-danger" role="alert">
<?php
echo htmlspecialchars($_SESSION['login_error']);
unset($_SESSION['login_error']);
?>
</div>
<?php endif; ?>
<form action="auth.php" method="POST">
<div class="mb-3">
<label for="email" class="form-label">Email address</label>
<input type="email" class="form-control" id="email" name="email" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Sign in</button>
</div>
</form>
<div class="text-center mt-3">
<small class="text-muted">Default credentials: admin@example.com / password</small>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

6
logout.php Normal file
View File

@ -0,0 +1,6 @@
<?php
session_start();
session_unset();
session_destroy();
header('Location: login.php');
exit;

9
partials/footer.php Normal file
View File

@ -0,0 +1,9 @@
</main>
<footer class="container-fluid text-center text-muted mt-5 py-3">
<small>&copy; <?php echo date('Y'); ?> Focuz International. All Rights Reserved.</small>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

56
partials/header.php Normal file
View File

@ -0,0 +1,56 @@
<?php
require_once __DIR__ . '/../auth.php';
require_login();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo isset($page_title) ? htmlspecialchars($page_title) : 'Dashboard'; ?> - Focuz Order Management</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<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;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body class="bg-light-gray">
<nav class="navbar navbar-expand-lg navbar-light bg-white border-bottom">
<div class="container-fluid">
<a class="navbar-brand fw-bold text-primary" href="index.php">
<i class="bi bi-box-seam-fill"></i>
Focuz Orders
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#main-nav" aria-controls="main-nav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="main-nav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="index.php">Dashboard</a>
</li>
<!-- More nav items will go here based on role -->
<?php if (isset($_SESSION['user_role']) && $_SESSION['user_role'] === 'Sales Rep'): ?>
<li class="nav-item">
<a class="nav-link" href="create_order.php">Create Order</a>
</li>
<?php endif; ?>
<?php if (isset($_SESSION['user_role']) && $_SESSION['user_role'] === 'Admin'): ?>
<li class="nav-item">
<a class="nav-link" href="admin_users.php">User Management</a>
</li>
<?php endif; ?>
</ul>
<div class="d-flex align-items-center">
<span class="navbar-text me-3">
Welcome, <?php echo htmlspecialchars($_SESSION['user_name']); ?> (<?php echo htmlspecialchars($_SESSION['user_role']); ?>)
</span>
<a href="logout.php" class="btn btn-outline-secondary btn-sm">Logout</a>
</div>
</div>
</div>
</nav>
<main class="container-fluid mt-4">

206
view_order.php Normal file
View File

@ -0,0 +1,206 @@
<?php
$page_title = 'View/Edit Order';
require_once __DIR__ . '/partials/header.php';
require_once __DIR__ . '/db/config.php';
$user_id = $_SESSION['user_id'];
$user_role = $_SESSION['user_role'];
$pdo = db();
$order = null;
$edit_mode = false;
$errors = [];
$success_message = '';
// Check if order ID is provided
if (isset($_GET['id']) && is_numeric($_GET['id'])) {
$order_id = $_GET['id'];
try {
$stmt = $pdo->prepare("SELECT o.*, o.query_text, o.reply_text, u.name as sales_rep_name FROM orders o LEFT JOIN users u ON o.sales_rep_id = u.id WHERE o.id = :id");
$stmt->bindParam(':id', $order_id, PDO::PARAM_INT);
$stmt->execute();
$order = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$order) {
$errors[] = "Order not found.";
} else {
// Determine if user can edit this order
if ($user_role === 'Sales Rep' && $order['sales_rep_id'] == $user_id) {
$edit_mode = true;
} elseif ($user_role === 'Dispatch' || $user_role === 'Admin') {
$edit_mode = true; // Dispatch and Admin can always edit
}
}
} catch (PDOException $e) {
$errors[] = "Error loading order details. Please try again later.";
}
} else {
$errors[] = "No order ID provided.";
}
// Handle form submission for updating order
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $edit_mode && $order) {
$new_order_number = trim($_POST['order_number'] ?? '');
$new_order_date = trim($_POST['order_date'] ?? '');
$new_order_text = trim($_POST['order_text'] ?? '');
$new_status = trim($_POST['status'] ?? '');
$new_query_text = trim($_POST['query_text'] ?? '');
$new_reply_text = trim($_POST['reply_text'] ?? '');
// Preserve existing query/reply if not being updated by a specific status change
$query_to_save = $order['query_text'];
$reply_to_save = $order['reply_text'];
if ($new_status === 'Query' && empty($order['query_text'])) {
$query_to_save = $new_query_text;
} elseif ($new_status === 'Query Replied' && empty($order['reply_text'])) {
$reply_to_save = $new_reply_text;
}
if (empty($new_order_number)) {
$errors[] = "Order number cannot be empty.";
}
if (empty($new_order_date)) {
$errors[] = "Order date cannot be empty.";
}
if (empty($new_order_text)) {
$errors[] = "Order text cannot be empty.";
}
if (empty($new_status)) {
$errors[] = "Status cannot be empty.";
}
if (empty($errors)) {
try {
$stmt = $pdo->prepare("UPDATE orders SET order_number = :order_number, order_date = :order_date, order_text = :order_text, status = :status, query_text = :query_text, reply_text = :reply_text WHERE id = :id");
$stmt->bindParam(':order_number', $new_order_number);
$stmt->bindParam(':order_date', $new_order_date);
$stmt->bindParam(':order_text', $new_order_text);
$stmt->bindParam(':status', $new_status);
$stmt->bindParam(':query_text', $query_to_save);
$stmt->bindParam(':reply_text', $reply_to_save);
$stmt->bindParam(':id', $order_id, PDO::PARAM_INT);
$stmt->execute();
$success_message = "Order updated successfully!";
// Re-fetch order to display updated data
$stmt = $pdo->prepare("SELECT o.*, u.name as sales_rep_name FROM orders o LEFT JOIN users u ON o.sales_rep_id = u.id WHERE o.id = :id");
$stmt->bindParam(':id', $order_id, PDO::PARAM_INT);
$stmt->execute();
$order = $stmt->fetch(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
error_log("Database error updating order: " . $e->getMessage());
$errors[] = "Error updating order. Please try again later.";
}
}
}
?>
<div class="container mt-4">
<div class="row">
<div class="col-md-8 mx-auto">
<div class="card shadow-sm">
<div class="card-header bg-primary text-white">
<h1 class="card-title h4 mb-0"><?php echo htmlspecialchars($page_title); ?></h1>
</div>
<div class="card-body">
<?php if (!empty($errors)): ?>
<div class="alert alert-danger">
<?php foreach ($errors as $error): ?>
<p class="mb-0"><?php echo htmlspecialchars($error); ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (!empty($success_message)): ?>
<div class="alert alert-success">
<?php echo htmlspecialchars($success_message); ?>
</div>
<?php endif; ?>
<?php if ($order): ?>
<form method="POST">
<div class="mb-3">
<label for="order_number" class="form-label">Order Number</label>
<input type="text" class="form-control" id="order_number" name="order_number" value="<?php echo htmlspecialchars($order['order_number']); ?>" <?php echo $edit_mode ? '' : 'readonly'; ?>>
</div>
<div class="mb-3">
<label for="order_date" class="form-label">Order Date</label>
<input type="date" class="form-control" id="order_date" name="order_date" value="<?php echo htmlspecialchars($order['order_date']); ?>" <?php echo $edit_mode ? '' : 'readonly'; ?>>
</div>
<div class="mb-3">
<label for="order_text" class="form-label">Order Details</label>
<textarea class="form-control" id="order_text" name="order_text" rows="5" <?php echo $edit_mode ? '' : 'readonly'; ?>><?php echo htmlspecialchars($order['order_text']); ?></textarea>
</div>
<div class="mb-3">
<label for="sales_rep_name" class="form-label">Sales Rep</label>
<input type="text" class="form-control" id="sales_rep_name" value="<?php echo htmlspecialchars($order['sales_rep_name']); ?>" readonly>
</div>
<div class="mb-3">
<label for="status" class="form-label">Status</label>
<?php if ($edit_mode): ?>
<select class="form-select" id="status" name="status">
<?php $statuses = ['Pending', 'Query', 'Query Replied', 'Shipped', 'Cancelled']; ?>
<?php foreach ($statuses as $status_option): ?>
<option value="<?php echo htmlspecialchars($status_option); ?>" <?php echo ($order['status'] === $status_option) ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($status_option); ?>
</option>
<?php endforeach; ?>
</select>
<?php else: ?>
<input type="text" class="form-control" id="status" name="status" value="<?php echo htmlspecialchars($order['status']); ?>" readonly>
<?php endif; ?>
</div>
<div class="mb-3" id="queryTextBox" style="display: none;">
<label for="query_text" class="form-label">Query</label>
<textarea class="form-control" id="query_text" name="query_text" rows="3" <?php echo ($order['query_text'] && $order['query_text'] !== '') ? 'readonly' : ''; ?>><?php echo htmlspecialchars($order['query_text'] ?? ''); ?></textarea>
</div>
<div class="mb-3" id="replyTextBox" style="display: none;">
<label for="reply_text" class="form-label">Query Reply</label>
<textarea class="form-control" id="reply_text" name="reply_text" rows="3" <?php echo ($order['reply_text'] && $order['reply_text'] !== '') ? 'readonly' : ''; ?>><?php echo htmlspecialchars($order['reply_text'] ?? ''); ?></textarea>
</div>
<?php if ($edit_mode): ?>
<button type="submit" class="btn btn-primary">Update Order</button>
<?php endif; ?>
<a href="index.php" class="btn btn-secondary">Back to Dashboard</a>
</form>
<?php else: ?>
<a href="index.php" class="btn btn-primary">Back to Dashboard</a>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const statusSelect = document.getElementById('status');
const queryTextBox = document.getElementById('queryTextBox');
const replyTextBox = document.getElementById('replyTextBox');
function toggleTextBoxes() {
const currentStatus = statusSelect.value;
queryTextBox.style.display = 'none';
replyTextBox.style.display = 'none';
if (currentStatus === 'Query') {
queryTextBox.style.display = 'block';
} else if (currentStatus === 'Query Replied') {
replyTextBox.style.display = 'block';
}
}
// Initial call to set correct visibility on page load
toggleTextBoxes();
// Add event listener for status change
if (statusSelect) {
statusSelect.addEventListener('change', toggleTextBoxes);
}
});
</script>
<?php require_once __DIR__ . '/partials/footer.php'; ?>