Compare commits

...

2 Commits

Author SHA1 Message Date
Flatlogic Bot
58e3bb7010 1.2 2025-11-09 03:37:51 +00:00
Flatlogic Bot
4708a2f224 1.1 2025-11-09 02:29:45 +00:00
27 changed files with 1998 additions and 145 deletions

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

@ -0,0 +1,173 @@
/* General Body Styles */
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
background-color: #F8F9FA; /* Light Mode Background */
color: #212529; /* Dark Text */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Sidebar Navigation */
.sidebar {
position: fixed;
top: 0;
left: 0;
bottom: 0;
width: 260px;
background-color: #FFFFFF; /* White Background */
border-right: 1px solid #DEE2E6;
padding: 1.5rem;
display: flex;
flex-direction: column;
}
.sidebar .logo {
font-size: 1.5rem;
font-weight: 700;
color: #000000; /* Black Text */
margin-bottom: 2rem;
text-align: center;
}
.sidebar .nav-link {
color: #495057; /* Gray Text */
font-size: 1rem;
font-weight: 500;
padding: 0.75rem 1rem;
border-radius: 8px;
margin-bottom: 0.5rem;
transition: background-color 0.2s ease, color 0.2s ease;
}
.sidebar .nav-link:hover, .sidebar .nav-link.active {
background-color: #0A84FF;
color: #FFFFFF;
}
/* Main Content Area */
.main-content {
margin-left: 260px;
padding: 2rem;
}
/* Header */
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
}
.header h1 {
font-size: 2rem;
font-weight: 700;
color: #000000; /* Black Text */
}
/* Buttons */
.btn-primary {
background-color: #0A84FF;
border-color: #0A84FF;
border-radius: 8px;
font-weight: 600;
padding: 0.75rem 1.5rem;
transition: background-color 0.2s ease, border-color 0.2s ease;
}
.btn-primary:hover {
background-color: #007BFF;
border-color: #007BFF;
}
/* Cards */
.card {
background-color: #FFFFFF; /* White Background */
border: 1px solid #DEE2E6;
border-radius: 12px;
margin-bottom: 1.5rem;
}
.card-header {
background-color: transparent;
border-bottom: 1px solid #DEE2E6;
font-weight: 600;
padding: 1rem 1.5rem;
}
.card-body {
padding: 1.5rem;
}
/* Empty State */
.empty-state {
text-align: center;
padding: 4rem 2rem;
color: #6C757D; /* Gray Text */
}
.empty-state h5 {
font-weight: 600;
color: #212529; /* Dark Text */
}
/* KPI Cards */
.kpi-card .card-body {
padding: 1.25rem;
}
.kpi-icon {
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12px;
font-size: 1.5rem;
}
.kpi-value {
font-size: 2rem;
font-weight: 700;
color: #000000; /* Black Text */
margin-bottom: 0;
}
.kpi-title {
font-size: 0.9rem;
color: #6C757D; /* Gray Text */
font-weight: 500;
}
/* Soft Background Colors */
.bg-primary-soft { background-color: rgba(10, 132, 255, 0.15); color: #0A84FF; }
.bg-warning-soft { background-color: rgba(255, 204, 0, 0.15); color: #FFCC00; }
.bg-success-soft { background-color: rgba(52, 199, 89, 0.15); color: #34C759; }
.bg-danger-soft { background-color: rgba(255, 59, 48, 0.15); color: #FF3B30; }
.bg-info-soft { background-color: rgba(13, 202, 240, 0.15); color: #0dcaf0; }
/* Text Colors for Badges */
.text-primary { color: #0A84FF !important; }
.text-warning { color: #FFCC00 !important; }
.text-success { color: #34C759 !important; }
.text-danger { color: #FF3B30 !important; }
.text-info { color: #0dcaf0 !important; }
/* List Group Customization */
.list-group-item {
background-color: transparent;
border-color: #DEE2E6;
padding: 1rem 0;
}
.list-group-flush > .list-group-item:last-child {
border-bottom-width: 0;
}
.list-group-item:first-child {
padding-top: 0;
}
/* Table Customization */
.table {
color: #000000; /* Black text for tables */
}

17
assets/js/main.js Normal file
View File

@ -0,0 +1,17 @@
// Custom JavaScript will go here
document.addEventListener('DOMContentLoaded', function() {
const copyButton = document.getElementById('copy-button');
if (copyButton) {
copyButton.addEventListener('click', function() {
const portalLinkInput = document.getElementById('portal-link');
portalLinkInput.select();
document.execCommand('copy');
// Optional: Provide feedback to the user
copyButton.innerHTML = '<i class="bi bi-check-lg"></i>';
setTimeout(function() {
copyButton.innerHTML = '<i class="bi bi-clipboard"></i>';
}, 2000);
});
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

150
create_work_order.php Normal file
View File

@ -0,0 +1,150 @@
<?php
require_once 'db/config.php';
require_once 'includes/functions.php';
require_once 'mail/MailService.php';
start_secure_session();
require_login();
$message = '';
$technicians = get_users_by_role('technician');
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$customer_name = $_POST['customer_name'] ?? '';
$customer_email = $_POST['customer_email'] ?? '';
$job_type = $_POST['job_type'] ?? '';
$status = $_POST['status'] ?? 'Pending Dispatch';
$address = $_POST['address'] ?? '';
$technician_id = $_POST['technician_id'] ?? null;
if (create_work_order($customer_name, $customer_email, $job_type, $status, $address, $technician_id)) {
if (!empty($customer_email)) {
$subject = "Work Order Created";
$body = "<p>Dear {$customer_name},</p><p>Your work order for <strong>{$job_type}</strong> has been created.</p><p>Thank you!</p>";
MailService::sendMail($customer_email, $subject, $body);
}
header("Location: index.php");
exit;
} else {
$message = "Failed to create work order.";
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Create Work Order - RedSolutions CRM</title>
<meta name="description" content="Create a new work order in RedSolutions CRM.">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" 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="d-flex">
<div class="sidebar d-flex flex-column p-3">
<a href="index.php" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-white text-decoration-none">
<i class="bi bi-buildings me-2"></i>
<span class="fs-4">Flatlogic</span>
</a>
<hr>
<ul class="nav nav-pills flex-column mb-auto">
<li class="nav-item">
<a href="index.php" class="nav-link text-white active" aria-current="page">
<i class="bi bi-speedometer2 me-2"></i>
Dashboard
</a>
</li>
<li class="nav-item">
<a href="reports.php" class="nav-link text-white">
<i class="bi bi-bar-chart-line me-2"></i>
Reports
</a>
</li>
<li class="nav-item">
<a href="inventory.php" class="nav-link text-white">
<i class="bi bi-box-seam me-2"></i>
Inventory
</a>
</li>
</ul>
<hr>
<div class="dropdown">
<a href="#" class="d-flex align-items-center text-white text-decoration-none dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-circle me-2"></i>
<strong><?php echo htmlspecialchars($_SESSION['username']); ?></strong>
</a>
<ul class="dropdown-menu dropdown-menu-dark text-small shadow" aria-labelledby="dropdownUser1">
<li><a class="dropdown-item" href="logout.php">Sign out</a></li>
</ul>
</div>
</div>
<main class="main-content flex-grow-1 p-4">
<header class="header">
<h1>Create New Work Order</h1>
<a href="index.php" class="btn btn-outline-secondary"><i class="bi bi-arrow-left me-2"></i> Back to Dashboard</a>
</header>
<div class="card">
<div class="card-body">
<?php if ($message): ?>
<div class="alert alert-danger"><?php echo $message; ?></div>
<?php endif; ?>
<form action="create_work_order.php" method="POST">
<div class="row">
<div class="col-md-6 mb-3">
<label for="customer_name" class="form-label">Customer Name</label>
<input type="text" class="form-control" id="customer_name" name="customer_name" required>
</div>
<div class="col-md-6 mb-3">
<label for="customer_email" class="form-label">Customer Email</label>
<input type="email" class="form-control" id="customer_email" name="customer_email">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="job_type" class="form-label">Job Type</label>
<input type="text" class="form-control" id="job_type" name="job_type" required>
</div>
<div class="col-md-6 mb-3">
<label for="status" class="form-label">Status</label>
<select class="form-select" id="status" name="status">
<option>Pending Dispatch</option>
<option>In Progress</option>
<option>On Hold</option>
<option>Completed</option>
<option>Canceled</option>
</select>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="technician_id" class="form-label">Assign Technician</label>
<select class="form-select" id="technician_id" name="technician_id">
<option value="">Unassigned</option>
<?php foreach ($technicians as $technician): ?>
<option value="<?php echo $technician['id']; ?>"><?php echo htmlspecialchars($technician['username']); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6 mb-3">
<label for="address" class="form-label">Service Address</label>
<input type="text" class="form-control" id="address" name="address" required>
</div>
</div>
<div class="mb-3">
<label for="notes" class="form-label">Notes / Description</label>
<textarea class="form-control" id="notes" name="notes" rows="4"></textarea>
</div>
<button type="submit" class="btn btn-primary">Create Work Order</button>
</form>
</div>
</div>
</main>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>

View File

@ -0,0 +1,19 @@
CREATE TABLE IF NOT EXISTS `work_orders` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`customer_name` VARCHAR(255) NOT NULL,
`job_type` VARCHAR(255) NOT NULL,
`status` VARCHAR(50) NOT NULL,
`address` VARCHAR(255) NOT NULL,
`technician` VARCHAR(255) NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Seed data
TRUNCATE TABLE `work_orders`;
INSERT INTO `work_orders` (`customer_name`, `job_type`, `status`, `address`, `technician`) VALUES
('John Doe', 'HVAC Repair', 'In Progress', '123 Main St, Anytown, USA', 'Mike R.'),
('Jane Smith', 'Plumbing', 'In Progress', '456 Oak Ave, Anytown, USA', 'Sarah J.'),
('ACME Corp', 'Network Install', 'Pending Dispatch', '789 Pine Ln, Anytown, USA', NULL),
('Big Biz Inc.', 'Server Maintenance', 'Pending Dispatch', '101 Maple Dr, Anytown, USA', NULL),
('Sam Wilson', 'Generator Check', 'Completed', '212 Birch Rd, Anytown, USA', 'Mike R.');

View File

@ -0,0 +1 @@
ALTER TABLE work_orders ADD COLUMN dispatched_at TIMESTAMP NULL;

View File

@ -0,0 +1,2 @@
ALTER TABLE work_orders ADD COLUMN uuid CHAR(36) NOT NULL;
UPDATE work_orders SET uuid = UUID() WHERE uuid IS NULL OR uuid = '';

View File

@ -0,0 +1,7 @@
CREATE TABLE IF NOT EXISTS `users` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(255) NOT NULL UNIQUE,
`password` VARCHAR(255) NOT NULL,
`role` VARCHAR(50) NOT NULL DEFAULT 'technician', -- Can be 'technician' or 'admin'
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;

View File

@ -0,0 +1 @@
ALTER TABLE `users` ADD COLUMN `email` VARCHAR(255) NOT NULL AFTER `username`;

View File

@ -0,0 +1 @@
ALTER TABLE `work_orders` ADD COLUMN `customer_email` VARCHAR(255) NULL AFTER `customer_name`;

View File

@ -0,0 +1,7 @@
CREATE TABLE IF NOT EXISTS `inventory` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`item_name` VARCHAR(255) NOT NULL,
`quantity` INT NOT NULL DEFAULT 0,
`price` DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB;

View File

@ -0,0 +1,9 @@
CREATE TABLE IF NOT EXISTS `work_order_parts` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`work_order_id` INT NOT NULL,
`inventory_id` INT NOT NULL,
`quantity_used` INT NOT NULL DEFAULT 1,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`work_order_id`) REFERENCES `work_orders`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`inventory_id`) REFERENCES `inventory`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB;

View File

@ -0,0 +1,2 @@
ALTER TABLE work_orders ADD COLUMN assigned_technician_id INT NULL;
ALTER TABLE work_orders ADD CONSTRAINT fk_technician FOREIGN KEY (assigned_technician_id) REFERENCES users(id) ON DELETE SET NULL;

View File

@ -0,0 +1 @@
ALTER TABLE work_orders ADD COLUMN completed_at TIMESTAMP NULL;

38
db/migrate.php Normal file
View File

@ -0,0 +1,38 @@
<?php
require_once __DIR__ . '/config.php';
try {
$pdo = db();
// Create migrations table if it doesn't exist
$pdo->exec("CREATE TABLE IF NOT EXISTS migrations (migration VARCHAR(255) NOT NULL, PRIMARY KEY (migration))");
// Get all migration files
$migration_files = glob(__DIR__ . '/*.sql');
// Get executed migrations
$executed_migrations = $pdo->query("SELECT migration FROM migrations")->fetchAll(PDO::FETCH_COLUMN);
foreach ($migration_files as $file) {
$migration_name = basename($file);
if (!in_array($migration_name, $executed_migrations)) {
$sql = file_get_contents($file);
$pdo->exec($sql);
$stmt = $pdo->prepare("INSERT INTO migrations (migration) VALUES (?)");
$stmt->execute([$migration_name]);
echo "Executed migration: $migration_name\n";
} else {
echo "Skipping already executed migration: $migration_name\n";
}
}
echo "Database migrations completed successfully.\n";
// Seed the database with default data
echo "Seeding database...\n";
require_once __DIR__ . '/seed.php';
echo "Database seeding completed.\n";
} catch (PDOException $e) {
die("Database migration failed: " . $e->getMessage() . "\n");
}

25
db/seed.php Normal file
View File

@ -0,0 +1,25 @@
<?php
require_once __DIR__ . '/config.php';
require_once __DIR__ . '/../includes/functions.php';
function seed_default_user() {
$pdo = db();
$stmt = $pdo->query("SELECT COUNT(*) FROM users WHERE username = 'admin'");
if ($stmt->fetchColumn() == 0) {
$username = 'admin';
$password = 'password'; // Default password, change this!
$email = 'admin@example.com';
$role = 'admin';
if (create_user($username, $email, $password, $role)) {
echo "Default admin user created successfully. Username: admin, Password: password\n";
} else {
echo "Failed to create default admin user.\n";
}
} else {
echo "Admin user already exists. No action taken.\n";
}
}
seed_default_user();

118
edit_inventory_item.php Normal file
View File

@ -0,0 +1,118 @@
<?php
require_once 'db/config.php';
require_once 'includes/functions.php';
start_secure_session();
require_login();
$message = '';
$item = null;
if (isset($_GET['id'])) {
$item = get_inventory_item_by_id($_GET['id']);
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$id = $_POST['id'];
$item_name = $_POST['item_name'];
$quantity = $_POST['quantity'];
$price = $_POST['price'];
if (update_inventory_item($id, $item_name, $quantity, $price)) {
header("Location: inventory.php?message=Item+updated+successfully");
exit;
} else {
$message = "Failed to update item.";
}
}
if (!$item) {
header("Location: inventory.php?error=Item+not+found");
exit;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Inventory Item - Flatlogic</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-icons/font/bootstrap-icons.css" rel="stylesheet">
<link href="assets/css/custom.css?v=<?php echo time(); ?>" rel="stylesheet">
</head>
<body class="d-flex">
<div class="sidebar d-flex flex-column p-3">
<a href="index.php" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-white text-decoration-none">
<i class="bi bi-buildings me-2"></i>
<span class="fs-4">Flatlogic</span>
</a>
<hr>
<ul class="nav nav-pills flex-column mb-auto">
<li class="nav-item">
<a href="index.php" class="nav-link text-white" aria-current="page">
<i class="bi bi-speedometer2 me-2"></i>
Dashboard
</a>
</li>
<li class="nav-item">
<a href="reports.php" class="nav-link text-white">
<i class="bi bi-bar-chart-line me-2"></i>
Reports
</a>
</li>
<li class="nav-item">
<a href="inventory.php" class="nav-link text-white active">
<i class="bi bi-box-seam me-2"></i>
Inventory
</a>
</li>
</ul>
<hr>
<div class="dropdown">
<a href="#" class="d-flex align-items-center text-white text-decoration-none dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-circle me-2"></i>
<strong><?php echo htmlspecialchars($_SESSION['username']); ?></strong>
</a>
<ul class="dropdown-menu dropdown-menu-dark text-small shadow" aria-labelledby="dropdownUser1">
<li><a class="dropdown-item" href="logout.php">Sign out</a></li>
</ul>
</div>
</div>
<main class="main-content flex-grow-1 p-4">
<header class="header">
<h1>Edit Inventory Item</h1>
<a href="inventory.php" class="btn btn-outline-secondary"><i class="bi bi-arrow-left me-2"></i> Back to Inventory</a>
</header>
<div class="card">
<div class="card-body">
<?php if ($message): ?>
<div class="alert alert-danger"><?php echo $message; ?></div>
<?php endif; ?>
<form action="edit_inventory_item.php?id=<?php echo $item['id']; ?>" method="POST">
<input type="hidden" name="id" value="<?php echo $item['id']; ?>">
<div class="mb-3">
<label for="item_name" class="form-label">Item Name</label>
<input type="text" class="form-control" id="item_name" name="item_name" value="<?php echo htmlspecialchars($item['item_name']); ?>" required>
</div>
<div class="mb-3">
<label for="quantity" class="form-label">Quantity</label>
<input type="number" class="form-control" id="quantity" name="quantity" value="<?php echo htmlspecialchars($item['quantity']); ?>" required>
</div>
<div class="mb-3">
<label for="price" class="form-label">Price</label>
<input type="text" class="form-control" id="price" name="price" value="<?php echo htmlspecialchars($item['price']); ?>" required>
</div>
<button type="submit" class="btn btn-primary">Save Changes</button>
</form>
</div>
</div>
</main>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

170
edit_work_order.php Normal file
View File

@ -0,0 +1,170 @@
<?php
require_once 'db/config.php';
require_once 'includes/functions.php';
require_once 'mail/MailService.php';
start_secure_session();
require_login();
$work_order = null;
$error_message = '';
$success_message = '';
$technicians = get_users_by_role('technician');
if (isset($_GET['id'])) {
$work_order = get_work_order_by_id($_GET['id']);
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$id = $_POST['id'];
$customer_name = $_POST['customer_name'];
$customer_email = $_POST['customer_email'];
$job_type = $_POST['job_type'];
$status = $_POST['status'];
$address = $_POST['address'];
$technician_id = $_POST['technician_id'] ?? null;
$original_work_order = get_work_order_by_id($id);
if (update_work_order($id, $customer_name, $customer_email, $job_type, $status, $address, $technician_id)) {
if ($status === 'Completed' && !empty($customer_email)) {
$subject = "Work Order Completed";
$body = "<p>Dear {$customer_name},</p><p>Your work order for <strong>{$job_type}</strong> has been completed.</p><p>Thank you for your business!</p>";
MailService::sendMail($customer_email, $subject, $body);
}
if ($technician_id != $original_work_order['assigned_technician_id']) {
$technician_user = get_user_by_id($technician_id);
if ($technician_user && !empty($technician_user['email'])) {
$subject = "You have been assigned a new work order";
$body = "<p>Hello {$technician_user['username']},</p><p>You have been assigned to a new work order #{$id}.</p><p><a href=\"http://{\$_SERVER['HTTP_HOST']}/work_order_details.php?id={$id}\">Click here to view the details.</a></p>";
MailService::sendMail($technician_user['email'], $subject, $body);
}
}
header("Location: work_order_details.php?id=" . $id . "&success=1");
exit;
} else {
$error_message = "Failed to update work order.";
}
}
if (!$work_order) {
header("Location: index.php?error=notfound");
exit;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Edit Work Order #<?php echo htmlspecialchars($work_order['id']); ?> - Flatlogic</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-icons/font/bootstrap-icons.css" rel="stylesheet">
<link href="assets/css/custom.css?v=<?php echo time(); ?>" rel="stylesheet">
</head>
<body class="d-flex">
<div class="sidebar d-flex flex-column p-3">
<a href="index.php" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-white text-decoration-none">
<i class="bi bi-buildings me-2"></i>
<span class="fs-4">Flatlogic</span>
</a>
<hr>
<ul class="nav nav-pills flex-column mb-auto">
<li class="nav-item">
<a href="index.php" class="nav-link text-white" aria-current="page">
<i class="bi bi-speedometer2 me-2"></i>
Dashboard
</a>
</li>
<li class="nav-item">
<a href="reports.php" class="nav-link text-white">
<i class="bi bi-bar-chart-line me-2"></i>
Reports
</a>
</li>
<li class="nav-item">
<a href="inventory.php" class="nav-link text-white">
<i class="bi bi-box-seam me-2"></i>
Inventory
</a>
</li>
</ul>
<hr>
<div class="dropdown">
<a href="#" class="d-flex align-items-center text-white text-decoration-none dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-circle me-2"></i>
<strong><?php echo htmlspecialchars($_SESSION['username']); ?></strong>
</a>
<ul class="dropdown-menu dropdown-menu-dark text-small shadow" aria-labelledby="dropdownUser1">
<li><a class="dropdown-item" href="logout.php">Sign out</a></li>
</ul>
</div>
</div>
<main class="main-content flex-grow-1 p-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">Edit Work Order #<?php echo htmlspecialchars($work_order['id']); ?></h1>
<a href="work_order_details.php?id=<?php echo htmlspecialchars($work_order['id']); ?>" class="btn btn-secondary">
<i class="bi bi-arrow-left me-1"></i>
Back to Details
</a>
</div>
<div class="card">
<div class="card-body">
<?php if ($error_message): ?>
<div class="alert alert-danger"><?php echo $error_message; ?></div>
<?php endif; ?>
<form action="edit_work_order.php?id=<?php echo htmlspecialchars($work_order['id']); ?>" method="POST">
<input type="hidden" name="id" value="<?php echo htmlspecialchars($work_order['id']); ?>">
<div class="mb-3">
<label for="customer_name" class="form-label">Customer Name</label>
<input type="text" class="form-control" id="customer_name" name="customer_name" value="<?php echo htmlspecialchars($work_order['customer_name']); ?>" required>
</div>
<div class="mb-3">
<label for="customer_email" class="form-label">Customer Email</label>
<input type="email" class="form-control" id="customer_email" name="customer_email" value="<?php echo htmlspecialchars($work_order['customer_email']); ?>">
</div>
<div class="mb-3">
<label for="job_type" class="form-label">Job Type</label>
<input type="text" class="form-control" id="job_type" name="job_type" value="<?php echo htmlspecialchars($work_order['job_type']); ?>" required>
</div>
<div class="mb-3">
<label for="status" class="form-label">Status</label>
<select class="form-select" id="status" name="status" required>
<option value="Pending Dispatch" <?php echo ($work_order['status'] == 'Pending Dispatch') ? 'selected' : ''; ?>>Pending Dispatch</option>
<option value="In Progress" <?php echo ($work_order['status'] == 'In Progress') ? 'selected' : ''; ?>>In Progress</option>
<option value="Completed" <?php echo ($work_order['status'] == 'Completed') ? 'selected' : ''; ?>>Completed</option>
<option value="On Hold" <?php echo ($work_order['status'] == 'On Hold') ? 'selected' : ''; ?>>On Hold</option>
<option value="Cancelled" <?php echo ($work_order['status'] == 'Cancelled') ? 'selected' : ''; ?>>Cancelled</option>
</select>
</div>
<div class="mb-3">
<label for="address" class="form-label">Address</label>
<textarea class="form-control" id="address" name="address" rows="3" required><?php echo htmlspecialchars($work_order['address']); ?></textarea>
</div>
<div class="mb-3">
<label for="technician_id" class="form-label">Assigned Technician</label>
<select class="form-select" id="technician_id" name="technician_id">
<option value="">Unassigned</option>
<?php foreach ($technicians as $technician):
$selected = ($work_order['assigned_technician_id'] == $technician['id']) ? 'selected' : '';
echo "<option value=\"{$technician['id']}\" {$selected}>" . htmlspecialchars($technician['username']) . "</option>";
endforeach; ?>
</select>
</div>
<button type="submit" class="btn btn-primary">Save Changes</button>
</form>
</div>
</div>
</main>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>

271
includes/functions.php Normal file
View File

@ -0,0 +1,271 @@
<?php
// includes/functions.php
function get_work_orders_by_status($status) {
$pdo = db();
$stmt = $pdo->prepare("SELECT wo.*, u.username as assigned_technician FROM work_orders wo LEFT JOIN users u ON wo.assigned_technician_id = u.id WHERE status = ? ORDER BY created_at DESC");
$stmt->execute([$status]);
return $stmt->fetchAll();
}
function get_all_work_orders() {
$pdo = db();
$stmt = $pdo->query("SELECT wo.*, u.username as assigned_technician FROM work_orders wo LEFT JOIN users u ON wo.assigned_technician_id = u.id ORDER BY created_at DESC");
return $stmt->fetchAll();
}
function get_work_order_by_id($id) {
$pdo = db();
$stmt = $pdo->prepare("SELECT wo.*, u.username as assigned_technician_name FROM work_orders wo LEFT JOIN users u ON wo.assigned_technician_id = u.id WHERE wo.id = ?");
$stmt->execute([$id]);
return $stmt->fetch();
}
function get_kpis($work_orders) {
$total_jobs = count($work_orders);
$completed_jobs = 0;
foreach ($work_orders as $order) {
if ($order['status'] === 'Completed') {
$completed_jobs++;
}
}
$completion_rate = $total_jobs > 0 ? ($completed_jobs / $total_jobs) * 100 : 0;
return [
'total_jobs' => $total_jobs,
'completion_rate' => round($completion_rate),
'revenue' => '12,345' // Static for now
];
}
function create_work_order($customer_name, $customer_email, $job_type, $status, $address, $technician_id) {
$pdo = db();
$sql = "INSERT INTO work_orders (customer_name, customer_email, job_type, status, address, assigned_technician_id, uuid) VALUES (?, ?, ?, ?, ?, ?, UUID())";
$stmt = $pdo->prepare($sql);
return $stmt->execute([$customer_name, $customer_email, $job_type, $status, $address, $technician_id]);
}
function update_work_order($id, $customer_name, $customer_email, $job_type, $status, $address, $technician_id) {
$pdo = db();
$completed_at_sql = ($status === 'Completed') ? ", completed_at = CURRENT_TIMESTAMP" : "";
$sql = "UPDATE work_orders SET customer_name = ?, customer_email = ?, job_type = ?, status = ?, address = ?, assigned_technician_id = ? {$completed_at_sql} WHERE id = ?";
$stmt = $pdo->prepare($sql);
return $stmt->execute([$customer_name, $customer_email, $job_type, $status, $address, $technician_id, $id]);
}
function dispatch_work_order($id) {
$pdo = db();
$sql = "UPDATE work_orders SET status = 'In Progress', dispatched_at = CURRENT_TIMESTAMP WHERE id = ?";
$stmt = $pdo->prepare($sql);
return $stmt->execute([$id]);
}
function get_work_order_by_uuid($uuid) {
$pdo = db();
$stmt = $pdo->prepare("SELECT wo.*, u.username as assigned_technician_name FROM work_orders wo LEFT JOIN users u ON wo.assigned_technician_id = u.id WHERE wo.uuid = ?");
$stmt->execute([$uuid]);
return $stmt->fetch();
}
function search_work_orders($search_term = '', $status = '') {
$pdo = db();
$sql = "SELECT wo.*, u.username as assigned_technician FROM work_orders wo LEFT JOIN users u ON wo.assigned_technician_id = u.id";
$params = [];
$where_clauses = [];
if (!empty($search_term)) {
$where_clauses[] = "(wo.customer_name LIKE ? OR wo.job_type LIKE ? OR wo.address LIKE ? OR u.username LIKE ?)";
$search_param = "%{$search_term}%";
array_push($params, $search_param, $search_param, $search_param, $search_param);
}
if (!empty($status)) {
$where_clauses[] = "wo.status = ?";
$params[] = $status;
}
if (!empty($where_clauses)) {
$sql .= " WHERE " . implode(' AND ', $where_clauses);
}
$sql .= " ORDER BY wo.created_at DESC";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll();
}
// --- User & Auth Functions ---
function create_user($username, $email, $password, $role) {
$pdo = db();
$hash = password_hash($password, PASSWORD_DEFAULT);
$sql = "INSERT INTO users (username, email, password, role) VALUES (?, ?, ?, ?)";
$stmt = $pdo->prepare($sql);
return $stmt->execute([$username, $email, $hash, $role]);
}
function get_user_by_username($username) {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]);
return $stmt->fetch();
}
function get_user_by_id($id) {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
return $stmt->fetch();
}
function get_users_by_role($role) {
$pdo = db();
$stmt = $pdo->prepare("SELECT id, username FROM users WHERE role = ? ORDER BY username ASC");
$stmt->execute([$role]);
return $stmt->fetchAll();
}
function start_secure_session() {
$session_name = 'sec_session_id'; // Set a custom session name
$secure = true; // Set to true if using https.
$httponly = true; // This stops javascript being able to access the session id.
ini_set('session.use_only_cookies', 1); // Forces sessions to only use cookies.
$cookieParams = session_get_cookie_params();
session_set_cookie_params($cookieParams["lifetime"], $cookieParams["path"], $cookieParams["domain"], $secure, $httponly);
session_name($session_name);
session_start();
session_regenerate_id();
}
function is_logged_in() {
return isset($_SESSION['user_id']);
}
function require_login() {
if (!is_logged_in()) {
header('Location: /login.php');
exit();
}
}
function get_user_role() {
return isset($_SESSION['role']) ? $_SESSION['role'] : null;
}
// --- Reporting Functions ---
function get_work_order_counts_by_status() {
$pdo = db();
$sql = "SELECT status, COUNT(*) as count FROM work_orders GROUP BY status";
$stmt = $pdo->query($sql);
return $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
}
function get_work_order_counts_by_technician() {
$pdo = db();
$sql = "SELECT u.username, COUNT(wo.id) as count FROM work_orders wo JOIN users u ON wo.assigned_technician_id = u.id GROUP BY u.username";
$stmt = $pdo->query($sql);
return $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
}
function get_average_completion_time() {
$pdo = db();
$sql = "SELECT AVG(TIMESTAMPDIFF(HOUR, created_at, completed_at)) as avg_hours FROM work_orders WHERE completed_at IS NOT NULL";
$stmt = $pdo->query($sql);
$result = $stmt->fetch();
return $result ? round($result['avg_hours'], 1) : 0;
}
// --- Inventory Functions ---
function get_inventory_items() {
$pdo = db();
$stmt = $pdo->query("SELECT * FROM inventory ORDER BY item_name ASC");
return $stmt->fetchAll();
}
function get_inventory_item_by_id($id) {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM inventory WHERE id = ?");
$stmt->execute([$id]);
return $stmt->fetch();
}
function add_inventory_item($item_name, $quantity, $price) {
$pdo = db();
$sql = "INSERT INTO inventory (item_name, quantity, price) VALUES (?, ?, ?)";
$stmt = $pdo->prepare($sql);
return $stmt->execute([$item_name, $quantity, $price]);
}
function update_inventory_item($id, $item_name, $quantity, $price) {
$pdo = db();
$sql = "UPDATE inventory SET item_name = ?, quantity = ?, price = ? WHERE id = ?";
$stmt = $pdo->prepare($sql);
return $stmt->execute([$item_name, $quantity, $price, $id]);
}
function delete_inventory_item($id) {
$pdo = db();
$sql = "DELETE FROM inventory WHERE id = ?";
$stmt = $pdo->prepare($sql);
return $stmt->execute([$id]);
}
function get_work_order_parts($work_order_id) {
$pdo = db();
$sql = "SELECT i.item_name, wp.quantity_used, i.price, wp.id as part_id FROM work_order_parts wp JOIN inventory i ON wp.inventory_id = i.id WHERE wp.work_order_id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$work_order_id]);
return $stmt->fetchAll();
}
function add_part_to_work_order($work_order_id, $inventory_id, $quantity_used) {
$pdo = db();
$pdo->beginTransaction();
try {
$sql = "INSERT INTO work_order_parts (work_order_id, inventory_id, quantity_used) VALUES (?, ?, ?)";
$stmt = $pdo->prepare($sql);
$stmt->execute([$work_order_id, $inventory_id, $quantity_used]);
$sql = "UPDATE inventory SET quantity = quantity - ? WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$quantity_used, $inventory_id]);
$pdo->commit();
return true;
} catch (Exception $e) {
$pdo->rollBack();
return false;
}
}
function remove_part_from_work_order($part_id) {
$pdo = db();
$pdo->beginTransaction();
try {
$sql = "SELECT inventory_id, quantity_used FROM work_order_parts WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$part_id]);
$part = $stmt->fetch();
if ($part) {
$sql = "UPDATE inventory SET quantity = quantity + ? WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$part['quantity_used'], $part['inventory_id']]);
$sql = "DELETE FROM work_order_parts WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$part_id]);
}
$pdo->commit();
return true;
} catch (Exception $e) {
$pdo->rollBack();
return false;
}
}

350
index.php
View File

@ -1,150 +1,216 @@
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
?>
<!doctype html>
<!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>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RedSolutions CRM</title>
<meta name="description" content="RedSolutions CRM - Built with Flatlogic Generator">
<meta name="keywords" content="crm, redsolutions, business management, proposals, work orders, dispatch, field service, flatlogic">
<meta property="og:title" content="RedSolutions CRM">
<meta property="og:description" content="A modern CRM for managing your business operations.">
<meta property="og:image" content="">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="">
<!-- Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<!-- Custom CSS -->
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</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>
<?php
require_once 'db/config.php';
require_once 'includes/functions.php';
start_secure_session();
require_login();
$search_term = $_GET['search'] ?? '';
$status = $_GET['status'] ?? '';
$work_orders = search_work_orders($search_term, $status);
$kpis = get_kpis($work_orders);
$all_statuses = ['Pending Dispatch', 'In Progress', 'Completed', 'On Hold', 'Cancelled'];
?>
<body class="d-flex">
<div class="sidebar d-flex flex-column p-3">
<a href="index.php" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-white text-decoration-none">
<i class="bi bi-buildings me-2"></i>
<span class="fs-4">Flatlogic</span>
</a>
<hr>
<ul class="nav nav-pills flex-column mb-auto">
<li class="nav-item">
<a href="index.php" class="nav-link text-white active" aria-current="page">
<i class="bi bi-speedometer2 me-2"></i>
Dashboard
</a>
</li>
<li class="nav-item">
<a href="reports.php" class="nav-link text-white">
<i class="bi bi-bar-chart-line me-2"></i>
Reports
</a>
</li>
<li class="nav-item">
<a href="inventory.php" class="nav-link text-white">
<i class="bi bi-box-seam me-2"></i>
Inventory
</a>
</li>
</ul>
<hr>
<div class="dropdown">
<a href="#" class="d-flex align-items-center text-white text-decoration-none dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-circle me-2"></i>
<strong><?php echo htmlspecialchars($_SESSION['username']); ?></strong>
</a>
<ul class="dropdown-menu dropdown-menu-dark text-small shadow" aria-labelledby="dropdownUser1">
<li><a class="dropdown-item" href="logout.php">Sign out</a></li>
</ul>
</div>
</div>
<main class="main-content flex-grow-1 p-4">
<header class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">Dashboard</h1>
<a href="create_work_order.php" class="btn btn-primary"><i class="bi bi-plus-lg me-2"></i> Create Work Order</a>
</header>
<!-- KPI Cards -->
<div class="row">
<div class="col-md-3">
<div class="card kpi-card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="kpi-icon bg-primary-soft">
<i class="bi bi-list-check"></i>
</div>
<div class="ms-3">
<h3 class="kpi-value"><?php echo $kpis['total_jobs']; ?></h3>
<span class="kpi-title">Total Jobs</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card kpi-card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="kpi-icon bg-warning-soft">
<i class="bi bi-clock-history"></i>
</div>
<div class="ms-3">
<h3 class="kpi-value"><?php echo count(array_filter($work_orders, function($order) { return $order['status'] === 'Pending Dispatch'; })); ?></h3>
<span class="kpi-title">Pending Dispatch</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card kpi-card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="kpi-icon bg-success-soft">
<i class="bi bi-check-circle"></i>
</div>
<div class="ms-3">
<h3 class="kpi-value"><?php echo $kpis['completion_rate']; ?>%</h3>
<span class="kpi-title">Completed Rate</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card kpi-card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="kpi-icon bg-info-soft">
<i class="bi bi-briefcase"></i>
</div>
<div class="ms-3">
<h3 class="kpi-value"><?php echo $kpis['revenue']; ?></h3>
<span class="kpi-title">Revenue</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Work Orders List -->
<div class="card mt-4">
<div class="card-header">
<h5 class="card-title mb-0">Work Orders</h5>
</div>
<div class="card-body">
<form action="index.php" method="GET" class="row g-3 mb-4">
<div class="col-md-6">
<input type="text" name="search" class="form-control" placeholder="Search by customer, job type, etc." value="<?php echo htmlspecialchars($search_term); ?>">
</div>
<div class="col-md-4">
<select name="status" class="form-select">
<option value="">All Statuses</option>
<?php foreach ($all_statuses as $stat): ?>
<option value="<?php echo $stat; ?>" <?php echo ($status === $stat) ? 'selected' : ''; ?>><?php echo $stat; ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-primary w-100">Filter</button>
</div>
</form>
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>ID</th>
<th>Customer</th>
<th>Job Type</th>
<th>Status</th>
<th>Technician</th>
<th>Created At</th>
<th></th>
</tr>
</thead>
<tbody>
<?php if (empty($work_orders)): ?>
<tr>
<td colspan="7" class="text-center">No work orders found.</td>
</tr>
<?php else: ?>
<?php foreach ($work_orders as $order): ?>
<tr>
<td>#<?php echo str_pad($order['id'], 4, '0', STR_PAD_LEFT); ?></td>
<td><?php echo htmlspecialchars($order['customer_name']); ?></td>
<td><?php echo htmlspecialchars($order['job_type']); ?></td>
<td><span class="badge bg-primary-soft text-primary"><?php echo htmlspecialchars($order['status']); ?></span></td>
<td><?php echo htmlspecialchars($order['assigned_technician'] ?? 'N/A'); ?></td>
<td><?php echo date("M d, Y", strtotime($order['created_at'])); ?></td>
<td>
<a href="work_order_details.php?id=<?php echo $order['id']; ?>" class="btn btn-sm btn-outline-primary">View</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
</div>
</main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer>
<!-- Bootstrap 5 JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<!-- Custom JS -->
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>

159
inventory.php Normal file
View File

@ -0,0 +1,159 @@
<?php
require_once 'db/config.php';
require_once 'includes/functions.php';
start_secure_session();
require_login();
$message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['add_item'])) {
$item_name = $_POST['item_name'];
$quantity = $_POST['quantity'];
$price = $_POST['price'];
if (add_inventory_item($item_name, $quantity, $price)) {
$message = "Item added successfully.";
} else {
$message = "Failed to add item.";
}
}
}
if (isset($_GET['action']) && $_GET['action'] === 'delete' && isset($_GET['id'])) {
if (delete_inventory_item($_GET['id'])) {
$message = "Item deleted successfully.";
} else {
$message = "Failed to delete item.";
}
}
$inventory_items = get_inventory_items();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Inventory - Flatlogic</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-icons/font/bootstrap-icons.css" rel="stylesheet">
<link href="assets/css/custom.css?v=<?php echo time(); ?>" rel="stylesheet">
</head>
<body class="d-flex">
<div class="sidebar d-flex flex-column p-3">
<a href="index.php" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-white text-decoration-none">
<i class="bi bi-buildings me-2"></i>
<span class="fs-4">Flatlogic</span>
</a>
<hr>
<ul class="nav nav-pills flex-column mb-auto">
<li class="nav-item">
<a href="index.php" class="nav-link text-white" aria-current="page">
<i class="bi bi-speedometer2 me-2"></i>
Dashboard
</a>
</li>
<li class="nav-item">
<a href="reports.php" class="nav-link text-white">
<i class="bi bi-bar-chart-line me-2"></i>
Reports
</a>
</li>
<li class="nav-item">
<a href="inventory.php" class="nav-link text-white active">
<i class="bi bi-box-seam me-2"></i>
Inventory
</a>
</li>
</ul>
<hr>
<div class="dropdown">
<a href="#" class="d-flex align-items-center text-white text-decoration-none dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-circle me-2"></i>
<strong><?php echo htmlspecialchars($_SESSION['username']); ?></strong>
</a>
<ul class="dropdown-menu dropdown-menu-dark text-small shadow" aria-labelledby="dropdownUser1">
<li><a class="dropdown-item" href="logout.php">Sign out</a></li>
</ul>
</div>
</div>
<main class="main-content flex-grow-1 p-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">Inventory</h1>
</div>
<?php if ($message): ?>
<div class="alert alert-info"><?php echo $message; ?></div>
<?php endif; ?>
<div class="card mb-4">
<div class="card-header">
Add New Item
</div>
<div class="card-body">
<form action="inventory.php" method="POST">
<div class="row">
<div class="col-md-4">
<input type="text" name="item_name" class="form-control" placeholder="Item Name" required>
</div>
<div class="col-md-4">
<input type="number" name="quantity" class="form-control" placeholder="Quantity" required>
</div>
<div class="col-md-2">
<input type="text" name="price" class="form-control" placeholder="Price" required>
</div>
<div class="col-md-2">
<button type="submit" name="add_item" class="btn btn-primary w-100">Add Item</button>
</div>
</div>
</form>
</div>
</div>
<div class="card">
<div class="card-header">
Inventory Items
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Item Name</th>
<th>Quantity</th>
<th>Price</th>
<th></th>
</tr>
</thead>
<tbody>
<?php if (empty($inventory_items)): ?>
<tr>
<td colspan="4" class="text-center">No inventory items found.</td>
</tr>
<?php else: ?>
<?php foreach ($inventory_items as $item): ?>
<tr>
<td><?php echo htmlspecialchars($item['item_name']); ?></td>
<td><?php echo htmlspecialchars($item['quantity']); ?></td>
<td>$<?php echo htmlspecialchars($item['price']); ?></td>
<td>
<a href="edit_inventory_item.php?id=<?php echo $item['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
<a href="inventory.php?action=delete&id=<?php echo $item['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this item?');">Delete</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</main>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

96
login.php Normal file
View File

@ -0,0 +1,96 @@
<?php
require_once 'db/config.php';
require_once 'includes/functions.php';
start_secure_session();
// If user is already logged in, redirect to dashboard
if (is_logged_in()) {
header('Location: /index.php');
exit();
}
$error_message = '';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
if (empty($username) || empty($password)) {
$error_message = 'Please enter both username and password.';
} else {
$user = get_user_by_username($username);
if ($user && password_verify($password, $user['password'])) {
// Password is correct, so start a new session
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['role'] = $user['role'];
// Redirect to user dashboard page
header('Location: /index.php');
exit();
} else {
// Password is not correct
$error_message = 'Invalid username or password.';
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - RedSolutions CRM</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" 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(); ?>">
<style>
body {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #f8f9fa;
}
.login-card {
width: 100%;
max-width: 400px;
padding: 2rem;
}
</style>
</head>
<body>
<div class="card login-card shadow-sm">
<div class="card-body">
<div class="text-center mb-4">
<i class="bi bi-buildings me-2 fs-2"></i>
<h1 class="h3 mb-3 fw-normal">RedSolutions CRM</h1>
<p>Please sign in to continue</p>
</div>
<?php if (!empty($error_message)): ?>
<div class="alert alert-danger" role="alert">
<?php echo $error_message; ?>
</div>
<?php endif; ?>
<form action="login.php" method="POST">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="username" name="username" placeholder="Username" required>
<label for="username">Username</label>
</div>
<div class="form-floating mb-3">
<input type="password" class="form-control" id="password" name="password" placeholder="Password" required>
<label for="password">Password</label>
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
</form>
<p class="mt-5 mb-3 text-muted text-center">&copy; <?php echo date('Y'); ?> Flatlogic</p>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

23
logout.php Normal file
View File

@ -0,0 +1,23 @@
<?php
require_once 'includes/functions.php';
start_secure_session();
// Unset all of the session variables.
$_SESSION = array();
// If it's desired to kill the session, also delete the session cookie.
// Note: This will destroy the session, and not just the session data!
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), '', time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]
);
}
// Finally, destroy the session.
session_destroy();
header('Location: /login.php');
exit();

88
portal.php Normal file
View File

@ -0,0 +1,88 @@
<?php
require_once 'db/config.php';
require_once 'includes/functions.php';
$work_order_uuid = isset($_GET['uuid']) ? $_GET['uuid'] : '';
$work_order = null;
if (!empty($work_order_uuid)) {
$work_order = get_work_order_by_uuid($work_order_uuid);
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Work Order Status - Flatlogic</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-icons/font/bootstrap-icons.css" rel="stylesheet">
<link href="assets/css/custom.css?v=<?php echo time(); ?>" rel="stylesheet">
<style>
body {
background-color: #f8f9fa;
}
.portal-container {
max-width: 800px;
margin: 50px auto;
background: #fff;
padding: 30px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
}
.portal-header {
text-align: center;
margin-bottom: 30px;
}
.portal-header .logo {
font-size: 2rem;
font-weight: bold;
color: #0A84FF;
}
.status-badge {
font-size: 1.2rem;
}
</style>
</head>
<body>
<div class="portal-container">
<div class="portal-header">
<div class="logo"><i class="bi bi-buildings me-2"></i>Flatlogic</div>
<h2 class="mt-3">Work Order Status</h2>
</div>
<?php if ($work_order): ?>
<div class="text-center mb-4">
<h4>Work Order #<?php echo str_pad($work_order['id'], 4, '0', STR_PAD_LEFT); ?></h4>
<p><strong>Status:</strong> <span class="badge rounded-pill bg-primary status-badge"><?php echo htmlspecialchars($work_order['status']); ?></span></p>
</div>
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-md-6">
<p><strong>Customer:</strong> <?php echo htmlspecialchars($work_order['customer_name']); ?></p>
<p><strong>Job Type:</strong> <?php echo htmlspecialchars($work_order['job_type']); ?></p>
</div>
<div class="col-md-6">
<p><strong>Assigned Technician:</strong> <?php echo htmlspecialchars($work_order['assigned_technician'] ?? 'Not Assigned'); ?></p>
<p><strong>Last Updated:</strong> <?php echo date("F j, Y, g:i a", strtotime($work_order['updated_at'])); ?></p>
</div>
</div>
</div>
</div>
<?php else: ?>
<div class="alert alert-danger text-center" role="alert">
<h4><i class="bi bi-exclamation-triangle-fill me-2"></i>Invalid Work Order</h4>
<p>The work order you are looking for could not be found. Please check the link and try again.</p>
</div>
<?php endif; ?>
<div class="text-center mt-4 text-muted">
<small>Powered by Flatlogic</small>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

176
reports.php Normal file
View File

@ -0,0 +1,176 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reports - RedSolutions CRM</title>
<meta name="description" content="RedSolutions CRM - Built with Flatlogic Generator">
<meta name="keywords" content="crm, redsolutions, business management, proposals, work orders, dispatch, field service, flatlogic">
<meta property="og:title" content="RedSolutions CRM">
<meta property="og:description" content="A modern CRM for managing your business operations.">
<meta property="og:image" content="">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="">
<!-- Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<!-- Custom CSS -->
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<?php
require_once 'db/config.php';
require_once 'includes/functions.php';
start_secure_session();
require_login();
$status_counts = get_work_order_counts_by_status();
$technician_counts = get_work_order_counts_by_technician();
$avg_completion_time = get_average_completion_time();
?>
<body class="d-flex">
<div class="sidebar d-flex flex-column p-3">
<a href="index.php" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-white text-decoration-none">
<i class="bi bi-buildings me-2"></i>
<span class="fs-4">Flatlogic</span>
</a>
<hr>
<ul class="nav nav-pills flex-column mb-auto">
<li class="nav-item">
<a href="index.php" class="nav-link text-white">
<i class="bi bi-speedometer2 me-2"></i>
Dashboard
</a>
</li>
<li class="nav-item">
<a href="reports.php" class="nav-link text-white active" aria-current="page">
<i class="bi bi-bar-chart-line me-2"></i>
Reports
</a>
</li>
<li class="nav-item">
<a href="inventory.php" class="nav-link text-white">
<i class="bi bi-box-seam me-2"></i>
Inventory
</a>
</li>
</ul>
<hr>
<div class="dropdown">
<a href="#" class="d-flex align-items-center text-white text-decoration-none dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-circle me-2"></i>
<strong><?php echo htmlspecialchars($_SESSION['username']); ?></strong>
</a>
<ul class="dropdown-menu dropdown-menu-dark text-small shadow" aria-labelledby="dropdownUser1">
<li><a class="dropdown-item" href="logout.php">Sign out</a></li>
</ul>
</div>
</div>
<main class="main-content flex-grow-1 p-4">
<header class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">Reports</h1>
</header>
<!-- KPI Cards -->
<div class="row">
<div class="col-md-4">
<div class="card kpi-card">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="kpi-icon bg-primary-soft">
<i class="bi bi-clock-history"></i>
</div>
<div class="ms-3">
<h3 class="kpi-value"><?php echo $avg_completion_time; ?> Hours</h3>
<span class="kpi-title">Avg. Completion Time</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<!-- Work Orders by Status -->
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Work Orders by Status</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Status</th>
<th>Count</th>
</tr>
</thead>
<tbody>
<?php if (empty($status_counts)): ?>
<tr>
<td colspan="2" class="text-center">No data available.</td>
</tr>
<?php else: ?>
<?php foreach ($status_counts as $status => $count): ?>
<tr>
<td><span class="badge bg-primary-soft text-primary"><?php echo htmlspecialchars($status); ?></span></td>
<td><?php echo $count; ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Work Orders by Technician -->
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Work Orders by Technician</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Technician</th>
<th>Assigned Orders</th>
</tr>
</thead>
<tbody>
<?php if (empty($technician_counts)): ?>
<tr>
<td colspan="2" class="text-center">No technicians assigned to orders.</td>
</tr>
<?php else: ?>
<?php foreach ($technician_counts as $technician => $count): ?>
<tr>
<td><?php echo htmlspecialchars($technician); ?></td>
<td><?php echo $count; ?></td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</main>
<!-- Bootstrap 5 JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<!-- Custom JS -->
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>

233
work_order_details.php Normal file
View File

@ -0,0 +1,233 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Work Order #<?php echo htmlspecialchars($work_order['id']); ?> - Flatlogic</title>
<meta name="description" content="Flatlogic - Built with Flatlogic Generator">
<meta name="robots" content="noindex, nofollow">
<!-- Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<!-- Custom CSS -->
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<?php
require_once 'db/config.php';
require_once 'includes/functions.php';
require_once 'mail/MailService.php';
start_secure_session();
require_login();
$work_order_id = isset($_GET['id']) ? (int)$_GET['id'] : 0;
$work_order = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['dispatch'])) {
dispatch_work_order($work_order_id);
$work_order = get_work_order_by_id($work_order_id); // Re-fetch to get updated data
if ($work_order && !empty($work_order['customer_email'])) {
$subject = "Work Order Dispatched";
$body = "<p>Dear {$work_order['customer_name']},</p><p>Your work order for <strong>{$work_order['job_type']}</strong> has been dispatched.</p><p>A technician will be in contact with you shortly.</p><p>Thank you!</p>";
MailService::sendMail($work_order['customer_email'], $subject, $body);
}
header("Location: work_order_details.php?id=" . $work_order_id . "&dispatch_success=1");
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_part'])) {
$inventory_id = $_POST['inventory_id'];
$quantity_used = $_POST['quantity_used'];
add_part_to_work_order($work_order_id, $inventory_id, $quantity_used);
header("Location: work_order_details.php?id=" . $work_order_id);
exit;
}
if (isset($_GET['remove_part'])) {
$part_id = $_GET['remove_part'];
remove_part_from_work_order($part_id);
header("Location: work_order_details.php?id=" . $work_order_id);
exit;
}
if ($work_order_id > 0) {
$work_order = get_work_order_by_id($work_order_id);
$work_order_parts = get_work_order_parts($work_order_id);
$inventory_items = get_inventory_items();
}
?>
<body class="d-flex">
<div class="sidebar d-flex flex-column p-3">
<a href="index.php" class="d-flex align-items-center mb-3 mb-md-0 me-md-auto text-white text-decoration-none">
<i class="bi bi-buildings me-2"></i>
<span class="fs-4">Flatlogic</span>
</a>
<hr>
<ul class="nav nav-pills flex-column mb-auto">
<li class="nav-item">
<a href="index.php" class="nav-link text-white" aria-current="page">
<i class="bi bi-speedometer2 me-2"></i>
Dashboard
</a>
</li>
<li class="nav-item">
<a href="reports.php" class="nav-link text-white">
<i class="bi bi-bar-chart-line me-2"></i>
Reports
</a>
</li>
<li class="nav-item">
<a href="inventory.php" class="nav-link text-white">
<i class="bi bi-box-seam me-2"></i>
Inventory
</a>
</li>
</ul>
<hr>
<div class="dropdown">
<a href="#" class="d-flex align-items-center text-white text-decoration-none dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-circle me-2"></i>
<strong><?php echo htmlspecialchars($_SESSION['username']); ?></strong>
</a>
<ul class="dropdown-menu dropdown-menu-dark text-small shadow" aria-labelledby="dropdownUser1">
<li><a class="dropdown-item" href="logout.php">Sign out</a></li>
</ul>
</div>
</div>
<div class="main-content flex-grow-1 p-4">
<header class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">Work Order #<?php echo str_pad($work_order['id'], 4, '0', STR_PAD_LEFT); ?></h1>
<div>
<?php if ($work_order && $work_order['status'] === 'Pending Dispatch' && !empty($work_order['assigned_technician_id']) && empty($work_order['dispatched_at'])): ?>
<form action="" method="POST" class="d-inline">
<button type="submit" name="dispatch" class="btn btn-success"><i class="bi bi-truck me-1"></i> Dispatch</button>
</form>
<?php endif; ?>
<a href="edit_work_order.php?id=<?php echo $work_order_id; ?>" class="btn btn-primary"><i class="bi bi-pencil-square me-1"></i> Edit</a>
<a href="index.php" class="btn btn-secondary"><i class="bi bi-arrow-left me-1"></i> Back to Dashboard</a>
</div>
</header>
<div class="row mt-4">
<div class="col-md-12">
<div class="card">
<div class="card-body">
<?php if (isset($_GET['dispatch_success'])) : ?>
<div class="alert alert-success">Work order dispatched successfully!</div>
<?php endif; ?>
<?php if ($work_order): ?>
<div class="row">
<div class="col-md-6">
<p><strong>Customer:</strong> <?php echo htmlspecialchars($work_order['customer_name']); ?></p>
<p><strong>Job Type:</strong> <?php echo htmlspecialchars($work_order['job_type']); ?></p>
<p><strong>Address:</strong> <?php echo htmlspecialchars($work_order['address']); ?></p>
</div>
<div class="col-md-6">
<p><strong>Status:</strong> <span class="badge bg-primary-soft text-primary"><?php echo htmlspecialchars($work_order['status']); ?></span></p>
<p><strong>Technician:</strong> <?php echo htmlspecialchars($work_order['assigned_technician_name'] ?? 'Not Assigned'); ?></p>
<p><strong>Created At:</strong> <?php echo date("F j, Y, g:i a", strtotime($work_order['created_at'])); ?></p>
<?php if (!empty($work_order['dispatched_at'])): ?>
<p><strong>Dispatched At:</strong> <?php echo date("F j, Y, g:i a", strtotime($work_order['dispatched_at'])); ?></p>
<?php endif; ?>
<?php if (!empty($work_order['completed_at'])): ?>
<p><strong>Completed At:</strong> <?php echo date("F j, Y, g:i a", strtotime($work_order['completed_at'])); ?></p>
<?php endif; ?>
</div>
</div>
<?php else: ?>
<div class="alert alert-danger" role="alert">
<strong>Error:</strong> Work Order not found or invalid ID provided.
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-md-12">
<div class="card">
<div class="card-header">
Parts Used
</div>
<div class="card-body">
<form action="work_order_details.php?id=<?php echo $work_order_id; ?>" method="POST" class="row g-3 mb-4">
<div class="col-md-6">
<select name="inventory_id" class="form-select" required>
<option value="">Select a part...</option>
<?php foreach ($inventory_items as $item): ?>
<option value="<?php echo $item['id']; ?>"><?php echo htmlspecialchars($item['item_name']); ?> (<?php echo $item['quantity']; ?> in stock)</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-4">
<input type="number" name="quantity_used" class="form-control" placeholder="Quantity" required min="1">
</div>
<div class="col-md-2">
<button type="submit" name="add_part" class="btn btn-primary w-100">Add Part</button>
</div>
</form>
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Part Name</th>
<th>Quantity Used</th>
<th>Unit Price</th>
<th>Total Price</th>
<th></th>
</tr>
</thead>
<tbody>
<?php if (empty($work_order_parts)): ?>
<tr>
<td colspan="5" class="text-center">No parts used yet.</td>
</tr>
<?php else: ?>
<?php foreach ($work_order_parts as $part): ?>
<tr>
<td><?php echo htmlspecialchars($part['item_name']); ?></td>
<td><?php echo htmlspecialchars($part['quantity_used']); ?></td>
<td>$<?php echo htmlspecialchars($part['price']); ?></td>
<td>$<?php echo htmlspecialchars($part['price'] * $part['quantity_used']); ?></td>
<td>
<a href="work_order_details.php?id=<?php echo $work_order_id; ?>&remove_part=<?php echo $part['part_id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to remove this part?');">Remove</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-md-12">
<div class="card">
<div class="card-body">
<h5 class="card-title">Customer Portal</h5>
<p>Share this link with the customer to allow them to view the status of their work order.</p>
<div class="input-group">
<input type="text" class="form-control" id="portal-link" value="<?php echo 'http://' . $_SERVER['HTTP_HOST'] . '/portal.php?uuid=' . $work_order['uuid']; ?>" readonly>
<button class="btn btn-outline-secondary" type="button" id="copy-button"><i class="bi bi-clipboard"></i></button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Bootstrap 5 JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<!-- Custom JS -->
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>