if (window.location.pathname.endsWith('/index.html')) {
const cleanUrl = window.location.pathname.replace('/index.html', '/') + window.location.search + window.location.hash;
window.history.replaceState(null, '', cleanUrl);
}
let state = {
user: null, role: null,
options: { items: [] },
data: [],
currentMode: 'ADD',
currentRowIndex: null,
currentPage: 1,
rowsPerPage: 10
};
const ENC = "aHR0cHM6Ly9zY3JpcHQuZ29vZ2xlLmNvbS9tYWNyb3Mvcy9BS2Z5Y2J4MG5tbDVJc1d0NXh1VWVNTGZpZjY5OU9MSEQ5NF9rdGZneExUWnRoQ1VpUDIyMFJhbE5kUDdfaFh1WGUyREpyMWhBQS9leGVj";
const GWP = atob(ENC);
async function apiCall(action, payload = {}) {
const reqPayload = { action, username: state.user, password: state.password, ...payload };
const response = await fetch(GWP, { method: 'POST', body: JSON.stringify(reqPayload) });
const res = await response.json();
if (res.status === 'error') throw new Error(res.message);
return res.data;
}
async function login() {
const u = document.getElementById('username').value;
const p = document.getElementById('password').value;
try {
const data = await apiCall('login', { username: u, password: p });
state.user = u; state.password = p; state.role = data.role;
document.getElementById('login-view').classList.add('d-none');
document.getElementById('app-view').classList.remove('d-none');
document.getElementById('nav-username').innerText = state.user;
document.getElementById('drop-username').innerText = state.user;
document.getElementById('drop-role').innerText = state.role;
const btnAdd = document.getElementById('btn-add-new');
btnAdd.classList.remove('d-none');
if (state.role === 'Viewer') {
btnAdd.disabled = true;
} else {
btnAdd.disabled = false;
}
await loadInitialData();
} catch (e) {
document.getElementById('login-error').classList.remove('d-none');
}
}
function logout() {
state.user = null; state.password = null; state.role = null;
document.getElementById('app-view').classList.add('d-none');
document.getElementById('login-view').classList.remove('d-none');
document.getElementById('username').value = '';
document.getElementById('password').value = '';
}
async function loadInitialData() {
document.getElementById('tableBody').innerHTML = `
| Loading data... |
`;
const result = await apiCall('getData');
state.options = result.options;
state.data = result.data;
populateDropdowns();
renderTable();
}
function populateDropdowns() {
const buildOptions = (arr, valKey, labelKey = null) =>
arr.map(i => `` +
state.options.subinv.map(i => ``).join('');
}
function updateUOM() {
const selectedItemDisplay = document.getElementById('f_item').value;
const itemData = state.options.items.find(i => i.display === selectedItemDisplay);
document.getElementById('f_uom').value = itemData ? itemData.uom : '';
checkFormValidity();
}
function resetPageAndRender() {
state.rowsPerPage = parseInt(document.getElementById('rowsPerPage').value);
state.currentPage = 1;
renderTable();
}
function changePage(page) {
state.currentPage = page;
renderTable();
}
function displayDate(dateStr) {
if (!dateStr) return '';
return new Date(dateStr).toLocaleDateString('id-ID', { year: 'numeric', month: '2-digit', day: '2-digit' });
}
function renderTable() {
const search = document.getElementById('searchInput').value.toLowerCase();
const filtered = state.data.filter(r => Object.values(r).some(v => String(v).toLowerCase().includes(search)));
const total = filtered.length;
const totalPages = Math.ceil(total / state.rowsPerPage);
if (state.currentPage > totalPages) state.currentPage = totalPages || 1;
const startIdx = (state.currentPage - 1) * state.rowsPerPage;
const paginated = filtered.slice(startIdx, startIdx + state.rowsPerPage);
document.getElementById('totalRows').innerText = total;
document.getElementById('pageStart').innerText = total === 0 ? 0 : startIdx + 1;
document.getElementById('pageEnd').innerText = Math.min(startIdx + state.rowsPerPage, total);
let pageHTML = ``;
let pStart = Math.max(1, state.currentPage - 2);
let pEnd = Math.min(totalPages, state.currentPage + 2);
for(let i = pStart; i <= pEnd; i++) {
pageHTML += ``;
}
pageHTML += ``;
document.getElementById('paginationNav').innerHTML = pageHTML;
let headHTML = `
| Nama Supplier | Tgl Kedatangan | No. SJ Supplier |
Item | Qty Awal | Qty Reject | Qty Nett |
UOM | Sub Inv | No. STG Manual | Ekspedisi PIC |
Nomer PO | Tanggal PO | No. Receive | Notes |
Status | Action |
`;
document.getElementById('tableHead').innerHTML = headHTML;
if (paginated.length === 0) {
document.getElementById('tableBody').innerHTML = `| No data found |
`;
return;
}
let bodyHTML = '';
paginated.forEach(row => {
let statColor = row.stat === 'OK' ? 'bg-success' : (row.stat === 'EDITED' ? 'bg-primary' : 'bg-secondary');
// Disable edit button if role is Viewer OR if status is OK
let isViewer = state.role === 'Viewer';
let btnDisabled = (isViewer || row.stat === 'OK') ? 'disabled' : '';
let btnClass = btnDisabled ? 'btn-secondary opacity-50' : 'btn-warning';
bodyHTML += `
| ${row.supplier} |
${displayDate(row.tglDatang)} |
${row.noSj} |
${row.item} |
${row.qtyAwal} |
${row.qtyReject} |
${row.qtyNett} |
${row.uom} |
${row.subInv} |
${row.stgManual} |
${displayDate(row.ekspedisi)} |
${row.noPo} |
${displayDate(row.tglPo)} |
${row.noReceive} |
${row.notes} |
${row.stat} |
|
`;
});
document.getElementById('tableBody').innerHTML = bodyHTML;
}
function openAddModal() {
if (state.role === 'Viewer') return;
state.currentMode = 'ADD';
document.getElementById('recordForm').reset();
document.getElementById('modalTitle').innerText = "Kedatangan Baru";
document.getElementById('section-edit').classList.add('d-none');
document.getElementById('divider-edit').classList.add('d-none');
lockFields(false);
document.getElementById('btnActionSave').innerText = "Save";
document.getElementById('btnActionSave').disabled = true;
new coreui.Modal(document.getElementById('dataModal')).show();
}
function openEditModal(rowIndex) {
if (state.role === 'Viewer') return;
state.currentMode = 'EDIT';
state.currentRowIndex = rowIndex;
const rowData = state.data.find(r => r._rowIndex === rowIndex);
document.getElementById('modalTitle').innerText = "Edit Row";
document.getElementById('section-edit').classList.remove('d-none');
document.getElementById('divider-edit').classList.remove('d-none');
document.getElementById('f_supplier').value = rowData.supplier;
document.getElementById('f_tglDatang').value = formatDateForInput(rowData.tglDatang);
document.getElementById('f_noSj').value = rowData.noSj;
document.getElementById('f_item').value = rowData.item;
document.getElementById('f_qtyAwal').value = rowData.qtyAwal;
document.getElementById('f_qtyReject').value = rowData.qtyReject;
document.getElementById('f_qtyNett').value = rowData.qtyNett;
document.getElementById('f_uom').value = rowData.uom;
document.getElementById('f_subInv').value = rowData.subInv;
document.getElementById('f_stgManual').value = rowData.stgManual;
document.getElementById('f_ekspedisi').value = formatDateForInput(rowData.ekspedisi);
document.getElementById('f_notes').value = rowData.notes;
const cleanPo = rowData.noPo ? String(rowData.noPo).replace('PO/SKL/', '') : '';
document.getElementById('f_noPo').value = cleanPo;
document.getElementById('f_tglPo').value = formatDateForInput(rowData.tglPo);
document.getElementById('f_noReceive').value = rowData.noReceive;
lockFields(true);
document.getElementById('btnActionSave').innerText = "Update";
document.getElementById('btnActionSave').disabled = true;
new coreui.Modal(document.getElementById('dataModal')).show();
}
function formatDateForInput(dateString) {
if (!dateString) return '';
const d = new Date(dateString);
return d.toISOString().split('T')[0];
}
function lockFields(isEdit) {
const toLock = ['f_supplier', 'f_tglDatang', 'f_noSj', 'f_item'];
toLock.forEach(id => document.getElementById(id).disabled = isEdit);
}
function checkFormValidity() {
let isValid = true;
if (state.currentMode === 'ADD') {
document.querySelectorAll('.req-add').forEach(el => {
if (!el.value.trim()) isValid = false;
});
} else {
document.querySelectorAll('.req-edit').forEach(el => {
if (!el.value.trim()) isValid = false;
});
const noPoVal = document.getElementById('f_noPo').value.trim();
const tglPoVal = document.getElementById('f_tglPo').value.trim();
if ((noPoVal && !tglPoVal) || (!noPoVal && tglPoVal)) {
isValid = false;
}
}
document.getElementById('btnActionSave').disabled = !isValid;
}
async function submitData() {
const btn = document.getElementById('btnActionSave');
btn.disabled = true;
btn.innerHTML = ` Processing...`;
const payload = {
qtyAwal: document.getElementById('f_qtyAwal').value,
qtyReject: document.getElementById('f_qtyReject').value,
qtyNett: document.getElementById('f_qtyNett').value,
subInv: document.getElementById('f_subInv').value,
stgManual: document.getElementById('f_stgManual').value.toUpperCase(),
ekspedisi: document.getElementById('f_ekspedisi').value,
notes: document.getElementById('f_notes').value
};
if (state.currentMode === 'ADD') {
payload.supplier = document.getElementById('f_supplier').value;
payload.tglDatang = document.getElementById('f_tglDatang').value;
payload.noSj = document.getElementById('f_noSj').value;
payload.item = document.getElementById('f_item').value;
payload.uom = document.getElementById('f_uom').value;
await apiCall('saveRow', { rowData: payload });
} else {
payload.noPo = document.getElementById('f_noPo').value;
payload.tglPo = document.getElementById('f_tglPo').value;
payload.noReceive = document.getElementById('f_noReceive').value;
await apiCall('updateRow', { rowData: payload, rowIndex: state.currentRowIndex });
}
closeModal('dataModal');
await loadInitialData();
}
function closeModal(id) {
const modal = coreui.Modal.getInstance(document.getElementById(id));
if (modal) modal.hide();
}