This commit is contained in:
Flatlogic Bot 2025-11-22 16:42:51 +00:00
parent b654b375c7
commit e0ac638716
20 changed files with 900 additions and 130 deletions

84
admin/add-task.php Normal file
View File

@ -0,0 +1,84 @@
<?php
require_once 'auth.php';
$pageTitle = "Add Task";
include 'partials/header.php';
require_once __DIR__ . '/../db/config.php';
$title = $icon = $description = $status = '';
$errors = [];
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$title = trim($_POST['title']);
$icon = trim($_POST['icon']);
$description = trim($_POST['description']);
$status = trim($_POST['status']);
if (empty($title)) {
$errors['title'] = 'Title is required';
}
if (empty($icon)) {
$errors['icon'] = 'Icon is required';
}
if (empty($errors)) {
try {
$pdo = db();
$sql = "INSERT INTO tasks (title, icon, description, status) VALUES (?, ?, ?, ?)";
$stmt = $pdo->prepare($sql);
$stmt->execute([$title, $icon, $description, $status]);
// Redirect to tasks list
header("Location: tasks.php");
exit;
} catch (PDOException $e) {
// Ideally, log this error
$errors['db'] = "Database error: " . $e->getMessage();
}
}
}
?>
<div class="container-fluid">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Add New Task</h1>
</div>
<?php if (!empty($errors['db'])): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($errors['db']); ?></div>
<?php endif; ?>
<form method="POST" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control <?php echo !empty($errors['title']) ? 'is-invalid' : ''; ?>" id="title" name="title" value="<?php echo htmlspecialchars($title); ?>">
<?php if (!empty($errors['title'])): ?>
<div class="invalid-feedback"><?php echo $errors['title']; ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<label for="icon" class="form-label">Icon</label>
<input type="text" class="form-control <?php echo !empty($errors['icon']) ? 'is-invalid' : ''; ?>" id="icon" name="icon" value="<?php echo htmlspecialchars($icon); ?>">
<?php if (!empty($errors['icon'])): ?>
<div class="invalid-feedback"><?php echo $errors['icon']; ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<label for="description" class="form-label">Description</label>
<textarea class="form-control" id="description" name="description" rows="3"><?php echo htmlspecialchars($description); ?></textarea>
</div>
<div class="mb-3">
<label for="status" class="form-label">Status</label>
<select class="form-control" id="status" name="status">
<option value="pending" selected>Pending</option>
<option value="in_progress">In Progress</option>
<option value="completed">Completed</option>
</select>
</div>
<button type="submit" class="btn btn-primary">Save Task</button>
<a href="tasks.php" class="btn btn-secondary">Cancel</a>
</form>
</div>
<?php include 'partials/footer.php'; ?>

104
admin/add-user.php Normal file
View File

