912 lines
37 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

document.addEventListener('DOMContentLoaded', () => {
const tableState = { data: [], page: 1, pageSize: 10, search: '', lastPage: 1, total: 0, startDate: '', endDate: '', mode: 'pengajuan' };
const historyState = { data: [], page: 1, pageSize: 10, search: '', lastPage: 1, total: 0, startDate: '', endDate: '', mode: 'history' };
const tbodyPending = document.getElementById('tablePendingFile');
const tbodyHistory = document.getElementById('tableHistoryFile');
const paginationPendingEl = document.getElementById('paginationControls');
const paginationHistoryEl = document.getElementById('paginationHistory');
const summaryEl = document.getElementById('tableSummary');
const pageSizeSelect = document.getElementById('tablePageSize');
const startDateInput = document.getElementById('startDate');
const endDateInput = document.getElementById('endDate');
const selectAllCheckbox = document.getElementById('selectAllPending');
const bulkApproveBtn = document.getElementById('bulkApproveBtn');
const clearSelectionBtn = document.getElementById('clearSelectionBtn');
const selectedCountEl = document.getElementById('selectedCount');
const pendingBulkActionsEl = document.getElementById('pendingBulkActions');
const tabsEl = document.getElementById('pengajuanTabs');
const titleEl = document.getElementById('pendingTitle');
const tabPendingEl = document.getElementById('tabPengajuan');
const tabHistoryEl = document.getElementById('tabHistory');
const isKomiteMutu = !!window.isKomiteMutu;
const isTurt = !!window.isTurt;
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
const selectedIds = new Set();
if (pageSizeSelect) {
const initialSize = parseInt(pageSizeSelect.value);
if (!isNaN(initialSize)) {
tableState.pageSize = initialSize;
historyState.pageSize = initialSize;
}
pageSizeSelect.addEventListener('change', (e) => {
const val = parseInt(e.target.value);
if (!isNaN(val) && val > 0) {
tableState.pageSize = val;
historyState.pageSize = val;
tableState.page = 1;
historyState.page = 1;
fetchData();
}
});
}
window.applyDateFilter = function(){
const start = startDateInput.value || '';
const end = endDateInput.value || '';
tableState.startDate = start;
tableState.endDate = end;
historyState.startDate = start;
historyState.endDate = end;
tableState.page = 1;
historyState.page = 1;
fetchData();
}
function formatTanggal(dateString) {
if (!dateString) return '-';
const d = new Date(dateString);
return d.toLocaleDateString('id-ID', {
day: '2-digit',
month: 'short',
year: 'numeric'
});
}
function approvalBadge(status, label){
const normalized = String(status || '').toLowerCase();
if (normalized === 'approved') return `<span class="badge bg-success">Approved ${label}</span>`;
if (normalized.startsWith('rejected')) return `<span class="badge bg-danger">Rejected ${label}</span>`;
if (normalized.startsWith('revised')) return `<span class="badge bg-info">Revised ${label}</span>`;
return `<span class="badge bg-warning text-dark">Pending ${label}</span>`;
}
function getWorkflowStatusBadges(item){
const actionStatus = String(item?.status_action || '').toLowerCase();
const mutuStatus = String(item?.status_mutu || '').toLowerCase();
const turtStatus = String(item?.status_turt || '').toLowerCase();
if (actionStatus.startsWith('revised')) {
return [`<span class="badge bg-info">Revised</span>`];
}
if (actionStatus.startsWith('rejected')) {
return [`<span class="badge bg-danger">Rejected Atasan</span>`];
}
if (actionStatus !== 'approved') {
return [`<span class="badge bg-warning text-dark">Pending Atasan</span>`];
}
const statuses = [];
if (item?.is_akre) {
if (mutuStatus.startsWith('rejected')) {
statuses.push(`<span class="badge bg-danger">Rejected Mutu</span>`);
} else if (mutuStatus !== 'approved') {
statuses.push(`<span class="badge bg-warning text-dark">Pending Mutu</span>`);
}
}
if (item?.master_kategori_directory_id) {
if (turtStatus.startsWith('rejected')) {
statuses.push(`<span class="badge bg-danger">Rejected TURT</span>`);
} else if (turtStatus !== 'approved') {
statuses.push(`<span class="badge bg-warning text-dark">Pending TURT</span>`);
}
}
if (statuses.length > 0) {
return statuses;
}
return [`<span class="badge bg-success">Approved</span>`];
}
function aksesBadge(akses){
if (akses){
return '<span class="badge bg-success">Umum</span>';
}
return '<span class="badge bg-primary">Internal Unit</span>';
}
function safeText(val){
return val ? String(val) : '-';
}
function isRejected(item){
return String(item?.status_action || '').toLowerCase().startsWith('rejected')
|| String(item?.status_mutu || '').toLowerCase().startsWith('rejected')
|| String(item?.status_turt || '').toLowerCase().startsWith('rejected');
}
function isAtasanApproved(item){
return String(item?.status_action || '').toLowerCase() === 'approved';
}
function canProcessRoleStatus(status){
const normalized = String(status || '').toLowerCase();
return normalized === '' || normalized.startsWith('revised');
}
function canApproveAsAtasan(item){
return item?.can_approve_atasan === true || item?.can_approve_atasan === 1 || item?.can_approve_atasan === '1';
}
function canEditSubmission(item){
return item?.can_edit_submission === true || item?.can_edit_submission === 1 || item?.can_edit_submission === '1';
}
function hasRevisionInfo(item){
return !!(item?.revision || item?.mutu_revision || item?.turt_revision);
}
function collectRevisionNotes(item){
const notes = [];
const genericRevision = String(item?.revision || '').trim();
const mutuRevision = String(item?.mutu_revision || '').trim();
const turtRevision = String(item?.turt_revision || '').trim();
if (genericRevision) {
const lowerGeneric = genericRevision.toLowerCase();
if (!lowerGeneric.startsWith('komite mutu:') && !lowerGeneric.startsWith('turt:')) {
notes.push({ label: 'Atasan', text: genericRevision });
}
}
if (mutuRevision) {
notes.push({ label: 'Komite Mutu', text: mutuRevision });
}
if (turtRevision) {
notes.push({ label: 'TURT', text: turtRevision });
}
return notes;
}
function escapeHtml(value){
return String(value ?? '')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function buildRevisionNotesHtml(notes){
if (!notes.length) {
return '<div class="text-muted">Tidak ada catatan revisi.</div>';
}
return `
<div class="text-start" style="line-height:1.6;">
${notes.map(note => `
<div class="mb-2">
<div><strong>${escapeHtml(note.label)}</strong></div>
<div>${escapeHtml(note.text)}</div>
</div>
`).join('')}
</div>
`;
}
function getItemById(id){
return (tableState.data || []).find((row) => String(row.file_directory_id) === String(id));
}
function getSelectableIdsOnPage(){
return (tableState.data || [])
.filter((item) => !isRejected(item) && !isAtasanApproved(item) && canApproveAsAtasan(item))
.map((item) => String(item.file_directory_id));
}
function updateSelectAllState(){
if (!selectAllCheckbox) return;
if (tableState.mode === 'history') {
selectAllCheckbox.checked = false;
selectAllCheckbox.indeterminate = false;
selectAllCheckbox.disabled = true;
return;
}
const selectableIds = getSelectableIdsOnPage();
if (selectableIds.length === 0) {
selectAllCheckbox.checked = false;
selectAllCheckbox.indeterminate = false;
selectAllCheckbox.disabled = true;
return;
}
selectAllCheckbox.disabled = false;
const selectedOnPage = selectableIds.filter((id) => selectedIds.has(id)).length;
selectAllCheckbox.checked = selectedOnPage > 0 && selectedOnPage === selectableIds.length;
selectAllCheckbox.indeterminate = selectedOnPage > 0 && selectedOnPage < selectableIds.length;
}
function updateSelectionUI(){
const count = selectedIds.size;
const selectableIds = getSelectableIdsOnPage();
const canBulkApprove = tableState.mode !== 'history' && selectableIds.length > 0;
if (selectedCountEl) selectedCountEl.textContent = String(count);
if (bulkApproveBtn) bulkApproveBtn.disabled = count === 0 || !canBulkApprove;
if (clearSelectionBtn) clearSelectionBtn.disabled = count === 0 || !canBulkApprove;
updateSelectAllState();
}
function buildPendingRow(item){
const tanggal = item.entry_at ? formatTanggal(item.entry_at) : '-';
const tanggalExp = item.tgl_expired ? formatTanggal(item.tgl_expired) : '-';
const tanggalTerbit = item.tanggal_terbit ? formatTanggal(item.tanggal_terbit) : '-';
const id = String(item.file_directory_id);
const rejected = isRejected(item);
const atasanApproved = isAtasanApproved(item);
const canAtasanApprove = canApproveAsAtasan(item);
const atasanRejected = String(item?.status_action || '').toLowerCase().startsWith('rejected');
const canMutuAct = atasanApproved && isKomiteMutu && item.is_akre && canProcessRoleStatus(item.status_mutu);
const canTurtAct = atasanApproved && isTurt && item.master_kategori_directory_id && canProcessRoleStatus(item.status_turt);
const checked = selectedIds.has(id);
const actions = [
`<button class="btn btn-sm btn-primary" onclick="infoDok(this)"
data-file="${item.file}"
data-fileName="${item.nama_dokumen}"
data-id="${item.file_directory_id}"
data-no_dokumen="${item.no_dokumen || '-'}"
data-tanggal_terbit="${item.tanggal_terbit || '-'}"
data-permission_file="${item.permission_file || '-'}">
<i class="fa-solid fa-eye"></i>
</button>`
];
if (isKomiteMutu || isTurt) {
if (canAtasanApprove && !atasanApproved) {
actions.push(`<button class="btn btn-sm btn-success" onclick="approvePending('${item.file_directory_id}', '${item.nama_dokumen || ''}')"><i class="fa-solid fa-check"></i></button>`);
actions.push(`<button class="btn btn-sm btn-danger" onclick="rejectPending('${item.file_directory_id}', '${item.nama_dokumen || ''}')"><i class="fa-solid fa-xmark"></i></button>`);
}
if (canMutuAct) {
actions.push(`<button class="btn btn-sm btn-success" onclick="approvePendingRole('mutu','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-check"></i></button>`);
actions.push(`<button class="btn btn-sm btn-danger" onclick="rejectPendingRole('mutu','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-xmark"></i></button>`);
}
if (canTurtAct) {
actions.push(`<button class="btn btn-sm btn-success" onclick="approvePendingRole('turt','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-check"></i></button>`);
actions.push(`<button class="btn btn-sm btn-danger" onclick="rejectPendingRole('turt','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-ban"></i></button>`);
}
} else {
if (!atasanApproved) {
actions.push(`<button class="btn btn-sm btn-success" onclick="approvePending('${item.file_directory_id}', '${item.nama_dokumen || ''}')"><i class="fa-solid fa-check"></i></button>`);
actions.push(`<button class="btn btn-sm btn-danger" onclick="rejectPending('${item.file_directory_id}', '${item.nama_dokumen || ''}')"><i class="fa-solid fa-xmark"></i></button>`);
}
}
if (rejected && atasanRejected && !canMutuAct && !canTurtAct) {
actions.length = 1;
}
if (rejected) {
if (hasRevisionInfo(item)) {
actions.push(`<button class="btn btn-sm btn-info" onclick="infoRejectPending('${item.file_directory_id}')"><i class="fa-solid fa-circle-info"></i></button>`);
}
if (canEditSubmission(item)) {
actions.push(`<button class="btn btn-sm btn-primary" onclick="editRejectedFromPending('${item.file_directory_id}')"><i class="fa-solid fa-pen-to-square"></i></button>`);
}
}
const statusContent = (isKomiteMutu || isTurt)
? getWorkflowStatusBadges(item).join(' ')
: getWorkflowStatusBadges(item).join(' ');
return `
<tr class="${checked ? 'table-active' : ''}">
<td class="text-center">
<input type="checkbox"
class="form-check-input row-select"
data-id="${id}"
${checked ? 'checked' : ''}
${(rejected || atasanApproved || !canAtasanApprove) ? 'disabled' : ''}>
</td>
<td class="text-center text-nowrap"><div class="d-flex justify-content-center align-items-center gap-1 flex-nowrap">${actions.join('')}</div></td>
<td>${safeText(item.no_dokumen)}</td>
<td>${statusContent}</td>
<td>${aksesBadge(item?.permission_file)}</td>
<td><a href="#" class="file-link"
data-file="${item.file}"
data-fileName="${item.nama_dokumen}"
data-id="${item.file_directory_id}"
data-no_dokumen="${item.no_dokumen || '-'}"
data-tanggal_terbit="${item.tanggal_terbit || '-'}"
data-permission_file="${item.permission_file || '-'}">${item.nama_dokumen}</a></td>
<td class="col-kategori"><div class="cell-wrap">${safeText(item.kategori)}</div></td>
<td class="col-unit"><div class="cell-wrap">${safeText(item.nama_unit)}</div></td>
<td class="text-nowrap">${tanggalTerbit}</td>
<td class="text-nowrap">${tanggalExp}</td>
<td class="text-nowrap">${tanggal}</td>
<td class="col-uploader"><div class="cell-wrap">${safeText(item.pegawai_nama_entry)}</div></td>
</tr>
`;
}
function buildHistoryRow(item){
const tanggal = item.entry_at ? formatTanggal(item.entry_at) : '-';
const tanggalExp = item.tgl_expired ? formatTanggal(item.tgl_expired) : '-';
const tanggalTerbit = item.tanggal_terbit ? formatTanggal(item.tanggal_terbit) : '-';
const actionType = item?.action_type || '-';
return `
<tr>
<td>${safeText(item.no_dokumen)}</td>
<td>${aksesBadge(item?.permission_file)}</td>
<td><a href="#" class="file-link"
data-file="${item.file || ''}"
data-fileName="${item.nama_dokumen || '-'}"
data-id="${item.file_directory_id || ''}"
data-no_dokumen="${item.no_dokumen || '-'}"
data-tanggal_terbit="${item.tanggal_terbit || '-'}"
data-permission_file="${item.permission_file || '-'}">${safeText(item.nama_dokumen)}</a></td>
<td>${safeText(item.kategori)}</td>
<td>${safeText(item.nama_unit)}</td>
<td><span class="badge bg-info">${actionType}</span></td>
<td class="text-nowrap">${tanggal}</td>
</tr>
`;
}
function renderPagination(totalPages){
const activeState = tableState.mode === 'history' ? historyState : tableState;
const paginationEl = tableState.mode === 'history' ? paginationHistoryEl : paginationPendingEl;
if (!paginationEl) return;
if (totalPages <= 1) {
paginationEl.innerHTML = '';
return;
}
const maxButtons = 5;
let start = Math.max(1, activeState.page - Math.floor(maxButtons/2));
let end = Math.min(totalPages, start + maxButtons - 1);
start = Math.max(1, end - maxButtons + 1);
let buttons = '';
buttons += `<button class="btn btn-outline-secondary btn-sm" data-page="prev" ${activeState.page === 1 ? 'disabled' : ''}></button>`;
for (let i = start; i <= end; i++) {
buttons += `<button class="btn btn-sm ${i === activeState.page ? 'btn-primary' : 'btn-outline-secondary'}" data-page="${i}">${i}</button>`;
}
buttons += `<button class="btn btn-outline-secondary btn-sm" data-page="next" ${activeState.page === totalPages ? 'disabled' : ''}></button>`;
paginationEl.innerHTML = `
<div class="d-flex align-items-center gap-2 flex-wrap">
<div class="btn-group" role="group">${buttons}</div>
<span class="small text-muted">Halaman ${activeState.page} dari ${totalPages}</span>
</div>
`;
}
function bindPagination(paginationEl, state){
if (!paginationEl) return;
paginationEl.addEventListener('click', (e) => {
const page = e.target.getAttribute('data-page');
if (!page) return;
if (page === 'prev' && state.page > 1) state.page--;
else if (page === 'next') {
if (state.page < state.lastPage) state.page++;
} else {
state.page = parseInt(page);
}
fetchData();
});
}
bindPagination(paginationPendingEl, tableState);
bindPagination(paginationHistoryEl, historyState);
function renderTable(){
const isHistoryMode = tableState.mode === 'history';
const activeState = isHistoryMode ? historyState : tableState;
const pageData = activeState.data || [];
const targetBody = isHistoryMode ? tbodyHistory : tbodyPending;
const rowBuilder = isHistoryMode ? buildHistoryRow : buildPendingRow;
const colSpan = isHistoryMode ? 9 : 12;
if (!targetBody) return;
if (pageData.length === 0) {
targetBody.innerHTML = `
<tr>
<td colspan="${colSpan}" class="text-center text-muted py-4">
Tidak ada data
</td>
</tr>
`;
} else {
targetBody.innerHTML = pageData.map(rowBuilder).join('');
}
const from = activeState.total === 0 ? 0 : ((activeState.page - 1) * activeState.pageSize) + 1;
const to = Math.min(((activeState.page - 1) * activeState.pageSize) + pageData.length, activeState.total);
if (summaryEl) {
summaryEl.textContent = activeState.total ? `Menampilkan ${from} - ${to} dari ${activeState.total} data` : 'Tidak ada data';
}
renderPagination(activeState.lastPage || 1);
updateSelectionUI();
}
let searchDebounce;
window.debouncedTableSearch = function(value){
clearTimeout(searchDebounce);
searchDebounce = setTimeout(() => {
const keyword = value.trim();
tableState.search = keyword;
historyState.search = keyword;
tableState.page = 1;
historyState.page = 1;
fetchData();
}, 250);
}
window.refreshLog = function(){
tableState.search = '';
tableState.startDate = '';
tableState.endDate = '';
historyState.search = '';
historyState.startDate = '';
historyState.endDate = '';
const tableSearch = document.getElementById('tableSearch');
if (tableSearch) tableSearch.value = '';
if (startDateInput) startDateInput.value = '';
if (endDateInput) endDateInput.value = '';
selectedIds.clear();
tableState.page = 1;
historyState.page = 1;
fetchData();
}
function updateTabUI(){
if (tabsEl) {
tabsEl.querySelectorAll('.nav-link').forEach((btn) => {
btn.classList.toggle('active', btn.getAttribute('data-mode') === tableState.mode);
});
}
if (titleEl) {
titleEl.textContent = tableState.mode === 'history' ? 'Log History' : 'Data Persetujuan';
}
if (tabPendingEl && tabHistoryEl) {
tabPendingEl.classList.toggle('d-none', tableState.mode === 'history');
tabHistoryEl.classList.toggle('d-none', tableState.mode !== 'history');
}
if (pendingBulkActionsEl) {
pendingBulkActionsEl.classList.toggle('d-none', tableState.mode === 'history' || isKomiteMutu || isTurt);
}
updateSelectionUI();
}
if (tabsEl) {
tabsEl.addEventListener('click', (e) => {
const btn = e.target.closest('[data-mode]');
if (!btn) return;
const mode = btn.getAttribute('data-mode');
if (!mode || mode === tableState.mode) return;
tableState.mode = mode;
if (mode === 'history') {
historyState.page = 1;
} else {
tableState.page = 1;
}
updateTabUI();
fetchData();
});
}
function fetchData(){
if (summaryEl) summaryEl.textContent = 'Memuat data...';
const activeState = tableState.mode === 'history' ? historyState : tableState;
const params = new URLSearchParams({
page: activeState.page,
per_page: activeState.pageSize,
keyword: activeState.search || '',
start_date: activeState.startDate || '',
end_date: activeState.endDate || ''
});
const endpoint = tableState.mode === 'history'
? `/data/log-dokumen?${params.toString()}`
: `/datatable/pending-file?${params.toString()}`;
fetch(endpoint)
.then(res => res.json())
.then(data => {
activeState.data = data?.data || [];
activeState.lastPage = data?.pagination?.last_page || 1;
activeState.total = data?.pagination?.total || 0;
renderTable();
})
.catch(err => {
console.error(err);
if (summaryEl) summaryEl.textContent = 'Gagal memuat data';
});
}
window.approvePending = function(id, fileName){
Swal.fire({
title: 'Approve dokumen?',
text: fileName || 'Dokumen akan disetujui.',
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Approve',
cancelButtonText: 'Batal',
}).then((result) => {
if (!result.isConfirmed) return;
fetch(`/pending-file/${id}/approve`, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': csrfToken,
'Content-Type': 'application/json'
},
}).then(async(res) => {
const data = await res.json();
if (!res.ok || !data?.status) {
throw new Error(data?.message || 'Gagal approve.');
}
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: data.message || 'Dokumen disetujui.',
timer: 1500,
showConfirmButton: false
});
selectedIds.delete(String(id));
if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount();
fetchData();
}).catch((err) => {
Swal.fire({
icon: 'error',
title: 'Gagal',
text: err.message || 'Terjadi kesalahan.'
});
});
});
}
window.rejectPending = function(id, fileName){
Swal.fire({
title: 'Reject dokumen?',
text: fileName || 'Dokumen akan ditolak.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Reject',
cancelButtonText: 'Batal',
input: 'textarea',
inputLabel: 'Catatan',
inputPlaceholder: 'Tulis alasan atau catatan revisi...',
inputAttributes: {
'aria-label': 'Catatan'
},
preConfirm: (value) => {
const revision = (value || '').trim();
if (!revision) {
Swal.showValidationMessage('Catatan revisi wajib diisi.');
}
return revision;
}
}).then((result) => {
if (!result.isConfirmed) return;
fetch(`/pending-file/${id}/reject`, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': csrfToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({
revision: result.value
})
}).then(async(res) => {
const data = await res.json();
if (!res.ok || !data?.status) {
throw new Error(data?.message || 'Gagal reject.');
}
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: data.message || 'Dokumen ditolak.',
timer: 1500,
showConfirmButton: false
});
selectedIds.delete(String(id));
if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount();
fetchData();
}).catch((err) => {
Swal.fire({
icon: 'error',
title: 'Gagal',
text: err.message || 'Terjadi kesalahan.'
});
});
});
}
window.infoRejectPending = function(id){
const item = getItemById(id);
Swal.fire({
title: 'Catatan Revisi',
html: buildRevisionNotesHtml(collectRevisionNotes(item)),
icon: 'info',
confirmButtonText: 'Tutup'
});
}
window.editRejectedFromPending = function(id){
const targetUrl = new URL('/pengajuan-file', window.location.origin);
targetUrl.searchParams.set('edit', id);
window.location.href = targetUrl.toString();
}
window.approvePendingRole = function(type, id, fileName){
const endpoint = type === 'mutu'
? `/pending-file/${id}/approve-mutu`
: `/pending-file/${id}/approve-turt`;
const label = type === 'mutu' ? 'Komite Mutu' : 'TURT';
Swal.fire({
title: `Approve ${label}?`,
text: fileName || 'Dokumen akan disetujui.',
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Approve',
cancelButtonText: 'Batal',
}).then((result) => {
if (!result.isConfirmed) return;
fetch(endpoint, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': csrfToken,
'Content-Type': 'application/json'
},
}).then(async(res) => {
const data = await res.json();
if (!res.ok || !data?.status) {
throw new Error(data?.message || 'Gagal approve.');
}
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: data.message || 'Dokumen disetujui.',
timer: 1500,
showConfirmButton: false
});
if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount();
fetchData();
}).catch((err) => {
Swal.fire({
icon: 'error',
title: 'Gagal',
text: err.message || 'Terjadi kesalahan.'
});
});
});
}
window.rejectPendingRole = function(type, id, fileName){
const endpoint = type === 'mutu'
? `/pending-file/${id}/reject-mutu`
: `/pending-file/${id}/reject-turt`;
const label = type === 'mutu' ? 'Komite Mutu' : 'TURT';
Swal.fire({
title: `Reject ${label}?`,
text: fileName || 'Dokumen akan ditolak.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Reject',
cancelButtonText: 'Batal',
input: 'textarea',
inputLabel: 'Catatan',
inputPlaceholder: 'Tulis alasan atau catatan revisi...',
inputAttributes: {
'aria-label': 'Catatan'
},
preConfirm: (value) => {
const revision = (value || '').trim();
if (!revision) {
Swal.showValidationMessage('Catatan revisi wajib diisi.');
}
return revision;
}
}).then((result) => {
if (!result.isConfirmed) return;
fetch(endpoint, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': csrfToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({ revision: result.value })
}).then(async(res) => {
const data = await res.json();
if (!res.ok || !data?.status) {
throw new Error(data?.message || 'Gagal reject.');
}
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: data.message || 'Dokumen ditolak.',
timer: 1500,
showConfirmButton: false
});
if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount();
fetchData();
}).catch((err) => {
Swal.fire({
icon: 'error',
title: 'Gagal',
text: err.message || 'Terjadi kesalahan.'
});
});
});
}
window.infoDok = function(e){
let fileUrl =$(e).data('file');
let noDokumen = $(e).data('no_dokumen');
let tanggalTerbit = $(e).data('tanggal_terbit');
let permissionFile = $(e).data('permission_file');
let fileName = $(e).data('fileName');
currentFile = fileUrl;
idDirectory = $(e).data('id');
const titleEl = document.getElementById('confirm_preview_file');
if (titleEl) titleEl.textContent = fileName;
const noEl = document.getElementById('confirm-upload-dokumen');
if (noEl) noEl.textContent = noDokumen;
const tglEl = document.getElementById('confirm-time-dokumen');
if (tglEl) tglEl.textContent = tanggalTerbit;
const permEl = document.getElementById('confirm-permission');
if (permEl) {
const publicDoc = isPublic(permissionFile);
permEl.textContent = publicDoc ? 'Umum' : 'Internal Unit';
permEl.className = 'badge ' + (publicDoc ? 'bg-success' : 'bg-secondary');
}
let previewBox = document.getElementById('file-preview');
previewBox.innerHTML = `<div id="pdfWrap" style="height:500px; overflow:auto; background:#f7f7f7; padding:8px;">
<div id="pdfPages"></div>
</div>`;
openPreview(idDirectory);
$("#previewModal").modal('show');
}
if (tbodyPending) {
tbodyPending.addEventListener('change', (e) => {
const target = e.target;
if (!target.classList.contains('row-select')) return;
const id = target.getAttribute('data-id');
if (!id) return;
if (target.checked) selectedIds.add(id);
else selectedIds.delete(id);
const row = target.closest('tr');
if (row) row.classList.toggle('table-active', target.checked);
updateSelectionUI();
});
}
if (selectAllCheckbox) {
selectAllCheckbox.addEventListener('change', () => {
const selectableIds = getSelectableIdsOnPage();
if (selectableIds.length === 0) return;
if (selectAllCheckbox.checked) {
selectableIds.forEach((id) => selectedIds.add(id));
} else {
selectableIds.forEach((id) => selectedIds.delete(id));
}
renderTable();
});
}
if (clearSelectionBtn) {
clearSelectionBtn.addEventListener('click', () => {
selectedIds.clear();
renderTable();
});
}
if (bulkApproveBtn) {
bulkApproveBtn.addEventListener('click', () => {
if (selectedIds.size === 0) return;
const ids = Array.from(selectedIds);
Swal.fire({
title: 'Approve dokumen terpilih?',
text: `Total ${ids.length} dokumen akan disetujui.`,
icon: 'question',
showCancelButton: true,
confirmButtonText: 'Approve',
cancelButtonText: 'Batal',
}).then((result) => {
if (!result.isConfirmed) return;
fetch(`/pending-file/approve-multiple`, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': csrfToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({ ids })
}).then(async(res) => {
const data = await res.json();
if (!res.ok || !data?.status) {
throw new Error(data?.message || 'Gagal approve dokumen.');
}
ids.forEach((id) => selectedIds.delete(String(id)));
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: data.message || 'Dokumen disetujui.',
timer: 1500,
showConfirmButton: false
});
if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount();
fetchData();
}).catch((err) => {
Swal.fire({
icon: 'error',
title: 'Gagal',
text: err.message || 'Terjadi kesalahan.'
});
});
});
});
}
updateTabUI();
fetchData();
});
document.addEventListener('click', function(e){
if(e.target.matches('.file-link')){
e.preventDefault();
let fileUrl = e.target.getAttribute('data-file');
let noDokumen = e.target.getAttribute('data-no_dokumen');
let tanggalTerbit = e.target.getAttribute('data-tanggal_terbit');
let permissionFile = e.target.getAttribute('data-permission_file');
let fileName = e.target.getAttribute('data-fileName');
currentFile = fileUrl;
idDirectory = e.target.getAttribute('data-id');
const titleEl = document.getElementById('confirm_preview_file');
if (titleEl) titleEl.textContent = fileName;
const noEl = document.getElementById('confirm-upload-dokumen');
if (noEl) noEl.textContent = noDokumen;
const tglEl = document.getElementById('confirm-time-dokumen');
if (tglEl) tglEl.textContent = tanggalTerbit;
const permEl = document.getElementById('confirm-permission');
if (permEl) {
const publicDoc = isPublic(permissionFile);
permEl.textContent = publicDoc ? 'Umum' : 'Internal Unit';
permEl.className = 'badge ' + (publicDoc ? 'bg-success' : 'bg-secondary');
}
let previewBox = document.getElementById('file-preview');
previewBox.innerHTML = `<div id="pdfWrap" style="height:500px; overflow:auto; background:#f7f7f7; padding:8px;">
<div id="pdfPages"></div>
</div>`;
openPreview(idDirectory);
$("#previewModal").modal('show');
}
if(e.target.matches('#btn-view-full')){
window.open(`/full-preview/${idDirectory}`, '_blank');
}
});
function isPublic(permissionVal){
if(permissionVal === null || permissionVal === undefined) return false;
const val = String(permissionVal).toLowerCase();
return val === '1' || val === 'true' || val === 'ya' || val === 'yes';
}