nov 20th, 2025 V.1
This commit is contained in:
parent
4d417098b6
commit
e6fd5f2ecb
421
app.php
Normal file
421
app.php
Normal file
@ -0,0 +1,421 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
|
||||
// Check if user is logged in
|
||||
if (!is_logged_in()) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
// Data Fetching for Dashboard
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
// 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("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 = [];
|
||||
}
|
||||
|
||||
function time_ago($datetime, $full = false) {
|
||||
$now = new DateTime;
|
||||
$ago = new DateTime($datetime);
|
||||
$diff = $now->diff($ago);
|
||||
|
||||
$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">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Executive Dashboard - FinMox Flow</title>
|
||||
<meta name="description" content="Executive dashboard for FinMox Flow, providing key metrics and insights for HR and operations.">
|
||||
|
||||
<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'); ?>">
|
||||
|
||||
<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">
|
||||
<a href="app.php">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="FinMox Logo" style="height: 32px;">
|
||||
</a>
|
||||
</div>
|
||||
<nav class="d-flex align-items-center">
|
||||
<a href="app.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>
|
||||
<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']); ?>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
|
||||
<?php if (hasPermission('manage_roles')): ?>
|
||||
<li><a class="dropdown-item" href="roles.php">Manage Roles</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<?php endif; ?>
|
||||
<li><a class="dropdown-item" href="logout.php">Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="main-content">
|
||||
<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>
|
||||
|
||||
<!-- 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>
|
||||
<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; ?>
|
||||
<?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>
|
||||
</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="app.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>
|
||||
|
||||
<!-- 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="app.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>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
assets/pasted-20251120-051320-b2b0cdfa.png
Normal file
BIN
assets/pasted-20251120-051320-b2b0cdfa.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 110 KiB |
8
chat.php
8
chat.php
@ -65,9 +65,13 @@ if (!is_logged_in()) {
|
||||
<body>
|
||||
|
||||
<header class="header d-flex justify-content-between align-items-center">
|
||||
<div class="logo">FinMox<span class="dot">.</span></div>
|
||||
<div class="logo">
|
||||
<a href="app.php">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="FinMox Logo" style="height: 32px;">
|
||||
</a>
|
||||
</div>
|
||||
<nav class="d-flex align-items-center">
|
||||
<a href="index.php" class="btn btn-outline-primary me-2">Home</a>
|
||||
<a href="app.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>
|
||||
|
||||
@ -85,9 +85,13 @@ $completed_tasks = $pdo->query("SELECT COUNT(*) FROM tasks WHERE status = 'Compl
|
||||
</head>
|
||||
<body>
|
||||
<header class="header d-flex justify-content-between align-items-center">
|
||||
<div class="logo">FinMox<span class="dot">.</span></div>
|
||||
<div class="logo">
|
||||
<a href="app.php">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="FinMox Logo" style="height: 32px;">
|
||||
</a>
|
||||
</div>
|
||||
<nav class="d-flex align-items-center">
|
||||
<a href="index.php" class="btn btn-outline-primary me-2">Home</a>
|
||||
<a href="app.php" class="btn btn-outline-primary me-2">Home</a>
|
||||
<a href="workflows.php" class="btn btn-outline-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">
|
||||
|
||||
@ -8,7 +8,7 @@ if (!is_logged_in()) {
|
||||
}
|
||||
|
||||
if (!hasPermission('manage_candidates')) {
|
||||
header('Location: index.php');
|
||||
header('Location: app.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ $pdo = db();
|
||||
$candidate_id = $_GET['id'] ?? null;
|
||||
|
||||
if (!$candidate_id) {
|
||||
header('Location: index.php');
|
||||
header('Location: app.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ $stmt->execute([$candidate_id]);
|
||||
$candidate = $stmt->fetch();
|
||||
|
||||
if (!$candidate) {
|
||||
header('Location: index.php');
|
||||
header('Location: app.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_candidate']))
|
||||
try {
|
||||
$stmt = $pdo->prepare("UPDATE candidates SET name = ?, email = ?, phone = ?, status = ?, notes = ? WHERE id = ?");
|
||||
$stmt->execute([$name, $email, $phone, $status, $notes, $candidate_id]);
|
||||
header('Location: index.php');
|
||||
header('Location: app.php');
|
||||
exit;
|
||||
} catch (PDOException $e) {
|
||||
error_log("Error updating candidate: " . $e->getMessage());
|
||||
@ -61,9 +61,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_candidate']))
|
||||
</head>
|
||||
<body>
|
||||
<header class="header d-flex justify-content-between align-items-center">
|
||||
<div class="logo">FinMox<span class="dot">.</span></div>
|
||||
<div class="logo">
|
||||
<a href="app.php">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="FinMox Logo" style="height: 32px;">
|
||||
</a>
|
||||
</div>
|
||||
<nav class="d-flex align-items-center">
|
||||
<a href="index.php" class="btn btn-outline-primary me-2">Home</a>
|
||||
<a href="app.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>
|
||||
@ -115,7 +119,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_candidate']))
|
||||
<textarea class="form-control" id="notes" name="notes" rows="3"><?php echo htmlspecialchars($candidate['notes']); ?></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Update Candidate</button>
|
||||
<a href="index.php" class="btn btn-secondary">Cancel</a>
|
||||
<a href="app.php" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -8,7 +8,7 @@ if (!is_logged_in()) {
|
||||
}
|
||||
|
||||
if (!hasPermission('manage_roles')) {
|
||||
header('Location: index.php');
|
||||
header('Location: app.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -92,9 +92,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_permissions']))
|
||||
</head>
|
||||
<body>
|
||||
<header class="header d-flex justify-content-between align-items-center">
|
||||
<div class="logo">FinMox<span class="dot">.</span></div>
|
||||
<div class="logo">
|
||||
<a href="app.php">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="FinMox Logo" style="height: 32px;">
|
||||
</a>
|
||||
</div>
|
||||
<nav class="d-flex align-items-center">
|
||||
<a href="index.php" class="btn btn-outline-primary me-2">Home</a>
|
||||
<a href="app.php" class="btn btn-outline-primary me-2">Home</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>
|
||||
<div class="dropdown">
|
||||
|
||||
@ -8,7 +8,7 @@ if (!is_logged_in()) {
|
||||
}
|
||||
|
||||
if (!hasPermission('manage_tasks')) {
|
||||
header('Location: index.php');
|
||||
header('Location: app.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ $pdo = db();
|
||||
$task_id = $_GET['id'] ?? null;
|
||||
|
||||
if (!$task_id) {
|
||||
header('Location: index.php');
|
||||
header('Location: app.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ $stmt->execute([$task_id]);
|
||||
$task = $stmt->fetch();
|
||||
|
||||
if (!$task) {
|
||||
header('Location: index.php');
|
||||
header('Location: app.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_task'])) {
|
||||
try {
|
||||
$stmt = $pdo->prepare("UPDATE tasks SET task_name = ?, candidate_id = ?, due_date = ?, status = ?, description = ? WHERE id = ?");
|
||||
$stmt->execute([$task_name, $candidate_id, $due_date, $status, $description, $task_id]);
|
||||
header('Location: index.php');
|
||||
header('Location: app.php');
|
||||
exit;
|
||||
} catch (PDOException $e) {
|
||||
error_log("Error updating task: " . $e->getMessage());
|
||||
@ -65,9 +65,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_task'])) {
|
||||
</head>
|
||||
<body>
|
||||
<header class="header d-flex justify-content-between align-items-center">
|
||||
<div class="logo">FinMox<span class="dot">.</span></div>
|
||||
<div class="logo">
|
||||
<a href="app.php">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="FinMox Logo" style="height: 32px;">
|
||||
</a>
|
||||
</div>
|
||||
<nav class="d-flex align-items-center">
|
||||
<a href="index.php" class="btn btn-outline-primary me-2">Home</a>
|
||||
<a href="app.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>
|
||||
@ -122,7 +126,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['update_task'])) {
|
||||
<textarea class="form-control" id="task_description" name="description" rows="3"><?php echo htmlspecialchars($task['description']); ?></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Update Task</button>
|
||||
<a href="index.php" class="btn btn-secondary">Cancel</a>
|
||||
<a href="app.php" class="btn btn-secondary">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
485
index.php
485
index.php
@ -1,417 +1,132 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
|
||||
// Check if user is logged in
|
||||
if (!is_logged_in()) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/db/config.php';
|
||||
|
||||
// Data Fetching for Dashboard
|
||||
try {
|
||||
$pdo = db();
|
||||
|
||||
// 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("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 = [];
|
||||
}
|
||||
|
||||
function time_ago($datetime, $full = false) {
|
||||
$now = new DateTime;
|
||||
$ago = new DateTime($datetime);
|
||||
$diff = $now->diff($ago);
|
||||
|
||||
$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">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Executive Dashboard - FinMox Flow</title>
|
||||
<meta name="description" content="Executive dashboard for FinMox Flow, providing key metrics and insights for HR and operations.">
|
||||
|
||||
<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'); ?>">
|
||||
|
||||
<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(); ?>">
|
||||
<title>FinMox | HR Automation Platform</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
.main-content {
|
||||
background-color: #f4f7fc;
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
||||
* {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
}
|
||||
.card {
|
||||
border: none;
|
||||
border-radius: 0.75rem;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.07);
|
||||
.btn-primary {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.metric-card .card-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.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;
|
||||
.product-screenshot {
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
</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-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>
|
||||
<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']); ?>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
|
||||
<?php if (hasPermission('manage_roles')): ?>
|
||||
<li><a class="dropdown-item" href="roles.php">Manage Roles</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<?php endif; ?>
|
||||
<li><a class="dropdown-item" href="logout.php">Logout</a></li>
|
||||
</ul>
|
||||
<body class="bg-white">
|
||||
<!-- Navigation -->
|
||||
<nav class="border-b border-gray-200 bg-white sticky top-0 z-50">
|
||||
<div class="max-w-6xl mx-auto px-6 py-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="FinMox Logo" class="h-8 w-auto">
|
||||
<span class="text-xl font-semibold text-gray-900">FinMox</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-8">
|
||||
<a href="#product" class="text-sm text-gray-600 hover:text-gray-900">Product</a>
|
||||
<a href="#pricing" class="text-sm text-gray-600 hover:text-gray-900">Pricing</a>
|
||||
<a href="/login.php" class="text-sm text-gray-600 hover:text-gray-900">Log in</a>
|
||||
<a href="/register.php" class="btn-primary bg-black text-white px-4 py-2 rounded-lg text-sm font-medium">Register</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Hero Section -->
|
||||
<header class="bg-white">
|
||||
<div class="max-w-6xl mx-auto px-6 pt-24 pb-16">
|
||||
<div class="text-center">
|
||||
<h1 class="text-4xl md:text-6xl font-bold text-gray-900 leading-tight">Intelligent HR & Operations</h1>
|
||||
<p class="mt-4 text-lg text-gray-600 max-w-2xl mx-auto">FinMox is an intelligent enterprise system that automates HR, compliance, and operations.</p>
|
||||
<div class="mt-8 flex justify-center gap-4">
|
||||
<a href="/register.php" class="btn-primary bg-black text-white px-6 py-3 rounded-lg font-medium">Get Started</a>
|
||||
<a href="#product" class="bg-gray-100 text-gray-800 px-6 py-3 rounded-lg font-medium">Learn More</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="main-content">
|
||||
<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>
|
||||
<!-- Product Section -->
|
||||
<section id="product" class="bg-gray-50 py-20">
|
||||
<div class="max-w-6xl mx-auto px-6">
|
||||
<div class="text-center mb-12">
|
||||
<h2 class="text-3xl md:text-4xl font-bold text-gray-900">A unified platform for growth</h2>
|
||||
<p class="mt-3 text-lg text-gray-600">Manage candidates, tasks, and onboarding in one place.</p>
|
||||
</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 class="product-screenshot">
|
||||
<img src="assets/vm-shot-2025-11-11T05-29-19-670Z.jpg" alt="FinMox Dashboard Screenshot">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<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>
|
||||
<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; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<!-- Pricing Section -->
|
||||
<section id="pricing" class="bg-white py-20">
|
||||
<div class="max-w-6xl mx-auto px-6">
|
||||
<div class="text-center mb-12">
|
||||
<h2 class="text-3xl md:text-4xl font-bold text-gray-900">Pricing Plans</h2>
|
||||
<p class="mt-3 text-lg text-gray-600">Choose the plan that's right for your team.</p>
|
||||
</div>
|
||||
<div class="grid md:grid-cols-3 gap-8">
|
||||
<!-- Pricing Plan 1 -->
|
||||
<div class="border border-gray-200 rounded-lg p-8">
|
||||
<h3 class="text-xl font-semibold text-gray-900">Starter</h3>
|
||||
<p class="mt-2 text-gray-600">For small teams just getting started.</p>
|
||||
<div class="mt-6">
|
||||
<span class="text-4xl font-bold">$49</span>
|
||||
<span class="text-gray-600">/ month</span>
|
||||
</div>
|
||||
<a href="/register.php" class="mt-8 block w-full text-center btn-primary bg-black text-white px-6 py-3 rounded-lg font-medium">Get Started</a>
|
||||
</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>
|
||||
<!-- Pricing Plan 2 -->
|
||||
<div class="border border-gray-200 rounded-lg p-8">
|
||||
<h3 class="text-xl font-semibold text-gray-900">Business</h3>
|
||||
<p class="mt-2 text-gray-600">For growing businesses that need more power.</p>
|
||||
<div class="mt-6">
|
||||
<span class="text-4xl font-bold">$99</span>
|
||||
<span class="text-gray-600">/ month</span>
|
||||
</div>
|
||||
<a href="/register.php" class="mt-8 block w-full text-center btn-primary bg-black text-white px-6 py-3 rounded-lg font-medium">Get Started</a>
|
||||
</div>
|
||||
<!-- Pricing Plan 3 -->
|
||||
<div class="border border-gray-200 rounded-lg p-8">
|
||||
<h3 class="text-xl font-semibold text-gray-900">Enterprise</h3>
|
||||
<p class="mt-2 text-gray-600">For large organizations with custom needs.</p>
|
||||
<div class="mt-6">
|
||||
<span class="text-4xl font-bold">Contact Us</span>
|
||||
</div>
|
||||
<a href="#" class="mt-8 block w-full text-center bg-gray-100 text-gray-800 px-6 py-3 rounded-lg font-medium">Contact Sales</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</section>
|
||||
|
||||
<!-- 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="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>
|
||||
<!-- Footer -->
|
||||
<footer class="bg-gray-50">
|
||||
<div class="max-w-6xl mx-auto px-6 py-12">
|
||||
<div class="flex justify-between items-center">
|
||||
<p class="text-gray-600">© 2025 FinMox. All rights reserved.</p>
|
||||
<div class="flex gap-6">
|
||||
<a href="#" class="text-sm text-gray-600 hover:text-gray-900">Terms</a>
|
||||
<a href="#" class="text-sm text-gray-600 hover:text-gray-900">Privacy</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -2,7 +2,7 @@
|
||||
require_once 'auth.php';
|
||||
|
||||
if (is_logged_in()) {
|
||||
header('Location: index.php');
|
||||
header('Location: app.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$error = 'Please fill in all fields.';
|
||||
} else {
|
||||
if (login_user($username, $password)) {
|
||||
header('Location: index.php');
|
||||
header('Location: app.php');
|
||||
exit;
|
||||
} else {
|
||||
$error = 'Invalid username or password.';
|
||||
|
||||
10
roles.php
10
roles.php
@ -9,7 +9,7 @@ if (!is_logged_in()) {
|
||||
|
||||
if (!hasPermission('manage_roles')) {
|
||||
// Redirect to a generic "access denied" page or the home page
|
||||
header('Location: index.php');
|
||||
header('Location: app.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -48,9 +48,13 @@ $roles = $stmt->fetchAll();
|
||||
</head>
|
||||
<body>
|
||||
<header class="header d-flex justify-content-between align-items-center">
|
||||
<div class="logo">FinMox<span class="dot">.</span></div>
|
||||
<div class="logo">
|
||||
<a href="app.php">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="FinMox Logo" style="height: 32px;">
|
||||
</a>
|
||||
</div>
|
||||
<nav class="d-flex align-items-center">
|
||||
<a href="index.php" class="btn btn-outline-primary me-2">Home</a>
|
||||
<a href="app.php" class="btn btn-outline-primary me-2">Home</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>
|
||||
<div class="dropdown">
|
||||
|
||||
@ -8,7 +8,7 @@ if (!is_logged_in()) {
|
||||
}
|
||||
|
||||
if (!hasPermission('view_workflows')) {
|
||||
header('Location: index.php');
|
||||
header('Location: app.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -119,11 +119,13 @@ $workflows = $stmt->fetchAll();
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center justify-between h-16">
|
||||
<div class="flex items-center">
|
||||
<a href="index.php" class="text-2xl font-bold">FinMox<span class="text-blue-500">.</span></a>
|
||||
<a href="app.php">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="FinMox Logo" style="height: 32px;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="hidden md:block">
|
||||
<div class="ml-10 flex items-baseline space-x-4">
|
||||
<a href="index.php" class="text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">Home</a>
|
||||
<a href="app.php" class="text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">Home</a>
|
||||
<a href="dashboard.php" class="text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">Dashboard</a>
|
||||
<a href="workflows.php" class="bg-gray-900 text-white px-3 py-2 rounded-md text-sm font-medium" aria-current="page">Workflows</a>
|
||||
|
||||
@ -160,7 +162,7 @@ $workflows = $stmt->fetchAll();
|
||||
<!-- Mobile menu, show/hide based on menu state. -->
|
||||
<div class="md:hidden hidden" id="mobile-menu">
|
||||
<div class="px-2 pt-2 pb-3 space-y-1 sm:px-3">
|
||||
<a href="index.php" class="text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">Home</a>
|
||||
<a href="app.php" class="text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">Home</a>
|
||||
<a href="dashboard.php" class="text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">Dashboard</a>
|
||||
<a href="workflows.php" class="bg-gray-900 text-white block px-3 py-2 rounded-md text-base font-medium" aria-current="page">Workflows</a>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user