@ -0,0 +1,104 @@
<?php
require_once 'auth.php';
$pageTitle = "Add User";
include 'partials/header.php';
require_once __DIR__ . '/../db/config.php';
$name = $email = $password = $password_confirm = '';
$errors = [];
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$name = trim($_POST['name']);
$email = trim($_POST['email']);
$password = $_POST['password'];
$password_confirm = $_POST['password_confirm'];
if (empty($name)) {
$errors['name'] = 'Name is required';
}
if (empty($email)) {
$errors['email'] = 'Email is required';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors['email'] = 'Invalid email format';
}
if (empty($password)) {
$errors['password'] = 'Password is required';
}
if ($password !== $password_confirm) {
$errors['password_confirm'] = 'Passwords do not match';
}
if (empty($errors)) {
try {
$pdo = db();
// Check if email already exists
$stmt = $pdo->prepare("SELECT id FROM users WHERE email = ?");
$stmt->execute([$email]);
if ($stmt->fetch()) {
$errors['email'] = 'Email already exists';
} else {
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$sql = "INSERT INTO users (name, email, password, role) VALUES (?, ?, ?, 'user')";
$stmt = $pdo->prepare($sql);
$stmt->execute([$name, $email, $hashed_password]);
// Redirect to users list
header("Location: users.php");
exit;
}
} catch (PDOException $e) {
// Ideally, log this error
$errors['db'] = "Database error: " . $e->getMessage();
}
}
}
?>
<div class="container-fluid">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Add New User</h1>
</div>
<?php if (!empty($errors['db'])): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($errors['db']); ?></div>
<?php endif; ?>
<form method="POST" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control <?php echo !empty($errors['name']) ? 'is-invalid' : ''; ?>" id="name" name="name" value="<?php echo htmlspecialchars($name); ?>">
<?php if (!empty($errors['name'])): ?>
<div class="invalid-feedback"><?php echo $errors['name']; ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email address</label>
<input type="email" class="form-control <?php echo !empty($errors['email']) ? 'is-invalid' : ''; ?>" id="email" name="email" value="<?php echo htmlspecialchars($email); ?>">
<?php if (!empty($errors['email'])): ?>
<div class="invalid-feedback"><?php echo $errors['email']; ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control <?php echo !empty($errors['password']) ? 'is-invalid' : ''; ?>" id="password" name="password">
<?php if (!empty($errors['password'])): ?>
<div class="invalid-feedback"><?php echo $errors['password']; ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<label for="password_confirm" class="form-label">Confirm Password</label>
<input type="password" class="form-control <?php echo !empty($errors['password_confirm']) ? 'is-invalid' : ''; ?>" id="password_confirm" name="password_confirm">
<?php if (!empty($errors['password_confirm'])): ?>
<div class="invalid-feedback"><?php echo $errors['password_confirm']; ?></div>
<?php endif; ?>
</div>
<button type="submit" class="btn btn-primary">Save User</button>
<a href="users.php" class="btn btn-secondary">Cancel</a>
</form>
</div>
<?php include 'partials/footer.php'; ?>

12
admin/auth.php Normal file
View File

@ -0,0 +1,12 @@
<?php
session_start();
if (!isset($_SESSION['user_id'])) {
header("Location: ../login.php");
exit;
}
// Optional: Role-based access control
function is_admin() {
return isset($_SESSION['user_role']) && $_SESSION['user_role'] === 'admin';
}

25
admin/delete-task.php Normal file
View File

@ -0,0 +1,25 @@
<?php
require_once 'auth.php';
require_once __DIR__ . '/../db/config.php';
// Check if ID is set
if (!isset($_GET['id']) || empty($_GET['id'])) {
header("Location: tasks.php");
exit;
}
$id = $_GET['id'];
try {
$pdo = db();
$sql = "DELETE FROM tasks WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$id]);
// Redirect back to task list
header("Location: tasks.php");
exit;
} catch (PDOException $e) {
// For a real app, you'd log this error and show a user-friendly message.
die("Error: Could not delete task. " . $e->getMessage());
}

25
admin/delete-user.php Normal file
View File

@ -0,0 +1,25 @@
<?php
require_once 'auth.php';
require_once __DIR__ . '/../db/config.php';
// Check if ID is set
if (!isset($_GET['id']) || empty($_GET['id'])) {
header("Location: users.php");
exit;
}
$id = $_GET['id'];
try {
$pdo = db();
$sql = "DELETE FROM users WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$id]);
// Redirect back to user list
header("Location: users.php");
exit;
} catch (PDOException $e) {
// For a real app, you'd log this error and show a user-friendly message.
die("Error: Could not delete user. " . $e->getMessage());
}

104
admin/edit-task.php Normal file
View File

