|
Server : LiteSpeed System : Linux terra.hostitbro.com 5.14.0-611.54.3.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Thu May 7 16:31:24 EDT 2026 x86_64 User : outerorb ( 1091) PHP Version : 8.1.34 Disable Function : mail Directory : /home2/outerorb/emp.outerorbittech.in/admin/ | |
|
Path: /home2/outerorb/emp.outerorbittech.in/admin/recruitment.php
Size: 23.14 KB
Permissions: 0666
<?php
require __DIR__ . '/../includes/helpers.php';
require_admin();
ensure_recruitment_tables();
ensure_employees_table();
$pdo = db();
$flash = flash();
$admin = current_admin();
$errors = [];
$view = $_GET['view'] ?? 'jobs';
$jobId = isset($_GET['job']) ? (int)$_GET['job'] : 0;
// ---- POST ----
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!verify_csrf($_POST['csrf_token'] ?? '')) {
redirect_with_message('recruitment.php', 'Session expired.', 'error');
}
$action = $_POST['action'] ?? '';
// Save job opening
if ($action === 'save_job') {
$id = (int)($_POST['id'] ?? 0);
$title = sanitize_text($_POST['job_title'] ?? '');
$dept = sanitize_text($_POST['department'] ?? '');
$desig = sanitize_text($_POST['designation'] ?? '');
$vacancies = max(1, (int)($_POST['vacancies'] ?? 1));
$type = in_array($_POST['employment_type'] ?? '', ['full_time','part_time','contract','internship']) ? $_POST['employment_type'] : 'full_time';
$status = in_array($_POST['status'] ?? '', ['open','closed','on_hold']) ? $_POST['status'] : 'open';
$desc = sanitize_text($_POST['description'] ?? '');
$closeDate = sanitize_text($_POST['close_date'] ?? '') ?: null;
if (!$title) $errors[] = 'Job title is required.';
if (empty($errors)) {
if ($id) {
$pdo->prepare('UPDATE job_openings SET job_title=?,department=?,designation=?,vacancies=?,employment_type=?,status=?,description=?,close_date=?,updated_at=NOW() WHERE id=?')
->execute([$title, $dept, $desig, $vacancies, $type, $status, $desc ?: null, $closeDate, $id]);
} else {
$pdo->prepare('INSERT INTO job_openings (job_title,department,designation,vacancies,employment_type,status,description,close_date,created_by) VALUES (?,?,?,?,?,?,?,?,?)')
->execute([$title, $dept, $desig, $vacancies, $type, $status, $desc ?: null, $closeDate, $admin['id'] ?? null]);
}
redirect_with_message('recruitment.php', $id ? 'Job updated.' : 'Job created.');
}
}
// Delete job
if ($action === 'delete_job') {
$id = (int)($_POST['id'] ?? 0);
if ($id) {
$pdo->prepare('DELETE FROM candidates WHERE job_id=?')->execute([$id]);
$pdo->prepare('DELETE FROM job_openings WHERE id=?')->execute([$id]);
}
redirect_with_message('recruitment.php', 'Job deleted.');
}
// Add candidate
if ($action === 'add_candidate') {
$jid = (int)($_POST['job_id'] ?? 0);
$name = sanitize_text($_POST['full_name'] ?? '');
$email = sanitize_text($_POST['email'] ?? '');
$phone = sanitize_text($_POST['phone'] ?? '');
$src = sanitize_text($_POST['source'] ?? '');
$notes = sanitize_text($_POST['notes'] ?? '');
if (!$name) $errors[] = 'Candidate name is required.';
if (empty($errors) && $jid) {
$pdo->prepare('INSERT INTO candidates (job_id,full_name,email,phone,source,notes) VALUES (?,?,?,?,?,?)')
->execute([$jid, $name, $email ?: null, $phone ?: null, $src ?: null, $notes ?: null]);
redirect_with_message('recruitment.php?view=candidates&job='.$jid, 'Candidate added.');
} else {
$view = 'candidates';
}
}
// Update candidate stage
if ($action === 'update_stage') {
$cid = (int)($_POST['candidate_id'] ?? 0);
$stage = in_array($_POST['stage'] ?? '', ['applied','screening','interview','offer','joined','rejected']) ? $_POST['stage'] : 'applied';
$notes = sanitize_text($_POST['notes'] ?? '');
$jid = (int)($_POST['job_id'] ?? 0);
if ($cid) {
$pdo->prepare('UPDATE candidates SET stage=?,notes=?,updated_at=NOW() WHERE id=?')->execute([$stage, $notes ?: null, $cid]);
}
redirect_with_message('recruitment.php?view=candidates&job='.$jid, 'Stage updated.');
}
// Delete candidate
if ($action === 'delete_candidate') {
$cid = (int)($_POST['candidate_id'] ?? 0);
$jid = (int)($_POST['job_id'] ?? 0);
if ($cid) { $pdo->prepare('DELETE FROM candidates WHERE id=?')->execute([$cid]); }
redirect_with_message('recruitment.php?view=candidates&job='.$jid, 'Candidate removed.');
}
}
// ---- Load data ----
$jobs = $pdo->query(
'SELECT jo.*, (SELECT COUNT(*) FROM candidates WHERE job_opening_id=jo.id) AS candidate_count
FROM job_openings jo ORDER BY jo.created_at DESC'
)->fetchAll();
$editJob = null;
$job = null;
$candidates = [];
if ($view === 'candidates' && $jobId) {
$js = $pdo->prepare('SELECT * FROM job_openings WHERE id=? LIMIT 1');
$js->execute([$jobId]);
$job = $js->fetch() ?: null;
if ($job) {
$cs = $pdo->prepare('SELECT * FROM candidates WHERE job_id=? ORDER BY created_at DESC');
$cs->execute([$jobId]);
$candidates = $cs->fetchAll();
}
}
if (isset($_GET['edit'])) {
$ej = $pdo->prepare('SELECT * FROM job_openings WHERE id=? LIMIT 1');
$ej->execute([(int)$_GET['edit']]);
$editJob = $ej->fetch() ?: null;
if ($editJob) $view = 'jobs';
}
// Departments for dropdown
$deptRows = $pdo->query("SELECT DISTINCT department FROM employees WHERE deleted_at IS NULL ORDER BY department")->fetchAll();
function e(string $v): string { return htmlspecialchars($v, ENT_QUOTES, 'UTF-8'); }
$stages = ['applied'=>'Applied','screening'=>'Screening','interview'=>'Interview','offer'=>'Offer','joined'=>'Joined','rejected'=>'Rejected'];
$stageColors = ['applied'=>'#dbeafe;color:#1e40af','screening'=>'#fef3c7;color:#92400e','interview'=>'#e9d5ff;color:#6b21a8','offer'=>'#d1fae5;color:#065f46','joined'=>'#bbf7d0;color:#14532d','rejected'=>'#fee2e2;color:#991b1b'];
$jobStatusColors = ['open'=>'#d1fae5;color:#065f46','closed'=>'#f3f4f6;color:#374151','on_hold'=>'#fef3c7;color:#92400e'];
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Recruitment — HRMS</title>
<link rel="stylesheet" href="../assets/css/style.css?v=<?php echo filemtime(__DIR__.'/../assets/css/style.css'); ?>" />
<style>
.status-badge { display:inline-block; padding:2px 10px; border-radius:999px; font-size:.72rem; font-weight:700; }
.table-std { width:100%; border-collapse:collapse; font-size:.83rem; }
.table-std th, .table-std td { padding:9px 12px; border-bottom:1px solid #f3f4f6; }
.table-std th { background:#f9fafb; border-bottom-color:#e5e7eb; text-align:left; font-weight:700; color:#374151; }
.pipeline { display:flex; gap:0; margin-bottom:20px; overflow-x:auto; }
.pipeline-col { flex:1; min-width:130px; border:1px solid #e5e7eb; border-right:none; }
.pipeline-col:last-child { border-right:1px solid #e5e7eb; }
.pipeline-head { padding:8px 12px; font-size:.75rem; font-weight:700; text-transform:uppercase; letter-spacing:.04em; }
.pipeline-item { padding:8px 10px; border-top:1px solid #f3f4f6; font-size:.8rem; cursor:pointer; }
.pipeline-item:hover { background:#f9fafb; }
</style>
</head>
<body class="dashboard-layout">
<div class="app-wrapper">
<?php require '_sidebar.php'; ?>
<div class="main-content">
<div class="top-bar">
<div class="top-bar-left">
<button class="sidebar-toggle" id="sidebarToggle" aria-label="Toggle sidebar">☰</button>
<div>
<h1 class="page-title">Recruitment</h1>
<div class="breadcrumb-nav"><span>Manage job openings and candidate pipeline.</span></div>
</div>
</div>
<div class="top-bar-right">
<?php if ($view === 'candidates' && $job): ?>
<a class="badge" href="recruitment.php">All Jobs</a>
<?php endif; ?>
<a class="badge" href="dashboard.php">Dashboard</a>
<a class="badge" href="logout.php">Logout</a>
</div>
</div>
<div class="dashboard-container">
<?php if ($flash): ?>
<div class="alert <?php echo $flash['type']==='error' ? 'alert-error' : 'alert-success'; ?>"><?php echo e($flash['message']); ?></div>
<?php endif; ?>
<?php foreach ($errors as $err): ?><div class="alert alert-error"><?php echo e($err); ?></div><?php endforeach; ?>
<?php /* ========================= JOBS VIEW ========================= */ ?>
<?php if ($view === 'jobs'): ?>
<div class="card" style="margin-bottom:14px;">
<h4 style="margin:0 0 14px; font-size:.85rem; text-transform:uppercase; color:#6b7280;">
<?php echo $editJob ? 'Edit Job Opening' : 'Post New Job Opening'; ?>
</h4>
<form method="POST">
<input type="hidden" name="csrf_token" value="<?php echo e(csrf_token()); ?>" />
<input type="hidden" name="action" value="save_job" />
<?php if ($editJob): ?><input type="hidden" name="id" value="<?php echo (int)$editJob['id']; ?>" /><?php endif; ?>
<div style="display:grid; grid-template-columns:2fr 1fr 1fr 1fr; gap:12px;">
<div class="form-group" style="margin:0;">
<label>Job Title *</label>
<input type="text" name="job_title" class="form-control" required value="<?php echo e($editJob ? $editJob['job_title'] : ''); ?>" />
</div>
<div class="form-group" style="margin:0;">
<label>Department</label>
<input type="text" name="department" class="form-control" list="dept-list" value="<?php echo e($editJob ? $editJob['department'] : ''); ?>" />
<datalist id="dept-list"><?php foreach ($deptRows as $d): ?><option><?php echo e($d['department']); ?></option><?php endforeach; ?></datalist>
</div>
<div class="form-group" style="margin:0;">
<label>Designation</label>
<input type="text" name="designation" class="form-control" value="<?php echo e($editJob ? $editJob['designation'] : ''); ?>" />
</div>
<div class="form-group" style="margin:0;">
<label>Vacancies</label>
<input type="number" name="vacancies" class="form-control" min="1" value="<?php echo $editJob ? (int)$editJob['vacancies'] : 1; ?>" />
</div>
<div class="form-group" style="margin:0;">
<label>Employment Type</label>
<select name="employment_type" class="form-control">
<?php foreach (['full_time'=>'Full Time','part_time'=>'Part Time','contract'=>'Contract','internship'=>'Internship'] as $k=>$v): ?>
<option value="<?php echo $k; ?>" <?php echo ($editJob && $editJob['employment_type']===$k)?'selected':''; ?>><?php echo $v; ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group" style="margin:0;">
<label>Status</label>
<select name="status" class="form-control">
<?php foreach (['open'=>'Open','on_hold'=>'On Hold','closed'=>'Closed'] as $k=>$v): ?>
<option value="<?php echo $k; ?>" <?php echo ($editJob && $editJob['status']===$k)?'selected':''; ?>><?php echo $v; ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group" style="margin:0;">
<label>Close Date</label>
<input type="date" name="close_date" class="form-control" value="<?php echo e($editJob ? ($editJob['close_date'] ?? '') : ''); ?>" />
</div>
<div class="form-group" style="margin:0; grid-column:span 4;">
<label>Description / Requirements</label>
<textarea name="description" class="form-control" rows="3"><?php echo e($editJob ? ($editJob['description'] ?? '') : ''); ?></textarea>
</div>
</div>
<button type="submit" class="btn btn-primary" style="margin-top:12px;"><?php echo $editJob ? 'Update Job' : 'Post Job'; ?></button>
<?php if ($editJob): ?><a href="recruitment.php" style="margin-left:10px; font-size:.85rem; color:#6b7280;">Cancel</a><?php endif; ?>
</form>
</div>
<div class="card" style="overflow-x:auto;">
<table class="table-std">
<thead><tr><th>Job Title</th><th>Dept</th><th>Type</th><th>Vacan.</th><th>Candidates</th><th>Close Date</th><th>Status</th><th></th></tr></thead>
<tbody>
<?php if (empty($jobs)): ?>
<tr><td colspan="8" style="text-align:center; padding:28px; color:#9ca3af;">No jobs posted yet.</td></tr>
<?php else: ?>
<?php foreach ($jobs as $j): ?>
<tr>
<td style="font-weight:700;"><?php echo e($j['job_title']); ?></td>
<td style="color:#6b7280;"><?php echo e($j['department']); ?></td>
<td><?php echo ucfirst(str_replace('_',' ',$j['employment_type'])); ?></td>
<td><?php echo (int)$j['vacancies']; ?></td>
<td>
<a href="recruitment.php?view=candidates&job=<?php echo (int)$j['id']; ?>" style="font-weight:700; color:#2563eb;">
<?php echo (int)$j['candidate_count']; ?>
</a>
</td>
<td style="color:#6b7280;"><?php echo $j['close_date'] ? date('d M Y', strtotime($j['close_date'])) : '—'; ?></td>
<td><span class="status-badge" style="background:<?php echo $jobStatusColors[$j['status']] ?? ''; ?>;"><?php echo ucfirst(str_replace('_',' ',$j['status'])); ?></span></td>
<td>
<a href="recruitment.php?edit=<?php echo (int)$j['id']; ?>" style="font-size:.78rem; color:#2563eb;">Edit</a>
<form method="POST" style="display:inline;" onsubmit="return confirm('Delete this job and all its candidates?');">
<input type="hidden" name="csrf_token" value="<?php echo e(csrf_token()); ?>" />
<input type="hidden" name="action" value="delete_job" />
<input type="hidden" name="id" value="<?php echo (int)$j['id']; ?>" />
<button type="submit" style="background:none; border:none; color:#ef4444; cursor:pointer; font-size:.78rem;">Delete</button>
</form>
</td>
</tr>
<?php endforeach; endif; ?>
</tbody>
</table>
</div>
<?php /* ========================= CANDIDATES VIEW ========================= */ ?>
<?php elseif ($view === 'candidates' && $job): ?>
<div style="margin-bottom:14px;">
<h2 style="margin:0 0 4px; font-size:1rem;"><?php echo e($job['job_title']); ?></h2>
<div style="font-size:.8rem; color:#6b7280;">
<?php echo e($job['department']); ?> • <?php echo ucfirst(str_replace('_',' ',$job['employment_type'])); ?>
• <?php echo (int)$job['vacancies']; ?> vacan.
<span class="status-badge" style="background:<?php echo $jobStatusColors[$job['status']] ?? ''; ?>; margin-left:8px;"><?php echo ucfirst(str_replace('_',' ',$job['status'])); ?></span>
</div>
</div>
<!-- Pipeline board -->
<div class="pipeline">
<?php foreach ($stages as $sk => $sv):
$stageEmps = array_filter($candidates, fn($c) => $c['stage'] === $sk);
?>
<div class="pipeline-col">
<div class="pipeline-head" style="background:<?php echo $stageColors[$sk] ?? '#f3f4f6;color:#374151'; ?>;">
<?php echo $sv; ?> (<?php echo count($stageEmps); ?>)
</div>
<?php foreach ($stageEmps as $c): ?>
<div class="pipeline-item" onclick="openStageModal(<?php echo htmlspecialchars(json_encode($c), ENT_QUOTES); ?>)">
<div style="font-weight:700;"><?php echo e($c['full_name']); ?></div>
<div style="font-size:.72rem; color:#6b7280;"><?php echo e($c['source'] ?? ''); ?></div>
</div>
<?php endforeach; ?>
</div>
<?php endforeach; ?>
</div>
<!-- Add candidate form -->
<div class="card" style="margin-bottom:14px;">
<h4 style="margin:0 0 12px; font-size:.85rem; text-transform:uppercase; color:#6b7280;">Add Candidate</h4>
<form method="POST">
<input type="hidden" name="csrf_token" value="<?php echo e(csrf_token()); ?>" />
<input type="hidden" name="action" value="add_candidate" />
<input type="hidden" name="job_id" value="<?php echo $jobId; ?>" />
<div style="display:grid; grid-template-columns:2fr 2fr 1fr 1fr 2fr; gap:12px;">
<div class="form-group" style="margin:0;">
<label>Full Name *</label>
<input type="text" name="full_name" class="form-control" required />
</div>
<div class="form-group" style="margin:0;">
<label>Email</label>
<input type="email" name="email" class="form-control" />
</div>
<div class="form-group" style="margin:0;">
<label>Phone</label>
<input type="text" name="phone" class="form-control" maxlength="20" />
</div>
<div class="form-group" style="margin:0;">
<label>Source</label>
<input type="text" name="source" class="form-control" list="src-list" />
<datalist id="src-list"><option>LinkedIn</option><option>Referral</option><option>Naukri</option><option>Indeed</option><option>Walk-in</option></datalist>
</div>
<div class="form-group" style="margin:0;">
<label>Notes</label>
<input type="text" name="notes" class="form-control" />
</div>
</div>
<button type="submit" class="btn btn-primary" style="margin-top:12px;">Add Candidate</button>
</form>
</div>
<!-- Full candidate list -->
<div class="card" style="overflow-x:auto;">
<table class="table-std">
<thead><tr><th>Name</th><th>Email</th><th>Phone</th><th>Source</th><th>Stage</th><th>Added</th><th></th></tr></thead>
<tbody>
<?php if (empty($candidates)): ?>
<tr><td colspan="7" style="text-align:center; padding:28px; color:#9ca3af;">No candidates yet.</td></tr>
<?php else: ?>
<?php foreach ($candidates as $c): ?>
<tr>
<td style="font-weight:700;"><?php echo e($c['full_name']); ?></td>
<td><?php echo e($c['email'] ?? ''); ?></td>
<td><?php echo e($c['phone'] ?? ''); ?></td>
<td style="color:#6b7280;"><?php echo e($c['source'] ?? ''); ?></td>
<td><span class="status-badge" style="background:<?php echo $stageColors[$c['stage']] ?? ''; ?>;"><?php echo $stages[$c['stage']] ?? e($c['stage']); ?></span></td>
<td style="color:#6b7280;"><?php echo date('d M Y', strtotime($c['created_at'])); ?></td>
<td>
<button type="button"
onclick="openStageModal(<?php echo htmlspecialchars(json_encode($c), ENT_QUOTES); ?>)"
style="background:none; border:none; color:#2563eb; cursor:pointer; font-size:.78rem;">Update</button>
<form method="POST" style="display:inline;" onsubmit="return confirm('Remove this candidate?');">
<input type="hidden" name="csrf_token" value="<?php echo e(csrf_token()); ?>" />
<input type="hidden" name="action" value="delete_candidate" />
<input type="hidden" name="candidate_id" value="<?php echo (int)$c['id']; ?>" />
<input type="hidden" name="job_id" value="<?php echo $jobId; ?>" />
<button type="submit" style="background:none; border:none; color:#ef4444; cursor:pointer; font-size:.78rem;">Remove</button>
</form>
</td>
</tr>
<?php endforeach; endif; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
<!-- Stage modal -->
<div id="stage-modal" style="display:none; position:fixed; inset:0; background:rgba(0,0,0,.4); z-index:999; align-items:center; justify-content:center;">
<div style="background:#fff; border-radius:12px; padding:28px; width:400px; max-width:95vw;">
<h3 id="stage-modal-name" style="margin:0 0 16px; font-size:1rem;"></h3>
<form method="POST" action="recruitment.php?view=candidates&job=<?php echo $jobId; ?>">
<input type="hidden" name="csrf_token" value="<?php echo e(csrf_token()); ?>" />
<input type="hidden" name="action" value="update_stage" />
<input type="hidden" name="candidate_id" id="stage-cand-id" />
<input type="hidden" name="job_id" value="<?php echo $jobId; ?>" />
<div class="form-group">
<label>Stage</label>
<select name="stage" id="stage-select" class="form-control">
<?php foreach ($stages as $sk=>$sv): ?>
<option value="<?php echo $sk; ?>"><?php echo $sv; ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label>Notes</label>
<textarea name="notes" id="stage-notes" class="form-control" rows="2"></textarea>
</div>
<div style="display:flex; gap:10px;">
<button type="submit" class="btn btn-primary" style="flex:1;">Save</button>
<button type="button" onclick="document.getElementById('stage-modal').style.display='none'"
style="flex:1; background:#f3f4f6; border:1px solid #e5e7eb; border-radius:6px; cursor:pointer;">Cancel</button>
</div>
</form>
</div>
</div>
<script>
function openStageModal(c) {
document.getElementById('stage-modal-name').textContent = c.full_name;
document.getElementById('stage-cand-id').value = c.id;
document.getElementById('stage-select').value = c.stage;
document.getElementById('stage-notes').value = c.notes || '';
document.getElementById('stage-modal').style.display = 'flex';
}
document.getElementById('stage-modal').addEventListener('click', function(e) {
if (e.target === this) this.style.display = 'none';
});
</script>
</div>
</div>
</div>