342 lines
18 KiB
PHP
342 lines
18 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
session_start();
|
|
require_once __DIR__ . '/cls/class.pef.php';
|
|
|
|
if (!isset($_SESSION['user'])) {
|
|
header('Location: login.php');
|
|
exit;
|
|
}
|
|
|
|
$user = $_SESSION['user'];
|
|
$pef = new Pef();
|
|
$stats = $pef->getStats();
|
|
$projects = $pef->getProjects();
|
|
|
|
$selectedProjectId = isset($_GET['project_id']) ? (int)$_GET['project_id'] : null;
|
|
$selectedProject = $selectedProjectId ? $pef->getProject($selectedProjectId) : null;
|
|
$logs = $selectedProjectId ? $pef->getProjectLogs($selectedProjectId) : [];
|
|
|
|
$view = $_GET['view'] ?? 'dashboard';
|
|
?>
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>CMS Dashboard - Construction & Education</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<style>
|
|
:root {
|
|
--sidebar-bg: #1e293b;
|
|
--sidebar-hover: #334155;
|
|
--primary: #3b82f6;
|
|
--bg: #f8fafc;
|
|
--card: #ffffff;
|
|
--text: #1e293b;
|
|
--text-muted: #64748b;
|
|
--border: #e2e8f0;
|
|
}
|
|
body { margin: 0; font-family: 'Inter', sans-serif; background: var(--bg); color: var(--text); display: flex; min-height: 100vh; }
|
|
|
|
/* Sidebar */
|
|
nav { width: 260px; background: var(--sidebar-bg); color: white; display: flex; flex-direction: column; }
|
|
.nav-header { padding: 2rem 1.5rem; font-weight: 700; font-size: 1.25rem; color: var(--primary); }
|
|
.nav-links { flex: 1; padding: 0 1rem; }
|
|
.nav-link { display: flex; align-items: center; padding: 0.75rem 1rem; color: #cbd5e1; text-decoration: none; border-radius: 8px; margin-bottom: 0.5rem; transition: all 0.2s; }
|
|
.nav-link:hover, .nav-link.active { background: var(--sidebar-hover); color: white; }
|
|
.nav-footer { padding: 1.5rem; border-top: 1px solid #334155; }
|
|
.user-info { font-size: 0.875rem; color: #94a3b8; margin-bottom: 1rem; }
|
|
.logout-btn { color: #f87171; text-decoration: none; font-size: 0.875rem; font-weight: 600; }
|
|
|
|
/* Main Content */
|
|
main { flex: 1; display: flex; flex-direction: column; }
|
|
.top-bar { height: 64px; background: white; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; padding: 0 2rem; }
|
|
.content { padding: 2rem; overflow-y: auto; }
|
|
|
|
/* Cards & Stats */
|
|
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1.5rem; margin-bottom: 2rem; }
|
|
.stat-card { background: white; padding: 1.5rem; border-radius: 12px; border: 1px solid var(--border); }
|
|
.stat-label { font-size: 0.875rem; color: var(--text-muted); font-weight: 500; }
|
|
.stat-value { font-size: 1.5rem; font-weight: 700; margin-top: 0.5rem; }
|
|
|
|
.card { background: white; border-radius: 12px; border: 1px solid var(--border); overflow: hidden; margin-bottom: 1.5rem; }
|
|
.card-header { padding: 1.25rem 1.5rem; border-bottom: 1px solid var(--border); display: flex; justify-content: space-between; align-items: center; }
|
|
.card-title { margin: 0; font-size: 1rem; font-weight: 600; }
|
|
.card-body { padding: 1.5rem; }
|
|
|
|
/* Table */
|
|
table { width: 100%; border-collapse: collapse; }
|
|
th { text-align: left; padding: 1rem; font-size: 0.875rem; color: var(--text-muted); border-bottom: 1px solid var(--border); }
|
|
td { padding: 1rem; border-bottom: 1px solid var(--border); font-size: 0.9375rem; }
|
|
tr:last-child td { border-bottom: none; }
|
|
|
|
/* Badges */
|
|
.badge { padding: 0.25rem 0.625rem; border-radius: 9999px; font-size: 0.75rem; font-weight: 600; }
|
|
.badge-active { background: #dcfce7; color: #166534; }
|
|
.badge-completed { background: #e0f2fe; color: #0369a1; }
|
|
.badge-on_hold { background: #fef3c7; color: #92400e; }
|
|
|
|
.btn { padding: 0.5rem 1rem; border-radius: 6px; font-weight: 600; font-size: 0.875rem; cursor: pointer; border: none; text-decoration: none; display: inline-block; }
|
|
.btn-primary { background: var(--primary); color: white; }
|
|
.btn-outline { border: 1px solid var(--border); background: white; color: var(--text); }
|
|
|
|
/* Log Items */
|
|
.log-entry { padding: 1rem; border-left: 3px solid var(--primary); background: #f8fafc; margin-bottom: 1rem; border-radius: 0 8px 8px 0; }
|
|
.log-meta { display: flex; justify-content: space-between; font-size: 0.8rem; color: var(--text-muted); margin-bottom: 0.5rem; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<nav>
|
|
<div class="nav-header">PEF CMS</div>
|
|
<div class="nav-links">
|
|
<a href="?view=dashboard" class="nav-link <?= $view === 'dashboard' ? 'active' : '' ?>">Overview</a>
|
|
<a href="?view=projects" class="nav-link <?= $view === 'projects' ? 'active' : '' ?>">Projects</a>
|
|
</div>
|
|
<div class="nav-footer">
|
|
<div class="user-info">
|
|
Logged in as: <strong><?= htmlspecialchars($user['full_name']) ?></strong><br>
|
|
<small><?= ucfirst($user['role']) ?></small>
|
|
</div>
|
|
<a href="logout.php" class="logout-btn">Sign Out</a>
|
|
</div>
|
|
</nav>
|
|
|
|
<main>
|
|
<div class="top-bar">
|
|
<h2 style="font-size: 1.125rem; margin: 0;"><?= ucfirst($view) ?></h2>
|
|
<div style="font-size: 0.875rem; color: var(--text-muted);">
|
|
<?= date('l, F j, Y') ?>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="content">
|
|
<?php if ($view === 'dashboard'): ?>
|
|
<div class="stats-grid">
|
|
<div class="stat-card">
|
|
<div class="stat-label">Total Projects</div>
|
|
<div class="stat-value"><?= $stats['total_projects'] ?></div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-label">Active Projects</div>
|
|
<div class="stat-value"><?= $stats['active_projects'] ?></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header"><h3 class="card-title">Recent Activity</h3></div>
|
|
<div class="card-body">
|
|
<?php if (empty($stats['latest_logs'])): ?>
|
|
<p style="text-align:center; color:var(--text-muted); padding: 2rem;">No recent activity. Start by creating a project or adding a log.</p>
|
|
<?php else: ?>
|
|
<?php foreach ($stats['latest_logs'] as $log): ?>
|
|
<div class="log-entry">
|
|
<div class="log-meta">
|
|
<strong><?= htmlspecialchars($log['project_name']) ?> (v<?= htmlspecialchars($log['version']) ?>)</strong>
|
|
<span><?= $log['created_at'] ?></span>
|
|
</div>
|
|
<div style="font-size: 0.9rem;"><?= htmlspecialchars($log['log_entry']) ?></div>
|
|
<div style="font-size: 0.75rem; color: var(--text-muted); margin-top: 0.25rem;">By: <?= htmlspecialchars($log['author']) ?></div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<?php elseif ($view === 'projects'): ?>
|
|
<?php if ($selectedProjectId): ?>
|
|
<!-- Project Detail View -->
|
|
<div style="margin-bottom: 1rem;">
|
|
<a href="?view=projects" class="btn btn-outline">← Back to Projects</a>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title"><?= htmlspecialchars($selectedProject['name']) ?></h3>
|
|
<div style="display:flex; gap:0.5rem; align-items:center;">
|
|
<span class="badge badge-<?= $selectedProject['status'] ?>"><?= ucfirst($selectedProject['status']) ?></span>
|
|
<?php if ($user['role'] !== 'viewer'): ?>
|
|
<button onclick="openEditModal()" class="btn btn-outline" style="padding: 0.25rem 0.5rem; font-size: 0.75rem;">Edit</button>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<p><?= nl2br(htmlspecialchars($selectedProject['description'])) ?></p>
|
|
<div style="font-size: 0.875rem; color: var(--text-muted);">
|
|
Industry: <?= $selectedProject['industry'] ?> | Current Version: v<?= $selectedProject['current_version'] ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if ($user['role'] !== 'viewer'): ?>
|
|
<div class="card">
|
|
<div class="card-header"><h3 class="card-title">Add New Log & Version</h3></div>
|
|
<div class="card-body">
|
|
<form action="add_log.php" method="POST" style="display: grid; gap: 1rem;">
|
|
<input type="hidden" name="project_id" value="<?= $selectedProjectId ?>">
|
|
<div>
|
|
<label style="display:block; font-size:0.8rem; font-weight:600; margin-bottom:0.25rem;">Version</label>
|
|
<input type="text" name="version" value="<?= htmlspecialchars($selectedProject['current_version']) ?>" style="width:100%; padding:0.5rem; border:1px solid var(--border); border-radius:4px;" required>
|
|
</div>
|
|
<div>
|
|
<label style="display:block; font-size:0.8rem; font-weight:600; margin-bottom:0.25rem;">What changed?</label>
|
|
<textarea name="log_entry" rows="3" style="width:100%; padding:0.5rem; border:1px solid var(--border); border-radius:4px;" required placeholder="Detailed log entry..."></textarea>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary" style="width:fit-content;">Post Update</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="card">
|
|
<div class="card-header"><h3 class="card-title">Version History</h3></div>
|
|
<div class="card-body">
|
|
<?php if (empty($logs)): ?>
|
|
<p style="text-align:center; color:var(--text-muted);">No logs available.</p>
|
|
<?php else: ?>
|
|
<?php foreach ($logs as $log): ?>
|
|
<div class="log-entry">
|
|
<div class="log-meta">
|
|
<strong>Version <?= htmlspecialchars($log['version']) ?></strong>
|
|
<span><?= $log['created_at'] ?></span>
|
|
</div>
|
|
<div><?= nl2br(htmlspecialchars($log['log_entry'])) ?></div>
|
|
<div style="font-size:0.75rem; color:var(--text-muted); margin-top:0.5rem;">Author: <?= htmlspecialchars($log['author']) ?></div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<?php else: ?>
|
|
<!-- Projects List -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3 class="card-title">Managed Projects</h3>
|
|
<?php if ($user['role'] !== 'viewer'): ?>
|
|
<button onclick="document.getElementById('addProjectModal').style.display='block'" class="btn btn-primary">+ New Project</button>
|
|
<?php endif; ?>
|
|
</div>
|
|
<div class="card-body" style="padding:0;">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Project Name</th>
|
|
<th>Industry</th>
|
|
<th>Status</th>
|
|
<th>Version</th>
|
|
<th>Last Updated</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($projects as $p): ?>
|
|
<tr>
|
|
<td><strong><?= htmlspecialchars($p['name']) ?></strong></td>
|
|
<td><?= $p['industry'] ?></td>
|
|
<td><span class="badge badge-<?= $p['status'] ?>"><?= ucfirst($p['status']) ?></span></td>
|
|
<td>v<?= htmlspecialchars($p['current_version']) ?></td>
|
|
<td><?= date('Y-m-d', strtotime($p['updated_at'])) ?></td>
|
|
<td>
|
|
<a href="?view=projects&project_id=<?= $p['id'] ?>" class="btn btn-outline" style="padding: 0.25rem 0.75rem;">View</a>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
</main>
|
|
|
|
<!-- Modals -->
|
|
<div id="addProjectModal" style="display:none; position:fixed; inset:0; background:rgba(0,0,0,0.5); z-index:100; align-items:center; justify-content:center;">
|
|
<div style="background:white; padding:2rem; border-radius:12px; width:450px; position:relative; margin: 10% auto;">
|
|
<h3 style="margin-top:0;">Create New Project</h3>
|
|
<form action="manage_project.php?action=create" method="POST" style="display:grid; gap:1rem;">
|
|
<div>
|
|
<label style="display:block; font-size:0.8rem; font-weight:600; margin-bottom:0.25rem;">Name</label>
|
|
<input type="text" name="name" required style="width:100%; padding:0.5rem; border:1px solid var(--border); border-radius:4px;">
|
|
</div>
|
|
<div>
|
|
<label style="display:block; font-size:0.8rem; font-weight:600; margin-bottom:0.25rem;">Industry</label>
|
|
<select name="industry" style="width:100%; padding:0.5rem; border:1px solid var(--border); border-radius:4px;">
|
|
<option>Construction</option>
|
|
<option>Education</option>
|
|
<option>Other</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label style="display:block; font-size:0.8rem; font-weight:600; margin-bottom:0.25rem;">Description</label>
|
|
<textarea name="description" rows="3" style="width:100%; padding:0.5rem; border:1px solid var(--border); border-radius:4px;"></textarea>
|
|
</div>
|
|
<div>
|
|
<label style="display:block; font-size:0.8rem; font-weight:600; margin-bottom:0.25rem;">Initial Version</label>
|
|
<input type="text" name="version" value="1.0.0" style="width:100%; padding:0.5rem; border:1px solid var(--border); border-radius:4px;">
|
|
</div>
|
|
<div style="display:flex; gap:0.5rem; justify-content:flex-end; margin-top:1rem;">
|
|
<button type="button" onclick="this.closest('#addProjectModal').style.display='none'" class="btn btn-outline">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">Create Project</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Edit Project Modal -->
|
|
<?php if ($selectedProject): ?>
|
|
<div id="editProjectModal" style="display:none; position:fixed; inset:0; background:rgba(0,0,0,0.5); z-index:100; align-items:center; justify-content:center;">
|
|
<div style="background:white; padding:2rem; border-radius:12px; width:450px; position:relative; margin: 10% auto;">
|
|
<h3 style="margin-top:0;">Edit Project</h3>
|
|
<form action="manage_project.php?action=update" method="POST" style="display:grid; gap:1rem;">
|
|
<input type="hidden" name="id" value="<?= $selectedProject['id'] ?>">
|
|
<div>
|
|
<label style="display:block; font-size:0.8rem; font-weight:600; margin-bottom:0.25rem;">Name</label>
|
|
<input type="text" name="name" value="<?= htmlspecialchars($selectedProject['name']) ?>" required style="width:100%; padding:0.5rem; border:1px solid var(--border); border-radius:4px;">
|
|
</div>
|
|
<div>
|
|
<label style="display:block; font-size:0.8rem; font-weight:600; margin-bottom:0.25rem;">Status</label>
|
|
<select name="status" style="width:100%; padding:0.5rem; border:1px solid var(--border); border-radius:4px;">
|
|
<option value="active" <?= $selectedProject['status'] === 'active' ? 'selected' : '' ?>>Active</option>
|
|
<option value="completed" <?= $selectedProject['status'] === 'completed' ? 'selected' : '' ?>>Completed</option>
|
|
<option value="on_hold" <?= $selectedProject['status'] === 'on_hold' ? 'selected' : '' ?>>On Hold</option>
|
|
<option value="archived" <?= $selectedProject['status'] === 'archived' ? 'selected' : '' ?>>Archived</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label style="display:block; font-size:0.8rem; font-weight:600; margin-bottom:0.25rem;">Industry</label>
|
|
<select name="industry" style="width:100%; padding:0.5rem; border:1px solid var(--border); border-radius:4px;">
|
|
<option <?= $selectedProject['industry'] === 'Construction' ? 'selected' : '' ?>>Construction</option>
|
|
<option <?= $selectedProject['industry'] === 'Education' ? 'selected' : '' ?>>Education</option>
|
|
<option <?= $selectedProject['industry'] === 'Other' ? 'selected' : '' ?>>Other</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label style="display:block; font-size:0.8rem; font-weight:600; margin-bottom:0.25rem;">Description</label>
|
|
<textarea name="description" rows="3" style="width:100%; padding:0.5rem; border:1px solid var(--border); border-radius:4px;"><?= htmlspecialchars($selectedProject['description']) ?></textarea>
|
|
</div>
|
|
<div style="display:flex; gap:0.5rem; justify-content:space-between; margin-top:1rem;">
|
|
<button type="button" onclick="if(confirm('Delete project?')) window.location.href='manage_project.php?action=delete&id=<?= $selectedProject['id'] ?>'" class="btn btn-outline" style="color:red; border-color:red;">Delete</button>
|
|
<div style="display:flex; gap:0.5rem;">
|
|
<button type="button" onclick="closeEditModal()" class="btn btn-outline">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">Save Changes</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
<script>
|
|
function openEditModal() {
|
|
document.getElementById('editProjectModal').style.display = 'block';
|
|
}
|
|
function closeEditModal() {
|
|
document.getElementById('editProjectModal').style.display = 'none';
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|