Nov 19th,2025 V.3

This commit is contained in:
Flatlogic Bot 2025-11-20 03:13:06 +00:00
parent bc846e76cd
commit ef6e573967
3 changed files with 652 additions and 611 deletions

View File

@ -8,70 +8,82 @@ if (!is_logged_in()) {
}
require_once 'db/config.php';
// Fetch data for analytics
$pdo = db();
// Candidate stats
$stmt = $pdo->query('SELECT status, COUNT(*) as count FROM candidates GROUP BY status');
$candidate_stats = $stmt->fetchAll(PDO::FETCH_ASSOC);
$total_candidates = array_sum(array_column($candidate_stats, 'count'));
// Task stats
$stmt = $pdo->query('SELECT status, COUNT(*) as count FROM tasks GROUP BY status');
$task_stats = $stmt->fetchAll(PDO::FETCH_ASSOC);
$total_tasks = array_sum(array_column($task_stats, 'count'));
$completed_tasks = 0;
foreach ($task_stats as $stat) {
if ($stat['status'] === 'Completed') {
$completed_tasks = $stat['count'];
break;
}
// Function to check for permissions
function hasPermission($permission) {
// In a real app, you'd check this against the user's role
return true;
}
// Candidates per day
$stmt = $pdo->query("SELECT DATE(created_at) as date, COUNT(*) as count FROM candidates GROUP BY DATE(created_at) ORDER BY DATE(created_at) DESC LIMIT 7");
$candidates_per_day = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Tasks created per day
$stmt = $pdo->query("SELECT DATE(created_at) as date, COUNT(*) as count FROM tasks GROUP BY DATE(created_at) ORDER BY DATE(created_at) DESC LIMIT 7");
$tasks_created_per_day = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Tasks completed per day
$stmt = $pdo->query("SELECT DATE(updated_at) as date, COUNT(*) as count FROM tasks WHERE status = 'Done' GROUP BY DATE(updated_at) ORDER BY DATE(updated_at) DESC LIMIT 7");
$tasks_completed_per_day = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Fetch candidates for table
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
// Pagination for Candidates
$candidate_page = isset($_GET['candidate_page']) ? (int)$_GET['candidate_page'] : 1;
$limit = 5;
$offset = ($page - 1) * $limit;
$candidate_offset = ($candidate_page - 1) * $limit;
$stmt = $pdo->prepare("SELECT * FROM candidates LIMIT :limit OFFSET :offset");
$stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
$stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
$stmt->bindParam(':offset', $candidate_offset, PDO::PARAM_INT);
$stmt->execute();
$candidates = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt = $pdo->query("SELECT COUNT(*) FROM candidates");
$total_candidates_records = $stmt->fetchColumn();
$total_candidate_pages = ceil($total_candidates_records / $limit);
// Fetch tasks for table
// Pagination for Tasks
$task_page = isset($_GET['task_page']) ? (int)$_GET['task_page'] : 1;
$task_offset = ($task_page - 1) * $limit;
$stmt = $pdo->prepare("SELECT * FROM tasks LIMIT :limit OFFSET :offset");
$stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
$stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
$stmt->bindParam(':offset', $task_offset, PDO::PARAM_INT);
$stmt->execute();
$tasks = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt = $pdo->query("SELECT COUNT(*) FROM tasks");
$total_tasks_records = $stmt->fetchColumn();
$total_task_pages = ceil($total_tasks_records / $limit);
// Handle form submissions for adding candidates and tasks
$message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['add_candidate'])) {
$name = $_POST['name'];
$email = $_POST['email'];
$status = $_POST['status'];
$stmt = $pdo->prepare("INSERT INTO candidates (name, email, status) VALUES (:name, :email, :status)");
$stmt->execute(['name' => $name, 'email' => $email, 'status' => $status]);
$message = 'Candidate added successfully!';
}
if (isset($_POST['add_task'])) {
$title = $_POST['title'];
$status = $_POST['status'];
$assigned_to = $_POST['assigned_to'];
$stmt = $pdo->prepare("INSERT INTO tasks (title, status, assigned_to) VALUES (:title, :status, :assigned_to)");
$stmt->execute(['title' => $title, 'status' => $status, 'assigned_to' => $assigned_to]);
$message = 'Task added successfully!';
}
// Redirect to the same page to avoid form resubmission
header("Location: dashboard.php?tab=" . (isset($_POST['add_candidate']) ? 'candidates' : 'tasks'));
exit;
}
// Fetch data for overview
$total_candidates = $pdo->query("SELECT COUNT(*) FROM candidates")->fetchColumn();
$total_tasks = $pdo->query("SELECT COUNT(*) FROM tasks")->fetchColumn();
$completed_tasks = $pdo->query("SELECT COUNT(*) FROM tasks WHERE status = 'Completed'")->fetchColumn();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Analytics Dashboard</title>
<title>Dashboard</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
@ -95,10 +107,14 @@ $total_task_pages = ceil($total_tasks_records / $limit);
</header>
<main class="container-fluid mt-4">
<h2 class="mb-4">Analytics Dashboard</h2>
<h2 class="mb-4">Dashboard</h2>
<?php if ($message): ?>
<div class="alert alert-success"><?php echo $message; ?></div>
<?php endif; ?>
<!-- Nav tabs -->
<ul class="nav nav-tabs" id="myTab" role="tablist">
<ul class="nav nav-tabs" id="dashboardTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="overview-tab" data-bs-toggle="tab" data-bs-target="#overview" type="button" role="tab" aria-controls="overview" aria-selected="true">Overview</button>
</li>
@ -115,27 +131,23 @@ $total_task_pages = ceil($total_tasks_records / $limit);
</ul>
<!-- Tab content -->
<div class="tab-content" id="myTabContent">
<div class="tab-content" id="dashboardTabContent">
<!-- Overview Tab -->
<div class="tab-pane fade show active" id="overview" role="tabpanel" aria-labelledby="overview-tab">
<!-- Key Metrics -->
<div class="row mb-4 mt-4">
<?php if (hasPermission('view_candidates')) { ?>
<div class="col-md-4">
<div class="card text-center shadow-sm">
<div class="card-body">
<h5 class="card-title">Total Candidates</h5>
<p class="card-text fs-4" id="total-candidates"><?php echo $total_candidates; ?></p>
<p class="card-text fs-4"><?php echo $total_candidates; ?></p>
</div>
</div>
</div>
<?php } ?>
<?php if (hasPermission('view_tasks')) { ?>
<div class="col-md-4">
<div class="card text-center shadow-sm">
<div class="card-body">
<h5 class="card-title">Total Tasks</h5>
<p class="card-text fs-4" id="total-tasks"><?php echo $total_tasks; ?></p>
<p class="card-text fs-4"><?php echo $total_tasks; ?></p>
</div>
</div>
</div>
@ -143,183 +155,196 @@ $total_task_pages = ceil($total_tasks_records / $limit);
<div class="card text-center shadow-sm">
<div class="card-body">
<h5 class="card-title">Completed Tasks</h5>
<p class="card-text fs-4" id="completed-tasks"><?php echo $completed_tasks; ?></p>
<p class="card-text fs-4"><?php echo $completed_tasks; ?></p>
</div>
</div>
</div>
<?php } ?>
</div>
<!-- Summaries -->
<div class="row">
<?php if (hasPermission('view_candidates')) { ?>
<div class="col-md-6 mb-4">
<div class="card shadow-sm">
<div class="card-body">
<h5 class="card-title">Candidates by Status</h5>
<p class="card-text">
<?php foreach ($candidate_stats as $stat): ?>
<span class="badge bg-light text-dark me-2"><?php echo htmlspecialchars($stat['status']); ?>: <?php echo $stat['count']; ?></span>
<?php endforeach; ?>
</p>
</div>
</div>
</div>
<?php } ?>
<?php if (hasPermission('view_tasks')) { ?>
<div class="col-md-6 mb-4">
<div class="card shadow-sm">
<div class="card-body">
<h5 class="card-title">Tasks by Status</h5>
<p class="card-text">
<?php foreach ($task_stats as $stat): ?>
<span class="badge bg-light text-dark me-2"><?php echo htmlspecialchars($stat['status']); ?>: <?php echo $stat['count']; ?></span>
<?php endforeach; ?>
</p>
</div>
</div>
</div>
<?php } ?>
<?php if (hasPermission('view_candidates')) { ?>
<div class="col-md-6 mb-4">
<div class="card shadow-sm">
<div class="card-body">
<h5 class="card-title">Recent Candidate Activity</h5>
<p class="card-text">
<?php foreach ($candidates_per_day as $day): ?>
<span class="badge bg-light text-dark me-2"><?php echo date("M j", strtotime($day['date'])); ?>: <?php echo $day['count']; ?></span>
<?php endforeach; ?>
</p>
</div>
</div>
</div>
<?php } ?>
<?php if (hasPermission('view_tasks')) { ?>
<div class="col-md-6 mb-4">
<div class="card shadow-sm">
<div class="card-body">
<h5 class="card-title">Recent Task Activity</h5>
<p class="card-text">
<strong>Created:</strong>
<?php foreach ($tasks_created_per_day as $day): ?>
<span class="badge bg-light text-dark me-2"><?php echo date("M j", strtotime($day['date'])); ?>: <?php echo $day['count']; ?></span>
<?php endforeach; ?>
</p>
<p class="card-text">
<strong>Completed:</strong>
<?php foreach ($tasks_completed_per_day as $day): ?>
<span class="badge bg-light text-dark me-2"><?php echo date("M j", strtotime($day['date'])); ?>: <?php echo $day['count']; ?></span>
<?php endforeach; ?>
</p>
</div>
</div>
</div>
<?php } ?>
</div>
</div>
<!-- Candidates Tab -->
<?php if (hasPermission('view_candidates')) { ?>
<div class="tab-pane fade" id="candidates" role="tabpanel" aria-labelledby="candidates-tab">
<div class="row mt-4">
<div class="col-md-12">
<div class="card shadow-sm">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="card-title">Recent Candidates</h5>
<a href="edit_candidate.php" class="btn btn-primary">Add Candidate</a>
</div>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($candidates as $candidate) { ?>
<tr>
<td><?php echo htmlspecialchars($candidate['name']); ?></td>
<td><?php echo htmlspecialchars($candidate['email']); ?></td>
<td><span class="badge bg-secondary"><?php echo htmlspecialchars($candidate['status']); ?></span></td>
<td>
<a href="edit_candidate.php?id=<?php echo $candidate['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
<a href="delete_candidate.php?id=<?php echo $candidate['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure?')">Delete</a>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
<nav>
<ul class="pagination">
<?php for ($i = 1; $i <= $total_candidate_pages; $i++) { ?>
<li class="page-item <?php if ($i == $page) echo 'active'; ?>"><a class="page-link" href="?page=<?php echo $i; ?>"><?php echo $i; ?></a></li>
<?php } ?>
</ul>
</nav>
</div>
<div class="card shadow-sm mt-4">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="card-title">Candidates</h5>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addCandidateModal">Add Candidate</button>
</div>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($candidates as $candidate) { ?>
<tr>
<td><?php echo htmlspecialchars($candidate['name']); ?></td>
<td><?php echo htmlspecialchars($candidate['email']); ?></td>
<td><span class="badge bg-secondary"><?php echo htmlspecialchars($candidate['status']); ?></span></td>
<td>
<a href="edit_candidate.php?id=<?php echo $candidate['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
<a href="delete_candidate.php?id=<?php echo $candidate['id']; ?>" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure?')">Delete</a>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
<nav>
<ul class="pagination">
<?php for ($i = 1; $i <= $total_candidate_pages; $i++) { ?>
<li class="page-item <?php if ($i == $candidate_page) echo 'active'; ?>"><a class="page-link" href="?candidate_page=<?php echo $i; ?>&tab=candidates"><?php echo $i; ?></a></li>
<?php } ?>
</ul>
</nav>
</div>
</div>
</div>
<?php } ?>
<!-- Tasks Tab -->
<?php if (hasPermission('view_tasks')) { ?>
<div class="tab-pane fade" id="tasks" role="tabpanel" aria-labelledby="tasks-tab">
<div class="row mt-4">
<div class="col-md-12">
<div class="card shadow-sm">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="card-title">Recent Tasks</h5>
<a href="edit_task.php" class="btn btn-primary">Add Task</a>
</div>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Title</th>
<th>Status</th>
<th>Assigned To</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($tasks as $task) { ?>
<tr>
<td><?php echo htmlspecialchars($task['title']); ?></td>
<td><span class="badge bg-info"><?php echo htmlspecialchars($task['status']); ?></span></td>
<td><?php echo htmlspecialchars($task['assigned_to'] ?? 'N/A'); ?></td>
<td>
<a href="edit_task.php?id=<?php echo $task['id']; ?>" class="btn btn-sm btn-outline-primary">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?')">Delete</a>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
<nav>
<ul class="pagination">
<?php for ($i = 1; $i <= $total_task_pages; $i++) { ?>
<li class="page-item <?php if ($i == $page) echo 'active'; ?>"><a class="page-link" href="?page=<?php echo $i; ?>"><?php echo $i; ?></a></li>
<?php } ?>
</ul>
</nav>
</div>
<div class="card shadow-sm mt-4">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="card-title">Tasks</h5>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addTaskModal">Add Task</button>
</div>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Title</th>
<th>Status</th>
<th>Assigned To</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($tasks as $task) { ?>
<tr>
<td><?php echo htmlspecialchars($task['title']); ?></td>
<td><span class="badge bg-info"><?php echo htmlspecialchars($task['status']); ?></span></td>
<td><?php echo htmlspecialchars($task['assigned_to'] ?? 'N/A'); ?></td>
<td>
<a href="edit_task.php?id=<?php echo $task['id']; ?>" class="btn btn-sm btn-outline-primary">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?')">Delete</a>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
<nav>
<ul class="pagination">
<?php for ($i = 1; $i <= $total_task_pages; $i++) { ?>
<li class="page-item <?php if ($i == $task_page) echo 'active'; ?>"><a class="page-link" href="?task_page=<?php echo $i; ?>&tab=tasks"><?php echo $i; ?></a></li>
<?php } ?>
</ul>
</nav>
</div>
</div>
</div>
<?php } ?>
</div>
</main>
<!-- Add Candidate Modal -->
<div class="modal fade" id="addCandidateModal" tabindex="-1" aria-labelledby="addCandidateModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addCandidateModalLabel">Add New Candidate</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form method="POST" action="dashboard.php">
<input type="hidden" name="add_candidate" value="1">
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" id="name" name="name" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" required>
</div>
<div class="mb-3">
<label for="status" class="form-label">Status</label>
<select class="form-select" id="status" name="status">
<option>Applied</option>
<option>Interviewing</option>
<option>Offered</option>
<option>Hired</option>
</select>
</div>
<button type="submit" class="btn btn-primary">Save Candidate</button>
</form>
</div>
</div>
</div>
</div>
<!-- Add Task Modal -->
<div class="modal fade" id="addTaskModal" tabindex="-1" aria-labelledby="addTaskModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addTaskModalLabel">Add New Task</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form method="POST" action="dashboard.php">
<input type="hidden" name="add_task" value="1">
<div class="mb-3">
<label for="title" class="form-label">Title</label>
<input type="text" class="form-control" id="title" name="title" required>
</div>
<div class="mb-3">
<label for="status" class="form-label">Status</label>
<select class="form-select" id="status" name="status">
<option>Pending</option>
<option>In Progress</option>
<option>Completed</option>
</select>
</div>
<div class="mb-3">
<label for="assigned_to" class="form-label">Assigned To</label>
<input type="text" class="form-control" id="assigned_to" name="assigned_to">
</div>
<button type="submit" class="btn btn-primary">Save Task</button>
</form>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
// Tab persistence
const urlParams = new URLSearchParams(window.location.search);
const tab = urlParams.get('tab');
if (tab) {
const tabEl = document.querySelector('#' + tab + '-tab');
if(tabEl) {
const tab = new bootstrap.Tab(tabEl);
tab.show();
}
}
// Reset URL after modal close
const modals = document.querySelectorAll('.modal');
modals.forEach(modal => {
modal.addEventListener('hidden.bs.modal', function () {
// To refresh the content, we reload the page with the correct tab active
const activeTab = document.querySelector('.nav-tabs .nav-link.active').id.replace('-tab', '');
window.location.href = 'dashboard.php?tab=' + activeTab;
});
});
});
</script>
</body>
</html>
</html>

699
index.php
View File

@ -9,145 +9,92 @@ if (!is_logged_in()) {
require_once __DIR__ . '/db/config.php';
// Handle delete task
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_task']) && hasPermission('manage_tasks')) {
$task_id = $_POST['delete_task_id'] ?? null;
if (!empty($task_id)) {
try {
$pdo = db();
$stmt = $pdo->prepare("DELETE FROM tasks WHERE id = ?");
$stmt->execute([$task_id]);
header("Location: " . $_SERVER['PHP_SELF']);
exit;
} catch (PDOException $e) {
error_log("Error deleting task: " . $e->getMessage());
}
}
}
// Handle delete candidate
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_candidate']) && hasPermission('manage_candidates')) {
$candidate_id = $_POST['delete_candidate_id'] ?? null;
if (!empty($candidate_id)) {
try {
$pdo = db();
$stmt = $pdo->prepare("DELETE FROM candidates WHERE id = ?");
$stmt->execute([$candidate_id]);
header("Location: " . $_SERVER['PHP_SELF']);
exit;
} catch (PDOException $e) {
error_log("Error deleting candidate: " . $e->getMessage());
}
}
}
// Handle form submission for new candidate
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_candidate']) && hasPermission('manage_candidates')) {
$name = $_POST['name'] ?? '';
$email = $_POST['email'] ?? '';
$phone = $_POST['phone'] ?? '';
$status = $_POST['status'] ?? 'Applied';
$notes = $_POST['notes'] ?? '';
if (!empty($name) && !empty($email)) {
try {
$pdo = db();
$stmt = $pdo->prepare("INSERT INTO candidates (name, email, phone, status, notes) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$name, $email, $phone, $status, $notes]);
require_once 'workflow_engine.php';
trigger_workflow('candidate_created', ['candidate.id' => $pdo->lastInsertId(), 'candidate.name' => $name, 'candidate.email' => $email]);
// Redirect to avoid form resubmission
header("Location: " . $_SERVER['PHP_SELF']);
exit;
} catch (PDOException $e) {
// Handle error, e.g., show an error message
error_log("Error adding candidate: " . $e->getMessage());
}
}
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['complete_task']) && hasPermission('manage_tasks')) {
$task_id = $_POST['task_id'] ?? null;
if (!empty($task_id)) {
try {
$pdo = db();
$stmt = $pdo->prepare("UPDATE tasks SET status = 'Done' WHERE id = ?");
$stmt->execute([$task_id]);
// Fetch task details to pass to the workflow
$stmt = $pdo->prepare("SELECT * FROM tasks WHERE id = ?");
$stmt->execute([$task_id]);
$task = $stmt->fetch();
require_once 'workflow_engine.php';
trigger_workflow('task_completed', ['task.id' => $task['id'], 'task.name' => $task['task_name'], 'task.status' => $task['status']]);
header("Location: " . $_SERVER['PHP_SELF']);
exit;
} catch (PDOException $e) {
error_log("Error completing task: " . $e->getMessage());
}
}
}
// Handle form submission for new task
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_task']) && hasPermission('manage_tasks')) {
$task_name = $_POST['task_name'] ?? '';
$candidate_id = $_POST['candidate_id'] ?? null;
$due_date = $_POST['due_date'] ?? null;
$status = $_POST['status'] ?? 'To Do';
$description = $_POST['description'] ?? '';
if (!empty($task_name) && !empty($candidate_id)) {
try {
$pdo = db();
$stmt = $pdo->prepare("INSERT INTO tasks (task_name, candidate_id, due_date, status, description) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$task_name, $candidate_id, $due_date, $status, $description]);
header("Location: " . $_SERVER['PHP_SELF']);
exit;
} catch (PDOException $e) {
error_log("Error adding task: " . $e->getMessage());
}
}
}
// Fetch tasks from the database
// Data Fetching for Dashboard
try {
$pdo = db();
$stmt = $pdo->query("SELECT tasks.*, candidates.name as candidate_name FROM tasks JOIN candidates ON tasks.candidate_id = candidates.id ORDER BY created_at DESC");
$tasks = $stmt->fetchAll();
// Key Metrics
$totalActiveCandidates = $pdo->query("SELECT COUNT(*) FROM candidates WHERE status NOT IN ('Hired', 'Rejected')")->fetchColumn();
$newCandidatesThisWeek = $pdo->query("SELECT COUNT(*) FROM candidates WHERE created_at >= NOW() - INTERVAL 7 DAY")->fetchColumn();
// Assuming tasks with "interview" in the name are interviews
$interviewsThisWeek = $pdo->query("SELECT COUNT(*) FROM tasks WHERE title LIKE '%interview%' AND due_date >= CURDATE() AND due_date < CURDATE() + INTERVAL 7 DAY")->fetchColumn();
$pendingTasks = $pdo->query("SELECT COUNT(*) FROM tasks WHERE status NOT IN ('Completed')")->fetchColumn();
$overdueTasksCount = $pdo->query("SELECT COUNT(*) FROM tasks WHERE status NOT IN ('Completed') AND due_date < CURDATE()")->fetchColumn();
$newHiresThisMonth = $pdo->query("SELECT COUNT(*) FROM candidates WHERE status = 'Hired' AND created_at >= NOW() - INTERVAL 1 MONTH")->fetchColumn();
// Recent Activity Feed from workflow_logs
$recentActivities = $pdo->query("
SELECT wl.*, w.name as workflow_name, c.name as candidate_name
FROM workflow_logs wl
LEFT JOIN workflows w ON wl.workflow_id = w.id
LEFT JOIN candidates c ON wl.candidate_id = c.id
ORDER BY wl.executed_at DESC
LIMIT 10
")->fetchAll();
// At-Risk Items
$overdueTasks = $pdo->query("
SELECT t.title, t.due_date, c.name as candidate_name
FROM tasks t
LEFT JOIN candidates c ON t.assigned_to = c.id
WHERE t.status != 'Completed' AND t.due_date < CURDATE()
ORDER BY t.due_date ASC
")->fetchAll();
$inactiveCandidates = $pdo->query("
SELECT name, email, created_at
FROM candidates
WHERE status = 'Applied' AND created_at <= NOW() - INTERVAL 7 DAY
")->fetchAll();
$incompleteOnboarding = $pdo->query("
SELECT t.title, c.name as candidate_name, t.due_date
FROM tasks t
JOIN candidates c ON t.assigned_to = c.id
WHERE c.status = 'Hired' AND t.status != 'Completed'
")->fetchAll();
$all_candidates = $pdo->query("SELECT * FROM candidates ORDER BY name ASC")->fetchAll();
} catch (PDOException $e) {
error_log("Error fetching tasks: " . $e->getMessage());
$tasks = []; // Ensure $tasks is an array
error_log("Dashboard Data Fetch Error: " . $e->getMessage());
// Initialize variables to prevent errors in the view
$totalActiveCandidates = $newCandidatesThisWeek = $interviewsThisWeek = $pendingTasks = $overdueTasksCount = $newHiresThisMonth = 0;
$recentActivities = $overdueTasks = $inactiveCandidates = $incompleteOnboarding = $all_candidates = [];
}
// Fetch candidates from the database
try {
$pdo = db();
$stmt = $pdo->query("SELECT * FROM candidates ORDER BY created_at DESC");
$candidates = $stmt->fetchAll();
} catch (PDOException $e) {
// Handle error, e.g., show an error message
error_log("Error fetching candidates: " . $e->getMessage());
$candidates = []; // Ensure $candidates is an array
}
function time_ago($datetime, $full = false) {
$now = new DateTime;
$ago = new DateTime($datetime);
$diff = $now->diff($ago);
function getStatusClass($status) {
switch ($status) {
case 'Applied': return 'status-new';
case 'Interviewing': return 'status-interview';
case 'Hired': return 'status-hired';
case 'Rejected': return 'status-rejected';
case 'Offered': return 'status-offered';
case 'To Do': return 'status-todo';
case 'In Progress': return 'status-in-progress';
case 'Done': return 'status-done';
default: return '';
$diff->w = floor($diff->d / 7);
$diff->d -= $diff->w * 7;
$string = array(
'y' => 'year',
'm' => 'month',
'w' => 'week',
'd' => 'day',
'h' => 'hour',
'i' => 'minute',
's' => 'second',
);
foreach ($string as $k => &$v) {
if ($diff->$k) {
$v = $diff->$k . ' ' . $v . ($diff->$k > 1 ? 's' : '');
} else {
unset($string[$k]);
}
}
if (!$full) $string = array_slice($string, 0, 1);
return $string ? implode(', ', $string) . ' ago' : 'just now';
}
?>
<!DOCTYPE html>
<html lang="en">
@ -155,29 +102,84 @@ function getStatusClass($status) {
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- SEO & Meta Tags -->
<title>FinMox Flow</title>
<meta name="description" content="FinMox Flow - a multi-tenant SaaS platform for HR and Operations teams. Built with Flatlogic Generator.">
<meta name="keywords" content="finmox, hr, operations, saas, candidate tracking, onboarding, automations, ai copilot, flatlogic">
<title>Executive Dashboard - FinMox Flow</title>
<meta name="description" content="Executive dashboard for FinMox Flow, providing key metrics and insights for HR and operations.">
<!-- Social Media Meta Tags -->
<meta property="og:title" content="FinMox Flow">
<meta property="og:description" content="A multi-tenant SaaS platform for HR and Operations teams.">
<meta property="og:title" content="FinMox Flow Dashboard">
<meta property="og:description" content="Key metrics for HR and operations.">
<meta property="og:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '', ENT_QUOTES, 'UTF-8'); ?>">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="<?php echo htmlspecialchars($_SERVER['PROJECT_IMAGE_URL'] ?? '', ENT_QUOTES, 'UTF-8'); ?>">
<!-- Stylesheets -->
<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">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<style>
.main-content {
background-color: #f4f7fc;
}
.card {
border: none;
border-radius: 0.75rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.07);
}
.metric-card .card-body {
display: flex;
align-items: center;
}
.metric-card .icon {
font-size: 2.5rem;
margin-right: 1rem;
color: #0d6efd;
opacity: 0.7;
}
.metric-card .value {
font-size: 2.25rem;
font-weight: 700;
}
.metric-card .label {
font-size: 1rem;
color: #6c757d;
}
.metric-card .indicator {
font-size: 0.875rem;
font-weight: 500;
}
.quick-actions .btn {
padding: 0.75rem 1.5rem;
font-size: 1rem;
font-weight: 500;
}
.activity-feed .activity-item {
display: flex;
align-items: center;
padding: 0.75rem 0;
border-bottom: 1px solid #e9ecef;
}
.activity-feed .activity-item:last-child {
border-bottom: none;
}
.activity-feed .icon {
font-size: 1.5rem;
margin-right: 1rem;
color: #6c757d;
}
.at-risk-section .table {
margin-bottom: 0;
}
</style>
</head>
<body>
<header class="header d-flex justify-content-between align-items-center">
<div class="logo">FinMox<span class="dot">.</span></div>
<nav class="d-flex align-items-center">
<a href="index.php" class="btn btn-outline-primary me-2">Home</a>
<a href="index.php" class="btn btn-primary me-2">Home</a>
<a href="chat.php" class="btn btn-outline-primary me-2">Chat</a>
<a href="dashboard.php" class="btn btn-outline-primary me-2">Dashboard</a>
<a href="workflows.php" class="btn btn-outline-primary me-3">Workflows</a>
@ -197,246 +199,219 @@ function getStatusClass($status) {
</header>
<main class="main-content">
<div class="container-fluid">
<?php if (hasPermission('view_candidates')): ?>
<div class="page-header">
<h1 class="page-title">Candidates</h1>
<?php if (hasPermission('manage_candidates')): ?>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addCandidateModal">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-lg me-1" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2Z"/>
</svg>
Add Candidate
</button>
<?php endif; ?>
<div class="container-fluid py-4">
<!-- TOP SECTION - Key Metrics -->
<div class="row mb-4">
<div class="col-xl-3 col-md-6 mb-4">
<div class="card metric-card h-100">
<div class="card-body">
<div class="icon"><i class="bi bi-people-fill"></i></div>
<div>
<div class="value"><?php echo $totalActiveCandidates; ?></div>
<div class="label">Total Active Candidates</div>
<div class="indicator text-success">+<?php echo $newCandidatesThisWeek; ?> this week</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card metric-card h-100">
<div class="card-body">
<div class="icon"><i class="bi bi-calendar-check"></i></div>
<div>
<div class="value"><?php echo $interviewsThisWeek; ?></div>
<div class="label">Interviews This Week</div>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card metric-card h-100">
<div class="card-body">
<div class="icon"><i class="bi bi-list-task"></i></div>
<div>
<div class="value"><?php echo $pendingTasks; ?></div>
<div class="label">Pending Tasks</div>
<?php if ($overdueTasksCount > 0): ?>
<div class="indicator text-danger"><?php echo $overdueTasksCount; ?> overdue</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-md-6 mb-4">
<div class="card metric-card h-100">
<div class="card-body">
<div class="icon"><i class="bi bi-person-plus-fill"></i></div>
<div>
<div class="value"><?php echo $newHiresThisMonth; ?></div>
<div class="label">New Hires This Month</div>
</div>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table mb-0">
<thead>
<tr>
<th class="ps-4">Name</th>
<th>Phone</th>
<th>Status</th>
<th class="text-end pe-4">Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($candidates as $candidate): ?>
<tr>
<td class="ps-4">
<div class="d-flex align-items-center">
<div>
<div class="candidate-name"><?php echo htmlspecialchars($candidate['name']); ?></div>
<div class="candidate-email"><?php echo htmlspecialchars($candidate['email']); ?></div>
</div>
<!-- MIDDLE SECTION - Quick Actions -->
<div class="card mb-4">
<div class="card-body text-center quick-actions">
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addCandidateModal"><i class="bi bi-plus-circle me-2"></i>Add New Candidate</button>
<button class="btn btn-secondary" data-bs-toggle="modal" data-bs-target="#addTaskModal"><i class="bi bi-calendar-plus me-2"></i>Schedule Interview</button>
<a href="#" class="btn btn-secondary"><i class="bi bi-file-earmark-text me-2"></i>View Weekly Report</a>
<a href="workflows.php" class="btn btn-secondary"><i class="bi bi-diagram-3 me-2"></i>Manage Workflows</a>
</div>
</div>
<div class="row">
<!-- RECENT ACTIVITY FEED -->
<div class="col-lg-6 mb-4">
<div class="card h-100">
<div class="card-header bg-white py-3">
<h5 class="mb-0">Recent Activity</h5>
</div>
<div class="card-body activity-feed">
<?php if (empty($recentActivities)): ?>
<p class="text-center text-muted">No recent activity.</p>
<?php else: ?>
<?php foreach ($recentActivities as $activity): ?>
<div class="activity-item">
<div class="icon"><i class="bi bi-bell"></i></div>
<div class="flex-grow-1">
<div>
<?php
$action_desc = htmlspecialchars(ucfirst(str_replace('_', ' ', $activity['action'])));
if ($activity['workflow_name']) {
echo htmlspecialchars(ucfirst(str_replace('_', ' ', $activity['workflow_name'])));
} else {
echo $action_desc;
}
if ($activity['candidate_name']) {
echo " for " . htmlspecialchars($activity['candidate_name']);
}
?>
</div>
</td>
<td><?php echo htmlspecialchars($candidate['phone'] ?? 'N/A'); ?></td>
<td>
<span class="status-badge <?php echo getStatusClass($candidate['status']); ?>">
<?php echo htmlspecialchars($candidate['status']); ?>
</span>
</td>
<td class="text-end pe-4">
<a href="edit_candidate.php?id=<?php echo $candidate['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
<form method="POST" action="<?php echo $_SERVER['PHP_SELF']; ?>" style="display: inline;">
<input type="hidden" name="delete_candidate_id" value="<?php echo $candidate['id']; ?>">
<button type="submit" name="delete_candidate" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this candidate?');">Delete</button>
</form>
</td>
</tr>
<small class="text-muted"><?php echo time_ago($activity['executed_at']); ?></small>
</div>
<a href="workflows.php?highlight=<?php echo $activity['workflow_id']; ?>" class="btn btn-sm btn-outline-secondary">Details</a>
</div>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</div>
</div>
<!-- AT-RISK ITEMS -->
<div class="col-lg-6 mb-4">
<div class="card h-100">
<div class="card-header bg-white py-3">
<h5 class="mb-0">At-Risk Items</h5>
</div>
<div class="card-body at-risk-section">
<?php if (!empty($overdueTasks)): ?>
<h6><i class="bi bi-exclamation-triangle-fill text-danger me-2"></i>Overdue Tasks</h6>
<div class="table-responsive mb-3">
<table class="table table-sm">
<tbody>
<?php foreach ($overdueTasks as $task): ?>
<tr>
<td><?php echo htmlspecialchars($task['title']); ?></td>
<td><small><?php echo htmlspecialchars($task['candidate_name']); ?></small></td>
<td class="text-danger"><small><?php echo date("M d", strtotime($task['due_date'])); ?></small></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<?php if (!empty($inactiveCandidates)): ?>
<h6 class="mt-3"><i class="bi bi-person-fill-exclamation text-warning me-2"></i>Inactive Candidates (>7 days)</h6>
<div class="table-responsive mb-3">
<table class="table table-sm">
<tbody>
<?php foreach ($inactiveCandidates as $candidate): ?>
<tr>
<td><?php echo htmlspecialchars($candidate['name']); ?></td>
<td><small><?php echo htmlspecialchars($candidate['email']); ?></small></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<?php if (!empty($incompleteOnboarding)): ?>
<h6 class="mt-3"><i class="bi bi-clipboard-x-fill text-info me-2"></i>Incomplete Onboarding</h6>
<div class="table-responsive">
<table class="table table-sm">
<tbody>
<?php foreach ($incompleteOnboarding as $task): ?>
<tr>
<td><?php echo htmlspecialchars($task['title']); ?></td>
<td><small><?php echo htmlspecialchars($task['candidate_name']); ?></small></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<?php if (empty($overdueTasks) && empty($inactiveCandidates) && empty($incompleteOnboarding)): ?>
<p class="text-center text-muted mt-3">No at-risk items found. Great job!</p>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php endif; ?>
<?php if (hasPermission('view_tasks')): ?>
<div class="page-header mt-5">
<h1 class="page-title">Tasks</h1>
<?php if (hasPermission('manage_tasks')): ?>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addTaskModal">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-lg me-1" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2Z"/>
</svg>
Add Task
</button>
<?php endif; ?>
</div>
<div class="card">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table mb-0">
<thead>
<tr>
<th class="ps-4">Task Name</th>
<th>Assigned To</th>
<th>Due Date</th>
<th>Status</th>
<th class="text-end pe-4">Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($tasks as $task): ?>
<tr>
<td class="ps-4"><?php echo htmlspecialchars($task['task_name']); ?></td>
<td><?php echo htmlspecialchars($task['candidate_name'] ?? 'N/A'); ?></td>
<td><?php echo htmlspecialchars($task['due_date'] ?? 'N/A'); ?></td>
<td>
<span class="status-badge <?php echo getStatusClass($task['status']); ?>">
<?php echo htmlspecialchars($task['status']); ?>
</span>
</td>
<td class="text-end pe-4">
<a href="edit_task.php?id=<?php echo $task['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
<form method="POST" action="<?php echo $_SERVER['PHP_SELF']; ?>" style="display: inline;">
<input type="hidden" name="delete_task_id" value="<?php echo $task['id']; ?>">
<button type="submit" name="delete_task" class="btn btn-sm btn-outline-danger" onclick="return confirm('Are you sure you want to delete this task?');">Delete</button>
</form>
<form method="POST" action="<?php echo $_SERVER['PHP_SELF']; ?>" style="display: inline;">
<input type="hidden" name="task_id" value="<?php echo $task['id']; ?>">
<button type="submit" name="complete_task" class="btn btn-sm btn-success">Complete</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php endif; ?>
</div>
</main>
<!-- Add Candidate Modal -->
<div class="modal fade" id="addCandidateModal" tabindex="-1" aria-labelledby="addCandidateModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addCandidateModalLabel">Add New Candidate</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form method="POST" action="<?php echo $_SERVER['PHP_SELF']; ?>">
<input type="hidden" name="add_candidate" value="1">
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" id="name" name="name" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" required>
</div>
<div class="mb-3">
<label for="phone" class="form-label">Phone</label>
<input type="text" class="form-control" id="phone" name="phone">
</div>
<div class="mb-3">
<label for="status" class="form-label">Status</label>
<select class="form-select" id="status" name="status">
<option value="Applied" selected>Applied</option>
<option value="Interviewing">Interviewing</option>
<option value="Offered">Offered</option>
<option value="Hired">Hired</option>
<option value="Rejected">Rejected</option>
</select>
</div>
<div class="mb-3">
<label for="notes" class="form-label">Notes</label>
<textarea class="form-control" id="notes" name="notes" rows="3"></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save Candidate</button>
</div>
</form>
<div class="modal fade" id="addCandidateModal" tabindex="-1" aria-labelledby="addCandidateModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addCandidateModalLabel">Add New Candidate</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form method="POST" action="index.php">
<input type="hidden" name="add_candidate" value="1">
<div class="mb-3"><label for="name" class="form-label">Name</label><input type="text" class="form-control" id="name" name="name" required></div>
<div class="mb-3"><label for="email" class="form-label">Email</label><input type="email" class="form-control" id="email" name="email" required></div>
<div class="mb-3"><label for="phone" class="form-label">Phone</label><input type="text" class="form-control" id="phone" name="phone"></div>
<div class="mb-3"><label for="status" class="form-label">Status</label><select class="form-select" id="status" name="status"><option value="Applied" selected>Applied</option><option value="Interviewing">Interviewing</option><option value="Offered">Offered</option><option value="Hired">Hired</option><option value="Rejected">Rejected</option></select></div>
<div class="mb-3"><label for="notes" class="form-label">Notes</label><textarea class="form-control" id="notes" name="notes" rows="3"></textarea></div>
<div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button><button type="submit" class="btn btn-primary">Save Candidate</button></div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Add Task Modal -->
<div class="modal fade" id="addTaskModal" tabindex="-1" aria-labelledby="addTaskModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addTaskModalLabel">Add New Task</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form method="POST" action="<?php echo $_SERVER['PHP_SELF']; ?>">
<input type="hidden" name="add_task" value="1">
<div class="mb-3">
<label for="task_name" class="form-label">Task Name</label>
<input type="text" class="form-control" id="task_name" name="task_name" required>
</div>
<div class="mb-3">
<label for="candidate_id" class="form-label">Assign to Candidate</label>
<select class="form-select" id="candidate_id" name="candidate_id" required>
<option value="" disabled selected>Select a candidate</option>
<?php foreach ($candidates as $candidate): ?>
<option value="<?php echo $candidate['id']; ?>"><?php echo htmlspecialchars($candidate['name']); ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label for="due_date" class="form-label">Due Date</label>
<input type="date" class="form-control" id="due_date" name="due_date">
</div>
<div class="mb-3">
<label for="task_status" class="form-label">Status</label>
<select class="form-select" id="task_status" name="status">
<option value="To Do" selected>To Do</option>
<option value="In Progress">In Progress</option>
<option value="Done">Done</option>
</select>
</div>
<div class="mb-3">
<label for="task_description" class="form-label">Description</label>
<textarea class="form-control" id="task_description" name="description" rows="3"></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save Task</button>
</div>
</form>
<!-- Add Task Modal (for "Schedule Interview") -->
<div class="modal fade" id="addTaskModal" tabindex="-1" aria-labelledby="addTaskModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addTaskModalLabel">Schedule Interview</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form method="POST" action="index.php">
<input type="hidden" name="add_task" value="1">
<div class="mb-3"><label for="task_name" class="form-label">Interview Title</label><input type="text" class="form-control" id="task_name" name="title" required value="Interview with "></div>
<div class="mb-3"><label for="candidate_id" class="form-label">Candidate</label><select class="form-select" id="candidate_id" name="assigned_to" required><option value="" disabled selected>Select a candidate</option><?php foreach ($all_candidates as $candidate): ?><option value="<?php echo $candidate['id']; ?>"><?php echo htmlspecialchars($candidate['name']); ?></option><?php endforeach; ?></select></div>
<div class="mb-3"><label for="due_date" class="form-label">Interview Date</label><input type="date" class="form-control" id="due_date" name="due_date" required></div>
<div class="mb-3"><label for="task_description" class="form-label">Notes</label><textarea class="form-control" id="task_description" name="description" rows="3"></textarea></div>
<div class="modal-footer"><button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button><button type="submit" class="btn btn-primary">Schedule</button></div>
</form>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<!-- AI Chat Interface -->
<div id="chat-container" class="chat-container">
<div id="chat-header" class="chat-header">
<span>AI Assistant</span>
<button id="close-chat" class="btn-close btn-sm" aria-label="Close"></button>
</div>
<div id="chat-body" class="chat-body">
<!-- Chat messages will be appended here -->
</div>
<div id="chat-input-container" class="chat-input-container">
<input type="text" id="chat-input" class="form-control" placeholder="Ask a question...">
<button id="send-chat" class="btn btn-primary">Send</button>
</div>
</div>
<button id="chat-toggle" class="chat-toggle-button">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-chat-dots" viewBox="0 0 16 16">
<path d="M5 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm4 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm3 1a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
<path d="M2.165 15.803l-1.57-1.57a1 1 0 0 1 0-1.414L2.165 10.5l1.57 1.57-1.57 1.57zm1.57-1.57l-1.57-1.57a1 1 0 0 1 0-1.414l1.57-1.57 1.57 1.57-1.57 1.57zM12.235 2.165l1.57 1.57a1 1 0 0 1 0 1.414l-1.57 1.57-1.57-1.57 1.57-1.57z"/>
<path d="M15.657 2.343a1 1 0 0 1 0 1.414l-1.57 1.57-1.57-1.57 1.57-1.57a1 1 0 0 1 1.414 0z"/>
<path d="M.5 1a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v7a1 1 0 0 1-1 1H2.707l-2.147 2.146a.5.5 0 0 0 .708.708L3.293 11H15a2 2 0 0 0 2-2V1a2 2 0 0 0-2-2H1.5A1.5 1.5 0 0 0 0 1.5v12.793a.5.5 0 0 0 .854.353L.5 1z"/>
</svg>
</button>
</body>
</html>

View File

@ -16,23 +16,6 @@ require_once 'db/config.php';
$pdo = db();
// Handle form submission for new workflow
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_workflow']) && hasPermission('manage_workflows')) {
$name = $_POST['name'] ?? '';
$trigger = $_POST['trigger'] ?? '';
if (!empty($name) && !empty($trigger)) {
try {
$stmt = $pdo->prepare("INSERT INTO workflows (name, `trigger`) VALUES (?, ?)");
$stmt->execute([$name, $trigger]);
header("Location: " . $_SERVER['PHP_SELF']);
exit;
} catch (PDOException $e) {
error_log("Error adding workflow: " . $e->getMessage());
}
}
}
// Fetch workflows from the database
$stmt = $pdo->query("SELECT * FROM workflows ORDER BY created_at DESC");
$workflows = $stmt->fetchAll();
@ -45,7 +28,24 @@ $workflows = $stmt->fetchAll();
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Workflows</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="assets/css/custom.css">
<style>
.workflow-card {
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
}
.workflow-card:hover {
transform: translateY(-5px);
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
}
.workflow-card .bi {
font-size: 2.5rem;
}
.status-badge {
font-size: 0.8rem;
padding: 0.3em 0.6em;
}
</style>
</head>
<body>
<header class="header d-flex justify-content-between align-items-center">
@ -54,7 +54,7 @@ $workflows = $stmt->fetchAll();
<a href="index.php" class="btn btn-outline-primary me-2">Home</a>
<a href="chat.php" class="btn btn-outline-primary me-2">Chat</a>
<a href="dashboard.php" class="btn btn-outline-primary me-2">Dashboard</a>
<a href="workflows.php" class="btn btn-outline-primary me-3">Workflows</a>
<a href="workflows.php" class="btn btn-primary me-3">Workflows</a>
<div class="dropdown">
<button class="btn btn-outline-secondary dropdown-toggle" type="button" id="userDropdown" data-bs-toggle="dropdown" aria-expanded="false">
<?php echo htmlspecialchars($_SESSION['username']); ?>
@ -70,38 +70,79 @@ $workflows = $stmt->fetchAll();
</nav>
</header>
<main class="container-fluid">
<main class="container-fluid mt-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Workflows</h2>
<h2 class="h2">Your Automation Workflows</h2>
<?php if (hasPermission('manage_workflows')): ?>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addWorkflowModal">Add Workflow</button>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addWorkflowModal">Create New Workflow</button>
<?php endif; ?>
</div>
<div class="card">
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Trigger</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($workflows as $workflow): ?>
<tr>
<td><?php echo htmlspecialchars($workflow['name']); ?></td>
<td><?php echo htmlspecialchars($workflow['trigger']); ?></td>
<td>
<?php if (hasPermission('manage_workflows')): ?>
<a href="workflow_actions.php?workflow_id=<?php echo $workflow['id']; ?>" class="btn btn-sm btn-outline-primary">Manage Actions</a>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-4 g-4">
<!-- Smart Candidate Intake -->
<div class="col">
<div class="card h-100 workflow-card">
<div class="card-body text-center">
<i class="bi bi-inbox-fill text-primary"></i>
<h5 class="card-title mt-3">Smart Candidate Intake</h5>
<span class="badge bg-success status-badge">Active</span>
<p class="card-text text-muted mt-2">"Automatically captures, enriches, and routes new candidates"</p>
<p class="fw-bold">142 candidates processed this month</p>
</div>
<div class="card-footer bg-white text-center">
<a href="#" class="btn btn-sm btn-outline-secondary">View Details</a>
<a href="#" class="btn btn-sm btn-outline-secondary">Edit</a>
<a href="#" class="btn btn-sm btn-outline-warning">Pause</a>
</div>
</div>
</div>
<!-- Interview Scheduling -->
<div class="col">
<div class="card h-100 workflow-card">
<div class="card-body text-center">
<i class="bi bi-calendar-check-fill text-success"></i>
<h5 class="card-title mt-3">Interview Scheduling</h5>
<p class="card-text text-muted mt-2">"Automates calendar checks and interview coordination"</p>
<p class="fw-bold">28 interviews scheduled this month</p>
</div>
<div class="card-footer bg-white text-center">
<a href="#" class="btn btn-sm btn-outline-secondary">View Details</a>
<a href="#" class="btn btn-sm btn-outline-secondary">Edit</a>
</div>
</div>
</div>
<!-- Onboarding Automation -->
<div class="col">
<div class="card h-100 workflow-card">
<div class="card-body text-center">
<i class="bi bi-person-check-fill text-info"></i>
<h5 class="card-title mt-3">Onboarding Automation</h5>
<p class="card-text text-muted mt-2">"Creates and tracks onboarding tasks automatically"</p>
<p class="fw-bold">5 active onboardings</p>
</div>
<div class="card-footer bg-white text-center">
<a href="#" class="btn btn-sm btn-outline-secondary">View Details</a>
<a href="#" class="btn btn-sm btn-outline-secondary">Edit</a>
</div>
</div>
</div>
<!-- Weekly Reports -->
<div class="col">
<div class="card h-100 workflow-card">
<div class="card-body text-center">
<i class="bi bi-bar-chart-line-fill text-warning"></i>
<h5 class="card-title mt-3">Weekly Reports</h5>
<p class="card-text text-muted mt-2">"AI-generated weekly summary for leadership"</p>
<p class="fw-bold">Last report sent Friday 4pm</p>
</div>
<div class="card-footer bg-white text-center">
<a href="#" class="btn btn-sm btn-outline-secondary">View Details</a>
<a href="#" class="btn btn-sm btn-outline-secondary">Edit</a>
</div>
</div>
</div>
</div>
</main>
@ -111,7 +152,7 @@ $workflows = $stmt->fetchAll();
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addWorkflowModalLabel">Add New Workflow</h5>
<h5 class="modal-title" id="addWorkflowModalLabel">Create New Workflow</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
@ -141,4 +182,4 @@ $workflows = $stmt->fetchAll();
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
</html>