/* app.js — Halaman daftar pelamar */
(function () {
'use strict';
var $ = function (s, r) { return (r || document).querySelector(s); };
var filters = { q: '', status: '', posisi: '', sumber: '', sort: 'tanggal-desc' };
var SUMBER = ['', 'Iklan', 'Referral', 'Walk-in', 'Job Portal', 'Lainnya'];
var MAX_FILE = 5 * 1024 * 1024; // 5 MB
var SLOTS = [
{ key: 'cv', label: 'CV' },
{ key: 'test', label: 'Hasil Test' },
{ key: 'portofolio', label: 'Portofolio' }
];
/* ---------------- Init ---------------- */
function init() {
Store.init();
bindEvents();
renderAll();
}
function renderAll() {
renderSummary();
fillFilters();
renderList();
}
function esc(v) { return UI.escape(v); }
function statusName(id) {
var s = Store.statusById(id);
return s ? s.nama : '-';
}
/* ---------------- Summary ---------------- */
function renderSummary() {
var list = Store.get().candidates;
var statuses = Store.sortedStatuses();
var html = '
' + list.length +
'
Total Pelamar
';
statuses.forEach(function (s) {
var n = list.filter(function (c) { return c.statusId === s.id; }).length;
html += '
' +
'
' + n + '
' + esc(s.nama) + '
';
});
$('#summary').innerHTML = html;
}
/* ---------------- Filters ---------------- */
function fillFilters() {
var candidates = Store.get().candidates;
var statusSel = $('#fStatus');
statusSel.innerHTML = '' +
Store.sortedStatuses().map(function (s) {
return '';
}).join('');
var posisiSet = {};
candidates.forEach(function (c) { if (c.posisi) posisiSet[c.posisi] = 1; });
var posisiList = Object.keys(posisiSet).sort();
$('#fPosisi').innerHTML = '' +
posisiList.map(function (p) {
return '';
}).join('');
$('#fSumber').innerHTML = '' +
SUMBER.slice(1).map(function (p) {
return '';
}).join('');
$('#fSearch').value = filters.q;
$('#fSort').value = filters.sort;
}
function applyFilters() {
var list = Store.get().candidates.slice();
var q = filters.q.trim().toLowerCase();
if (q) {
var digits = q.replace(/\D/g, '');
list = list.filter(function (c) {
return (c.nama || '').toLowerCase().indexOf(q) >= 0 ||
(c.posisi || '').toLowerCase().indexOf(q) >= 0 ||
(c.email || '').toLowerCase().indexOf(q) >= 0 ||
(c.phoneRaw || '').toLowerCase().indexOf(q) >= 0 ||
(digits !== '' && (c.phoneNorm || '').indexOf(digits) >= 0);
});
}
if (filters.status) list = list.filter(function (c) { return c.statusId === filters.status; });
if (filters.posisi) list = list.filter(function (c) { return c.posisi === filters.posisi; });
if (filters.sumber) list = list.filter(function (c) { return c.sumber === filters.sumber; });
list.sort(function (a, b) {
if (filters.sort === 'nama-asc') return (a.nama || '').localeCompare(b.nama || '', 'id');
var da = a.tanggalMelamar || '', db = b.tanggalMelamar || '';
return filters.sort === 'tanggal-asc' ? da.localeCompare(db) : db.localeCompare(da);
});
return list;
}
/* ---------------- List ---------------- */
function renderList() {
var list = applyFilters();
var tbody = $('#tbody');
var empty = $('#emptyState');
if (!list.length) {
tbody.innerHTML = '';
var adaData = Store.get().candidates.length > 0;
empty.innerHTML = adaData
? 'Tidak ada pelamar yang cocok dengan pencarian/filter.'
: 'Belum ada data pelamar. Klik + Tambah Pelamar untuk memulai.';
empty.classList.remove('hidden');
$('.table-wrap').classList.add('hidden');
return;
}
empty.classList.add('hidden');
$('.table-wrap').classList.remove('hidden');
tbody.innerHTML = list.map(function (c) {
var st = Store.statusById(c.statusId);
var phone = c.phoneNorm ? Phone.formatDisplay(c.phoneNorm) : (c.phoneRaw || '-');
return '
' +
'
' + esc(c.nama) + '' +
(c.email ? '
' + esc(c.email) + '
' : '') + '
' +
'
' + esc(c.posisi) + '
' +
'
' + esc(phone) + '
' +
'
' + UI.badge(st) + '
' +
'
' + esc(UI.formatDate(c.tanggalMelamar)) + '
' +
'
' +
'' +
'' +
'' +
'' +
'
' +
'
';
}).join('');
}
/* ---------------- Form ---------------- */
function statusOptions(selected) {
return Store.sortedStatuses().map(function (s) {
return '';
}).join('');
}
function sumberOptions(selected) {
return SUMBER.map(function (s) {
return '';
}).join('');
}
function openForm(id) {
var c = id ? Store.candidateById(id) : null;
var iv = (c && c.interview) || {};
var defStatus = Store.sortedStatuses()[0];
var today = new Date().toISOString().slice(0, 10);
var body =
'
' +
'' +
'
' +
'
' +
'
' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'
' +
'
' +
'' +
'' +
'' +
'
' +
'
Jadwal Interview
' +
'
' +
'' +
'' +
'' +
'
' +
'
' +
'' +
'
' +
'' +
'
' +
'';
var footer =
'' +
'';
UI.openModal({
title: id ? 'Edit Pelamar' : 'Tambah Pelamar',
size: 'lg',
body: body,
footer: footer,
onMount: function (el) {
var phoneInput = el.querySelector('#f-phone');
var hint = el.querySelector('#phoneHint');
function updHint() {
var norm = Phone.normalize(phoneInput.value);
if (!phoneInput.value.trim()) { hint.textContent = ''; return; }
hint.innerHTML = Phone.isValid(norm)
? '✓ ' + Phone.formatDisplay(norm) + ''
: 'Nomor belum valid';
}
phoneInput.addEventListener('input', updHint);
updHint();
var telpInput = el.querySelector('#i-telp');
var telpHint = el.querySelector('#telpHint');
function updTelpHint() {
var norm = Phone.normalize(telpInput.value);
if (!telpInput.value.trim()) { telpHint.textContent = ''; return; }
telpHint.innerHTML = Phone.isValid(norm)
? '✓ ' + Phone.formatDisplay(norm) + ''
: 'Nomor belum valid';
}
telpInput.addEventListener('input', updTelpHint);
updTelpHint();
el.querySelector('[data-close]').addEventListener('click', UI.closeModal);
el.querySelector('#saveCandidate').addEventListener('click', function () { saveCandidate(id); });
}
});
}
function saveCandidate(id) {
var el = document;
var nama = $('#f-nama').value.trim();
var phoneRaw = $('#f-phone').value.trim();
var phoneNorm = Phone.normalize(phoneRaw);
var posisi = $('#f-posisi').value.trim();
if (!nama) { UI.toast('Nama wajib diisi.', 'err'); return; }
if (!phoneRaw) { UI.toast('No. WhatsApp wajib diisi.', 'err'); return; }
if (!Phone.isValid(phoneNorm)) { UI.toast('Nomor WhatsApp tidak valid.', 'err'); return; }
if (!posisi) { UI.toast('Posisi wajib diisi.', 'err'); return; }
var data = {
nama: nama, phoneRaw: phoneRaw, phoneNorm: phoneNorm,
email: $('#f-email').value.trim(),
posisi: posisi,
statusId: $('#f-status').value,
sumber: $('#f-sumber').value,
domisili: $('#f-domisili').value.trim(),
gajiEkspektasi: $('#f-gaji').value.trim(),
tanggalMelamar: $('#f-tanggal').value,
catatan: $('#f-catatan').value.trim(),
interview: {
tanggal: $('#i-tanggal').value,
jam: $('#i-jam').value,
metode: $('#i-metode').value,
lokasi: $('#i-lokasi').value.trim(),
pewawancara: $('#i-pewawancara').value.trim(),
telpPewawancara: $('#i-telp').value.trim()
}
};
if (id) { Store.updateCandidate(id, data); UI.toast('Data pelamar diperbarui.', 'ok'); }
else { Store.addCandidate(data); UI.toast('Pelamar berhasil ditambahkan.', 'ok'); }
UI.closeModal();
renderAll();
}
/* ---------------- Detail ---------------- */
function openDetail(id) {
var c = Store.candidateById(id);
if (!c) return;
var st = Store.statusById(c.statusId);
var iv = c.interview || {};
var info =
'
' + SLOTS.map(function (slot) {
var b = c.berkas && c.berkas[slot.key];
var value = '-';
if (b && b.mode === 'link') value = '' + esc(b.url) + '';
else if (b && b.mode === 'file') value = esc(b.name) + ' (' + UI.formatBytes(b.size) + ')';
return '
' +
'
' + slot.label + '
' +
'
' + value + '
' +
'
' +
(b ? '' : '') +
'' +
'' +
(b ? '' : '') +
'
' +
'
';
}).join('');
var history = (c.history || []).slice().reverse().map(function (h) {
var dari = h.statusLama ? statusName(h.statusLama) : '(baru)';
return '