1.1
This commit is contained in:
parent
8f16e91e88
commit
4708a2f224
167
assets/css/custom.css
Normal file
167
assets/css/custom.css
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
/* 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: #1C1C1E; /* Dark Mode Background */
|
||||||
|
color: #F5F5F7; /* Dark Mode 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: #2C2C2E; /* Dark Mode Surface */
|
||||||
|
border-right: 1px solid #3A3A3C;
|
||||||
|
padding: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .logo {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #FFFFFF;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .nav-link {
|
||||||
|
color: #E5E5EA;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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: #2C2C2E; /* Dark Mode Surface */
|
||||||
|
border: 1px solid #3A3A3C;
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
background-color: transparent;
|
||||||
|
border-bottom: 1px solid #3A3A3C;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Empty State */
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: 4rem 2rem;
|
||||||
|
color: #8E8E93;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state h5 {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #E5E5EA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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: #FFFFFF;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-title {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #8E8E93;
|
||||||
|
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: #3A3A3C;
|
||||||
|
padding: 1rem 0;
|
||||||
|
}
|
||||||
|
.list-group-flush > .list-group-item:last-child {
|
||||||
|
border-bottom-width: 0;
|
||||||
|
}
|
||||||
|
.list-group-item:first-child {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
1
assets/js/main.js
Normal file
1
assets/js/main.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
// Custom JavaScript will go here
|
||||||
19
db/001_create_work_orders.sql
Normal file
19
db/001_create_work_orders.sql
Normal 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.');
|
||||||
12
db/migrate.php
Normal file
12
db/migrate.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/config.php';
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = db();
|
||||||
|
$sql = file_get_contents(__DIR__ . '/001_create_work_orders.sql');
|
||||||
|
$pdo->exec($sql);
|
||||||
|
echo "Database migration and seeding completed successfully.\n";
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
die("Database migration failed: " . $e->getMessage() . "\n");
|
||||||
|
}
|
||||||
|
|
||||||
32
includes/functions.php
Normal file
32
includes/functions.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
// includes/functions.php
|
||||||
|
|
||||||
|
function get_work_orders_by_status($status) {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM work_orders WHERE status = ? ORDER BY created_at DESC");
|
||||||
|
$stmt->execute([$status]);
|
||||||
|
return $stmt->fetchAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_all_work_orders() {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->query("SELECT * FROM work_orders ORDER BY created_at DESC");
|
||||||
|
return $stmt->fetchAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
];
|
||||||
|
}
|
||||||
329
index.php
329
index.php
@ -1,150 +1,191 @@
|
|||||||
<?php
|
<!DOCTYPE html>
|
||||||
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>
|
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>New Style</title>
|
<title>RedSolutions CRM</title>
|
||||||
<?php
|
<meta name="description" content="RedSolutions CRM - Built with Flatlogic Generator">
|
||||||
// Read project preview data from environment
|
<meta name="keywords" content="crm, redsolutions, business management, proposals, work orders, dispatch, field service, flatlogic">
|
||||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
<meta property="og:title" content="RedSolutions CRM">
|
||||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
<meta property="og:description" content="A modern CRM for managing your business operations.">
|
||||||
?>
|
<meta property="og:image" content="">
|
||||||
<?php if ($projectDescription): ?>
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
<!-- Meta description -->
|
<meta name="twitter:image" content="">
|
||||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
|
||||||
<!-- Open Graph meta tags -->
|
<!-- Bootstrap 5 CSS -->
|
||||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<!-- Twitter meta tags -->
|
<!-- Bootstrap Icons -->
|
||||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||||
<?php endif; ?>
|
<!-- Custom CSS -->
|
||||||
<?php if ($projectImageUrl): ?>
|
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
|
||||||
<!-- 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>
|
</head>
|
||||||
|
<?php
|
||||||
|
require_once 'db/config.php';
|
||||||
|
require_once 'includes/functions.php';
|
||||||
|
|
||||||
|
$all_work_orders = get_all_work_orders();
|
||||||
|
$kpis = get_kpis($all_work_orders);
|
||||||
|
|
||||||
|
$active_jobs = get_work_orders_by_status('In Progress');
|
||||||
|
$pending_dispatch = get_work_orders_by_status('Pending Dispatch');
|
||||||
|
?>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
|
||||||
<div class="card">
|
<div class="sidebar">
|
||||||
<h1>Analyzing your requirements and generating your website…</h1>
|
<div class="logo">RedSolutions</div>
|
||||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
<ul class="nav flex-column">
|
||||||
<span class="sr-only">Loading…</span>
|
<li class="nav-item">
|
||||||
</div>
|
<a class="nav-link active" href="#"><i class="bi bi-grid-fill me-2"></i> Overview</a>
|
||||||
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p>
|
</li>
|
||||||
<p class="hint">This page will update automatically as the plan is implemented.</p>
|
<li class="nav-item">
|
||||||
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
|
<a class="nav-link" href="#"><i class="bi bi-file-earmark-text-fill me-2"></i> Proposals</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#"><i class="bi bi-truck me-2"></i> Dispatch</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#"><i class="bi bi-list-check me-2"></i> Work Orders</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#"><i class="bi bi-people-fill me-2"></i> CRM</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#"><i class="bi bi-folder-fill me-2"></i> Documents</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#"><i class="bi bi-robot me-2"></i> AI Assistant</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
|
||||||
<footer>
|
<div class="main-content">
|
||||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
<header class="header">
|
||||||
</footer>
|
<h1>Dashboard</h1>
|
||||||
|
<button class="btn btn-primary"><i class="bi bi-plus-lg me-2"></i> Create Work Order</button>
|
||||||
|
</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 count($active_jobs); ?></h3>
|
||||||
|
<span class="kpi-title">Active 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($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['total_jobs']; ?></h3>
|
||||||
|
<span class="kpi-title">Total Jobs</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Main Dashboard Cards -->
|
||||||
|
<div class="row mt-4">
|
||||||
|
<div class="col-md-7">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<span>Active Jobs</span>
|
||||||
|
<a href="#" class="btn btn-sm btn-outline-secondary">View All</a>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
<?php if (empty($active_jobs)): ?>
|
||||||
|
<li class="list-group-item">No active jobs.</li>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($active_jobs as $job): ?>
|
||||||
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
|
<div>
|
||||||
|
<h6 class="mb-0">WO-<?php echo str_pad($job['id'], 5, '0', STR_PAD_LEFT); ?>: <?php echo htmlspecialchars($job['job_type']); ?></h6>
|
||||||
|
<small class="text-muted">Customer: <?php echo htmlspecialchars($job['customer_name']); ?> | Technician: <?php echo htmlspecialchars($job['technician'] ?? 'N/A'); ?></small>
|
||||||
|
</div>
|
||||||
|
<span class="badge bg-primary-soft text-primary"><?php echo htmlspecialchars($job['status']); ?></span>
|
||||||
|
</li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-5">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
|
<span>Pending Dispatch</span>
|
||||||
|
<a href="#" class="btn btn-sm btn-outline-secondary">View All</a>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
<?php if (empty($pending_dispatch)): ?>
|
||||||
|
<li class="list-group-item">No jobs pending dispatch.</li>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($pending_dispatch as $job): ?>
|
||||||
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||||
|
<div>
|
||||||
|
<h6 class="mb-0">WO-<?php echo str_pad($job['id'], 5, '0', STR_PAD_LEFT); ?>: <?php echo htmlspecialchars($job['job_type']); ?></h6>
|
||||||
|
<small class="text-muted">Customer: <?php echo htmlspecialchars($job['customer_name']); ?></small>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-sm btn-primary">Dispatch</button>
|
||||||
|
</li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</ul>
|
||||||
|
</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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user