December 16th, 2025 V.4
440
app.php
@ -1,99 +1,18 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
|
||||
// Check if user is logged in
|
||||
if (!is_logged_in()) {
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
require_once 'db/config.php';
|
||||
$pdo = db();
|
||||
|
||||
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';
|
||||
}
|
||||
$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();
|
||||
$completion_rate = ($total_tasks > 0) ? ($completed_tasks / $total_tasks) * 100 : 0;
|
||||
// For now, open cases will be a static number
|
||||
$open_cases = 12;
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
@ -101,322 +20,55 @@ function time_ago($datetime, $full = false) {
|
||||
<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>Dashboard - FinMox</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;
|
||||
}
|
||||
.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;
|
||||
body {
|
||||
background-color: #fafafa;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
</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>
|
||||
<body class="bg-gray-100">
|
||||
<div class="flex h-screen bg-gray-200">
|
||||
<!-- Sidebar -->
|
||||
<div class="w-64 bg-white shadow-md">
|
||||
<div class="p-6">
|
||||
<a href="app.php">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="FinMox Logo" style="height: 32px;">
|
||||
</a>
|
||||
</div>
|
||||
<nav class="mt-6">
|
||||
<a href="dashboard.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Dashboard</a>
|
||||
<a href="chat.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">AI Copilot</a>
|
||||
<a href="workflows.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Workflows</a>
|
||||
<a href="roles.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Roles</a>
|
||||
<a href="settings.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Settings</a>
|
||||
<a href="logout.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Logout</a>
|
||||
</nav>
|
||||
</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-2">Workflows</a>
|
||||
<a href="settings.php" class="btn btn-outline-primary me-3">Settings</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">
|
||||
<!-- Main content -->
|
||||
<div class="flex-1 flex flex-col overflow-hidden">
|
||||
<main class="flex-1 overflow-x-hidden overflow-y-auto bg-[#fafafa] pb-20 md:pb-0">
|
||||
<div class="max-w-7xl mx-auto px-4 md:px-6 py-4 md:py-8">
|
||||
|
||||
<!-- 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>
|
||||
<!-- Dashboard Header -->
|
||||
<div class="md:flex md:items-center md:justify-between">
|
||||
<div class="flex-1 min-w-0">
|
||||
<h2 class="text-2xl font-bold leading-7 text-gray-900 sm:text-3xl sm:truncate">
|
||||
Welcome to FinMox
|
||||
</h2>
|
||||
</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>
|
||||
</main>
|
||||
</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-20251217-000622-29db5cc0.png
Normal file
|
After Width: | Height: | Size: 318 KiB |
BIN
assets/pasted-20251217-001100-a685060f.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
assets/pasted-20251217-002253-bcae6fdf.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
assets/pasted-20251217-002642-56a7ab8d.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
assets/pasted-20251217-003058-b729a304.png
Normal file
|
After Width: | Height: | Size: 130 KiB |
BIN
assets/pasted-20251217-003438-084e5a8d.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
assets/pasted-20251217-003745-75dfb2ae.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
assets/pasted-20251217-004335-14bb2c20.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
assets/pasted-20251217-005032-5f07fa20.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
assets/pasted-20251217-005612-9c5a31fd.png
Normal file
|
After Width: | Height: | Size: 356 KiB |
BIN
assets/pasted-20251217-005953-75c0724e.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
assets/pasted-20251217-010834-2413763b.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
assets/pasted-20251217-011539-6a343400.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
assets/pasted-20251217-013922-5ab893cf.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
21
auth.php
@ -3,20 +3,20 @@ session_start();
|
||||
require_once 'db/config.php';
|
||||
|
||||
function register_user($username, $password, $email) {
|
||||
$pdo = db();
|
||||
$password_hash = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
// Get the default role ID (e.g., the role with the name 'Admin')
|
||||
$stmt = $pdo->prepare("SELECT id FROM roles WHERE name = 'Admin'");
|
||||
$stmt->execute();
|
||||
$role = $stmt->fetch();
|
||||
$default_role_id = $role ? $role['id'] : null;
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$password_hash = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
// Get the default role ID (e.g., the role with the name 'Admin')
|
||||
$stmt = $pdo->prepare("SELECT id FROM roles WHERE name = 'Admin'");
|
||||
$stmt->execute();
|
||||
$role = $stmt->fetch();
|
||||
$default_role_id = $role ? $role['id'] : null;
|
||||
|
||||
$stmt = $pdo->prepare("INSERT INTO users (username, password, role_id, email) VALUES (?, ?, ?, ?)");
|
||||
return $stmt->execute([$username, $password_hash, $default_role_id, $email]);
|
||||
} catch (PDOException $e) {
|
||||
// Handle duplicate username
|
||||
// Handle duplicate username or other db errors
|
||||
error_log($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
@ -32,6 +32,7 @@ function login_user($username, $password) {
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['username'] = $user['username'];
|
||||
$_SESSION['role_id'] = $user['role_id'];
|
||||
$_SESSION['email'] = $user['email'];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
226
chat.php
@ -1,28 +1,36 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
if (!is_logged_in()) {
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit();
|
||||
exit;
|
||||
}
|
||||
require_once 'db/config.php';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AI Chat - FinMox</title>
|
||||
<title>AI Copilot - FinMox</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
||||
<script src="https://unpkg.com/lucide@latest"></script>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
||||
* {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
}
|
||||
body {
|
||||
background-color: #fafafa;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.chat-container {
|
||||
height: calc(100vh - 80px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100vh - 120px); /* Adjust based on header/footer height */
|
||||
}
|
||||
.chat-messages {
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.message-bubble {
|
||||
max-width: 75%;
|
||||
@ -32,131 +40,131 @@ if (!is_logged_in()) {
|
||||
color: white;
|
||||
}
|
||||
.ai-message {
|
||||
background-color: #E5E7EB;
|
||||
background-color: #F3F4F6;
|
||||
color: #1F2937;
|
||||
}
|
||||
#chat-input:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
border-color: #3B82F6;
|
||||
#chat-input-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
#chat-input {
|
||||
padding-right: 40px; /* Space for the button */
|
||||
}
|
||||
#send-btn {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: #9CA3AF;
|
||||
}
|
||||
#send-btn:hover {
|
||||
color: #374151;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-100">
|
||||
<div class="flex h-screen">
|
||||
<div class="flex h-screen bg-gray-200">
|
||||
<!-- Sidebar -->
|
||||
<div class="w-1/4 bg-white border-r border-gray-200 flex flex-col">
|
||||
<div class="p-4 border-b border-gray-200">
|
||||
<a href="chat.php" class="w-full bg-black text-white px-4 py-2 rounded-lg text-sm font-medium flex items-center justify-center gap-2">
|
||||
<i class="fas fa-plus"></i>
|
||||
New Chat
|
||||
<div class="w-64 bg-white shadow-md">
|
||||
<div class="p-6">
|
||||
<a href="app.php">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="FinMox Logo" style="height: 32px;">
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex-grow overflow-y-auto">
|
||||
<!-- Conversation List -->
|
||||
<div class="p-4">
|
||||
<h2 class="text-xs text-gray-500 font-semibold uppercase mb-2">Past Conversations</h2>
|
||||
<ul id="conversation-list">
|
||||
<!-- Placeholder for conversation items -->
|
||||
<li class="p-2 rounded-lg hover:bg-gray-100 cursor-pointer">
|
||||
<p class="font-semibold text-sm truncate">Onboarding Workflow Ideas</p>
|
||||
<p class="text-xs text-gray-500 truncate">Sure, here are some ideas for your onboarding...</p>
|
||||
<p class="text-xs text-gray-400 mt-1">2 hours ago</p>
|
||||
</li>
|
||||
<li class="p-2 rounded-lg hover:bg-gray-100 cursor-pointer mt-2">
|
||||
<p class="font-semibold text-sm truncate">Compliance Questions</p>
|
||||
<p class="text-xs text-gray-500 truncate">Let's break down the compliance requirements...</p>
|
||||
<p class="text-xs text-gray-400 mt-1">Yesterday</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="mt-6">
|
||||
<a href="dashboard.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Dashboard</a>
|
||||
<a href="chat.php" class="block py-2.5 px-4 rounded transition duration-200 bg-gray-200 text-gray-900 font-semibold">AI Copilot</a>
|
||||
<a href="workflows.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Workflows</a>
|
||||
<a href="roles.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Roles</a>
|
||||
<a href="settings.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Settings</a>
|
||||
<a href="logout.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Logout</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- Main Chat Area -->
|
||||
<div class="w-3/4 flex flex-col">
|
||||
<header class="bg-white border-b border-gray-200 p-4 flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="app.php">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="FinMox Logo" style="height: 32px;">
|
||||
</a>
|
||||
</div>
|
||||
<nav class="flex items-center gap-4">
|
||||
<a href="app.php" class="text-sm text-gray-600 hover:text-gray-900">Home</a>
|
||||
<a href="dashboard.php" class="text-sm text-gray-600 hover:text-gray-900">Dashboard</a>
|
||||
<a href="workflows.php" class="text-sm text-gray-600 hover:text-gray-900">Workflows</a>
|
||||
<a href="settings.php" class="text-sm text-gray-600 hover:text-gray-900">Settings</a>
|
||||
<a href="logout.php" class="text-sm text-gray-600 hover:text-gray-900">Logout</a>
|
||||
</nav>
|
||||
</header>
|
||||
<!-- Main content -->
|
||||
<main class="flex-1 flex flex-col overflow-hidden bg-[#fafafa]">
|
||||
<div class="max-w-4xl w-full mx-auto px-4 sm:px-6 md:px-8 py-8 flex flex-col flex-grow">
|
||||
<h1 class="text-3xl font-bold text-gray-900 mb-6">AI Copilot</h1>
|
||||
|
||||
<main class="flex-grow p-6 overflow-y-auto" id="chat-messages">
|
||||
<!-- Messages will be appended here -->
|
||||
</main>
|
||||
<div class="chat-container bg-white border border-gray-200 rounded-xl shadow-sm flex-grow">
|
||||
<div class="chat-messages p-6" id="chat-messages">
|
||||
<!-- Initial Welcome Screen -->
|
||||
<div id="welcome-screen" class="text-center h-full flex flex-col justify-center items-center">
|
||||
<div class="mb-8">
|
||||
<h2 class="text-2xl font-semibold text-gray-800">Hi, I'm FinMox AI.</h2>
|
||||
<p class="text-gray-500">How can I help you today?</p>
|
||||
</div>
|
||||
<div class="w-full max-w-2xl">
|
||||
<p class="text-sm text-gray-600 mb-3">Here are a few suggestions:</p>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<button class="suggestion-card text-left p-4 bg-gray-50 hover:bg-gray-100 rounded-lg border border-gray-200 transition">
|
||||
<i data-lucide="file-text" class="w-5 h-5 mb-2 text-gray-600"></i>
|
||||
<p class="text-sm font-medium text-gray-800">Summarize key candidate skills</p>
|
||||
</button>
|
||||
<button class="suggestion-card text-left p-4 bg-gray-50 hover:bg-gray-100 rounded-lg border border-gray-200 transition">
|
||||
<i data-lucide="mail" class="w-5 h-5 mb-2 text-gray-600"></i>
|
||||
<p class="text-sm font-medium text-gray-800">Draft a response to a candidate</p>
|
||||
</button>
|
||||
<button class="suggestion-card text-left p-4 bg-gray-50 hover:bg-gray-100 rounded-lg border border-gray-200 transition">
|
||||
<i data-lucide="bar-chart-2" class="w-5 h-5 mb-2 text-gray-600"></i>
|
||||
<p class="text-sm font-medium text-gray-800">Analyze data from a resume</p>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Chat messages will be appended here -->
|
||||
</div>
|
||||
|
||||
<div class="bg-white border-t border-gray-200 p-4">
|
||||
<div class="typing-indicator text-sm text-gray-500 mb-2 hidden" id="typing-indicator">
|
||||
AI is thinking...
|
||||
</div>
|
||||
<div class="relative">
|
||||
<textarea id="chat-input" class="w-full border border-gray-300 rounded-lg p-4 pr-28" placeholder="Ask the AI Assistant..." rows="1"></textarea>
|
||||
<div class="absolute bottom-3 right-3 flex items-center">
|
||||
<button class="mr-2 text-gray-400 hover:text-gray-600">
|
||||
<i class="fas fa-paperclip"></i>
|
||||
</button>
|
||||
<button id="send-btn" class="bg-black text-white px-4 py-2 rounded-lg text-sm font-medium">Send</button>
|
||||
<div class="p-4 border-t border-gray-200">
|
||||
<div class="typing-indicator text-sm text-gray-500 mb-2 hidden" id="typing-indicator">
|
||||
AI is thinking...
|
||||
</div>
|
||||
<div id="chat-input-wrapper" class="relative">
|
||||
<input type="text" id="chat-input" class="w-full border-gray-300 rounded-lg p-3 pr-12 text-sm" placeholder="Send a message...">
|
||||
<button id="send-btn">
|
||||
<i data-lucide="send" class="w-5 h-5"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-xs text-gray-400 mt-1 text-right" id="char-counter">0 / 4000</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
lucide.createIcons();
|
||||
|
||||
const chatMessages = document.getElementById('chat-messages');
|
||||
const userInput = document.getElementById('chat-input');
|
||||
const sendBtn = document.getElementById('send-btn');
|
||||
const typingIndicator = document.getElementById('typing-indicator');
|
||||
const charCounter = document.getElementById('char-counter');
|
||||
const welcomeScreen = document.getElementById('welcome-screen');
|
||||
let welcomeMessageCleared = false;
|
||||
|
||||
function addMessage(sender, content) {
|
||||
if (!welcomeMessageCleared) {
|
||||
chatMessages.innerHTML = '';
|
||||
welcomeMessageCleared = true;
|
||||
}
|
||||
|
||||
const messageWrapper = document.createElement('div');
|
||||
messageWrapper.classList.add('flex', 'mb-4');
|
||||
|
||||
const avatar = document.createElement('div');
|
||||
avatar.classList.add('w-8', 'h-8', 'rounded-full', 'flex', 'items-center', 'justify-center', 'text-white', 'font-bold');
|
||||
|
||||
const messageBubble = document.createElement('div');
|
||||
messageBubble.classList.add('p-3', 'rounded-lg', 'message-bubble');
|
||||
|
||||
const timestamp = document.createElement('div');
|
||||
timestamp.classList.add('text-xs', 'text-gray-400', 'mt-1');
|
||||
timestamp.textContent = new Date().toLocaleTimeString();
|
||||
const bubble = document.createElement('div');
|
||||
bubble.classList.add('p-3', 'rounded-lg', 'message-bubble');
|
||||
bubble.textContent = content;
|
||||
|
||||
if (sender === 'user') {
|
||||
messageWrapper.classList.add('justify-end');
|
||||
avatar.classList.add('bg-blue-500', 'ml-3');
|
||||
avatar.textContent = 'U';
|
||||
messageBubble.classList.add('user-message');
|
||||
messageBubble.textContent = content;
|
||||
messageWrapper.appendChild(messageBubble);
|
||||
messageWrapper.appendChild(avatar);
|
||||
bubble.classList.add('user-message');
|
||||
} else {
|
||||
messageWrapper.classList.add('justify-start');
|
||||
avatar.classList.add('bg-gray-700', 'mr-3');
|
||||
avatar.textContent = 'AI';
|
||||
messageBubble.classList.add('ai-message');
|
||||
messageBubble.innerHTML = content; // Use innerHTML to render potential markdown
|
||||
messageWrapper.appendChild(avatar);
|
||||
messageWrapper.appendChild(messageBubble);
|
||||
bubble.classList.add('ai-message');
|
||||
}
|
||||
|
||||
const messageContainer = document.createElement('div');
|
||||
messageContainer.classList.add('flex', 'flex-col', (sender === 'user' ? 'items-end' : 'items-start'));
|
||||
messageContainer.appendChild(messageBubble);
|
||||
messageContainer.appendChild(timestamp);
|
||||
|
||||
messageWrapper.appendChild(messageContainer);
|
||||
messageWrapper.appendChild(bubble);
|
||||
chatMessages.appendChild(messageWrapper);
|
||||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||||
}
|
||||
@ -167,16 +175,13 @@ if (!is_logged_in()) {
|
||||
|
||||
addMessage('user', message);
|
||||
userInput.value = '';
|
||||
charCounter.textContent = '0 / 4000';
|
||||
sendBtn.disabled = true;
|
||||
typingIndicator.classList.remove('hidden');
|
||||
|
||||
try {
|
||||
const response = await fetch('api/chat.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ message })
|
||||
});
|
||||
|
||||
@ -197,24 +202,27 @@ if (!is_logged_in()) {
|
||||
}
|
||||
}
|
||||
|
||||
function handleSuggestionClick(event) {
|
||||
const button = event.target.closest('.suggestion-card');
|
||||
if (button) {
|
||||
const text = button.querySelector('p').textContent;
|
||||
userInput.value = text;
|
||||
userInput.focus();
|
||||
}
|
||||
}
|
||||
|
||||
sendBtn.addEventListener('click', sendMessage);
|
||||
userInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
sendMessage();
|
||||
}
|
||||
});
|
||||
|
||||
userInput.addEventListener('input', () => {
|
||||
const count = userInput.value.length;
|
||||
charCounter.textContent = `${count} / 4000`;
|
||||
document.querySelectorAll('.suggestion-card').forEach(card => {
|
||||
card.addEventListener('click', handleSuggestionClick);
|
||||
});
|
||||
|
||||
// Auto-resize textarea
|
||||
userInput.addEventListener('input', () => {
|
||||
userInput.style.height = 'auto';
|
||||
userInput.style.height = (userInput.scrollHeight) + 'px';
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
487
dashboard.php
@ -1,350 +1,225 @@
|
||||
<?php
|
||||
require_once 'auth.php';
|
||||
|
||||
// Check if user is logged in
|
||||
if (!is_logged_in()) {
|
||||
session_start();
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once 'db/config.php';
|
||||
// Note: The following PHP variables are placeholders and not currently used in the new design.
|
||||
// They can be integrated later when making the dashboard dynamic.
|
||||
/*
|
||||
$pdo = db();
|
||||
|
||||
|
||||
|
||||
// Pagination for Candidates
|
||||
$candidate_page = isset($_GET['candidate_page']) ? (int)$_GET['candidate_page'] : 1;
|
||||
$limit = 5;
|
||||
$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', $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);
|
||||
|
||||
// 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', $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();
|
||||
|
||||
$completion_rate = ($total_tasks > 0) ? ($completed_tasks / $total_tasks) * 100 : 0;
|
||||
$open_cases = 12;
|
||||
*/
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<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(); ?>">
|
||||
<title>Dashboard - FinMox</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://unpkg.com/lucide@latest"></script>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
||||
* {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
}
|
||||
body {
|
||||
background-color: #fafafa;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.stat-card .percentage.positive { color: #22c55e; }
|
||||
.stat-card .percentage.negative { color: #ef4444; }
|
||||
</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-outline-primary me-2">Home</a>
|
||||
<a href="workflows.php" class="btn btn-outline-primary me-2">Workflows</a>
|
||||
<a href="settings.php" class="btn btn-outline-primary me-3">Settings</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">
|
||||
<li><a class="dropdown-item" href="roles.php">Manage Roles</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><a class="dropdown-item" href="logout.php">Logout</a></li>
|
||||
</ul>
|
||||
<body class="bg-gray-100">
|
||||
<div class="flex h-screen bg-gray-200">
|
||||
<!-- Sidebar -->
|
||||
<div class="w-64 bg-white shadow-md">
|
||||
<div class="p-6">
|
||||
<a href="app.php">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="FinMox Logo" style="height: 32px;">
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<nav class="mt-6">
|
||||
<a href="dashboard.php" class="block py-2.5 px-4 rounded transition duration-200 bg-gray-200 text-gray-900 font-semibold">Dashboard</a>
|
||||
<a href="chat.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Chat</a>
|
||||
<a href="workflows.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Workflows</a>
|
||||
<a href="roles.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Roles</a>
|
||||
<a href="settings.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Settings</a>
|
||||
<a href="logout.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Logout</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<main class="container-fluid mt-4">
|
||||
<h2 class="mb-4">Dashboard</h2>
|
||||
<!-- Main content -->
|
||||
<main class="flex-1 overflow-x-hidden overflow-y-auto bg-[#fafafa]">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 md:px-8 py-8">
|
||||
<!-- Header -->
|
||||
<div class="mb-8">
|
||||
<h1 class="text-3xl font-bold text-gray-900">HR Dashboard</h1>
|
||||
<p class="text-gray-600 mt-1">Overview of recruiting, onboarding, and HR operations</p>
|
||||
</div>
|
||||
|
||||
<?php if ($message): ?>
|
||||
<div class="alert alert-success"><?php echo $message; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Nav tabs -->
|
||||
<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>
|
||||
<?php if (hasPermission('view_candidates')) { ?>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="candidates-tab" data-bs-toggle="tab" data-bs-target="#candidates" type="button" role="tab" aria-controls="candidates" aria-selected="false">Candidates</button>
|
||||
</li>
|
||||
<?php } ?>
|
||||
<?php if (hasPermission('view_tasks')) { ?>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="tasks-tab" data-bs-toggle="tab" data-bs-target="#tasks" type="button" role="tab" aria-controls="tasks" aria-selected="false">Tasks</button>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
|
||||
<!-- Tab content -->
|
||||
<div class="tab-content" id="dashboardTabContent">
|
||||
<!-- Overview Tab -->
|
||||
<div class="tab-pane fade show active" id="overview" role="tabpanel" aria-labelledby="overview-tab">
|
||||
<div class="row mb-4 mt-4">
|
||||
<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"><?php echo $total_candidates; ?></p>
|
||||
<!-- Key Metrics -->
|
||||
<div class="mb-8">
|
||||
<h2 class="text-xl font-semibold text-gray-900 mb-4">Key Metrics</h2>
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
<div class="stat-card bg-white p-6 rounded-xl shadow-sm border border-gray-200">
|
||||
<p class="text-gray-500 text-sm">Active Candidates</p>
|
||||
<div class="flex items-baseline space-x-2 mt-1">
|
||||
<p class="text-3xl font-bold text-gray-900">47</p>
|
||||
<p class="percentage positive text-sm font-semibold">+12%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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"><?php echo $total_tasks; ?></p>
|
||||
<div class="stat-card bg-white p-6 rounded-xl shadow-sm border border-gray-200">
|
||||
<p class="text-gray-500 text-sm">Open Cases</p>
|
||||
<div class="flex items-baseline space-x-2 mt-1">
|
||||
<p class="text-3xl font-bold text-gray-900">23</p>
|
||||
<p class="percentage negative text-sm font-semibold">-8%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card text-center shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Completed Tasks</h5>
|
||||
<p class="card-text fs-4"><?php echo $completed_tasks; ?></p>
|
||||
<div class="stat-card bg-white p-6 rounded-xl shadow-sm border border-gray-200">
|
||||
<p class="text-gray-500 text-sm">Onboarding Tasks</p>
|
||||
<div class="flex items-baseline space-x-2 mt-1">
|
||||
<p class="text-3xl font-bold text-gray-900">156</p>
|
||||
<p class="percentage positive text-sm font-semibold">+5%</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card bg-white p-6 rounded-xl shadow-sm border border-gray-200">
|
||||
<p class="text-gray-500 text-sm">Completion Rate</p>
|
||||
<div class="flex items-baseline space-x-2 mt-1">
|
||||
<p class="text-3xl font-bold text-gray-900">94%</p>
|
||||
<p class="percentage positive text-sm font-semibold">+2%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Candidates Tab -->
|
||||
<div class="tab-pane fade" id="candidates" role="tabpanel" aria-labelledby="candidates-tab">
|
||||
<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>
|
||||
<!-- Main Grid -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<!-- Hiring Funnel (Left Column) -->
|
||||
<div class="lg:col-span-2">
|
||||
<div class="bg-white p-6 rounded-xl shadow-sm border border-gray-200">
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-4">Hiring Funnel</h3>
|
||||
<div class="h-80">
|
||||
<canvas id="hiringFunnelChart"></canvas>
|
||||
</div>
|
||||
</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 } ?>
|
||||
</div>
|
||||
|
||||
<!-- Recent Activity (Right Column) -->
|
||||
<div>
|
||||
<div class="bg-white p-6 rounded-xl shadow-sm border border-gray-200">
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-4">Recent Activity</h3>
|
||||
<ul class="space-y-4">
|
||||
<li class="flex items-center">
|
||||
<div class="p-2 bg-blue-100 rounded-full"><i data-lucide="user-plus" class="w-5 h-5 text-blue-600"></i></div>
|
||||
<div class="ml-3"><p class="text-sm text-gray-700">New candidate profile created</p><p class="text-xs text-gray-500">5 minutes ago</p></div>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<div class="p-2 bg-yellow-100 rounded-full"><i data-lucide="file-text" class="w-5 h-5 text-yellow-600"></i></div>
|
||||
<div class="ml-3"><p class="text-sm text-gray-700">Case updated</p><p class="text-xs text-gray-500">23 minutes ago</p></div>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<div class="p-2 bg-green-100 rounded-full"><i data-lucide="check-circle" class="w-5 h-5 text-green-600"></i></div>
|
||||
<div class="ml-3"><p class="text-sm text-gray-700">Onboarding task completed</p><p class="text-xs text-gray-500">1 hour ago</p></div>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<div class="p-2 bg-red-100 rounded-full"><i data-lucide="clock" class="w-5 h-5 text-red-600"></i></div>
|
||||
<div class="ml-3"><p class="text-sm text-gray-700">Deadline approaching</p><p class="text-xs text-gray-500">2 hours ago</p></div>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tasks Tab -->
|
||||
<div class="tab-pane fade" id="tasks" role="tabpanel" aria-labelledby="tasks-tab">
|
||||
<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>
|
||||
<!-- Open HR Cases -->
|
||||
<div class="mt-8">
|
||||
<div class="bg-white p-6 rounded-xl shadow-sm border border-gray-200">
|
||||
<h3 class="text-lg font-semibold text-gray-900 mb-4">Open HR Cases</h3>
|
||||
<table class="w-full text-left">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="p-2 text-sm font-semibold text-gray-600">Case</th>
|
||||
<th class="p-2 text-sm font-semibold text-gray-600">Priority</th>
|
||||
<th class="p-2 text-sm font-semibold text-gray-600">Assignee</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="border-t border-gray-200">
|
||||
<td class="p-3 text-sm text-gray-800">Performance Review - Marcus Chen</td>
|
||||
<td class="p-3"><span class="px-2 py-1 text-xs font-semibold text-red-800 bg-red-100 rounded-full">High</span></td>
|
||||
<td class="p-3 text-sm text-gray-800">John Doe</td>
|
||||
</tr>
|
||||
<tr class="border-t border-gray-200">
|
||||
<td class="p-3 text-sm text-gray-800">Background Check - Sarah Johnson</td>
|
||||
<td class="p-3"><span class="px-2 py-1 text-xs font-semibold text-yellow-800 bg-yellow-100 rounded-full">Medium</span></td>
|
||||
<td class="p-3 text-sm text-gray-800">Jane Smith</td>
|
||||
</tr>
|
||||
<tr class="border-t border-gray-200">
|
||||
<td class="p-3 text-sm text-gray-800">Onboarding Checklist - Emily Davis</td>
|
||||
<td class="p-3"><span class="px-2 py-1 text-xs font-semibold text-green-800 bg-green-100 rounded-full">Low</span></td>
|
||||
<td class="p-3 text-sm text-gray-800">Alice Johnson</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</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="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>
|
||||
</main>
|
||||
</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();
|
||||
lucide.createIcons();
|
||||
|
||||
const ctx = document.getElementById('hiringFunnelChart').getContext('2d');
|
||||
const hiringFunnelChart = new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: ['Applications', 'Screening', 'Interviews', 'Offers', 'Hires'],
|
||||
datasets: [{
|
||||
label: 'Candidates',
|
||||
data: [150, 100, 60, 30, 15],
|
||||
backgroundColor: '#3b82f6',
|
||||
borderRadius: 4,
|
||||
barThickness: 30,
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: {
|
||||
color: '#e5e7eb',
|
||||
borderDash: [2, 4],
|
||||
},
|
||||
ticks: {
|
||||
color: '#6b7280'
|
||||
}
|
||||
},
|
||||
x: {
|
||||
grid: {
|
||||
display: false,
|
||||
},
|
||||
ticks: {
|
||||
color: '#6b7280'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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>
|
||||
|
||||
1
db/migrations/014_add_email_to_users.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE `users` ADD COLUMN `email` VARCHAR(255) NOT NULL UNIQUE AFTER `password`;
|
||||
23
login.php
@ -7,6 +7,11 @@ if (is_logged_in()) {
|
||||
}
|
||||
|
||||
$error = '';
|
||||
$success = '';
|
||||
|
||||
if (isset($_GET['registered']) && $_GET['registered'] === 'true') {
|
||||
$success = 'Registration successful! You can now log in.';
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = $_POST['username'] ?? '';
|
||||
@ -15,11 +20,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if (empty($username) || empty($password)) {
|
||||
$error = 'Please fill in all fields.';
|
||||
} else {
|
||||
if (login_user($username, $password)) {
|
||||
header('Location: app.php');
|
||||
exit;
|
||||
} else {
|
||||
$error = 'Invalid username or password.';
|
||||
try {
|
||||
if (login_user($username, $password)) {
|
||||
header('Location: app.php');
|
||||
exit;
|
||||
} else {
|
||||
$error = 'Invalid username or password.';
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
error_log("Database connection error during login: " . $e->getMessage());
|
||||
$error = "A server error occurred. Please try again later.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -42,6 +52,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<h3>Login</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if ($success): ?>
|
||||
<div class="alert alert-success"><?php echo $success; ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($error): ?>
|
||||
<div class="alert alert-danger"><?php echo $error; ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
23
register.php
@ -5,16 +5,23 @@ $error = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = $_POST['username'] ?? '';
|
||||
$email = $_POST['email'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if (empty($username) || empty($password)) {
|
||||
if (empty($username) || empty($password) || empty($email)) {
|
||||
$error = 'Please fill in all fields.';
|
||||
} else {
|
||||
if (register_user($username, $password)) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
} else {
|
||||
$error = 'Username already exists.';
|
||||
try {
|
||||
if (register_user($username, $password, $email)) {
|
||||
// Redirect to login page with a success message
|
||||
header('Location: login.php?registered=true');
|
||||
exit;
|
||||
} else {
|
||||
$error = 'Username or email already exists.';
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
error_log("Database error during registration: " . $e->getMessage());
|
||||
$error = "A server error occurred. Please try again later.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -45,6 +52,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
<label for="username" class="form-label">Username</label>
|
||||
<input type="text" class="form-control" id="username" name="username" 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="password" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
|
||||
133
roles.php
@ -43,65 +43,90 @@ $roles = $stmt->fetchAll();
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Role Management</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">
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
||||
* {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
}
|
||||
body {
|
||||
background-color: #fafafa;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
</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-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-2">Workflows</a>
|
||||
<a href="settings.php" class="btn btn-outline-primary me-3">Settings</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-gray-100">
|
||||
<div class="flex h-screen bg-gray-200">
|
||||
<!-- Sidebar -->
|
||||
<div class="w-64 bg-white shadow-md">
|
||||
<div class="p-6">
|
||||
<a href="app.php">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="FinMox Logo" style="height: 32px;">
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container-fluid">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2>Role Management</h2>
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addRoleModal">Add Role</button>
|
||||
<nav class="mt-6">
|
||||
<a href="dashboard.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Dashboard</a>
|
||||
<a href="chat.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Chat</a>
|
||||
<a href="workflows.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Workflows</a>
|
||||
<a href="roles.php" class="block py-2.5 px-4 rounded transition duration-200 bg-gray-200 text-gray-900 font-semibold">Roles</a>
|
||||
<a href="settings.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Settings</a>
|
||||
<a href="logout.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Logout</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Role Name</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($roles as $role): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($role['name']); ?></td>
|
||||
<td>
|
||||
<a href="edit_role.php?role_id=<?php echo $role['id']; ?>" class="btn btn-sm btn-outline-primary">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Main content -->
|
||||
<div class="flex-1 flex flex-col overflow-hidden">
|
||||
<main class="flex-1 overflow-x-hidden overflow-y-auto bg-[#fafafa] pb-20 md:pb-0">
|
||||
<div class="max-w-7xl mx-auto px-4 md:px-6 py-4 md:py-8">
|
||||
<div class="md:flex md:items-center md:justify-between">
|
||||
<div class="flex-1 min-w-0">
|
||||
<h2 class="text-2xl font-bold leading-7 text-gray-900 sm:text-3xl sm:truncate">
|
||||
Role Management
|
||||
</h2>
|
||||
</div>
|
||||
<div class="mt-4 flex md:mt-0 md:ml-4">
|
||||
<button type="button" class="inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" data-bs-toggle="modal" data-bs-target="#addRoleModal">
|
||||
Add Role
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 md:mt-8 flex flex-col">
|
||||
<div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
||||
<div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
|
||||
<div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Role Name
|
||||
</th>
|
||||
<th scope="col" class="relative px-6 py-3">
|
||||
<span class="sr-only">Edit</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
<?php foreach ($roles as $role): ?>
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||
<?php echo htmlspecialchars($role['name']); ?>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<a href="edit_role.php?role_id=<?php echo $role['id']; ?>" class="text-indigo-600 hover:text-indigo-900">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Add Role Modal -->
|
||||
<div class="modal fade" id="addRoleModal" tabindex="-1" aria-labelledby="addRoleModalLabel" aria-hidden="true">
|
||||
|
||||
308
settings.php
@ -4,6 +4,19 @@ if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Mock data for roles and users, replace with database data
|
||||
$roles = [
|
||||
['id' => 1, 'name' => 'Admin', 'description' => 'Full access to all features and settings.'],
|
||||
['id' => 2, 'name' => 'HR Manager', 'description' => 'Can manage candidates, workflows, and view reports.'],
|
||||
['id' => 3, 'name' => 'Team Member', 'description' => 'Limited access to assigned tasks and candidates.']
|
||||
];
|
||||
|
||||
$users = [
|
||||
['id' => 1, 'name' => 'Alexandre Dubois', 'email' => 'alex@finmox.com', 'role' => 'Admin', 'status' => 'Active', 'avatar' => 'https://i.pravatar.cc/40?u=1'],
|
||||
['id' => 2, 'name' => 'Béatrice Martin', 'email' => 'beatrice@finmox.com', 'role' => 'HR Manager', 'status' => 'Active', 'avatar' => 'https://i.pravatar.cc/40?u=2'],
|
||||
['id' => 3, 'name' => 'Charles Leclerc', 'email' => 'charles@finmox.com', 'role' => 'Team Member', 'status' => 'Invited', 'avatar' => 'https://i.pravatar.cc/40?u=3'],
|
||||
];
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@ -18,17 +31,9 @@ if (!isset($_SESSION['user_id'])) {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
}
|
||||
body {
|
||||
background-color: #fafafa;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.tab-active {
|
||||
border-bottom-color: #000;
|
||||
color: #000;
|
||||
font-weight: 600;
|
||||
}
|
||||
.tab-inactive {
|
||||
border-bottom-color: transparent;
|
||||
color: #6b7280;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-100">
|
||||
@ -52,194 +57,135 @@ if (!isset($_SESSION['user_id'])) {
|
||||
|
||||
<!-- Main content -->
|
||||
<div class="flex-1 flex flex-col overflow-hidden">
|
||||
<header class="flex justify-between items-center p-6 bg-white border-b">
|
||||
<h1 class="text-2xl font-bold text-gray-900">Settings</h1>
|
||||
<div>
|
||||
<!-- Any header content can go here -->
|
||||
</div>
|
||||
</header>
|
||||
<main class="flex-1 overflow-x-hidden overflow-y-auto bg-gray-100 p-6">
|
||||
<div>
|
||||
<div class="border-b border-gray-200">
|
||||
<nav class="-mb-px flex space-x-8" aria-label="Tabs">
|
||||
<button onclick="changeTab('profile')" id="tab-profile" class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm tab-active">Company Profile</button>
|
||||
<button onclick="changeTab('integrations')" id="tab-integrations" class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm tab-inactive">Integrations</button>
|
||||
<button onclick="changeTab('team')" id="tab-team" class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm tab-inactive">Team Members</button>
|
||||
<button onclick="changeTab('billing')" id="tab-billing" class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm tab-inactive">Billing</button>
|
||||
</nav>
|
||||
</div>
|
||||
<main class="flex-1 overflow-x-hidden overflow-y-auto bg-[#fafafa] pb-20 md:pb-0">
|
||||
<div class="max-w-7xl mx-auto px-4 md:px-6 py-4 md:py-8">
|
||||
|
||||
<!-- Tab Content -->
|
||||
<div id="content-profile" class="mt-6">
|
||||
<div class="bg-white shadow rounded-lg p-6">
|
||||
<h2 class="text-xl font-semibold mb-4">Company Profile</h2>
|
||||
<form>
|
||||
<div class="mb-4">
|
||||
<label for="company-name" class="block text-sm font-medium text-gray-700">Company Name</label>
|
||||
<input type="text" id="company-name" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-black focus:ring-black sm:text-sm" placeholder="FinMox">
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="logo-upload" class="block text-sm font-medium text-gray-700">Company Logo</label>
|
||||
<div class="mt-1 flex items-center">
|
||||
<span class="inline-block h-12 w-12 rounded-full overflow-hidden bg-gray-100">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="Current Logo">
|
||||
</span>
|
||||
<input type="file" id="logo-upload" class="ml-5 bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="timezone" class="block text-sm font-medium text-gray-700">Timezone</label>
|
||||
<select id="timezone" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-black focus:ring-black sm:text-sm">
|
||||
<option>Pacific Standard Time (PST)</option>
|
||||
<option selected>Eastern Standard Time (EST)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="date-format" class="block text-sm font-medium text-gray-700">Date Format</label>
|
||||
<select id="date-format" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-black focus:ring-black sm:text-sm">
|
||||
<option>MM/DD/YYYY</option>
|
||||
<option>DD/MM/YYYY</option>
|
||||
<option selected>YYYY-MM-DD</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<button type="submit" class="bg-black text-white px-4 py-2 rounded-lg text-sm font-medium">Save Changes</button>
|
||||
</div>
|
||||
</form>
|
||||
<!-- SettingsHeader -->
|
||||
<div class="md:flex md:items-center md:justify-between">
|
||||
<div class="flex-1 min-w-0">
|
||||
<h2 class="text-2xl font-bold leading-7 text-gray-900 sm:text-3xl sm:truncate">
|
||||
Settings
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-gray-500">
|
||||
Manage your workspace and team settings.
|
||||
</p>
|
||||
</div>
|
||||
<div class="mt-4 flex md:mt-0 md:ml-4">
|
||||
<button type="button" class="inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||
Invite team
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="content-integrations" class="hidden mt-6">
|
||||
<div class="bg-white shadow rounded-lg p-6">
|
||||
<h2 class="text-xl font-semibold mb-4">Integrations</h2>
|
||||
<div class="space-y-4">
|
||||
<!-- Gmail -->
|
||||
<div class="flex items-center justify-between p-4 border rounded-lg">
|
||||
<div class="flex items-center gap-4">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/7/7e/Gmail_icon_%282020%29.svg/1200px-Gmail_icon_%282020%29.svg.png" alt="Gmail" class="h-8 w-8">
|
||||
<span class="font-medium">Gmail</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<span class="text-sm text-green-600 font-semibold">Connected</span>
|
||||
<button class="bg-gray-200 text-gray-700 px-4 py-2 rounded-lg text-sm font-medium">Configure</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Slack -->
|
||||
<div class="flex items-center justify-between p-4 border rounded-lg">
|
||||
<div class="flex items-center gap-4">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/d/d5/Slack_icon_2019.svg" alt="Slack" class="h-8 w-8">
|
||||
<span class="font-medium">Slack</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<span class="text-sm text-gray-500">Not Connected</span>
|
||||
<button class="bg-black text-white px-4 py-2 rounded-lg text-sm font-medium">Connect</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Google Calendar -->
|
||||
<div class="flex items-center justify-between p-4 border rounded-lg">
|
||||
<div class="flex items-center gap-4">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/a/a5/Google_Calendar_icon_%282020%29.svg" alt="Google Calendar" class="h-8 w-8">
|
||||
<span class="font-medium">Google Calendar</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<span class="text-sm text-green-600 font-semibold">Connected</span>
|
||||
<button class="bg-gray-200 text-gray-700 px-4 py-2 rounded-lg text-sm font-medium">Configure</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Generic ATS -->
|
||||
<div class="flex items-center justify-between p-4 border rounded-lg">
|
||||
<div class="flex items-center gap-4">
|
||||
<svg class="h-8 w-8 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>
|
||||
<span class="font-medium">Your ATS</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<span class="text-sm text-gray-500">Not Connected</span>
|
||||
<button class="bg-black text-white px-4 py-2 rounded-lg text-sm font-medium">Connect</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-6 md:mt-8 space-y-4 md:space-y-6">
|
||||
|
||||
<div id="content-team" class="hidden mt-6">
|
||||
<div class="bg-white shadow rounded-lg">
|
||||
<div class="flex justify-between items-center p-6">
|
||||
<h2 class="text-xl font-semibold">Team Members</h2>
|
||||
<button class="bg-black text-white px-4 py-2 rounded-lg text-sm font-medium">Invite Team Member</button>
|
||||
<!-- PermissionsTable -->
|
||||
<div class="bg-white shadow-sm rounded-lg">
|
||||
<div class="px-4 py-5 sm:px-6">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">Roles & Permissions</h3>
|
||||
<p class="mt-1 text-sm text-gray-500">Define what each role can see and do.</p>
|
||||
</div>
|
||||
<div class="border-t border-gray-200">
|
||||
<ul class="divide-y divide-gray-200">
|
||||
<?php foreach ($roles as $role): ?>
|
||||
<li class="p-4 sm:p-6 hover:bg-gray-50">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="font-medium text-gray-900"><?php echo htmlspecialchars($role['name']); ?></p>
|
||||
<p class="text-sm text-gray-500"><?php echo htmlspecialchars($role['description']); ?></p>
|
||||
</div>
|
||||
<a href="edit_role.php?id=<?php echo $role['id']; ?>" class="text-sm font-medium text-indigo-600 hover:text-indigo-800">Edit</a>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Role</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
|
||||
<th scope="col" class="relative px-6 py-3"><span class="sr-only">Edit</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">Admin User</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">admin@finmox.com</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Admin</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap"><span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Active</span></td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"><a href="#" class="text-indigo-600 hover:text-indigo-900">Edit</a></td>
|
||||
</tr>
|
||||
<!-- More users... -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="content-billing" class="hidden mt-6">
|
||||
<div class="bg-white shadow rounded-lg p-6">
|
||||
<h2 class="text-xl font-semibold mb-4">Billing</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<!-- Current Plan -->
|
||||
<div class="bg-gray-50 rounded-lg p-6">
|
||||
<h3 class="font-semibold text-gray-900">Current Plan</h3>
|
||||
<p class="mt-2 text-3xl font-bold text-gray-900">Pro Plan</p>
|
||||
<p class="mt-1 text-sm text-gray-500">$99 / month</p>
|
||||
<button class="mt-4 w-full bg-black text-white px-4 py-2 rounded-lg text-sm font-medium">Upgrade Plan</button>
|
||||
</div>
|
||||
<!-- Usage -->
|
||||
<div class="bg-gray-50 rounded-lg p-6">
|
||||
<h3 class="font-semibold text-gray-900">Usage this month</h3>
|
||||
<ul class="mt-4 space-y-2 text-sm">
|
||||
<li class="flex justify-between"><span>Team Members</span><span>1/10</span></li>
|
||||
<li class="flex justify-between"><span>Workflows</span><span>5/20</span></li>
|
||||
<li class="flex justify-between"><span>API Calls</span><span>1,203 / 10,000</span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- WorkspaceSettings -->
|
||||
<div class="bg-white shadow-sm rounded-lg">
|
||||
<div class="px-4 py-5 sm:px-6">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">Workspace</h3>
|
||||
<p class="mt-1 text-sm text-gray-500">Manage your company profile and branding.</p>
|
||||
</div>
|
||||
<div class="mt-6">
|
||||
<h3 class="font-semibold text-gray-900">Payment Method</h3>
|
||||
<div class="mt-4 flex items-center justify-between p-4 border rounded-lg">
|
||||
<div class="flex items-center gap-4">
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/a/a4/Visa_Inc._logo.svg" alt="Visa" class="h-6">
|
||||
<span class="font-medium">Visa ending in 1234</span>
|
||||
<div class="border-t border-gray-200 px-4 py-5 sm:p-6">
|
||||
<form class="space-y-6">
|
||||
<div>
|
||||
<label for="company-name" class="block text-sm font-medium text-gray-700">Company Name</label>
|
||||
<input type="text" id="company-name" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" value="FinMox">
|
||||
</div>
|
||||
<button class="bg-gray-200 text-gray-700 px-4 py-2 rounded-lg text-sm font-medium">Update</button>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700">Company Logo</label>
|
||||
<div class="mt-2 flex items-center space-x-4">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="Logo" class="h-10 w-auto">
|
||||
<input type="file" id="logo-upload" class="text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-indigo-50 file:text-indigo-600 hover:file:bg-indigo-100">
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end">
|
||||
<button type="submit" class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||
Save Changes
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- UserManagement -->
|
||||
<div class="bg-white shadow-sm rounded-lg">
|
||||
<div class="px-4 py-5 sm:px-6">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">User Management</h3>
|
||||
<p class="mt-1 text-sm text-gray-500">Invite, remove, and manage your team members.</p>
|
||||
</div>
|
||||
<div class="border-t border-gray-200">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Role</th>
|
||||
<th scope="col" class="relative px-6 py-3"><span class="sr-only">Edit</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white divide-y divide-gray-200">
|
||||
<?php foreach ($users as $user): ?>
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<div class="flex-shrink-0 h-10 w-10">
|
||||
<img class="h-10 w-10 rounded-full" src="<?php echo htmlspecialchars($user['avatar']); ?>" alt="">
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<div class="text-sm font-medium text-gray-900"><?php echo htmlspecialchars($user['name']); ?></div>
|
||||
<div class="text-sm text-gray-500"><?php echo htmlspecialchars($user['email']); ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<?php if ($user['status'] === 'Active'): ?>
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
|
||||
Active
|
||||
</span>
|
||||
<?php else: ?>
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800">
|
||||
Invited
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"><?php echo htmlspecialchars($user['role']); ?></td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<a href="#" class="text-indigo-600 hover:text-indigo-900">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function changeTab(tabName) {
|
||||
const tabs = ['profile', 'integrations', 'team', 'billing'];
|
||||
tabs.forEach(tab => {
|
||||
document.getElementById('tab-' + tab).classList.remove('tab-active');
|
||||
document.getElementById('tab-' + tab).classList.add('tab-inactive');
|
||||
document.getElementById('content-' + tab).classList.add('hidden');
|
||||
});
|
||||
document.getElementById('tab-' + tabName).classList.add('tab-active');
|
||||
document.getElementById('tab-' + tabName).classList.remove('tab-inactive');
|
||||
document.getElementById('content-' + tabName).classList.remove('hidden');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
200
workflows.php
@ -107,120 +107,84 @@ $workflows = $stmt->fetchAll();
|
||||
<title>FinMox Workflows</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
||||
* {
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
}
|
||||
body {
|
||||
background-color: #fafafa;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
.trigger-dropdown-options::-webkit-scrollbar { width: 6px; }
|
||||
.trigger-dropdown-options::-webkit-scrollbar-track { background: #f1f1f1; }
|
||||
.trigger-dropdown-options::-webkit-scrollbar-thumb { background: #888; border-radius: 3px; }
|
||||
.trigger-dropdown-options::-webkit-scrollbar-thumb:hover { background: #555; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50">
|
||||
<body class="bg-gray-100">
|
||||
<div class="flex h-screen bg-gray-200">
|
||||
<!-- Sidebar -->
|
||||
<div class="w-64 bg-white shadow-md">
|
||||
<div class="p-6">
|
||||
<a href="app.php">
|
||||
<img src="assets/pasted-20251120-051320-b2b0cdfa.png" alt="FinMox Logo" style="height: 32px;">
|
||||
</a>
|
||||
</div>
|
||||
<nav class="mt-6">
|
||||
<a href="dashboard.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Dashboard</a>
|
||||
<a href="chat.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Chat</a>
|
||||
<a href="workflows.php" class="block py-2.5 px-4 rounded transition duration-200 bg-gray-200 text-gray-900 font-semibold">Workflows</a>
|
||||
<a href="roles.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Roles</a>
|
||||
<a href="settings.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Settings</a>
|
||||
<a href="logout.php" class="block py-2.5 px-4 rounded transition duration-200 hover:bg-gray-200 text-gray-700">Logout</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<header class="bg-gray-800 text-white shadow-md">
|
||||
<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="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="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>
|
||||
<a href="settings.php" class="text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">Settings</a>
|
||||
|
||||
<!-- User Dropdown -->
|
||||
<div class="relative ml-3">
|
||||
<!-- Main content -->
|
||||
<div class="flex-1 flex flex-col overflow-hidden">
|
||||
<main class="flex-1 overflow-x-hidden overflow-y-auto bg-[#fafafa] pb-20 md:pb-0">
|
||||
<div id="workflows-page" class="min-h-screen p-8">
|
||||
<div class="max-w-7xl mx-auto">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between mb-8">
|
||||
<div>
|
||||
<button type="button" id="user-menu-button" class="max-w-xs bg-gray-800 rounded-full flex items-center text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white" aria-expanded="false" aria-haspopup="true">
|
||||
<span class="sr-only">Open user menu</span>
|
||||
<span class="text-white px-3 py-2 rounded-md text-sm font-medium"><?php echo htmlspecialchars($_SESSION['username']); ?></span>
|
||||
</button>
|
||||
</div>
|
||||
<div id="user-menu" class="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none hidden" role="menu" aria-orientation="vertical" aria-labelledby="user-menu-button" tabindex="-1">
|
||||
<a href="roles.php" class="block px-4 py-2 text-sm text-gray-700" role="menuitem" tabindex="-1" id="user-menu-item-1">Manage Roles</a>
|
||||
<a href="logout.php" class="block px-4 py-2 text-sm text-gray-700" role="menuitem" tabindex="-1" id="user-menu-item-2">Logout</a>
|
||||
<h1 class="text-3xl font-bold text-gray-900">Your Automation Workflows</h1>
|
||||
<p class="text-gray-600 mt-1">Click any workflow to view and edit its actions</p>
|
||||
</div>
|
||||
<?php if (hasPermission('manage_workflows')): ?>
|
||||
<button onclick="openModal('addWorkflowModal')" class="bg-blue-600 text-white px-6 py-3 rounded-lg font-semibold hover:bg-blue-700 flex items-center gap-2">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path></svg>
|
||||
Create New Workflow
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Workflow Cards Grid -->
|
||||
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<?php if (empty($workflows)): ?>
|
||||
<!-- Static Example Cards -->
|
||||
<div class="bg-white rounded-xl shadow-sm border-2 border-gray-200 p-6"><div class="flex items-center gap-3 mb-4"><div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center"><svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"></path></svg></div><div><h3 class="text-lg font-bold text-gray-900">Smart Candidate Intake</h3></div></div><p class="text-gray-600">Automatically captures, enriches, and routes new candidates.</p></div>
|
||||
<div class="bg-white rounded-xl shadow-sm border-2 border-gray-200 p-6"><div class="flex items-center gap-3 mb-4"><div class="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center"><svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg></div><div><h3 class="text-lg font-bold text-gray-900">Interview Scheduling</h3></div></div><p class="text-gray-600">Automates calendar checks and interview coordination.</p></div>
|
||||
<div class="bg-white rounded-xl shadow-sm border-2 border-gray-200 p-6"><div class="flex items-center gap-3 mb-4"><div class="w-12 h-12 bg-purple-100 rounded-lg flex items-center justify-center"><svg class="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path></svg></div><div><h3 class="text-lg font-bold text-gray-900">Onboarding Automation</h3></div></div><p class="text-gray-600">Creates and tracks onboarding tasks for new hires.</p></div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($workflows as $workflow): ?>
|
||||
<a href="workflow_actions.php?id=<?php echo $workflow['id']; ?>" class="block bg-white rounded-xl shadow-sm border-2 border-gray-200 hover:border-blue-500 hover:shadow-lg transition-all p-6">
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<div class="flex items-center gap-3"><div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center"><svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h6m-6 4h6m-6 4h6"></path></svg></div>
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-gray-900"><?php echo htmlspecialchars($workflow['name']); ?></h3>
|
||||
<span class="inline-block px-2 py-1 bg-green-100 text-green-700 text-xs font-semibold rounded mt-1">Active</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-gray-600">Trigger: <strong><?php echo htmlspecialchars(str_replace('_', ' ', ucfirst($workflow['trigger_event']))); ?></strong></p>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="-mr-2 flex md:hidden">
|
||||
<!-- Mobile menu button -->
|
||||
<button type="button" id="mobile-menu-button" class="bg-gray-800 inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white" aria-controls="mobile-menu" aria-expanded="false">
|
||||
<span class="sr-only">Open main menu</span>
|
||||
<svg class="block h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||
</svg>
|
||||
<svg class="hidden h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 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="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>
|
||||
<a href="settings.php" class="text-gray-300 hover:bg-gray-700 hover:text-white block px-3 py-2 rounded-md text-base font-medium">Settings</a>
|
||||
</div>
|
||||
<div class="pt-4 pb-3 border-t border-gray-700">
|
||||
<div class="flex items-center px-5">
|
||||
<div class="ml-3">
|
||||
<div class="text-base font-medium leading-none text-white"><?php echo htmlspecialchars($_SESSION['username']); ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 px-2 space-y-1">
|
||||
<a href="roles.php" class="block px-3 py-2 rounded-md text-base font-medium text-gray-400 hover:text-white hover:bg-gray-700">Manage Roles</a>
|
||||
<a href="logout.php" class="block px-3 py-2 rounded-md text-base font-medium text-gray-400 hover:text-white hover:bg-gray-700">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div id="workflows-page" class="min-h-screen p-8">
|
||||
<div class="max-w-7xl mx-auto">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between mb-8">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold text-gray-900">Your Automation Workflows</h1>
|
||||
<p class="text-gray-600 mt-1">Click any workflow to view and edit its actions</p>
|
||||
</div>
|
||||
<?php if (hasPermission('manage_workflows')): ?>
|
||||
<button onclick="openModal('addWorkflowModal')" class="bg-blue-600 text-white px-6 py-3 rounded-lg font-semibold hover:bg-blue-700 flex items-center gap-2">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path></svg>
|
||||
Create New Workflow
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Workflow Cards Grid -->
|
||||
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<?php if (empty($workflows)): ?>
|
||||
<!-- Static Example Cards -->
|
||||
<div class="bg-white rounded-xl shadow-sm border-2 border-gray-200 p-6"><div class="flex items-center gap-3 mb-4"><div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center"><svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"></path></svg></div><div><h3 class="text-lg font-bold text-gray-900">Smart Candidate Intake</h3></div></div><p class="text-gray-600">Automatically captures, enriches, and routes new candidates.</p></div>
|
||||
<div class="bg-white rounded-xl shadow-sm border-2 border-gray-200 p-6"><div class="flex items-center gap-3 mb-4"><div class="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center"><svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg></div><div><h3 class="text-lg font-bold text-gray-900">Interview Scheduling</h3></div></div><p class="text-gray-600">Automates calendar checks and interview coordination.</p></div>
|
||||
<div class="bg-white rounded-xl shadow-sm border-2 border-gray-200 p-6"><div class="flex items-center gap-3 mb-4"><div class="w-12 h-12 bg-purple-100 rounded-lg flex items-center justify-center"><svg class="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path></svg></div><div><h3 class="text-lg font-bold text-gray-900">Onboarding Automation</h3></div></div><p class="text-gray-600">Creates and tracks onboarding tasks for new hires.</p></div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($workflows as $workflow): ?>
|
||||
<a href="workflow_actions.php?id=<?php echo $workflow['id']; ?>" class="block bg-white rounded-xl shadow-sm border-2 border-gray-200 hover:border-blue-500 hover:shadow-lg transition-all p-6">
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<div class="flex items-center gap-3"><div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center"><svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h6m-6 4h6m-6 4h6"></path></svg></div>
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-gray-900"><?php echo htmlspecialchars($workflow['name']); ?></h3>
|
||||
<span class="inline-block px-2 py-1 bg-green-100 text-green-700 text-xs font-semibold rounded mt-1">Active</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-gray-600">Trigger: <strong><?php echo htmlspecialchars(str_replace('_', ' ', ucfirst($workflow['trigger_event']))); ?></strong></p>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -330,38 +294,6 @@ $workflows = $stmt->fetchAll();
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Header dropdowns
|
||||
const userMenuButton = document.getElementById('user-menu-button');
|
||||
const userMenu = document.getElementById('user-menu');
|
||||
const mobileMenuButton = document.getElementById('mobile-menu-button');
|
||||
const mobileMenu = document.getElementById('mobile-menu');
|
||||
|
||||
if (userMenuButton) {
|
||||
userMenuButton.addEventListener('click', () => {
|
||||
const isExpanded = userMenuButton.getAttribute('aria-expanded') === 'true';
|
||||
userMenuButton.setAttribute('aria-expanded', !isExpanded);
|
||||
userMenu.classList.toggle('hidden');
|
||||
});
|
||||
}
|
||||
if (mobileMenuButton) {
|
||||
mobileMenuButton.addEventListener('click', () => {
|
||||
const isExpanded = mobileMenuButton.getAttribute('aria-expanded') === 'true';
|
||||
mobileMenuButton.setAttribute('aria-expanded', !isExpanded);
|
||||
mobileMenu.classList.toggle('hidden');
|
||||
// Toggle icons
|
||||
mobileMenuButton.querySelector('svg.block').classList.toggle('hidden');
|
||||
mobileMenuButton.querySelector('svg.hidden').classList.toggle('hidden');
|
||||
});
|
||||
}
|
||||
|
||||
// Close dropdowns when clicking outside
|
||||
document.addEventListener('click', (event) => {
|
||||
if (userMenu && userMenuButton && !userMenu.classList.contains('hidden') && !userMenuButton.contains(event.target) && !userMenu.contains(event.target)) {
|
||||
userMenu.classList.add('hidden');
|
||||
userMenuButton.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
});
|
||||
|
||||
// Modal and Tab JS
|
||||
const tabTemplates = document.getElementById('tab-templates');
|
||||
const tabScratch = document.getElementById('tab-scratch');
|
||||
|
||||