@ -0,0 +1,104 @@
<?php
require_once 'auth.php';
$pageTitle = "Edit Task";
include 'partials/header.php';
require_once __DIR__ . '/../db/config.php';
$id = $_GET['id'] ?? null;
if (!$id) {
header("Location: tasks.php");
exit;
}
$pdo = db();
$title = $icon = $description = $status = '';
$errors = [];
// Fetch task data for the form
$stmt = $pdo->prepare("SELECT * FROM tasks WHERE id = ?");
$stmt->execute([$id]);
$task = $stmt->fetch();
if (!$task) {
// Optional: Add a flash message here
header("Location: tasks.php");
exit;
}
$title = $task['title'];
$icon = $task['icon'];
$description = $task['description'];
$status = $task['status'];
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$title = trim($_POST['title']);
$icon = trim($_POST['icon']);
$description = trim($_POST['description']);
$status = trim($_POST['status']);
if (empty($title)) {
$errors['title'] = 'Title is required';
}
if (empty($icon)) {
$errors['icon'] = 'Icon is required';
}
if (empty($errors)) {
try {
$sql = "UPDATE tasks SET title = ?, icon = ?, description = ?, status = ? WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$title, $icon, $description, $status, $id]);
header("Location: tasks.php");
exit;
} catch (PDOException $e) {
$errors['db'] = "Database error: " . $e->getMessage();
}
}
}
?>
<div class="container-fluid">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Edit Task</h1>
</div>
<?php if (!empty($errors['db'])): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($errors['db']); ?></div>
<?php endif; ?>
<form method="POST" action="edit-task.php?id=<?php echo htmlspecialchars($id); ?>">
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control <?php echo !empty($errors['title']) ? 'is-invalid' : ''; ?>" id="title" name="title" value="<?php echo htmlspecialchars($title); ?>">
<?php if (!empty($errors['title'])): ?>
<div class="invalid-feedback"><?php echo $errors['title']; ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<label for="icon" class="form-label">Icon</label>
<input type="text" class="form-control <?php echo !empty($errors['icon']) ? 'is-invalid' : ''; ?>" id="icon" name="icon" value="<?php echo htmlspecialchars($icon); ?>">
<?php if (!empty($errors['icon'])): ?>
<div class="invalid-feedback"><?php echo $errors['icon']; ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<label for="description" class="form-label">Description</label>
<textarea class="form-control" id="description" name="description" rows="3"><?php echo htmlspecialchars($description); ?></textarea>
</div>
<div class="mb-3">
<label for="status" class="form-label">Status</label>
<select class="form-control" id="status" name="status">
<option value="pending" <?php echo ($status == 'pending') ? 'selected' : ''; ?>>Pending</option>
<option value="in_progress" <?php echo ($status == 'in_progress') ? 'selected' : ''; ?>>In Progress</option>
<option value="completed" <?php echo ($status == 'completed') ? 'selected' : ''; ?>>Completed</option>
</select>
</div>
<button type="submit" class="btn btn-primary">Update Task</button>
<a href="tasks.php" class="btn btn-secondary">Cancel</a>
</form>
</div>
<?php include 'partials/footer.php'; ?>

124
admin/edit-user.php Normal file
View File

