diff --git a/add_log.php b/add_log.php
new file mode 100644
index 0000000..1b18b03
--- /dev/null
+++ b/add_log.php
@@ -0,0 +1,26 @@
+addLog($projectId, $version, $logEntry, $author);
+ }
+
+ header("Location: index.php?view=projects&project_id=$projectId");
+ exit;
+}
+
+header('Location: index.php');
+exit;
diff --git a/cls/class.pef.php b/cls/class.pef.php
new file mode 100644
index 0000000..e8dd6a6
--- /dev/null
+++ b/cls/class.pef.php
@@ -0,0 +1,137 @@
+db = db();
+ }
+
+ /**
+ * Get all projects, optionally filtered by industry or status.
+ */
+ public function getProjects(?string $industry = null, ?string $status = null): array {
+ $sql = "SELECT * FROM projects WHERE 1=1";
+ $params = [];
+ if ($industry) {
+ $sql .= " AND industry = :industry";
+ $params['industry'] = $industry;
+ }
+ if ($status) {
+ $sql .= " AND status = :status";
+ $params['status'] = $status;
+ }
+ $sql .= " ORDER BY updated_at DESC";
+
+ $stmt = $this->db->prepare($sql);
+ $stmt->execute($params);
+ return $stmt->fetchAll();
+ }
+
+ /**
+ * User authentication.
+ */
+ public function login(string $username, string $password): ?array {
+ $stmt = $this->db->prepare("SELECT * FROM users WHERE username = :username");
+ $stmt->execute(['username' => $username]);
+ $user = $stmt->fetch();
+
+ if ($user && password_verify($password, $user['password'])) {
+ unset($user['password']);
+ return $user;
+ }
+ return null;
+ }
+
+ /**
+ * Update project status or details.
+ */
+ public function updateProject(int $id, array $data): bool {
+ $fields = [];
+ foreach ($data as $key => $value) {
+ $fields[] = "$key = :$key";
+ }
+ $sql = "UPDATE projects SET " . implode(', ', $fields) . " WHERE id = :id";
+ $data['id'] = $id;
+ $stmt = $this->db->prepare($sql);
+ return $stmt->execute($data);
+ }
+
+ /**
+ * Delete project.
+ */
+ public function deleteProject(int $id): bool {
+ $stmt = $this->db->prepare("DELETE FROM projects WHERE id = :id");
+ return $stmt->execute(['id' => $id]);
+ }
+
+ /**
+ * Dashboard stats.
+ */
+ public function getStats(): array {
+ $stats = [];
+ $stats['total_projects'] = $this->db->query("SELECT COUNT(*) FROM projects")->fetchColumn();
+ $stats['active_projects'] = $this->db->query("SELECT COUNT(*) FROM projects WHERE status = 'active'")->fetchColumn();
+ $stats['latest_logs'] = $this->db->query("SELECT pl.*, p.name as project_name FROM project_logs pl JOIN projects p ON pl.project_id = p.id ORDER BY pl.created_at DESC LIMIT 5")->fetchAll();
+ return $stats;
+ }
+
+ /**
+ * Get a single project by ID.
+ */
+ public function getProject(int $id): ?array {
+ $stmt = $this->db->prepare("SELECT * FROM projects WHERE id = :id");
+ $stmt->execute(['id' => $id]);
+ return $stmt->fetch() ?: null;
+ }
+
+ /**
+ * Get all logs for a specific project.
+ */
+ public function getProjectLogs(int $projectId): array {
+ $stmt = $this->db->prepare("SELECT * FROM project_logs WHERE project_id = :project_id ORDER BY created_at DESC");
+ $stmt->execute(['project_id' => $projectId]);
+ return $stmt->fetchAll();
+ }
+
+ /**
+ * Create a new project.
+ */
+ public function createProject(string $name, string $description, string $industry, string $version = '1.0.0'): int {
+ $stmt = $this->db->prepare("INSERT INTO projects (name, description, industry, current_version) VALUES (:name, :description, :industry, :version)");
+ $stmt->execute([
+ 'name' => $name,
+ 'description' => $description,
+ 'industry' => $industry,
+ 'version' => $version
+ ]);
+ return (int)$this->db->lastInsertId();
+ }
+
+ /**
+ * Add a log entry for a project and update its current version.
+ */
+ public function addLog(int $projectId, string $version, string $logEntry, string $author = 'System'): bool {
+ try {
+ $this->db->beginTransaction();
+
+ $stmt = $this->db->prepare("INSERT INTO project_logs (project_id, version, log_entry, author) VALUES (:project_id, :version, :log_entry, :author)");
+ $stmt->execute([
+ 'project_id' => $projectId,
+ 'version' => $version,
+ 'log_entry' => $logEntry,
+ 'author' => $author
+ ]);
+
+ $stmt = $this->db->prepare("UPDATE projects SET current_version = :version WHERE id = :id");
+ $stmt->execute(['version' => $version, 'id' => $projectId]);
+
+ $this->db->commit();
+ return true;
+ } catch (Exception $e) {
+ $this->db->rollBack();
+ return false;
+ }
+ }
+}
diff --git a/db/migrations/20260215_init.sql b/db/migrations/20260215_init.sql
new file mode 100644
index 0000000..38070d5
--- /dev/null
+++ b/db/migrations/20260215_init.sql
@@ -0,0 +1,20 @@
+-- Initial schema for Construction and Education CMS
+CREATE TABLE IF NOT EXISTS projects (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(255) NOT NULL,
+ description TEXT,
+ industry ENUM('Construction', 'Education', 'Other') DEFAULT 'Construction',
+ current_version VARCHAR(50) DEFAULT '1.0.0',
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+);
+
+CREATE TABLE IF NOT EXISTS project_logs (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ project_id INT NOT NULL,
+ version VARCHAR(50) NOT NULL,
+ log_entry TEXT NOT NULL,
+ author VARCHAR(255),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
+);
diff --git a/db/migrations/20260216_auth_and_status.sql b/db/migrations/20260216_auth_and_status.sql
new file mode 100644
index 0000000..c47272a
--- /dev/null
+++ b/db/migrations/20260216_auth_and_status.sql
@@ -0,0 +1,30 @@
+-- Add users table
+CREATE TABLE IF NOT EXISTS users (
+ id INT AUTO_INCREMENT PRIMARY KEY,
+ username VARCHAR(50) NOT NULL UNIQUE,
+ password VARCHAR(255) NOT NULL,
+ role ENUM('admin', 'editor', 'viewer') DEFAULT 'viewer',
+ full_name VARCHAR(100),
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+);
+
+-- Safely add status column
+SET @dbname = DATABASE();
+SET @tablename = "projects";
+SET @columnname = "status";
+SET @preparedStatement = (SELECT IF(
+ (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
+ WHERE TABLE_SCHEMA = @dbname
+ AND TABLE_NAME = @tablename
+ AND COLUMN_NAME = @columnname) > 0,
+ "SELECT 1",
+ "ALTER TABLE projects ADD COLUMN status ENUM('active', 'completed', 'on_hold', 'archived') DEFAULT 'active' AFTER industry"
+));
+PREPARE stmt FROM @preparedStatement;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+-- Create default admin user (password: admin123)
+INSERT INTO users (username, password, role, full_name)
+VALUES ('admin', '$2y$10$Tsw26nakbueEO40q9tx8Pe4g1yQuWfQFhKXjyVYvbNakTgBi/7NkG', 'admin', 'System Administrator')
+ON DUPLICATE KEY UPDATE password = VALUES(password);
diff --git a/index.php b/index.php
index 7205f3d..8c34ad0 100644
--- a/index.php
+++ b/index.php
@@ -1,150 +1,341 @@
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';
?>
- New Style
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ CMS Dashboard - Construction & Education
-
+
-
-
-
Analyzing your requirements and generating your website…
-
-
Loading…
+
+
+
+
+
= ucfirst($view) ?>
+
+ = date('l, F j, Y') ?>
+
+
+
+
+
+
+
+
Total Projects
+
= $stats['total_projects'] ?>
+
+
+
Active Projects
+
= $stats['active_projects'] ?>
+
+
+
+
+
+
+
+
No recent activity. Start by creating a project or adding a log.
+
+
+
+
+ = htmlspecialchars($log['project_name']) ?> (v= htmlspecialchars($log['version']) ?>)
+ = $log['created_at'] ?>
+
+
= htmlspecialchars($log['log_entry']) ?>
+
By: = htmlspecialchars($log['author']) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
= nl2br(htmlspecialchars($selectedProject['description'])) ?>
+
+ Industry: = $selectedProject['industry'] ?> | Current Version: v= $selectedProject['current_version'] ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
No logs available.
+
+
+
+
+ Version = htmlspecialchars($log['version']) ?>
+ = $log['created_at'] ?>
+
+
= nl2br(htmlspecialchars($log['log_entry'])) ?>
+
Author: = htmlspecialchars($log['author']) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | Project Name |
+ Industry |
+ Status |
+ Version |
+ Last Updated |
+ Actions |
+
+
+
+
+
+ | = htmlspecialchars($p['name']) ?> |
+ = $p['industry'] ?> |
+ = ucfirst($p['status']) ?> |
+ v= htmlspecialchars($p['current_version']) ?> |
+ = date('Y-m-d', strtotime($p['updated_at'])) ?> |
+
+ View
+ |
+
+
+
+
+
+
+
+
-
+
+
+
+
+
Create New Project
+
+
+
+
+
+
+
+
+
Edit Project
+
+
+
+
+
diff --git a/login.php b/login.php
new file mode 100644
index 0000000..c486746
--- /dev/null
+++ b/login.php
@@ -0,0 +1,63 @@
+login($_POST['username'] ?? '', $_POST['password'] ?? '');
+ if ($user) {
+ $_SESSION['user'] = $user;
+ header('Location: index.php');
+ exit;
+ } else {
+ $error = 'Invalid username or password.';
+ }
+}
+?>
+
+
+
+
+
+
Login - Construction & Education CMS
+
+
+
+
+
+
CMS Login
+
+
= htmlspecialchars($error) ?>
+
+
+
+
+
+
diff --git a/logout.php b/logout.php
new file mode 100644
index 0000000..37bc5ab
--- /dev/null
+++ b/logout.php
@@ -0,0 +1,5 @@
+createProject(
+ $_POST['name'],
+ $_POST['description'],
+ $_POST['industry'],
+ $_POST['version'] ?: '1.0.0'
+ );
+} elseif ($action === 'update') {
+ $pef->updateProject((int)$_POST['id'], [
+ 'name' => $_POST['name'],
+ 'description' => $_POST['description'],
+ 'industry' => $_POST['industry'],
+ 'status' => $_POST['status']
+ ]);
+} elseif ($action === 'delete') {
+ $id = (int)($_POST['id'] ?? $_GET['id'] ?? 0);
+ $pef->deleteProject($id);
+}
+
+header('Location: index.php');
+exit;
diff --git a/migrate.php b/migrate.php
new file mode 100644
index 0000000..9f5ede3
--- /dev/null
+++ b/migrate.php
@@ -0,0 +1,23 @@
+exec($sql);
+ echo "Successfully ran migration: " . basename($file) . "\n";
+ } catch (PDOException $e) {
+ echo "Error running migration " . basename($file) . ": " . $e->getMessage() . "\n";
+ }
+ }
+}
+
+if (php_sapi_name() === 'cli' || isset($_GET['run'])) {
+ runMigrations();
+}
diff --git a/seed.php b/seed.php
new file mode 100644
index 0000000..de0148c
--- /dev/null
+++ b/seed.php
@@ -0,0 +1,18 @@
+getProjects()) === 0) {
+ $p1 = $pef->createProject('Bridge Construction A1', 'Main bridge construction for the highway project.', 'Construction', '1.0.0');
+ $pef->addLog($p1, '1.0.0', 'Project initiated.', 'Admin');
+ $pef->addLog($p1, '1.1.0', 'Foundation completed.', 'Site Manager');
+
+ $p2 = $pef->createProject('Online Learning Platform', 'LMS for local schools.', 'Education', '0.9.0');
+ $pef->addLog($p2, '0.9.0', 'Beta version launched for testing.', 'Dev Team');
+
+ echo "Sample data seeded.\n";
+} else {
+ echo "Database already contains data.\n";
+}