This commit is contained in:
Flatlogic Bot 2025-11-09 02:29:45 +00:00
parent 8f16e91e88
commit 4708a2f224
6 changed files with 416 additions and 144 deletions

167
assets/css/custom.css Normal file
View 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
View File

@ -0,0 +1 @@
// Custom JavaScript will go here

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.');

12
db/migrate.php Normal file
View 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
View 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
];
}

323
index.php
View File

@ -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="sidebar">
<div class="logo">RedSolutions</div>
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="#"><i class="bi bi-grid-fill me-2"></i> Overview</a>
</li>
<li class="nav-item">
<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 class="main-content">
<header class="header">
<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">
<h1>Analyzing your requirements and generating your website…</h1> <div class="card-header d-flex justify-content-between align-items-center">
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes"> <span>Active Jobs</span>
<span class="sr-only">Loading…</span> <a href="#" class="btn btn-sm btn-outline-secondary">View All</a>
</div> </div>
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p> <div class="card-body">
<p class="hint">This page will update automatically as the plan is implemented.</p> <ul class="list-group list-group-flush">
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p> <?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> </div>
</main> <span class="badge bg-primary-soft text-primary"><?php echo htmlspecialchars($job['status']); ?></span>
<footer> </li>
Page updated: <?= htmlspecialchars($now) ?> (UTC) <?php endforeach; ?>
</footer> <?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>