@ -0,0 +1,124 @@
<?php
require_once 'auth.php';
$pageTitle = "Edit User";
include 'partials/header.php';
require_once __DIR__ . '/../db/config.php';
$id = $_GET['id'] ?? null;
if (!$id) {
header("Location: users.php");
exit;
}
$pdo = db();
$name = $email = '';
$errors = [];
// Fetch user data for the form
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
$user = $stmt->fetch();
if (!$user) {
// Optional: Add a flash message here
header("Location: users.php");
exit;
}
$name = $user['name'];
$email = $user['email'];
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$name = trim($_POST['name']);
$email = trim($_POST['email']);
$password = $_POST['password'];
$password_confirm = $_POST['password_confirm'];
if (empty($name)) {
$errors['name'] = 'Name is required';
}
if (empty($email)) {
$errors['email'] = 'Email is required';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors['email'] = 'Invalid email format';
}
if (!empty($password) && ($password !== $password_confirm)) {
$errors['password_confirm'] = 'Passwords do not match';
}
if (empty($errors)) {
try {
// Check if email already exists for another user
$stmt = $pdo->prepare("SELECT id FROM users WHERE email = ? AND id != ?");
$stmt->execute([$email, $id]);
if ($stmt->fetch()) {
$errors['email'] = 'Email already exists for another user';
} else {
if (!empty($password)) {
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$sql = "UPDATE users SET name = ?, email = ?, password = ? WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$name, $email, $hashed_password, $id]);
} else {
$sql = "UPDATE users SET name = ?, email = ? WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$name, $email, $id]);
}
header("Location: users.php");
exit;
}
} catch (PDOException $e) {
$errors['db'] = "Database error: " . $e->getMessage();
}
}
}
?>
<div class="container-fluid">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Edit User</h1>
</div>
<?php if (!empty($errors['db'])): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($errors['db']); ?></div>
<?php endif; ?>
<form method="POST" action="edit-user.php?id=<?php echo htmlspecialchars($id); ?>">
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control <?php echo !empty($errors['name']) ? 'is-invalid' : ''; ?>" id="name" name="name" value="<?php echo htmlspecialchars($name); ?>">
<?php if (!empty($errors['name'])): ?>
<div class="invalid-feedback"><?php echo $errors['name']; ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email address</label>
<input type="email" class="form-control <?php echo !empty($errors['email']) ? 'is-invalid' : ''; ?>" id="email" name="email" value="<?php echo htmlspecialchars($email); ?>">
<?php if (!empty($errors['email'])): ?>
<div class="invalid-feedback"><?php echo $errors['email']; ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<label for="password" class="form-label">New Password (leave blank to keep current password)</label>
<input type="password" class="form-control <?php echo !empty($errors['password']) ? 'is-invalid' : ''; ?>" id="password" name="password">
<?php if (!empty($errors['password'])): ?>
<div class="invalid-feedback"><?php echo $errors['password']; ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<label for="password_confirm" class="form-label">Confirm New Password</label>
<input type="password" class="form-control <?php echo !empty($errors['password_confirm']) ? 'is-invalid' : ''; ?>" id="password_confirm" name="password_confirm">
<?php if (!empty($errors['password_confirm'])): ?>
<div class="invalid-feedback"><?php echo $errors['password_confirm']; ?></div>
<?php endif; ?>
</div>
<button type="submit" class="btn btn-primary">Update User</button>
<a href="users.php" class="btn btn-secondary">Cancel</a>
</form>
</div>
<?php include 'partials/footer.php'; ?>

6
admin/logout.php Normal file
View File

@ -0,0 +1,6 @@
<?php
session_start();
session_unset();
session_destroy();
header("Location: ../login.php");
exit;

View File

@ -14,19 +14,33 @@
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-nav">
<div class="nav-item text-nowrap">
<a class="nav-link px-3" href="logout.php">Sign out</a>
</div>
</div>
</header>
<div class="container-fluid">
<div class="row">
<nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
<div class="position-sticky pt-3">
<?php
$currentPage = basename($_SERVER['PHP_SELF']);
?>
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="users.php">
<a class="nav-link <?php echo ($currentPage == 'users.php' || $currentPage == 'add-user.php' || $currentPage == 'edit-user.php') ? 'active' : ''; ?>" href="users.php">
<span data-feather="users"></span>
Users
</a>
</li>
<li class="nav-item">
<a class="nav-link <?php echo ($currentPage == 'tasks.php' || $currentPage == 'add-task.php' || $currentPage == 'edit-task.php') ? 'active' : ''; ?>" href="tasks.php">
<span data-feather="file-text"></span>
Tasks
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="../index.php">
<span data-feather="arrow-left"></span>

105
admin/tasks.php Normal file
View File

