204 lines
8.5 KiB
JavaScript
204 lines
8.5 KiB
JavaScript
/* settings.js — Halaman pengaturan: perusahaan, HR, status, data */
|
|
(function () {
|
|
'use strict';
|
|
|
|
var $ = function (s, r) { return (r || document).querySelector(s); };
|
|
function esc(v) { return UI.escape(v); }
|
|
|
|
function init() {
|
|
Store.init();
|
|
loadSettings();
|
|
renderStatuses();
|
|
renderInfo();
|
|
bind();
|
|
}
|
|
|
|
function loadSettings() {
|
|
var s = Store.get().settings;
|
|
$('#s-perusahaan').value = s.perusahaan || '';
|
|
$('#s-hr').value = s.hr || '';
|
|
$('#s-jabatan').value = s.jabatanHr || '';
|
|
}
|
|
|
|
function renderStatuses() {
|
|
var list = Store.sortedStatuses();
|
|
$('#statusBody').innerHTML = list.map(function (s) {
|
|
return '<tr>' +
|
|
'<td>' + s.urutan + '</td>' +
|
|
'<td><span class="badge" style="background:' + esc(s.warna) + '">' + esc(s.nama) + '</span></td>' +
|
|
'<td><code>' + esc(s.warna) + '</code></td>' +
|
|
'<td>' + (s.isFinal ? 'Ya' : '-') + '</td>' +
|
|
'<td><div class="actions">' +
|
|
'<button class="btn btn-ghost btn-sm" data-st-edit="' + s.id + '">Edit</button>' +
|
|
'<button class="btn btn-danger btn-sm" data-st-del="' + s.id + '">Hapus</button>' +
|
|
'</div></td>' +
|
|
'</tr>';
|
|
}).join('');
|
|
}
|
|
|
|
function renderInfo() {
|
|
Store.fileCount().then(function (n) {
|
|
$('#dataInfo').textContent = 'Jumlah pelamar: ' + Store.get().candidates.length +
|
|
' • Template: ' + Store.get().templates.length +
|
|
' • Berkas tersimpan: ' + n;
|
|
}).catch(function () {
|
|
$('#dataInfo').textContent = 'Jumlah pelamar: ' + Store.get().candidates.length +
|
|
' • Template: ' + Store.get().templates.length;
|
|
});
|
|
}
|
|
|
|
function openStatusForm(id) {
|
|
var s = id ? Store.statusById(id) : null;
|
|
var body =
|
|
'<div class="field"><label for="st-nama">Nama status *</label><input type="text" id="st-nama" value="' + esc(s ? s.nama : '') + '"></div>' +
|
|
'<div class="grid cols-3">' +
|
|
'<div class="field"><label for="st-warna">Warna</label><input type="color" id="st-warna" value="' + esc(s ? s.warna : '#64748b') + '"></div>' +
|
|
'<div class="field"><label for="st-urutan">Urutan</label><input type="number" id="st-urutan" value="' + (s ? s.urutan : '') + '"></div>' +
|
|
'<div class="field"><label for="st-final">Status final</label><select id="st-final">' +
|
|
'<option value="0"' + (s && !s.isFinal ? ' selected' : '') + '>Tidak</option>' +
|
|
'<option value="1"' + (s && s.isFinal ? ' selected' : '') + '>Ya</option>' +
|
|
'</select></div>' +
|
|
'</div>';
|
|
var footer = '<button class="btn btn-ghost" type="button" data-close>Batal</button>' +
|
|
'<button class="btn btn-primary" type="button" id="saveStatus">Simpan</button>';
|
|
|
|
UI.openModal({
|
|
title: id ? 'Edit Status' : 'Tambah Status', body: body, footer: footer,
|
|
onMount: function (el) {
|
|
el.querySelector('[data-close]').addEventListener('click', UI.closeModal);
|
|
el.querySelector('#saveStatus').addEventListener('click', function () {
|
|
var nama = el.querySelector('#st-nama').value.trim();
|
|
if (!nama) { UI.toast('Nama status wajib diisi.', 'err'); return; }
|
|
var urutanVal = el.querySelector('#st-urutan').value;
|
|
Store.saveStatus({
|
|
id: id || undefined,
|
|
nama: nama,
|
|
warna: el.querySelector('#st-warna').value,
|
|
urutan: urutanVal ? parseInt(urutanVal, 10) : undefined,
|
|
isFinal: el.querySelector('#st-final').value === '1'
|
|
});
|
|
UI.toast('Status disimpan.', 'ok');
|
|
UI.closeModal();
|
|
renderStatuses();
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function download(filename, text) {
|
|
var blob = new Blob([text], { type: 'application/json' });
|
|
var url = URL.createObjectURL(blob);
|
|
var a = document.createElement('a');
|
|
a.href = url; a.download = filename;
|
|
document.body.appendChild(a); a.click(); a.remove();
|
|
setTimeout(function () { URL.revokeObjectURL(url); }, 500);
|
|
}
|
|
|
|
function doExport() {
|
|
Store.exportData().then(function (data) {
|
|
var text = JSON.stringify(data);
|
|
var stamp = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
download('e-recruitment-backup-' + stamp + '.json', text);
|
|
UI.toast('Backup berhasil diunduh.', 'ok');
|
|
}).catch(function (err) { UI.toast('Gagal export: ' + err.message, 'err'); });
|
|
}
|
|
|
|
function doImport(file) {
|
|
var reader = new FileReader();
|
|
reader.onload = function () {
|
|
var data;
|
|
try { data = JSON.parse(reader.result); }
|
|
catch (e) { UI.toast('File JSON tidak valid.', 'err'); return; }
|
|
if (!UI.confirmDialog('Import akan MENGGANTI seluruh data saat ini. Lanjutkan?')) return;
|
|
Store.importData(data).then(function () {
|
|
UI.toast('Data berhasil diimport.', 'ok');
|
|
loadSettings(); renderStatuses(); renderInfo();
|
|
}).catch(function (err) { UI.toast('Gagal import: ' + err.message, 'err'); });
|
|
};
|
|
reader.readAsText(file);
|
|
}
|
|
|
|
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' : '') +
|
|
'.\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');
|
|
renderInfo();
|
|
}).catch(function (err) { UI.toast('Gagal import Excel: ' + err.message, 'err'); });
|
|
}
|
|
|
|
function bind() {
|
|
$('#saveSettings').addEventListener('click', function () {
|
|
Store.setSettings({
|
|
perusahaan: $('#s-perusahaan').value.trim(),
|
|
hr: $('#s-hr').value.trim(),
|
|
jabatanHr: $('#s-jabatan').value.trim()
|
|
});
|
|
UI.toast('Pengaturan disimpan.', 'ok');
|
|
});
|
|
|
|
$('#btnAddStatus').addEventListener('click', function () { openStatusForm(null); });
|
|
$('#btnExport').addEventListener('click', doExport);
|
|
$('#btnImport').addEventListener('click', function () { $('#importFile').click(); });
|
|
$('#importFile').addEventListener('change', function (e) {
|
|
if (e.target.files && e.target.files[0]) doImport(e.target.files[0]);
|
|
e.target.value = '';
|
|
});
|
|
|
|
$('#btnExportExcel').addEventListener('click', doExportExcel);
|
|
$('#btnImportExcel').addEventListener('click', function () { $('#importExcelFile').click(); });
|
|
$('#btnTemplateExcel').addEventListener('click', function () {
|
|
try { Excel.exportTemplate(); UI.toast('Template Excel diunduh.', 'ok'); }
|
|
catch (err) { UI.toast('Gagal mengunduh template: ' + err.message, 'err'); }
|
|
});
|
|
$('#importExcelFile').addEventListener('change', function (e) {
|
|
if (e.target.files && e.target.files[0]) doImportExcel(e.target.files[0]);
|
|
e.target.value = '';
|
|
});
|
|
$('#btnReset').addEventListener('click', function () {
|
|
if (!UI.confirmDialog('Reset akan menghapus SEMUA data pelamar, template, dan berkas. Lanjutkan?')) return;
|
|
Store.reset().then(function () {
|
|
UI.toast('Data telah direset.', 'ok');
|
|
loadSettings(); renderStatuses(); renderInfo();
|
|
});
|
|
});
|
|
|
|
document.addEventListener('click', function (e) {
|
|
var edit = e.target.closest('[data-st-edit]');
|
|
if (edit) { openStatusForm(edit.getAttribute('data-st-edit')); return; }
|
|
var del = e.target.closest('[data-st-del]');
|
|
if (del) {
|
|
var s = Store.statusById(del.getAttribute('data-st-del'));
|
|
if (s && UI.confirmDialog('Hapus status "' + s.nama + '"?')) {
|
|
try { Store.deleteStatus(s.id); UI.toast('Status dihapus.', 'ok'); renderStatuses(); }
|
|
catch (err) { UI.toast(err.message, 'err'); }
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
})();
|