194 lines
14 KiB
JavaScript
194 lines
14 KiB
JavaScript
/* ============================================================
|
||
settings.js — Settings page with many tabs
|
||
============================================================ */
|
||
window.Views = window.Views || {};
|
||
window.Settings = {};
|
||
|
||
Views.settings = function () {
|
||
const tabs = ['General', 'Users', 'Roles', 'Permissions', 'Notifications', 'Email Templates', 'Career Portal', 'Branding', 'Security', 'Appearance'];
|
||
|
||
const html = `
|
||
<div class="page">
|
||
<div class="page-head">
|
||
<div><h1 class="page-title">Settings</h1><p class="page-sub">Configure your workspace and team preferences</p></div>
|
||
<div class="page-head-actions"><button class="btn btn-primary" onclick="UI.toast('Settings saved','success')">${UI.icon('check')} Save Changes</button></div>
|
||
</div>
|
||
<div class="tabs" id="setTabs">${tabs.map((t, i) => `<div class="tab ${i === 0 ? 'active' : ''}" data-tab="${i}">${t}</div>`).join('')}</div>
|
||
<div id="setPanes">
|
||
${tabs.map((t, i) => `<div class="tab-pane ${i === 0 ? 'active' : ''}" data-pane="${i}">${Settings.pane(t)}</div>`).join('')}
|
||
</div>
|
||
</div>`;
|
||
|
||
return {
|
||
html,
|
||
onMount() {
|
||
const panes = document.querySelectorAll('#setPanes .tab-pane');
|
||
document.querySelectorAll('#setTabs .tab').forEach(tab => tab.onclick = () => {
|
||
document.querySelectorAll('#setTabs .tab').forEach(t => t.classList.remove('active'));
|
||
tab.classList.add('active');
|
||
panes.forEach(p => p.classList.remove('active'));
|
||
panes[+tab.dataset.tab].classList.add('active');
|
||
if (tab.textContent === 'Appearance') Settings._bindTheme();
|
||
});
|
||
Settings._bindTheme();
|
||
}
|
||
};
|
||
};
|
||
|
||
function toggleRow(title, desc, checked) {
|
||
return `<div class="setting-row"><div class="setting-info"><h4>${title}</h4><p>${desc}</p></div>
|
||
<label class="switch"><input type="checkbox" ${checked ? 'checked' : ''} onchange="UI.toast('Preference updated','success')"/><span class="switch-track"></span></label></div>`;
|
||
}
|
||
|
||
Settings.pane = function (name) {
|
||
if (name === 'General') {
|
||
return `<div class="card"><div class="card-body">
|
||
<div class="form-grid">
|
||
<div class="form-field"><label>Organization Name</label><input value="Utopia Brands Inc."/></div>
|
||
<div class="form-field"><label>Company Website</label><input value="https://utopiabrands.com"/></div>
|
||
<div class="form-field"><label>Industry</label><select><option>Consumer Goods</option><option>Technology</option><option>Retail</option></select></div>
|
||
<div class="form-field"><label>Company Size</label><select><option>201–500</option><option>51–200</option><option>500+</option></select></div>
|
||
<div class="form-field"><label>Default Time Zone</label><select><option>(GMT-08:00) Pacific Time</option><option>(GMT-05:00) Eastern Time</option><option>(GMT+00:00) UTC</option></select></div>
|
||
<div class="form-field"><label>Default Currency</label><select><option>USD ($)</option><option>EUR (€)</option><option>GBP (£)</option></select></div>
|
||
</div>
|
||
<div class="divider"></div>
|
||
${toggleRow('Auto-archive stale jobs', 'Automatically close requisitions inactive for 90 days', true)}
|
||
${toggleRow('Duplicate detection', 'Flag candidates that already exist in the system', true)}
|
||
</div></div>`;
|
||
}
|
||
if (name === 'Users') {
|
||
const rows = DB.users.map(u => `<tr>
|
||
<td><div class="user-cell">${UI.avatar(u.name, u.initials, u.color)}<div><div class="cell-primary">${u.name}</div><div class="cell-sub">${u.email}</div></div></div></td>
|
||
<td>${UI.badge(u.role, 'b-indigo')}</td>
|
||
<td>${UI.badge(u.status)}</td>
|
||
<td class="text-muted">${u.lastActive}</td>
|
||
<td style="text-align:right"><div class="row-actions"><button class="act-btn" onclick="UI.toast('Editing ${u.name}','info')">${UI.icon('edit')}</button><button class="act-btn danger" onclick="UI.toast('User removed','success')">${UI.icon('trash')}</button></div></td>
|
||
</tr>`).join('');
|
||
return `<div class="card">
|
||
<div class="card-head"><div><h3>Team Members</h3><span class="ch-sub">${DB.users.length} users</span></div>
|
||
<button class="btn btn-primary btn-sm" onclick="UI.toast('Invite sent','success')">${UI.icon('plus')} Invite User</button></div>
|
||
<div class="table-wrap"><table class="data"><thead><tr><th>User</th><th>Role</th><th>Status</th><th>Last Active</th><th style="text-align:right">Actions</th></tr></thead><tbody>${rows}</tbody></table></div>
|
||
</div>`;
|
||
}
|
||
if (name === 'Roles') {
|
||
return `<div class="card"><div class="card-head"><div><h3>Roles</h3><span class="ch-sub">Define access levels</span></div>
|
||
<button class="btn btn-primary btn-sm" onclick="UI.toast('New role dialog','info')">${UI.icon('plus')} Add Role</button></div>
|
||
<div class="card-body"><div class="list-tight">
|
||
${DB.roles.map(r => `<div class="list-row">
|
||
<span class="kpi-icn i-purple" style="width:40px;height:40px;border-radius:11px">${UI.icon('users')}</span>
|
||
<div class="lr-main"><div class="lr-title">${r.name}</div><div class="lr-sub">${r.desc}</div></div>
|
||
<div class="lr-right"><div class="fw-600">${r.users} users</div><div class="lr-sub">${r.perms}</div></div>
|
||
<button class="act-btn" onclick="UI.toast('Editing ${r.name} role','info')">${UI.icon('edit')}</button>
|
||
</div>`).join('')}
|
||
</div></div></div>`;
|
||
}
|
||
if (name === 'Permissions') {
|
||
const modules = ['Jobs', 'Candidates', 'Interviews', 'Offers', 'Reports', 'Settings'];
|
||
const perms = ['View', 'Create', 'Edit', 'Delete'];
|
||
return `<div class="card"><div class="card-head"><div><h3>Permission Matrix</h3><span class="ch-sub">Recruiter role</span></div>
|
||
<select class="select"><option>Recruiter</option><option>Hiring Manager</option><option>Administrator</option></select></div>
|
||
<div class="table-wrap"><table class="data"><thead><tr><th>Module</th>${perms.map(p => `<th style="text-align:center">${p}</th>`).join('')}</tr></thead>
|
||
<tbody>${modules.map(m => `<tr><td class="cell-primary">${m}</td>${perms.map((p, i) => `<td style="text-align:center">
|
||
<label class="switch"><input type="checkbox" ${(i < 3 || m !== 'Settings') && !(m === 'Settings') ? 'checked' : ''} onchange="UI.toast('Permission updated','success')"/><span class="switch-track"></span></label></td>`).join('')}</tr>`).join('')}</tbody></table></div>
|
||
</div>`;
|
||
}
|
||
if (name === 'Notifications') {
|
||
return `<div class="card"><div class="card-body">
|
||
<div class="form-section-title" style="margin-top:0">Email Notifications</div>
|
||
${toggleRow('New applications', 'Get notified when a candidate applies', true)}
|
||
${toggleRow('Interview reminders', 'Reminders 30 minutes before interviews', true)}
|
||
${toggleRow('Offer responses', 'When candidates accept or decline offers', true)}
|
||
${toggleRow('Weekly digest', 'A summary of hiring activity every Monday', false)}
|
||
<div class="form-section-title">In-App Notifications</div>
|
||
${toggleRow('Mentions', 'When a teammate @mentions you', true)}
|
||
${toggleRow('Stage changes', 'When a candidate moves stages', false)}
|
||
${toggleRow('Task assignments', 'When you are assigned a task', true)}
|
||
</div></div>`;
|
||
}
|
||
if (name === 'Email Templates') {
|
||
const templates = ['Application Received', 'Interview Invitation', 'Assessment Assignment', 'Offer Letter', 'Rejection — Post Interview', 'Reference Request'];
|
||
return `<div class="card"><div class="card-head"><div><h3>Email Templates</h3></div>
|
||
<button class="btn btn-primary btn-sm" onclick="UI.toast('New template','info')">${UI.icon('plus')} New Template</button></div>
|
||
<div class="card-body"><div class="list-tight">
|
||
${templates.map(t => `<div class="list-row"><span class="kpi-icn i-blue" style="width:38px;height:38px;border-radius:10px">${UI.icon('mail')}</span>
|
||
<div class="lr-main"><div class="lr-title">${t}</div><div class="lr-sub">Last edited 3 days ago</div></div>
|
||
${UI.badge('Active', 'b-green')}<button class="act-btn" onclick="UI.toast('Editing template','info')">${UI.icon('edit')}</button></div>`).join('')}
|
||
</div></div></div>`;
|
||
}
|
||
if (name === 'Career Portal') {
|
||
return `<div class="card"><div class="card-body">
|
||
<div class="form-grid">
|
||
<div class="form-field col-span-2"><label>Careers Page URL</label><input value="https://careers.utopiabrands.com"/></div>
|
||
<div class="form-field"><label>Page Headline</label><input value="Build the future with us"/></div>
|
||
<div class="form-field"><label>Primary CTA Text</label><input value="View Open Roles"/></div>
|
||
</div>
|
||
<div class="divider"></div>
|
||
${toggleRow('Public job board', 'Make open roles visible to the public', true)}
|
||
${toggleRow('Allow one-click apply', 'Let candidates apply with LinkedIn', true)}
|
||
${toggleRow('Show salary ranges', 'Display compensation on job listings', false)}
|
||
${toggleRow('Enable referrals', 'Employees can refer candidates', true)}
|
||
</div></div>`;
|
||
}
|
||
if (name === 'Branding') {
|
||
return `<div class="card"><div class="card-body">
|
||
<div class="setting-row"><div class="setting-info"><h4>Company Logo</h4><p>Displayed on career pages and emails</p></div>
|
||
<div class="flex items-center gap-12"><span class="brand-logo" style="width:48px;height:48px">UB</span><button class="btn btn-secondary btn-sm" onclick="UI.toast('Upload dialog','info')">Upload</button></div></div>
|
||
<div class="setting-row"><div class="setting-info"><h4>Brand Color</h4><p>Primary accent across the portal</p></div>
|
||
<div class="flex items-center gap-8">
|
||
${['#004d43', '#ceff71', '#25e9a5', '#8e92ff', '#1a3134', '#eafff4'].map(c => `<span style="width:28px;height:28px;border-radius:8px;background:${c};cursor:pointer;border:2px solid var(--border)" onclick="UI.toast('Brand color updated','success')"></span>`).join('')}
|
||
</div></div>
|
||
<div class="form-grid" style="margin-top:16px">
|
||
<div class="form-field"><label>Email Footer</label><input value="Utopia Brands · San Francisco, CA"/></div>
|
||
<div class="form-field"><label>Support Email</label><input value="talent@utopiabrands.com"/></div>
|
||
</div>
|
||
</div></div>`;
|
||
}
|
||
if (name === 'Security') {
|
||
return `<div class="card"><div class="card-body">
|
||
${toggleRow('Two-factor authentication', 'Require 2FA for all team members', true)}
|
||
${toggleRow('Single Sign-On (SSO)', 'Enable SAML-based SSO login', false)}
|
||
${toggleRow('IP allowlist', 'Restrict access to approved IP ranges', false)}
|
||
${toggleRow('Audit logging', 'Track all data access and changes', true)}
|
||
<div class="form-grid" style="margin-top:16px">
|
||
<div class="form-field"><label>Session Timeout</label><select><option>30 minutes</option><option>1 hour</option><option>8 hours</option></select></div>
|
||
<div class="form-field"><label>Password Policy</label><select><option>Strong (12+ chars)</option><option>Medium (8+ chars)</option></select></div>
|
||
</div>
|
||
<div class="divider"></div>
|
||
<div class="setting-row"><div class="setting-info"><h4>Data Retention</h4><p>Auto-delete candidate data after set period</p></div>
|
||
<select class="select"><option>24 months</option><option>12 months</option><option>36 months</option></select></div>
|
||
</div></div>`;
|
||
}
|
||
if (name === 'Appearance') {
|
||
return `<div class="card"><div class="card-body">
|
||
<div class="form-section-title" style="margin-top:0">Theme</div>
|
||
<div class="grid g-3" style="margin-bottom:8px">
|
||
<div class="card theme-opt" data-theme-set="light" style="cursor:pointer;overflow:hidden">
|
||
<div style="height:80px;background:#f1f7f4;border-bottom:1px solid var(--border);display:flex"><div style="width:30%;background:#1a3134"></div><div style="flex:1;padding:10px"><div style="height:8px;background:#fff;border-radius:4px;margin-bottom:6px"></div><div style="height:8px;width:45%;background:#004d43;border-radius:4px"></div></div></div>
|
||
<div class="card-body" style="padding:12px"><div class="fw-600">Light</div><div class="lr-sub">Clean and bright</div></div>
|
||
</div>
|
||
<div class="card theme-opt" data-theme-set="dark" style="cursor:pointer;overflow:hidden">
|
||
<div style="height:80px;background:#0e1d1f;border-bottom:1px solid var(--border);display:flex"><div style="width:30%;background:#0a1618"></div><div style="flex:1;padding:10px"><div style="height:8px;background:#24403f;border-radius:4px;margin-bottom:6px"></div><div style="height:8px;width:45%;background:#ceff71;border-radius:4px"></div></div></div>
|
||
<div class="card-body" style="padding:12px"><div class="fw-600">Dark</div><div class="lr-sub">Easy on the eyes</div></div>
|
||
</div>
|
||
<div class="card theme-opt" data-theme-set="system" style="cursor:pointer;overflow:hidden">
|
||
<div style="height:80px;background:linear-gradient(90deg,#f1f7f4 50%,#0e1d1f 50%);border-bottom:1px solid var(--border)"></div>
|
||
<div class="card-body" style="padding:12px"><div class="fw-600">System</div><div class="lr-sub">Match OS setting</div></div>
|
||
</div>
|
||
</div>
|
||
<div class="divider"></div>
|
||
${toggleRow('Compact mode', 'Reduce spacing for denser layouts', false)}
|
||
${toggleRow('Show animations', 'Enable transitions and motion', true)}
|
||
</div></div>`;
|
||
}
|
||
return '';
|
||
};
|
||
|
||
Settings._bindTheme = function () {
|
||
document.querySelectorAll('.theme-opt').forEach(opt => opt.onclick = () => {
|
||
const mode = opt.dataset.themeSet;
|
||
if (mode === 'system') { App.setTheme(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); }
|
||
else App.setTheme(mode);
|
||
UI.toast('Theme updated to ' + mode, 'success');
|
||
});
|
||
};
|