@ -0,0 +1,105 @@
<?php
require_once 'auth.php';
$pageTitle = "Task Management";
include 'partials/header.php';
require_once __DIR__ . '/../db/config.php';
$pdo = db();
// Search and filter logic
$search = $_GET['search'] ?? '';
$status = $_GET['status'] ?? '';
$sql = 'SELECT id, title, icon, description, status FROM tasks';
$params = [];
$whereClauses = [];
if ($search) {
$whereClauses[] = '(title LIKE ? OR description LIKE ?)';
$params[] = '%' . $search . '%';
$params[] = '%' . $search . '%';
}
if ($status) {
$whereClauses[] = 'status = ?';
$params[] = $status;
}
if (!empty($whereClauses)) {
$sql .= ' WHERE ' . implode(' AND ', $whereClauses);
}
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$tasks = $stmt->fetchAll();
?>
<div class="container-fluid">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Task Management</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<a href="add-task.php" class="btn btn-sm btn-outline-secondary">
<span data-feather="plus-circle"></span>
Add Task
</a>
</div>
</div>
<!-- Search and filter form -->
<form method="GET" action="tasks.php" class="mb-3">
<div class="row g-2">
<div class="col-md-6">
<input type="text" name="search" class="form-control" placeholder="Search by title or description..." value="<?php echo htmlspecialchars($search); ?>">
</div>
<div class="col-md-4">
<select name="status" class="form-select">
<option value="">All Statuses</option>
<option value="pending" <?php echo ($status == 'pending') ? 'selected' : ''; ?>>Pending</option>
<option value="in_progress" <?php echo ($status == 'in_progress') ? 'selected' : ''; ?>>In Progress</option>
<option value="completed" <?php echo ($status == 'completed') ? 'selected' : ''; ?>>Completed</option>
</select>
</div>
<div class="col-md-2">
<button class="btn btn-primary w-100" type="submit">Filter</button>
</div>
</div>
</form>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>Icon</th>
<th>Description</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php if (empty($tasks)): ?>
<tr>
<td colspan="6" class="text-center">No tasks found.</td>
</tr>
<?php else: ?>
<?php foreach ($tasks as $task): ?>
<tr>
<td><?php echo htmlspecialchars($task['id']); ?></td>
<td><?php echo htmlspecialchars($task['title']); ?></td>
<td><i data-feather="<?php echo htmlspecialchars($task['icon']); ?>"></i></td>
<td><?php echo htmlspecialchars($task['description']); ?></td>
<td><?php echo htmlspecialchars($task['status']); ?></td>
<td>
<a href="edit-task.php?id=<?php echo $task['id']; ?>" class="btn btn-sm btn-outline-secondary">Edit</a>
<a href="delete-task.php?id=<?php echo $task['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this task?');">Delete</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<?php include 'partials/footer.php'; ?>

View File

@ -1,10 +1,19 @@
<?php
require_once 'auth.php';
$pageTitle = "User Management";
include 'partials/header.php';
require_once __DIR__ . '/../db/config.php';
$pdo = db();
// Search logic
$search = $_GET['search'] ?? '';
if ($search) {
$stmt = $pdo->prepare('SELECT id, name, email, role FROM users WHERE name LIKE ? OR email LIKE ?');
$stmt->execute(['%' . $search . '%', '%' . $search . '%']);
} else {
$stmt = $pdo->query('SELECT id, name, email, role FROM users');
}
$users = $stmt->fetchAll();
?>
@ -12,13 +21,21 @@ $users = $stmt->fetchAll();
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">User Management</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<button type="button" class="btn btn-sm btn-outline-secondary">
<a href="add-user.php" class="btn btn-sm btn-outline-secondary">
<span data-feather="plus-circle"></span>
Add User
</button>
</a>
</div>
</div>
<!-- Search form -->
<form method="GET" action="users.php" class="mb-3">
<div class="input-group">
<input type="text" name="search" class="form-control" placeholder="Search by name or email..." value="<?php echo htmlspecialchars($search); ?>">
<button class="btn btn-outline-secondary" type="submit">Search</button>
</div>
</form>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
@ -31,6 +48,11 @@ $users = $stmt->fetchAll();
</tr>
</thead>
<tbody>
<?php if (empty($users)): ?>
<tr>
<td colspan="5" class="text-center">No users found.</td>
</tr>
<?php else: ?>
<?php foreach ($users as $user): ?>
<tr>
<td><?php echo htmlspecialchars($user['id']); ?></td>
@ -38,11 +60,12 @@ $users = $stmt->fetchAll();
<td><?php echo htmlspecialchars($user['email']); ?></td>
<td><?php echo htmlspecialchars($user['role']); ?></td>
<td>
<button type="button" class="btn btn-sm btn-outline-secondary">Edit</button>
<button type="button" class="btn btn-sm btn-outline-danger">Delete</button>
<a href="edit-user.php?id=<?php echo $user['id']; ?>" class="btn btn-sm btn-outline-secondary">Edit</a>
<a href="delete-user.php?id=<?php echo $user['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this user?');">Delete</a>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>

