feat: inital commit project
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
/* 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);
|
||||
})();
|
||||
Reference in New Issue
Block a user