2026-02-18 23:01:51 -05:00

1829 lines
59 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ResumeGate — Recruiter Dashboard</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--primary: #6366f1;
--primary-light: #818cf8;
--primary-dark: #4f46e5;
--primary-50: #eef2ff;
--primary-100: #e0e7ff;
--sidebar-bg: #1e1b4b;
--sidebar-hover: #312e81;
--sidebar-active: #3730a3;
--success: #10b981;
--success-bg: #ecfdf5;
--danger: #ef4444;
--danger-bg: #fef2f2;
--warning: #f59e0b;
--warning-bg: #fffbeb;
--info: #3b82f6;
--info-bg: #eff6ff;
--gray-50: #f9fafb;
--gray-100: #f3f4f6;
--gray-200: #e5e7eb;
--gray-300: #d1d5db;
--gray-400: #9ca3af;
--gray-500: #6b7280;
--gray-600: #4b5563;
--gray-700: #374151;
--gray-800: #1f2937;
--gray-900: #111827;
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--radius: 8px;
--radius-lg: 12px;
--radius-xl: 16px;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
background: var(--gray-50);
color: var(--gray-800);
line-height: 1.5;
display: flex;
min-height: 100vh;
-webkit-font-smoothing: antialiased;
}
/* Sidebar */
.sidebar {
width: 260px;
background: var(--sidebar-bg);
color: white;
display: flex;
flex-direction: column;
position: fixed;
top: 0;
left: 0;
bottom: 0;
z-index: 100;
transition: transform 0.3s ease;
}
.sidebar-brand {
padding: 24px 20px;
border-bottom: 1px solid rgba(255,255,255,0.08);
display: flex;
align-items: center;
gap: 12px;
}
.sidebar-brand .logo {
width: 36px;
height: 36px;
background: var(--primary);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
font-weight: 800;
letter-spacing: -1px;
}
.sidebar-brand h1 {
font-size: 18px;
font-weight: 700;
letter-spacing: -0.3px;
}
.sidebar-brand span {
font-size: 11px;
color: rgba(255,255,255,0.5);
text-transform: uppercase;
letter-spacing: 1px;
display: block;
margin-top: 2px;
}
.sidebar-nav {
padding: 16px 12px;
flex: 1;
}
.sidebar-nav a {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 12px;
color: rgba(255,255,255,0.6);
text-decoration: none;
border-radius: var(--radius);
font-size: 14px;
font-weight: 500;
transition: all 0.15s ease;
margin-bottom: 2px;
cursor: pointer;
}
.sidebar-nav a:hover {
background: var(--sidebar-hover);
color: rgba(255,255,255,0.9);
}
.sidebar-nav a.active {
background: var(--sidebar-active);
color: white;
}
.sidebar-nav a svg {
width: 20px;
height: 20px;
flex-shrink: 0;
opacity: 0.7;
}
.sidebar-nav a.active svg { opacity: 1; }
.sidebar-nav .nav-section {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 1.2px;
color: rgba(255,255,255,0.3);
padding: 20px 12px 8px;
font-weight: 600;
}
.sidebar-footer {
padding: 16px 20px;
border-top: 1px solid rgba(255,255,255,0.08);
display: flex;
align-items: center;
gap: 12px;
}
.sidebar-footer .avatar {
width: 36px;
height: 36px;
border-radius: 50%;
background: var(--primary);
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
}
.sidebar-footer .user-info {
flex: 1;
min-width: 0;
}
.sidebar-footer .user-name {
font-size: 13px;
font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.sidebar-footer .user-role {
font-size: 11px;
color: rgba(255,255,255,0.5);
}
/* Main Content */
.main {
flex: 1;
margin-left: 260px;
min-height: 100vh;
}
.topbar {
background: white;
border-bottom: 1px solid var(--gray-200);
padding: 16px 32px;
display: flex;
align-items: center;
justify-content: space-between;
position: sticky;
top: 0;
z-index: 50;
}
.topbar h2 {
font-size: 20px;
font-weight: 700;
color: var(--gray-900);
letter-spacing: -0.3px;
}
.topbar-actions {
display: flex;
align-items: center;
gap: 12px;
}
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 16px;
border-radius: var(--radius);
font-size: 13px;
font-weight: 500;
font-family: inherit;
cursor: pointer;
border: 1px solid var(--gray-200);
background: white;
color: var(--gray-700);
transition: all 0.15s ease;
white-space: nowrap;
}
.btn:hover { background: var(--gray-50); border-color: var(--gray-300); }
.btn-primary {
background: var(--primary);
color: white;
border-color: var(--primary);
}
.btn-primary:hover {
background: var(--primary-dark);
border-color: var(--primary-dark);
}
.btn-sm { padding: 6px 12px; font-size: 12px; }
.btn-success {
background: var(--success);
color: white;
border-color: var(--success);
}
.btn-danger {
background: var(--danger);
color: white;
border-color: var(--danger);
}
.content {
padding: 28px 32px;
}
.page { display: none; }
.page.active { display: block; }
/* Cards */
.metrics-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 28px;
}
.metric-card {
background: white;
border-radius: var(--radius-lg);
padding: 24px;
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
transition: all 0.2s ease;
}
.metric-card:hover {
box-shadow: var(--shadow-md);
transform: translateY(-1px);
}
.metric-card .metric-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}
.metric-card .metric-icon {
width: 40px;
height: 40px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
}
.metric-card .metric-change {
font-size: 12px;
font-weight: 600;
padding: 2px 8px;
border-radius: 20px;
}
.metric-card .metric-change.up { color: var(--success); background: var(--success-bg); }
.metric-card .metric-change.down { color: var(--danger); background: var(--danger-bg); }
.metric-card .metric-value {
font-size: 32px;
font-weight: 800;
color: var(--gray-900);
letter-spacing: -1px;
line-height: 1;
margin-bottom: 4px;
}
.metric-card .metric-label {
font-size: 13px;
color: var(--gray-500);
font-weight: 500;
}
/* Charts */
.charts-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 20px;
margin-bottom: 28px;
}
.chart-card {
background: white;
border-radius: var(--radius-lg);
padding: 24px;
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
}
.chart-card h3 {
font-size: 15px;
font-weight: 600;
color: var(--gray-900);
margin-bottom: 20px;
}
.chart-container {
position: relative;
height: 260px;
}
/* Table */
.table-card {
background: white;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
overflow: hidden;
}
.table-header {
padding: 20px 24px;
border-bottom: 1px solid var(--gray-100);
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
}
.table-header h3 {
font-size: 15px;
font-weight: 600;
color: var(--gray-900);
}
.table-filters {
display: flex;
align-items: center;
gap: 8px;
}
.filter-btn {
padding: 6px 14px;
border-radius: 20px;
font-size: 12px;
font-weight: 500;
border: 1px solid var(--gray-200);
background: white;
color: var(--gray-600);
cursor: pointer;
transition: all 0.15s ease;
font-family: inherit;
}
.filter-btn:hover { border-color: var(--primary); color: var(--primary); }
.filter-btn.active { background: var(--primary); color: white; border-color: var(--primary); }
.search-input {
padding: 7px 12px 7px 36px;
border: 1px solid var(--gray-200);
border-radius: var(--radius);
font-size: 13px;
font-family: inherit;
background: var(--gray-50);
width: 240px;
transition: all 0.15s ease;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='%239ca3af' viewBox='0 0 24 24'%3E%3Cpath d='M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z' stroke='%239ca3af' stroke-width='2' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: 10px center;
}
.search-input:focus {
outline: none;
border-color: var(--primary);
background-color: white;
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
}
table {
width: 100%;
border-collapse: collapse;
}
th {
text-align: left;
padding: 12px 24px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.8px;
color: var(--gray-500);
background: var(--gray-50);
border-bottom: 1px solid var(--gray-100);
cursor: pointer;
user-select: none;
white-space: nowrap;
}
th:hover { color: var(--gray-700); }
td {
padding: 14px 24px;
font-size: 13px;
border-bottom: 1px solid var(--gray-100);
vertical-align: middle;
}
tr:last-child td { border-bottom: none; }
tr:hover td { background: var(--gray-50); }
.applicant-cell {
display: flex;
align-items: center;
gap: 12px;
}
.applicant-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 13px;
color: white;
flex-shrink: 0;
}
.applicant-name { font-weight: 600; color: var(--gray-900); }
.applicant-email { font-size: 12px; color: var(--gray-500); }
.badge {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 3px 10px;
border-radius: 20px;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.3px;
}
.badge-pass { background: var(--success-bg); color: #059669; }
.badge-fail { background: var(--danger-bg); color: #dc2626; }
.badge-review { background: var(--warning-bg); color: #d97706; }
.badge-override { background: var(--info-bg); color: #2563eb; }
.experience-bar {
width: 80px;
height: 6px;
background: var(--gray-100);
border-radius: 3px;
overflow: hidden;
display: inline-block;
vertical-align: middle;
margin-right: 8px;
}
.experience-bar-fill {
height: 100%;
border-radius: 3px;
background: var(--primary);
transition: width 0.3s ease;
}
.table-footer {
padding: 14px 24px;
border-top: 1px solid var(--gray-100);
display: flex;
align-items: center;
justify-content: space-between;
font-size: 13px;
color: var(--gray-500);
}
.pagination {
display: flex;
align-items: center;
gap: 4px;
}
.pagination button {
width: 32px;
height: 32px;
border-radius: var(--radius);
border: 1px solid var(--gray-200);
background: white;
color: var(--gray-600);
font-size: 13px;
font-family: inherit;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s ease;
}
.pagination button:hover { border-color: var(--primary); color: var(--primary); }
.pagination button.active { background: var(--primary); color: white; border-color: var(--primary); }
/* Detail Modal */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.5);
backdrop-filter: blur(4px);
z-index: 200;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
visibility: hidden;
transition: all 0.2s ease;
}
.modal-overlay.active {
opacity: 1;
visibility: visible;
}
.modal {
background: white;
border-radius: var(--radius-xl);
width: 680px;
max-height: 85vh;
overflow-y: auto;
box-shadow: var(--shadow-lg);
transform: scale(0.95);
transition: transform 0.2s ease;
}
.modal-overlay.active .modal { transform: scale(1); }
.modal-header {
padding: 24px 28px;
border-bottom: 1px solid var(--gray-100);
display: flex;
align-items: center;
justify-content: space-between;
position: sticky;
top: 0;
background: white;
border-radius: var(--radius-xl) var(--radius-xl) 0 0;
z-index: 10;
}
.modal-header h3 {
font-size: 18px;
font-weight: 700;
color: var(--gray-900);
}
.modal-close {
width: 32px;
height: 32px;
border-radius: 50%;
border: none;
background: var(--gray-100);
color: var(--gray-500);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
transition: all 0.15s ease;
}
.modal-close:hover { background: var(--gray-200); color: var(--gray-700); }
.modal-body { padding: 28px; }
.detail-section {
margin-bottom: 24px;
}
.detail-section h4 {
font-size: 13px;
font-weight: 600;
color: var(--gray-500);
text-transform: uppercase;
letter-spacing: 0.8px;
margin-bottom: 12px;
}
.detail-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.detail-item {
padding: 12px 16px;
background: var(--gray-50);
border-radius: var(--radius);
}
.detail-item .label {
font-size: 11px;
color: var(--gray-500);
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 4px;
}
.detail-item .value {
font-size: 14px;
font-weight: 600;
color: var(--gray-900);
}
.skills-list {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.skill-tag {
padding: 4px 12px;
background: var(--primary-50);
color: var(--primary-dark);
border-radius: 20px;
font-size: 12px;
font-weight: 500;
}
.modal-actions {
padding: 20px 28px;
border-top: 1px solid var(--gray-100);
display: flex;
justify-content: flex-end;
gap: 8px;
}
/* Rules Page */
.rules-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.rule-card {
background: white;
border-radius: var(--radius-lg);
padding: 24px;
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
}
.rule-card h3 {
font-size: 15px;
font-weight: 600;
color: var(--gray-900);
margin-bottom: 4px;
}
.rule-card .rule-desc {
font-size: 13px;
color: var(--gray-500);
margin-bottom: 16px;
}
.form-group {
margin-bottom: 16px;
}
.form-group label {
display: block;
font-size: 13px;
font-weight: 500;
color: var(--gray-700);
margin-bottom: 6px;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 9px 12px;
border: 1px solid var(--gray-200);
border-radius: var(--radius);
font-size: 13px;
font-family: inherit;
color: var(--gray-800);
background: white;
transition: all 0.15s ease;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
}
.form-group textarea { resize: vertical; min-height: 80px; }
.tag-input-container {
display: flex;
flex-wrap: wrap;
gap: 6px;
padding: 8px 10px;
border: 1px solid var(--gray-200);
border-radius: var(--radius);
background: white;
min-height: 42px;
align-items: center;
cursor: text;
}
.tag-input-container:focus-within {
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
}
.tag-item {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 3px 10px;
background: var(--primary-50);
color: var(--primary-dark);
border-radius: 20px;
font-size: 12px;
font-weight: 500;
}
.tag-item .tag-remove {
cursor: pointer;
font-size: 14px;
line-height: 1;
opacity: 0.6;
margin-left: 2px;
}
.tag-item .tag-remove:hover { opacity: 1; }
.tag-input-field {
border: none;
outline: none;
font-size: 13px;
font-family: inherit;
flex: 1;
min-width: 80px;
padding: 0;
}
/* Settings Page */
.settings-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.settings-card {
background: white;
border-radius: var(--radius-lg);
padding: 24px;
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
}
.settings-card h3 {
font-size: 15px;
font-weight: 600;
color: var(--gray-900);
margin-bottom: 4px;
}
.settings-card .settings-desc {
font-size: 13px;
color: var(--gray-500);
margin-bottom: 16px;
}
.code-block {
background: var(--gray-900);
color: #e5e7eb;
padding: 16px;
border-radius: var(--radius);
font-family: 'JetBrains Mono', 'Fira Code', monospace;
font-size: 12px;
line-height: 1.6;
overflow-x: auto;
position: relative;
}
.code-block .copy-btn {
position: absolute;
top: 8px;
right: 8px;
padding: 4px 10px;
border-radius: 4px;
border: 1px solid rgba(255,255,255,0.2);
background: rgba(255,255,255,0.1);
color: rgba(255,255,255,0.7);
font-size: 11px;
font-family: inherit;
cursor: pointer;
transition: all 0.15s ease;
}
.code-block .copy-btn:hover {
background: rgba(255,255,255,0.2);
color: white;
}
.api-key-display {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 14px;
background: var(--gray-50);
border: 1px solid var(--gray-200);
border-radius: var(--radius);
font-family: monospace;
font-size: 13px;
color: var(--gray-600);
}
.api-key-display .key-text { flex: 1; }
.toggle-switch {
position: relative;
width: 44px;
height: 24px;
display: inline-block;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
inset: 0;
background: var(--gray-300);
border-radius: 12px;
cursor: pointer;
transition: all 0.2s ease;
}
.toggle-slider::before {
content: '';
position: absolute;
left: 3px;
top: 3px;
width: 18px;
height: 18px;
background: white;
border-radius: 50%;
transition: all 0.2s ease;
}
.toggle-switch input:checked + .toggle-slider {
background: var(--primary);
}
.toggle-switch input:checked + .toggle-slider::before {
transform: translateX(20px);
}
.setting-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 0;
border-bottom: 1px solid var(--gray-100);
}
.setting-row:last-child { border-bottom: none; }
.setting-row .setting-info h4 {
font-size: 14px;
font-weight: 500;
color: var(--gray-800);
}
.setting-row .setting-info p {
font-size: 12px;
color: var(--gray-500);
margin-top: 2px;
}
/* Recent Activity */
.activity-list { list-style: none; }
.activity-item {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 12px 0;
border-bottom: 1px solid var(--gray-100);
}
.activity-item:last-child { border-bottom: none; }
.activity-dot {
width: 8px;
height: 8px;
border-radius: 50%;
margin-top: 6px;
flex-shrink: 0;
}
.activity-dot.pass { background: var(--success); }
.activity-dot.fail { background: var(--danger); }
.activity-dot.review { background: var(--warning); }
.activity-text {
font-size: 13px;
color: var(--gray-700);
}
.activity-text strong { color: var(--gray-900); }
.activity-time {
font-size: 11px;
color: var(--gray-400);
margin-top: 2px;
}
/* Toast */
.toast {
position: fixed;
bottom: 24px;
right: 24px;
background: var(--gray-900);
color: white;
padding: 14px 20px;
border-radius: var(--radius);
font-size: 13px;
font-weight: 500;
box-shadow: var(--shadow-lg);
z-index: 300;
transform: translateY(100px);
opacity: 0;
transition: all 0.3s ease;
}
.toast.show {
transform: translateY(0);
opacity: 1;
}
/* Responsive */
@media (max-width: 1200px) {
.metrics-grid { grid-template-columns: repeat(2, 1fr); }
.charts-grid { grid-template-columns: 1fr; }
.rules-container { grid-template-columns: 1fr; }
.settings-grid { grid-template-columns: 1fr; }
}
@media (max-width: 768px) {
.sidebar { transform: translateX(-100%); }
.sidebar.open { transform: translateX(0); }
.main { margin-left: 0; }
.metrics-grid { grid-template-columns: 1fr; }
.content { padding: 20px 16px; }
.topbar { padding: 14px 16px; }
.detail-grid { grid-template-columns: 1fr; }
}
/* Animations */
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-in {
animation: fadeInUp 0.3s ease forwards;
}
.animate-in:nth-child(1) { animation-delay: 0s; }
.animate-in:nth-child(2) { animation-delay: 0.05s; }
.animate-in:nth-child(3) { animation-delay: 0.1s; }
.animate-in:nth-child(4) { animation-delay: 0.15s; }
/* Scrollbar */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--gray-300); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--gray-400); }
</style>
</head>
<body>
<!-- Sidebar -->
<aside class="sidebar">
<div class="sidebar-brand">
<div class="logo">RG</div>
<div>
<h1>ResumeGate</h1>
<span>Classification Engine</span>
</div>
</div>
<nav class="sidebar-nav">
<div class="nav-section">Main</div>
<a class="active" data-page="overview" onclick="switchPage('overview')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
Overview
</a>
<a data-page="applications" onclick="switchPage('applications')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
Applications
<span style="margin-left:auto;background:var(--primary);color:white;font-size:11px;padding:2px 8px;border-radius:10px;font-weight:600;">18</span>
</a>
<div class="nav-section">Configure</div>
<a data-page="rules" onclick="switchPage('rules')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
Eligibility Rules
</a>
<a data-page="settings" onclick="switchPage('settings')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
Settings
</a>
<div class="nav-section">Resources</div>
<a href="widget-demo.html" style="opacity:0.8;">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>
Widget Demo
</a>
</nav>
<div class="sidebar-footer">
<div class="avatar">JR</div>
<div class="user-info">
<div class="user-name">Jessica Rivera</div>
<div class="user-role">Senior Recruiter</div>
</div>
</div>
</aside>
<!-- Main Content -->
<main class="main">
<div class="topbar">
<h2 id="page-title">Overview</h2>
<div class="topbar-actions">
<button class="btn" onclick="exportData()">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
Export CSV
</button>
<button class="btn btn-primary" onclick="switchPage('rules')">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Configure Rules
</button>
</div>
</div>
<div class="content">
<!-- Overview Page -->
<div id="page-overview" class="page active">
<div class="metrics-grid">
<div class="metric-card animate-in">
<div class="metric-header">
<div class="metric-icon" style="background:var(--primary-50);color:var(--primary);">📄</div>
<span class="metric-change up">+12%</span>
</div>
<div class="metric-value">247</div>
<div class="metric-label">Total Applications</div>
</div>
<div class="metric-card animate-in">
<div class="metric-header">
<div class="metric-icon" style="background:var(--success-bg);color:var(--success);"></div>
<span class="metric-change up">+5%</span>
</div>
<div class="metric-value">68%</div>
<div class="metric-label">Pass Rate</div>
</div>
<div class="metric-card animate-in">
<div class="metric-header">
<div class="metric-icon" style="background:var(--warning-bg);color:var(--warning);"></div>
<span class="metric-change down">-0.3</span>
</div>
<div class="metric-value">5.2</div>
<div class="metric-label">Avg. Years Experience</div>
</div>
<div class="metric-card animate-in">
<div class="metric-header">
<div class="metric-icon" style="background:var(--danger-bg);color:var(--danger);"></div>
<span class="metric-change up">3 new</span>
</div>
<div class="metric-value">7</div>
<div class="metric-label">Pending Reviews</div>
</div>
</div>
<div class="charts-grid">
<div class="chart-card animate-in">
<h3>Applications Over Time</h3>
<div class="chart-container">
<canvas id="timelineChart"></canvas>
</div>
</div>
<div class="chart-card animate-in">
<h3>Pass / Fail Distribution</h3>
<div class="chart-container">
<canvas id="passFailChart"></canvas>
</div>
</div>
</div>
<div class="charts-grid">
<div class="chart-card animate-in">
<h3>Industry Distribution</h3>
<div class="chart-container">
<canvas id="industryChart"></canvas>
</div>
</div>
<div class="chart-card animate-in">
<h3>Recent Activity</h3>
<ul class="activity-list" id="activity-list"></ul>
</div>
</div>
</div>
<!-- Applications Page -->
<div id="page-applications" class="page">
<div class="table-card">
<div class="table-header">
<h3>All Applications</h3>
<div class="table-filters">
<button class="filter-btn active" onclick="filterApps('all', this)">All</button>
<button class="filter-btn" onclick="filterApps('pass', this)">Passed</button>
<button class="filter-btn" onclick="filterApps('fail', this)">Failed</button>
<button class="filter-btn" onclick="filterApps('review', this)">Review</button>
<input type="text" class="search-input" placeholder="Search applicants..." oninput="searchApps(this.value)">
</div>
</div>
<div style="overflow-x:auto;">
<table>
<thead>
<tr>
<th onclick="sortApps('name')">Applicant</th>
<th onclick="sortApps('position')">Position</th>
<th onclick="sortApps('experience')">Experience</th>
<th onclick="sortApps('industry')">Industry</th>
<th onclick="sortApps('score')">Score</th>
<th onclick="sortApps('status')">Status</th>
<th onclick="sortApps('date')">Date</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="applications-tbody"></tbody>
</table>
</div>
<div class="table-footer">
<span id="table-info">Showing 1-18 of 18 applications</span>
<div class="pagination">
<button class="active">1</button>
</div>
</div>
</div>
</div>
<!-- Rules Page -->
<div id="page-rules" class="page">
<div class="rules-container">
<div class="rule-card">
<h3>Experience Requirements</h3>
<p class="rule-desc">Set minimum experience thresholds for automatic screening</p>
<div class="form-group">
<label>Minimum Years of Experience</label>
<input type="number" value="3" min="0" max="30" id="rule-min-years">
</div>
<div class="form-group">
<label>Preferred Years of Experience</label>
<input type="number" value="5" min="0" max="30" id="rule-pref-years">
</div>
<div class="form-group">
<label>Maximum Years (Senior cap)</label>
<input type="number" value="20" min="0" max="40" id="rule-max-years">
</div>
<button class="btn btn-primary btn-sm" onclick="saveRules()">Save Changes</button>
</div>
<div class="rule-card">
<h3>Required Skills</h3>
<p class="rule-desc">Candidates must have at least 2 of these skills to pass</p>
<div class="form-group">
<label>Skills (press Enter to add)</label>
<div class="tag-input-container" id="skills-tags">
<span class="tag-item">JavaScript <span class="tag-remove" onclick="removeTag(this)">×</span></span>
<span class="tag-item">React <span class="tag-remove" onclick="removeTag(this)">×</span></span>
<span class="tag-item">Node.js <span class="tag-remove" onclick="removeTag(this)">×</span></span>
<span class="tag-item">TypeScript <span class="tag-remove" onclick="removeTag(this)">×</span></span>
<span class="tag-item">Python <span class="tag-remove" onclick="removeTag(this)">×</span></span>
<input type="text" class="tag-input-field" placeholder="Add skill..." onkeydown="addTag(event, this)">
</div>
</div>
<div class="form-group">
<label>Minimum Skill Matches</label>
<input type="number" value="2" min="1" max="10">
</div>
<button class="btn btn-primary btn-sm" onclick="saveRules()">Save Changes</button>
</div>
<div class="rule-card">
<h3>Industry Preferences</h3>
<p class="rule-desc">Preferred industries for incoming candidates</p>
<div class="form-group">
<label>Preferred Industries</label>
<div class="tag-input-container" id="industry-tags">
<span class="tag-item">Technology <span class="tag-remove" onclick="removeTag(this)">×</span></span>
<span class="tag-item">SaaS <span class="tag-remove" onclick="removeTag(this)">×</span></span>
<span class="tag-item">FinTech <span class="tag-remove" onclick="removeTag(this)">×</span></span>
<input type="text" class="tag-input-field" placeholder="Add industry..." onkeydown="addTag(event, this)">
</div>
</div>
<div class="form-group">
<label>Industry Match Required</label>
<select>
<option>Preferred (bonus points)</option>
<option>Required (must match)</option>
<option>Informational only</option>
</select>
</div>
<button class="btn btn-primary btn-sm" onclick="saveRules()">Save Changes</button>
</div>
<div class="rule-card">
<h3>Blocked Companies</h3>
<p class="rule-desc">Applications from these companies will be auto-flagged for review</p>
<div class="form-group">
<label>Company Names</label>
<div class="tag-input-container" id="blocked-tags">
<span class="tag-item">CompetitorCorp <span class="tag-remove" onclick="removeTag(this)">×</span></span>
<input type="text" class="tag-input-field" placeholder="Add company..." onkeydown="addTag(event, this)">
</div>
</div>
<div class="form-group">
<label>Action on Match</label>
<select>
<option>Flag for manual review</option>
<option>Auto-reject</option>
<option>Allow with warning</option>
</select>
</div>
<button class="btn btn-primary btn-sm" onclick="saveRules()">Save Changes</button>
</div>
</div>
</div>
<!-- Settings Page -->
<div id="page-settings" class="page">
<div class="settings-grid">
<div class="settings-card" style="grid-column: 1 / -1;">
<h3>Embed Widget</h3>
<p class="settings-desc">Copy this snippet and paste it into your career page HTML</p>
<div class="code-block">
<button class="copy-btn" onclick="copyEmbed()">Copy</button>
&lt;!-- ResumeGate Widget --&gt;
&lt;div id="resumegate-widget" data-job-id="sr-frontend-2024"&gt;&lt;/div&gt;
&lt;script src="https://resumegate.mcpengage.com/widget.js"&gt;&lt;/script&gt;
</div>
</div>
<div class="settings-card">
<h3>API Configuration</h3>
<p class="settings-desc">Your API credentials for programmatic access</p>
<div class="form-group">
<label>API Key</label>
<div class="api-key-display">
<span class="key-text" id="api-key-text">rg_live_••••••••••••••••k4Qm</span>
<button class="btn btn-sm" onclick="toggleApiKey()">Show</button>
<button class="btn btn-sm" onclick="copyApiKey()">Copy</button>
</div>
</div>
<div class="form-group">
<label>Webhook URL</label>
<input type="url" placeholder="https://your-ats.com/webhook/resumegate" value="https://api.greenhouse.io/v1/webhooks/resumegate">
</div>
<div class="form-group">
<label>Webhook Events</label>
<div style="display:flex;flex-direction:column;gap:8px;margin-top:4px;">
<label style="display:flex;align-items:center;gap:8px;font-size:13px;cursor:pointer;">
<input type="checkbox" checked> Application Received
</label>
<label style="display:flex;align-items:center;gap:8px;font-size:13px;cursor:pointer;">
<input type="checkbox" checked> Classification Complete
</label>
<label style="display:flex;align-items:center;gap:8px;font-size:13px;cursor:pointer;">
<input type="checkbox"> Override Applied
</label>
</div>
</div>
<button class="btn btn-primary btn-sm" onclick="saveSettings()">Save Settings</button>
</div>
<div class="settings-card">
<h3>Preferences</h3>
<p class="settings-desc">Configure your dashboard experience</p>
<div class="setting-row">
<div class="setting-info">
<h4>Email Notifications</h4>
<p>Receive alerts for new applications</p>
</div>
<label class="toggle-switch">
<input type="checkbox" checked>
<span class="toggle-slider"></span>
</label>
</div>
<div class="setting-row">
<div class="setting-info">
<h4>Auto-process Applications</h4>
<p>Automatically classify without manual trigger</p>
</div>
<label class="toggle-switch">
<input type="checkbox" checked>
<span class="toggle-slider"></span>
</label>
</div>
<div class="setting-row">
<div class="setting-info">
<h4>Slack Integration</h4>
<p>Post results to your Slack channel</p>
</div>
<label class="toggle-switch">
<input type="checkbox">
<span class="toggle-slider"></span>
</label>
</div>
<div class="setting-row">
<div class="setting-info">
<h4>GDPR Auto-Delete</h4>
<p>Delete resume data after 90 days</p>
</div>
<label class="toggle-switch">
<input type="checkbox" checked>
<span class="toggle-slider"></span>
</label>
</div>
</div>
</div>
</div>
</div>
</main>
<!-- Detail Modal -->
<div class="modal-overlay" id="detail-modal">
<div class="modal">
<div class="modal-header">
<h3 id="modal-title">Applicant Details</h3>
<button class="modal-close" onclick="closeModal()">&times;</button>
</div>
<div class="modal-body" id="modal-body"></div>
<div class="modal-actions" id="modal-actions"></div>
</div>
</div>
<!-- Toast -->
<div class="toast" id="toast"></div>
<script>
// ============================================================
// MOCK DATA
// ============================================================
const avatarColors = ['#6366f1','#8b5cf6','#ec4899','#f59e0b','#10b981','#3b82f6','#ef4444','#14b8a6','#f97316','#6366f1'];
const applications = [
{ id:1, name:'Sarah Chen', email:'sarah.chen@gmail.com', position:'Senior Frontend Engineer', experience:7, industry:'Technology', company:'Stripe', skills:['React','TypeScript','Node.js','GraphQL','AWS'], score:92, status:'pass', date:'2024-01-15', education:'MS Computer Science, Stanford', location:'San Francisco, CA' },
{ id:2, name:'Marcus Johnson', email:'m.johnson@outlook.com', position:'Senior Frontend Engineer', experience:4, industry:'FinTech', company:'Robinhood', skills:['React','JavaScript','Python','Docker'], score:78, status:'pass', date:'2024-01-15', education:'BS Software Engineering, Georgia Tech', location:'Atlanta, GA' },
{ id:3, name:'Emily Rodriguez', email:'emily.r@yahoo.com', position:'Senior Frontend Engineer', experience:2, industry:'Healthcare', company:'Oscar Health', skills:['Vue.js','CSS','HTML','jQuery'], score:35, status:'fail', date:'2024-01-14', education:'Bootcamp, General Assembly', location:'New York, NY' },
{ id:4, name:'David Kim', email:'d.kim@proton.me', position:'Full Stack Developer', experience:9, industry:'Technology', company:'Google', skills:['React','TypeScript','Go','Kubernetes','GCP'], score:96, status:'pass', date:'2024-01-14', education:'MS Computer Science, MIT', location:'Seattle, WA' },
{ id:5, name:'Ashley Williams', email:'ashley.w@gmail.com', position:'Senior Frontend Engineer', experience:5, industry:'E-commerce', company:'Shopify', skills:['React','Next.js','TypeScript','Node.js','PostgreSQL'], score:85, status:'pass', date:'2024-01-13', education:'BS Computer Science, UC Berkeley', location:'Toronto, Canada' },
{ id:6, name:'James O\'Brien', email:'jobrien@outlook.com', position:'Full Stack Developer', experience:1, industry:'Consulting', company:'Accenture', skills:['Java','Spring Boot','SQL'], score:28, status:'fail', date:'2024-01-13', education:'BS Information Systems, NYU', location:'New York, NY' },
{ id:7, name:'Priya Patel', email:'priya.p@gmail.com', position:'Senior Frontend Engineer', experience:6, industry:'Technology', company:'Meta', skills:['React','JavaScript','TypeScript','GraphQL','Testing'], score:88, status:'pass', date:'2024-01-12', education:'BE Computer Engineering, IIT Bombay', location:'Menlo Park, CA' },
{ id:8, name:'Michael Torres', email:'m.torres@gmail.com', position:'Senior Frontend Engineer', experience:3, industry:'Media', company:'BuzzFeed', skills:['React','JavaScript','CSS','Webpack'], score:52, status:'fail', date:'2024-01-12', education:'Self-taught', location:'Los Angeles, CA' },
{ id:9, name:'Lisa Chang', email:'lisa.chang@outlook.com', position:'Full Stack Developer', experience:11, industry:'Finance', company:'Goldman Sachs', skills:['Java','React','Python','AWS','Terraform'], score:90, status:'pass', date:'2024-01-11', education:'PhD Computer Science, Carnegie Mellon', location:'New York, NY' },
{ id:10, name:'Robert Garcia', email:'r.garcia@gmail.com', position:'Senior Frontend Engineer', experience:4, industry:'Technology', company:'Netflix', skills:['React','TypeScript','Node.js','Docker','CI/CD'], score:82, status:'pass', date:'2024-01-11', education:'BS Computer Science, UCLA', location:'Los Angeles, CA' },
{ id:11, name:'Anna Kowalski', email:'a.kowalski@proton.me', position:'Senior Frontend Engineer', experience:8, industry:'SaaS', company:'Datadog', skills:['React','TypeScript','Python','Elasticsearch','AWS'], score:91, status:'pass', date:'2024-01-10', education:'MS Software Engineering, ETH Zurich', location:'Berlin, Germany' },
{ id:12, name:'Chris Nakamura', email:'c.naka@gmail.com', position:'Full Stack Developer', experience:2, industry:'Startup', company:'Seed-stage startup', skills:['React','Node.js','MongoDB'], score:45, status:'fail', date:'2024-01-10', education:'BS Computer Science, UIUC', location:'Chicago, IL' },
{ id:13, name:'Sofia Martinez', email:'sofia.m@outlook.com', position:'Senior Frontend Engineer', experience:6, industry:'FinTech', company:'Plaid', skills:['React','TypeScript','Python','PostgreSQL','Docker'], score:87, status:'pass', date:'2024-01-09', education:'MS Information Technology, Columbia', location:'New York, NY' },
{ id:14, name:'Tyler Washington', email:'t.wash@gmail.com', position:'Senior Frontend Engineer', experience:5, industry:'Technology', company:'Airbnb', skills:['React','TypeScript','Node.js','GraphQL'], score:84, status:'pass', date:'2024-01-09', education:'BS Computer Science, Howard University', location:'San Francisco, CA' },
{ id:15, name:'Nina Volkov', email:'nina.v@proton.me', position:'Full Stack Developer', experience:3, industry:'Healthcare', company:'Teladoc', skills:['Angular','Java','Spring','MySQL'], score:41, status:'review', date:'2024-01-08', education:'BS Computer Science, Moscow State', location:'Austin, TX' },
{ id:16, name:'Daniel Park', email:'d.park@gmail.com', position:'Senior Frontend Engineer', experience:10, industry:'Technology', company:'Apple', skills:['Swift','React','TypeScript','Python','ML'], score:94, status:'pass', date:'2024-01-08', education:'MS Computer Science, UC San Diego', location:'Cupertino, CA', override: 'rejected', overrideBy: 'Jessica R.', overrideReason: 'Non-compete clause conflict' },
{ id:17, name:'Rachel Thompson', email:'r.thompson@outlook.com', position:'Senior Frontend Engineer', experience:1, industry:'Retail', company:'Target', skills:['HTML','CSS','JavaScript','WordPress'], score:22, status:'fail', date:'2024-01-07', education:'Associates, Community College', location:'Minneapolis, MN', override: 'approved', overrideBy: 'Jessica R.', overrideReason: 'Strong portfolio despite low experience' },
{ id:18, name:'Hassan Ali', email:'h.ali@gmail.com', position:'Full Stack Developer', experience:7, industry:'Technology', company:'Amazon', skills:['React','TypeScript','Java','AWS','DynamoDB','Terraform'], score:93, status:'pass', date:'2024-01-07', education:'MS Computer Science, University of Michigan', location:'Seattle, WA' }
];
// ============================================================
// PAGE SWITCHING
// ============================================================
function switchPage(page) {
document.querySelectorAll('.page').forEach(p => p.classList.remove('active'));
document.getElementById('page-' + page).classList.add('active');
document.querySelectorAll('.sidebar-nav a').forEach(a => a.classList.remove('active'));
const link = document.querySelector(`[data-page="${page}"]`);
if(link) link.classList.add('active');
const titles = { overview:'Overview', applications:'Applications', rules:'Eligibility Rules', settings:'Settings' };
document.getElementById('page-title').textContent = titles[page] || page;
if(page === 'applications') renderApplications();
}
// ============================================================
// APPLICATIONS TABLE
// ============================================================
let currentFilter = 'all';
let currentSearch = '';
let sortField = 'date';
let sortDir = -1;
function getFilteredApps() {
return applications.filter(a => {
if(currentFilter !== 'all' && a.status !== currentFilter) return false;
if(currentSearch) {
const q = currentSearch.toLowerCase();
return a.name.toLowerCase().includes(q) || a.position.toLowerCase().includes(q) || a.industry.toLowerCase().includes(q) || a.company.toLowerCase().includes(q);
}
return true;
}).sort((a, b) => {
let va = a[sortField], vb = b[sortField];
if(typeof va === 'string') return va.localeCompare(vb) * sortDir;
return (va - vb) * sortDir;
});
}
function filterApps(filter, el) {
currentFilter = filter;
document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
el.classList.add('active');
renderApplications();
}
function searchApps(value) {
currentSearch = value;
renderApplications();
}
function sortApps(field) {
if(sortField === field) sortDir *= -1;
else { sortField = field; sortDir = 1; }
renderApplications();
}
function renderApplications() {
const filtered = getFilteredApps();
const tbody = document.getElementById('applications-tbody');
tbody.innerHTML = filtered.map((a, i) => {
const initials = a.name.split(' ').map(n => n[0]).join('');
const color = avatarColors[i % avatarColors.length];
const expPercent = Math.min(100, (a.experience / 15) * 100);
let statusBadge = '';
if(a.override === 'approved') statusBadge = '<span class="badge badge-override">Override: Approved</span>';
else if(a.override === 'rejected') statusBadge = '<span class="badge badge-override">Override: Rejected</span>';
else if(a.status === 'pass') statusBadge = '<span class="badge badge-pass">✓ Passed</span>';
else if(a.status === 'fail') statusBadge = '<span class="badge badge-fail">✗ Failed</span>';
else statusBadge = '<span class="badge badge-review">⏳ Review</span>';
return `<tr onclick="showDetail(${a.id})" style="cursor:pointer">
<td><div class="applicant-cell">
<div class="applicant-avatar" style="background:${color}">${initials}</div>
<div><div class="applicant-name">${a.name}</div><div class="applicant-email">${a.email}</div></div>
</div></td>
<td>${a.position}</td>
<td><span class="experience-bar"><span class="experience-bar-fill" style="width:${expPercent}%"></span></span>${a.experience} yrs</td>
<td>${a.industry}</td>
<td><strong style="color:${a.score >= 70 ? 'var(--success)' : a.score >= 50 ? 'var(--warning)' : 'var(--danger)'}">${a.score}</strong></td>
<td>${statusBadge}</td>
<td style="color:var(--gray-500);font-size:12px">${formatDate(a.date)}</td>
<td><button class="btn btn-sm" onclick="event.stopPropagation();showDetail(${a.id})">View</button></td>
</tr>`;
}).join('');
document.getElementById('table-info').textContent = `Showing 1-${filtered.length} of ${filtered.length} applications`;
}
function formatDate(d) {
return new Date(d + 'T00:00:00').toLocaleDateString('en-US', { month:'short', day:'numeric' });
}
// ============================================================
// DETAIL MODAL
// ============================================================
function showDetail(id) {
const a = applications.find(x => x.id === id);
if(!a) return;
const initials = a.name.split(' ').map(n => n[0]).join('');
document.getElementById('modal-title').textContent = a.name;
let overrideHtml = '';
if(a.override) {
overrideHtml = `<div class="detail-section">
<h4>Recruiter Override</h4>
<div class="detail-grid">
<div class="detail-item"><div class="label">Decision</div><div class="value" style="color:${a.override==='approved'?'var(--success)':'var(--danger)'}">${a.override === 'approved' ? '✓ Approved' : '✗ Rejected'}</div></div>
<div class="detail-item"><div class="label">By</div><div class="value">${a.overrideBy}</div></div>
<div class="detail-item" style="grid-column:1/-1"><div class="label">Reason</div><div class="value">${a.overrideReason}</div></div>
</div>
</div>`;
}
document.getElementById('modal-body').innerHTML = `
<div class="detail-section">
<h4>Classification Results</h4>
<div style="display:flex;align-items:center;gap:16px;margin-bottom:16px">
<div class="applicant-avatar" style="background:var(--primary);width:48px;height:48px;font-size:16px">${initials}</div>
<div>
<div style="font-size:16px;font-weight:600;color:var(--gray-900)">${a.name}</div>
<div style="font-size:13px;color:var(--gray-500)">${a.email} · ${a.location}</div>
</div>
<div style="margin-left:auto">
<span class="badge ${a.status === 'pass' ? 'badge-pass' : a.status === 'fail' ? 'badge-fail' : 'badge-review'}" style="font-size:14px;padding:6px 16px">
Score: ${a.score}/100
</span>
</div>
</div>
<div class="detail-grid">
<div class="detail-item"><div class="label">Position Applied</div><div class="value">${a.position}</div></div>
<div class="detail-item"><div class="label">Years of Experience</div><div class="value">${a.experience} years</div></div>
<div class="detail-item"><div class="label">Industry</div><div class="value">${a.industry}</div></div>
<div class="detail-item"><div class="label">Current Company</div><div class="value">${a.company}</div></div>
<div class="detail-item"><div class="label">Education</div><div class="value">${a.education}</div></div>
<div class="detail-item"><div class="label">Applied</div><div class="value">${a.date}</div></div>
</div>
</div>
<div class="detail-section">
<h4>Matched Skills</h4>
<div class="skills-list">
${a.skills.map(s => `<span class="skill-tag">${s}</span>`).join('')}
</div>
</div>
${overrideHtml}
<div class="detail-section">
<h4>Eligibility Breakdown</h4>
<div style="background:var(--gray-50);border-radius:var(--radius);padding:16px;">
<div style="display:flex;justify-content:space-between;margin-bottom:8px;font-size:13px;">
<span>Experience (${a.experience}+ yrs)</span>
<span style="color:${a.experience >= 3 ? 'var(--success)' : 'var(--danger)'};font-weight:600">${a.experience >= 3 ? '✓ Pass' : '✗ Fail'}</span>
</div>
<div style="display:flex;justify-content:space-between;margin-bottom:8px;font-size:13px;">
<span>Skill Match (${Math.min(a.skills.length, 4)}/${a.skills.length} required)</span>
<span style="color:${a.skills.length >= 2 ? 'var(--success)' : 'var(--danger)'};font-weight:600">${a.skills.length >= 2 ? '✓ Pass' : '✗ Fail'}</span>
</div>
<div style="display:flex;justify-content:space-between;font-size:13px;">
<span>Industry Relevance</span>
<span style="color:${['Technology','FinTech','SaaS','Finance'].includes(a.industry) ? 'var(--success)' : 'var(--warning)'};font-weight:600">${['Technology','FinTech','SaaS','Finance'].includes(a.industry) ? '✓ Match' : '⚠ Partial'}</span>
</div>
</div>
</div>
`;
const isPass = a.status === 'pass' && !a.override;
const isFail = (a.status === 'fail' || a.status === 'review') && !a.override;
document.getElementById('modal-actions').innerHTML = `
${isFail ? `<button class="btn btn-success btn-sm" onclick="overrideApp(${a.id},'approved')">✓ Override: Approve</button>` : ''}
${isPass ? `<button class="btn btn-danger btn-sm" onclick="overrideApp(${a.id},'rejected')">✗ Override: Reject</button>` : ''}
<button class="btn btn-sm" onclick="closeModal()">Close</button>
`;
document.getElementById('detail-modal').classList.add('active');
}
function closeModal() {
document.getElementById('detail-modal').classList.remove('active');
}
function overrideApp(id, decision) {
const a = applications.find(x => x.id === id);
if(a) {
a.override = decision;
a.overrideBy = 'Jessica R.';
a.overrideReason = decision === 'approved' ? 'Manually approved by recruiter' : 'Manually rejected by recruiter';
showDetail(id);
renderApplications();
showToast(`Application ${decision === 'approved' ? 'approved' : 'rejected'} successfully`);
}
}
// ============================================================
// CHARTS
// ============================================================
function initCharts() {
Chart.defaults.font.family = 'Inter';
Chart.defaults.font.size = 12;
Chart.defaults.color = '#6b7280';
// Timeline Chart
new Chart(document.getElementById('timelineChart'), {
type: 'line',
data: {
labels: ['Jan 1','Jan 3','Jan 5','Jan 7','Jan 9','Jan 11','Jan 13','Jan 15'],
datasets: [{
label: 'Passed',
data: [8, 12, 15, 22, 28, 31, 35, 38],
borderColor: '#10b981',
backgroundColor: 'rgba(16,185,129,0.1)',
fill: true,
tension: 0.4,
borderWidth: 2,
pointRadius: 3,
pointHoverRadius: 6,
},{
label: 'Failed',
data: [3, 5, 8, 10, 13, 14, 16, 18],
borderColor: '#ef4444',
backgroundColor: 'rgba(239,68,68,0.1)',
fill: true,
tension: 0.4,
borderWidth: 2,
pointRadius: 3,
pointHoverRadius: 6,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { position: 'top', align: 'end', labels: { usePointStyle: true, pointStyle: 'circle', padding: 20 } }
},
scales: {
x: { grid: { display: false }, border: { display: false } },
y: { grid: { color: '#f3f4f6' }, border: { display: false }, beginAtZero: true }
},
interaction: { intersect: false, mode: 'index' }
}
});
// Pass/Fail Doughnut
new Chart(document.getElementById('passFailChart'), {
type: 'doughnut',
data: {
labels: ['Passed', 'Failed', 'Under Review'],
datasets: [{
data: [168, 72, 7],
backgroundColor: ['#10b981', '#ef4444', '#f59e0b'],
borderWidth: 0,
hoverOffset: 8,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
cutout: '70%',
plugins: {
legend: { position: 'bottom', labels: { usePointStyle: true, pointStyle: 'circle', padding: 16, font: { size: 12 } } }
}
}
});
// Industry Chart
new Chart(document.getElementById('industryChart'), {
type: 'bar',
data: {
labels: ['Technology', 'FinTech', 'Healthcare', 'E-commerce', 'Finance', 'SaaS', 'Media', 'Consulting'],
datasets: [{
label: 'Applications',
data: [82, 45, 32, 28, 24, 18, 12, 6],
backgroundColor: '#6366f1',
borderRadius: 6,
barPercentage: 0.6,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
indexAxis: 'y',
plugins: { legend: { display: false } },
scales: {
x: { grid: { color: '#f3f4f6' }, border: { display: false }, beginAtZero: true },
y: { grid: { display: false }, border: { display: false } }
}
}
});
}
// ============================================================
// ACTIVITY FEED
// ============================================================
function initActivity() {
const activities = [
{ type:'pass', text:'<strong>Sarah Chen</strong> passed screening — Score: 92', time:'2 hours ago' },
{ type:'pass', text:'<strong>Marcus Johnson</strong> passed screening — Score: 78', time:'3 hours ago' },
{ type:'fail', text:'<strong>Emily Rodriguez</strong> failed screening — Insufficient experience', time:'5 hours ago' },
{ type:'pass', text:'<strong>David Kim</strong> passed screening — Score: 96', time:'8 hours ago' },
{ type:'review', text:'<strong>Nina Volkov</strong> flagged for manual review', time:'1 day ago' },
{ type:'pass', text:'<strong>Ashley Williams</strong> passed screening — Score: 85', time:'1 day ago' },
{ type:'fail', text:'<strong>James O\'Brien</strong> failed screening — Insufficient experience', time:'2 days ago' },
];
document.getElementById('activity-list').innerHTML = activities.map(a =>
`<li class="activity-item">
<div class="activity-dot ${a.type}"></div>
<div>
<div class="activity-text">${a.text}</div>
<div class="activity-time">${a.time}</div>
</div>
</li>`
).join('');
}
// ============================================================
// UTILITY FUNCTIONS
// ============================================================
function removeTag(el) { el.parentElement.remove(); }
function addTag(e, input) {
if(e.key === 'Enter' && input.value.trim()) {
e.preventDefault();
const tag = document.createElement('span');
tag.className = 'tag-item';
tag.innerHTML = `${input.value.trim()} <span class="tag-remove" onclick="removeTag(this)">×</span>`;
input.parentElement.insertBefore(tag, input);
input.value = '';
}
}
function saveRules() { showToast('Eligibility rules saved successfully'); }
function saveSettings() { showToast('Settings saved successfully'); }
function copyEmbed() {
navigator.clipboard?.writeText(`<div id="resumegate-widget" data-job-id="sr-frontend-2024"></div>\n<script src="https://resumegate.mcpengage.com/widget.js"><\/script>`);
showToast('Embed code copied to clipboard');
}
let apiKeyVisible = false;
function toggleApiKey() {
apiKeyVisible = !apiKeyVisible;
document.getElementById('api-key-text').textContent = apiKeyVisible ? 'rg_live_a7f3k92m4x8p1q6w3b5n9d2yk4Qm' : 'rg_live_••••••••••••••••k4Qm';
}
function copyApiKey() {
navigator.clipboard?.writeText('rg_live_a7f3k92m4x8p1q6w3b5n9d2yk4Qm');
showToast('API key copied to clipboard');
}
function exportData() {
const header = 'Name,Email,Position,Experience,Industry,Company,Score,Status,Date\n';
const rows = applications.map(a => `${a.name},${a.email},${a.position},${a.experience},${a.industry},${a.company},${a.score},${a.override || a.status},${a.date}`).join('\n');
const blob = new Blob([header + rows], { type:'text/csv' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url; link.download = 'resumegate-export.csv'; link.click();
URL.revokeObjectURL(url);
showToast('Export downloaded successfully');
}
function showToast(msg) {
const toast = document.getElementById('toast');
toast.textContent = msg;
toast.classList.add('show');
setTimeout(() => toast.classList.remove('show'), 3000);
}
// ============================================================
// INIT
// ============================================================
document.addEventListener('DOMContentLoaded', () => {
initCharts();
initActivity();
renderApplications();
});
// Close modal on overlay click
document.getElementById('detail-modal').addEventListener('click', e => {
if(e.target === e.currentTarget) closeModal();
});
// Close modal on Escape
document.addEventListener('keydown', e => {
if(e.key === 'Escape') closeModal();
});
</script>
</body>
</html>