View File

@ -1,52 +1,84 @@
body {
background-color: #f8f9fa;
font-family: 'Verdana', 'Arial', sans-serif;
background-color: #F9FAFB;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
color: #374151;
}
.hero {
background: linear-gradient(135deg, #f8f9fa, #e9ecef);
background: linear-gradient(135deg, #FFFFFF, #F9FAFB);
padding: 4rem 2rem;
border-bottom: 1px solid #dee2e6;
border-bottom: 1px solid #E5E7EB;
text-align: center;
}
.hero h1 {
font-size: 3rem;
font-weight: bold;
font-weight: 700;
color: #111827;
}
.hero p {
font-size: 1.25rem;
color: #6c757d;
color: #6B7280;
}
.action-card {
border: none;
border-radius: 0.5rem;
box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
border: 1px solid #E5E7EB;
border-radius: 0.75rem;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.05), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
cursor: pointer;
padding: 2rem;
text-align: center;
background-color: #ffffff;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.action-card:hover {
transform: translateY(-5px);
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.action-card .icon {
font-size: 4rem;
color: #007bff;
width: 3rem;
height: 3rem;
stroke-width: 1.5;
color: #3B82F6;
margin: 1.5rem auto;
}
.action-card .card-body {
padding: 1.5rem;
text-align: center;
}
.action-card .card-title {
font-size: 1.5rem;
font-weight: bold;
margin-top: 1rem;
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: #111827;
}
.action-card .card-text {
color: #6B7280;
margin-bottom: 1.5rem;
}
.btn-primary {
background-color: #3B82F6;
border-color: #3B82F6;
font-weight: 600;
padding: 0.75rem 1.5rem;
border-radius: 0.5rem;
transition: background-color 0.2s;
}
.btn-primary:hover {
background-color: #2563EB;
border-color: #2563EB;
}
.toast-container {
position: fixed;
top: 20px;

View File

@ -1,18 +1,41 @@
<?php
require_once __DIR__ . '/config.php';
try {
$pdo = db();
$sql_file = __DIR__ . '/migrations/001_create_users_table.sql';
if (!file_exists($sql_file)) {
die("Migration file not found: " . $sql_file);
// 1. Create migrations table if it doesn't exist
$pdo->exec("CREATE TABLE IF NOT EXISTS migrations (
id INT AUTO_INCREMENT PRIMARY KEY,
migration VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=INNODB;");
// 2. Get all migrations that have been run
$stmt = $pdo->query("SELECT migration FROM migrations");
$run_migrations = $stmt->fetchAll(PDO::FETCH_COLUMN);
// 3. Get all migration files
$migration_files = glob(__DIR__ . '/migrations/*.sql');
sort($migration_files);
// 4. Run new migrations
foreach ($migration_files as $file) {
$migration_name = basename($file);
if (!in_array($migration_name, $run_migrations)) {
$sql = file_get_contents($file);
$pdo->exec($sql);
// Record the migration
$stmt = $pdo->prepare("INSERT INTO migrations (migration) VALUES (?)");
$stmt->execute([$migration_name]);
echo "Migration successful: $migration_name\n";
}
}
try {
$sql = file_get_contents($sql_file);
$pdo->exec($sql);
echo "Migration successful!\n";
echo "All migrations are up to date.\n";
} catch (PDOException $e) {
die("Migration failed: " . $e->getMessage() . "\n");
}

View File

@ -0,0 +1,14 @@
CREATE TABLE IF NOT EXISTS tasks (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
icon VARCHAR(100) NOT NULL,
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=INNODB;
-- Insert some default tasks
INSERT INTO tasks (title, icon, description) VALUES
('Email', 'envelope', 'Get help with sending or reading emails.'),
('Internet', 'search', 'Get help with browsing the web.'),
('Video Call', 'video', 'Get help with making video calls to family and friends.'),
('Support', 'help-circle', 'Request technical support.');

View File

@ -0,0 +1 @@
ALTER TABLE users ADD COLUMN password VARCHAR(255) NOT NULL;

View File

@ -0,0 +1 @@
ALTER TABLE tasks ADD COLUMN status VARCHAR(255) NOT NULL DEFAULT 'pending';

103
index.php
View File

@ -1,88 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OldHelp - Your AI Companion</title>
<?php
$pageTitle = "Request Help";
include 'partials/header.php';
require_once __DIR__ . '/db/config.php';
<!-- SEO Meta Tags -->
<meta name="description" content="<?php echo htmlspecialchars($_SERVER['PROJECT_DESCRIPTION'] ?? 'An AI companion for elderly users.'); ?>">
<meta name="author" content="OldHelp">
<meta name="keywords" content="AI assistant, elderly support, computer help">
$pdo = db();
$stmt = $pdo->query('SELECT id, title, description, icon FROM tasks WHERE status = \'pending\' ORDER BY id DESC');
$tasks = $stmt->fetchAll();
?>
<!-- Open Graph / Twitter Meta Tags (Platform Managed) -->
<meta property="og:title" content="OldHelp - Your AI Companion">
<meta property="og:description" content="<?php echo htmlspecialchars($_SERVER['PROJECT_DESCRIPTION'] ?? 'An AI companion for elderly users.'); ?>">
<meta property="og:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? ''); ?>">
<meta name="twitter:card" content="summary_large_image">
<!-- Bootstrap 5.3 CDN -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/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">
<!-- Custom CSS -->
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body>
<header class="hero text-center">
<div class="container">
<h1 class="display-4">How can I help you today?</h1>
<p class="lead">Click one of the buttons below to get started.</p>
<div class="hero text-center">
<h1 class="display-4">How can we help you today?</h1>
<p class="lead">Select a task from the options below to get started.</p>
</div>
</header>
<main class="container my-5">
<div class="container mt-5">
<div class="row g-4">
<div class="col-md-6 col-lg-4">
<div class="action-card" data-task="Email">
<i class="bi bi-envelope-fill icon"></i>
<h3 class="card-title">Email</h3>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="action-card" data-task="Internet">
<i class="bi bi-globe icon"></i>
<h3 class="card-title">Internet</h3>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="action-card" data-task="Video Call">
<i class="bi bi-camera-video-fill icon"></i>
<h3 class="card-title">Video Call</h3>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="action-card" data-task="Files">
<i class="bi bi-folder-fill icon"></i>
<h3 class="card-title">My Files</h3>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="action-card" data-task="Computer Health">
<i class="bi bi-heart-pulse-fill icon"></i>
<h3 class="card-title">Computer Health</h3>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="action-card" data-task="Live Support">
<i class="bi bi-question-circle-fill icon"></i>
<h3 class="card-title">Live Support</h3>
<?php foreach ($tasks as $task): ?>
<div class="col-lg-4 col-md-6">
<div class="action-card h-100">
<i data-feather="<?php echo htmlspecialchars($task['icon']); ?>" class="icon"></i>
<div class="card-body">
<h5 class="card-title"><?php echo htmlspecialchars($task['title']); ?></h5>
<p class="card-text"><?php echo htmlspecialchars($task['description']); ?></p>
<a href="#" class="btn btn-primary">Request Help</a>
</div>
</div>
</div>
</main>
<?php endforeach; ?>
</div>
</div>
<footer class="mt-5 text-center text-muted">
<p>&copy; <?php echo date("Y"); ?> OldHelp. All rights reserved. | <a href="admin/">Admin</a></p>
</footer>
<!-- Toast Container for Notifications -->
<div class="toast-container"></div>
<!-- Bootstrap 5.3 JS Bundle -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<!-- Custom JS -->
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>
<?php include 'partials/footer.php'; ?>

88
login.php Normal file
View File

@ -0,0 +1,88 @@
<?php
session_start();
require_once __DIR__ . '/db/config.php';
$email = $password = '';
$errors = [];
// If user is already logged in, redirect to admin dashboard
if (isset($_SESSION['user_id'])) {
header("Location: admin/index.php");
exit;
}
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$email = trim($_POST['email']);
$password = $_POST['password'];
if (empty($email)) {
$errors['email'] = 'Email is required';
}
if (empty($password)) {
$errors['password'] = 'Password is required';
}
if (empty($errors)) {
try {
$pdo = db();
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_name'] = $user['name'];
$_SESSION['user_role'] = $user['role'];
header("Location: admin/index.php");
exit;
} else {
$errors['login'] = 'Invalid email or password';
}
} catch (PDOException $e) {
$errors['db'] = "Database error: " . $e->getMessage();
}
}
}
$pageTitle = "Login";
include 'partials/header.php';
?>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-6">
<div class="card mt-5">
<div class="card-body">
<h3 class="card-title text-center">Admin Login</h3>
<?php if (!empty($errors['login'])): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($errors['login']); ?></div>
<?php endif; ?>
<?php if (!empty($errors['db'])): ?>
<div class="alert alert-danger"><?php echo htmlspecialchars($errors['db']); ?></div>
<?php endif; ?>
<form method="POST" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<div class="mb-3">
<label for="email" class="form-label">Email address</label>
<input type="email" class="form-control <?php echo !empty($errors['email']) ? 'is-invalid' : ''; ?>" id="email" name="email" value="<?php echo htmlspecialchars($email); ?>">
<?php if (!empty($errors['email'])): ?>
<div class="invalid-feedback"><?php echo $errors['email']; ?></div>
<?php endif; ?>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control <?php echo !empty($errors['password']) ? 'is-invalid' : ''; ?>" id="password" name="password">
<?php if (!empty($errors['password'])): ?>
<div class="invalid-feedback"><?php echo $errors['password']; ?></div>
<?php endif; ?>
</div>
<button type="submit" class="btn btn-primary w-100">Login</button>
</form>
</div>
</div>
</div>
</div>
</div>
<?php include 'partials/footer.php'; ?>

14
partials/footer.php Normal file
View File

@ -0,0 +1,14 @@
<div class="container">
<footer class="py-3 my-4">
<p class="text-center text-muted">&copy; <?php echo date('Y'); ?> OldHelp</p>
</footer>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script>
feather.replace()
</script>
<script src="assets/js/main.js"></script>
</body>
</html>

26
partials/header.php Normal file
View File

@ -0,0 +1,26 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?php echo isset($pageTitle) ? htmlspecialchars($pageTitle) : "OldHelp"; ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="assets/css/custom.css" rel="stylesheet">
</head>
<body>
<header class="navbar navbar-expand-lg navbar-light bg-white py-3">
<div class="container">
<a class="navbar-brand" href="/"><strong>OldHelp</strong></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/">Home</a>
</li>
</ul>
</div>
</div>
</header>