Files
E-Recruitment/assets/js/app.js
T
2026-09-18 19:30:05 +07:00

569 lines
26 KiB
JavaScript

/* 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 = '<div class="stat" style="--accent:linear-gradient(135deg,#2563eb,#10b981)"><div class="num">' + list.length +
'</div><div class="lbl">Total Pelamar</div></div>';
statuses.forEach(function (s) {
var n = list.filter(function (c) { return c.statusId === s.id; }).length;
html += '<div class="stat" style="--accent:' + esc(s.warna) + '">' +
'<div class="num">' + n + '</div><div class="lbl">' + esc(s.nama) + '</div></div>';
});
$('#summary').innerHTML = html;
}
/* ---------------- Filters ---------------- */
function fillFilters() {
var candidates = Store.get().candidates;
var statusSel = $('#fStatus');
statusSel.innerHTML = '<option value="">Semua Status</option>' +
Store.sortedStatuses().map(function (s) {
return '<option value="' + esc(s.id) + '"' + (filters.status === s.id ? ' selected' : '') + '>' + esc(s.nama) + '</option>';
}).join('');
var posisiSet = {};
candidates.forEach(function (c) { if (c.posisi) posisiSet[c.posisi] = 1; });
var posisiList = Object.keys(posisiSet).sort();
$('#fPosisi').innerHTML = '<option value="">Semua Posisi</option>' +
posisiList.map(function (p) {
return '<option value="' + esc(p) + '"' + (filters.posisi === p ? ' selected' : '') + '>' + esc(p) + '</option>';
}).join('');
$('#fSumber').innerHTML = '<option value="">Semua Sumber</option>' +
SUMBER.slice(1).map(function (p) {
return '<option value="' + esc(p) + '"' + (filters.sumber === p ? ' selected' : '') + '>' + esc(p) + '</option>';
}).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 <strong>+ Tambah Pelamar</strong> 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 '<tr>' +
'<td><strong>' + esc(c.nama) + '</strong>' +
(c.email ? '<div class="muted small">' + esc(c.email) + '</div>' : '') + '</td>' +
'<td>' + esc(c.posisi) + '</td>' +
'<td>' + esc(phone) + '</td>' +
'<td>' + UI.badge(st) + '</td>' +
'<td>' + esc(UI.formatDate(c.tanggalMelamar)) + '</td>' +
'<td><div class="actions">' +
'<button class="btn btn-warn btn-sm" data-act="reminder" data-id="' + c.id + '">Reminder Pelamar</button>' +
'<button class="btn btn-ghost btn-sm" data-act="detail" data-id="' + c.id + '">Detail</button>' +
'<button class="btn btn-ghost btn-sm" data-act="edit" data-id="' + c.id + '">Edit</button>' +
'<button class="btn btn-danger btn-sm" data-act="del" data-id="' + c.id + '">Hapus</button>' +
'</div></td>' +
'</tr>';
}).join('');
}
/* ---------------- Form ---------------- */
function statusOptions(selected) {
return Store.sortedStatuses().map(function (s) {
return '<option value="' + esc(s.id) + '"' + (s.id === selected ? ' selected' : '') + '>' + esc(s.nama) + '</option>';
}).join('');
}
function sumberOptions(selected) {
return SUMBER.map(function (s) {
return '<option value="' + esc(s) + '"' + (s === selected ? ' selected' : '') + '>' + (s || '- Pilih -') + '</option>';
}).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 =
'<div class="grid cols-2">' +
'<div class="field"><label for="f-nama">Nama lengkap *</label><input type="text" id="f-nama" value="' + esc(c ? c.nama : '') + '"></div>' +
'<div class="field"><label for="f-phone">No. WhatsApp *</label><input type="text" id="f-phone" placeholder="08xxxxxxxxxx" value="' + esc(c ? c.phoneRaw : '') + '"><div class="hint" id="phoneHint"></div></div>' +
'</div>' +
'<div class="grid cols-2">' +
'<div class="field"><label for="f-posisi">Posisi dilamar *</label><input type="text" id="f-posisi" value="' + esc(c ? c.posisi : '') + '"></div>' +
'<div class="field"><label for="f-status">Status</label><select id="f-status">' + statusOptions(c ? c.statusId : (defStatus && defStatus.id)) + '</select></div>' +
'</div>' +
'<div class="grid cols-2">' +
'<div class="field"><label for="f-email">Email</label><input type="email" id="f-email" value="' + esc(c ? c.email : '') + '"></div>' +
'<div class="field"><label for="f-domisili">Domisili</label><input type="text" id="f-domisili" value="' + esc(c ? c.domisili : '') + '"></div>' +
'</div>' +
'<div class="grid cols-3">' +
'<div class="field"><label for="f-sumber">Sumber</label><select id="f-sumber">' + sumberOptions(c ? c.sumber : '') + '</select></div>' +
'<div class="field"><label for="f-gaji">Gaji ekspektasi</label><input type="text" id="f-gaji" placeholder="cth: 5.000.000" value="' + esc(c ? c.gajiEkspektasi : '') + '"></div>' +
'<div class="field"><label for="f-tanggal">Tanggal melamar</label><input type="date" id="f-tanggal" value="' + esc(c ? c.tanggalMelamar : today) + '"></div>' +
'</div>' +
'<h4 class="mt2">Jadwal Interview</h4>' +
'<div class="grid cols-3">' +
'<div class="field"><label for="i-tanggal">Tanggal</label><input type="date" id="i-tanggal" value="' + esc(iv.tanggal || '') + '"></div>' +
'<div class="field"><label for="i-jam">Jam</label><input type="time" id="i-jam" value="' + esc(iv.jam || '') + '"></div>' +
'<div class="field"><label for="i-metode">Metode</label><select id="i-metode">' +
['', 'Online', 'Offline'].map(function (m) {
return '<option value="' + m + '"' + (iv.metode === m ? ' selected' : '') + '>' + (m || '- Pilih -') + '</option>';
}).join('') + '</select></div>' +
'</div>' +
'<div class="grid cols-3">' +
'<div class="field"><label for="i-pewawancara">Nama pewawancara</label><input type="text" id="i-pewawancara" value="' + esc(iv.pewawancara || '') + '"></div>' +
'<div class="field"><label for="i-telp">No. WA pewawancara</label><input type="text" id="i-telp" placeholder="08xxxxxxxxxx" value="' + esc(iv.telpPewawancara || '') + '"><div class="hint" id="telpHint"></div></div>' +
'<div class="field"><label for="i-lokasi">Lokasi / Link meeting</label><input type="text" id="i-lokasi" value="' + esc(iv.lokasi || '') + '"></div>' +
'</div>' +
'<div class="field"><label for="f-catatan">Catatan HR (internal)</label><textarea id="f-catatan">' + esc(c ? c.catatan : '') + '</textarea></div>';
var footer =
'<button class="btn btn-ghost" type="button" data-close>Batal</button>' +
'<button class="btn btn-primary" type="button" id="saveCandidate">' + (id ? 'Simpan Perubahan' : 'Tambah Pelamar') + '</button>';
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)
? '<span style="color:#16a34a">✓ ' + Phone.formatDisplay(norm) + '</span>'
: '<span style="color:#dc2626">Nomor belum valid</span>';
}
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)
? '<span style="color:#16a34a">✓ ' + Phone.formatDisplay(norm) + '</span>'
: '<span style="color:#dc2626">Nomor belum valid</span>';
}
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 =
'<div class="grid cols-2">' +
'<div><div class="muted small">Nama</div><div><strong>' + esc(c.nama) + '</strong></div></div>' +
'<div><div class="muted small">Posisi</div><div>' + esc(c.posisi) + '</div></div>' +
'<div><div class="muted small">No. WhatsApp</div><div>' + esc(c.phoneNorm ? Phone.formatDisplay(c.phoneNorm) : c.phoneRaw) + '</div></div>' +
'<div><div class="muted small">Status</div><div>' + UI.badge(st) + '</div></div>' +
'<div><div class="muted small">Email</div><div>' + esc(c.email || '-') + '</div></div>' +
'<div><div class="muted small">Domisili</div><div>' + esc(c.domisili || '-') + '</div></div>' +
'<div><div class="muted small">Sumber</div><div>' + esc(c.sumber || '-') + '</div></div>' +
'<div><div class="muted small">Gaji ekspektasi</div><div>' + esc(c.gajiEkspektasi || '-') + '</div></div>' +
'<div><div class="muted small">Tanggal melamar</div><div>' + esc(UI.formatDate(c.tanggalMelamar)) + '</div></div>' +
'<div><div class="muted small">Pewawancara</div><div>' + esc(iv.pewawancara || '-') +
(iv.telpPewawancara ? '<div class="muted small">' + esc(Phone.formatDisplay(Phone.normalize(iv.telpPewawancara))) + '</div>' : '') + '</div></div>' +
'</div>';
var jadwal = iv.tanggal
? '<div class="mt1"><div class="muted small">Jadwal Interview</div><div>' +
esc(WA.tanggalLengkap(iv.tanggal)) + (iv.jam ? ' pukul ' + esc(iv.jam) : '') +
(iv.metode ? ' (' + esc(iv.metode) + ')' : '') +
(iv.lokasi ? '<br><span class="muted">' + esc(iv.lokasi) + '</span>' : '') +
'</div></div>'
: '<div class="mt1 muted">Belum ada jadwal interview.</div>';
var catatan = c.catatan ? '<div class="mt1"><div class="muted small">Catatan HR</div><div>' + esc(c.catatan) + '</div></div>' : '';
var berkas = '<h4 class="mt2">Berkas</h4>' + SLOTS.map(function (slot) {
var b = c.berkas && c.berkas[slot.key];
var value = '-';
if (b && b.mode === 'link') value = '<a href="' + esc(b.url) + '" target="_blank" rel="noopener">' + esc(b.url) + '</a>';
else if (b && b.mode === 'file') value = esc(b.name) + ' <span class="muted small">(' + UI.formatBytes(b.size) + ')</span>';
return '<div class="berkas-row">' +
'<div class="berkas-label">' + slot.label + '</div>' +
'<div class="berkas-value">' + value + '</div>' +
'<div class="berkas-actions">' +
(b ? '<button class="btn btn-ghost btn-sm" data-berkas-open="' + slot.key + '" data-id="' + c.id + '">Lihat</button>' : '') +
'<label class="btn btn-ghost btn-sm" style="margin:0">Upload<input type="file" hidden data-berkas-file="' + slot.key + '" data-id="' + c.id + '" accept=".pdf,.doc,.docx,.jpg,.jpeg,.png"></label>' +
'<button class="btn btn-ghost btn-sm" data-berkas-link="' + slot.key + '" data-id="' + c.id + '">Link</button>' +
(b ? '<button class="btn btn-danger btn-sm" data-berkas-del="' + slot.key + '" data-id="' + c.id + '">Hapus</button>' : '') +
'</div>' +
'</div>';
}).join('');
var history = (c.history || []).slice().reverse().map(function (h) {
var dari = h.statusLama ? statusName(h.statusLama) : '(baru)';
return '<div class="small muted">' + esc(UI.formatDateTime(h.at)) + ' — ' + esc(dari) + ' → <strong>' + esc(statusName(h.statusBaru)) + '</strong></div>';
}).join('') || '<div class="small muted">Belum ada riwayat.</div>';
var footer =
'<button class="btn btn-ghost" type="button" data-close>Tutup</button>' +
'<button class="btn btn-warn" type="button" data-act="reminder" data-id="' + c.id + '">Reminder Pelamar</button>' +
'<button class="btn btn-warn" type="button" data-act="pewawancara" data-id="' + c.id + '">Reminder Pewawancara</button>' +
'<button class="btn btn-primary" type="button" data-act="edit" data-id="' + c.id + '">Edit</button>';
UI.openModal({
title: 'Detail Pelamar',
size: 'lg',
body: info + jadwal + catatan + berkas +
'<h4 class="mt2">Riwayat Status</h4><div>' + history + '</div>',
footer: footer,
onMount: function (el) {
el.querySelector('[data-close]').addEventListener('click', UI.closeModal);
}
});
}
/* ---------------- WhatsApp ---------------- */
function openWaDialog(id, mode) {
var c = Store.candidateById(id);
if (!c) return;
var targetPhone = c.phoneNorm;
if (mode === 'pewawancara') {
targetPhone = Phone.normalize((c.interview && c.interview.telpPewawancara) || '');
if (!Phone.isValid(targetPhone)) {
UI.toast('Nomor WA pewawancara belum diisi / tidak valid. Isi lewat tombol Edit.', 'err');
return;
}
} else if (!Phone.isValid(targetPhone)) {
UI.toast('Nomor WhatsApp tidak valid.', 'err');
return;
}
if (mode === 'reminder' && !(c.interview && c.interview.tanggal)) {
if (!UI.confirmDialog('Jadwal interview belum diisi. Bagian hari/tanggal/jam pada pesan akan kosong. Tetap lanjutkan?')) return;
}
var templates = Store.get().templates.filter(function (t) { return t.isActive !== false; });
if (!templates.length) { UI.toast('Belum ada template aktif. Buka menu Template Pesan.', 'err'); return; }
var preselect = null;
var jenisCari = mode === 'reminder' ? 'reminder' : (mode === 'pewawancara' ? 'pewawancara' : null);
if (jenisCari) preselect = templates.filter(function (t) { return t.jenis === jenisCari; })[0];
if (!preselect) preselect = templates.filter(function (t) { return t.statusId === c.statusId; })[0];
if (!preselect) preselect = templates[0];
var ctx = WA.context(c, Store.get().settings);
ctx.status = statusName(c.statusId);
var options = templates.map(function (t) {
return '<option value="' + t.id + '"' + (t.id === preselect.id ? ' selected' : '') + '>' + esc(t.nama) + '</option>';
}).join('');
var body =
'<div class="field"><label for="wa-tpl">Template pesan</label><select id="wa-tpl">' + options + '</select></div>' +
'<div class="field"><label>Preview pesan</label><div class="preview-box" id="wa-preview"></div></div>' +
'<div class="hint">Pesan akan terbuka di WhatsApp dan Anda tinggal menekan tombol kirim.</div>';
var footer =
'<button class="btn btn-ghost" type="button" data-close>Batal</button>' +
'<button class="btn btn-wa" type="button" id="waOpen">Buka WhatsApp</button>';
UI.openModal({
title: mode === 'reminder' ? 'Reminder Interview (Pelamar)' : (mode === 'pewawancara' ? 'Reminder Interview (Pewawancara)' : 'Kirim WhatsApp'),
body: body,
footer: footer,
onMount: function (el) {
var sel = el.querySelector('#wa-tpl');
var prev = el.querySelector('#wa-preview');
function update() {
var t = Store.templateById(sel.value);
prev.textContent = t ? WA.render(t.isi, ctx) : '';
}
sel.addEventListener('change', update);
update();
el.querySelector('[data-close]').addEventListener('click', UI.closeModal);
el.querySelector('#waOpen').addEventListener('click', function () {
var t = Store.templateById(sel.value);
if (!t) return;
WA.open(targetPhone, WA.render(t.isi, ctx));
});
}
});
}
/* ---------------- Berkas ---------------- */
function setBerkas(id, slot, value) {
var c = Store.candidateById(id);
if (!c) return;
c.berkas = c.berkas || { cv: null, test: null, portofolio: null };
var old = c.berkas[slot];
if (old && old.mode === 'file' && old.fileId && (!value || value.fileId !== old.fileId)) {
Store.deleteFile(old.fileId).catch(function () {});
}
c.berkas[slot] = value;
Store.save();
}
function handleBerkasFile(input) {
var file = input.files && input.files[0];
if (!file) return;
var id = input.getAttribute('data-id');
var slot = input.getAttribute('data-berkas-file');
if (file.size > MAX_FILE) { UI.toast('Ukuran berkas maksimal 5 MB.', 'err'); input.value = ''; return; }
UI.toast('Mengunggah berkas...');
Store.saveFile(file).then(function (meta) {
setBerkas(id, slot, { mode: 'file', fileId: meta.id, name: meta.name, size: meta.size, type: meta.type });
UI.toast('Berkas tersimpan.', 'ok');
openDetail(id);
}).catch(function (err) {
UI.toast('Gagal menyimpan berkas: ' + err.message, 'err');
});
input.value = '';
}
/* ---------------- Export / Import Excel ---------------- */
function doExportExcel() {
var list = Store.get().candidates;
if (!list.length) { UI.toast('Belum ada data pelamar untuk diexport.', 'err'); return; }
try {
Excel.exportCandidates(list, Store.get().statuses);
UI.toast('File Excel diunduh.', 'ok');
} catch (err) {
UI.toast('Gagal export Excel: ' + err.message, 'err');
}
}
function doImportExcel(file) {
UI.toast('Membaca file Excel...');
Excel.readRows(file).then(function (json) {
var mapped = Excel.mapRows(json, Store.get().statuses);
var valid = [], invalid = 0;
mapped.forEach(function (row) {
var norm = Phone.normalize(row.phoneRaw || '');
if (!row.nama || !Phone.isValid(norm)) { invalid++; return; }
row.phoneNorm = norm;
valid.push(row);
});
if (!valid.length) {
UI.toast('Tidak ada baris valid. Kolom "Nama" dan "No WhatsApp" wajib terisi.', 'err');
return;
}
var pesan = 'Ditemukan ' + valid.length + ' baris valid' +
(invalid ? ' dan ' + invalid + ' baris dilewati (nama/No WhatsApp kosong atau tidak valid)' : '') +
'.\n\nTambahkan baris valid ke daftar pelamar?';
if (!UI.confirmDialog(pesan)) return;
valid.forEach(function (row) { Store.addCandidate(row); });
UI.toast(valid.length + ' pelamar ditambahkan.', 'ok');
renderAll();
}).catch(function (err) {
UI.toast('Gagal import Excel: ' + err.message, 'err');
});
}
/* ---------------- Events ---------------- */
function bindEvents() {
$('#btnAdd').addEventListener('click', function () { openForm(null); });
$('#fSearch').addEventListener('input', function (e) { filters.q = e.target.value; renderList(); });
$('#fStatus').addEventListener('change', function (e) { filters.status = e.target.value; renderList(); });
$('#fPosisi').addEventListener('change', function (e) { filters.posisi = e.target.value; renderList(); });
$('#fSumber').addEventListener('change', function (e) { filters.sumber = e.target.value; renderList(); });
$('#fSort').addEventListener('change', function (e) { filters.sort = e.target.value; renderList(); });
$('#btnExportExcel').addEventListener('click', doExportExcel);
$('#btnImportExcel').addEventListener('click', function () { $('#importExcelFile').click(); });
$('#importExcelFile').addEventListener('change', function (e) {
if (e.target.files && e.target.files[0]) doImportExcel(e.target.files[0]);
e.target.value = '';
});
// Delegasi klik aksi
document.addEventListener('click', function (e) {
var btn = e.target.closest('[data-act]');
if (btn) {
var id = btn.getAttribute('data-id');
var act = btn.getAttribute('data-act');
if (act === 'wa') openWaDialog(id, 'status');
else if (act === 'reminder') openWaDialog(id, 'reminder');
else if (act === 'pewawancara') openWaDialog(id, 'pewawancara');
else if (act === 'detail') openDetail(id);
else if (act === 'edit') openForm(id);
else if (act === 'del') {
var c = Store.candidateById(id);
if (c && UI.confirmDialog('Hapus pelamar "' + c.nama + '"? Tindakan ini tidak bisa dibatalkan.')) {
Store.deleteCandidate(id);
UI.toast('Pelamar dihapus.', 'ok');
UI.closeModal();
renderAll();
}
}
return;
}
var openBtn = e.target.closest('[data-berkas-open]');
if (openBtn) {
var cid = openBtn.getAttribute('data-id');
var slot = openBtn.getAttribute('data-berkas-open');
var cand = Store.candidateById(cid);
var b = cand && cand.berkas && cand.berkas[slot];
if (!b) return;
if (b.mode === 'link') { window.open(b.url, '_blank', 'noopener'); return; }
Store.getFileUrl(b.fileId).then(function (url) { window.open(url, '_blank', 'noopener'); })
.catch(function () { UI.toast('Berkas tidak ditemukan.', 'err'); });
return;
}
var linkBtn = e.target.closest('[data-berkas-link]');
if (linkBtn) {
var cid2 = linkBtn.getAttribute('data-id');
var slot2 = linkBtn.getAttribute('data-berkas-link');
var url = window.prompt('Masukkan URL berkas (Google Drive, dll):');
if (url && url.trim()) {
setBerkas(cid2, slot2, { mode: 'link', url: url.trim() });
UI.toast('Link tersimpan.', 'ok');
openDetail(cid2);
}
return;
}
var delBtn = e.target.closest('[data-berkas-del]');
if (delBtn) {
var cid3 = delBtn.getAttribute('data-id');
var slot3 = delBtn.getAttribute('data-berkas-del');
if (UI.confirmDialog('Hapus berkas ini?')) {
setBerkas(cid3, slot3, null);
UI.toast('Berkas dihapus.', 'ok');
openDetail(cid3);
}
}
});
// Delegasi input file berkas
document.addEventListener('change', function (e) {
var input = e.target.closest('[data-berkas-file]');
if (input) handleBerkasFile(input);
});
}
document.addEventListener('DOMContentLoaded', init);
})();