2026-03-05 14:49:18 +07:00

913 lines
38 KiB
PHP

@extends('layout.main')
<style>
/* Baris Folder */
.tree-folder {
background-color: #fcfcfc;
cursor: pointer;
transition: background 0.2s;
}
.tree-folder:hover {
background-color: #f4f7fb;
}
/* Container Indentasi */
.indent-container {
display: inline-flex;
align-items: center;
}
/* Garis Pandu Vertikal */
.tree-line {
display: inline-block;
width: 24px;
height: 40px; /* Sesuaikan dengan tinggi row */
border-left: 1px solid #e0e0e0;
margin-left: 12px;
position: relative;
}
/* Menghilangkan garis terakhir agar lebih rapi */
.tree-file:last-child .tree-line {
height: 20px;
}
/* Ikon dan Teks */
.folder-icon { color: #ffc107; font-size: 1.2rem; }
.file-icon { color: #6c757d; font-size: 1.1rem; }
.badge-pdf { background-color: #fff0f0; color: #e44d26; border: 1px solid #ffcccc; }
</style>
@section('body_main')
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-body p-3">
<div class="tab-content">
<div class="tab-pane fade show active">
<div class="d-flex justify-content-between align-items-start mb-3">
<h4 class="mb-0">Dokumen Akreditasi</h4>
<div class="d-flex align-items-start gap-3">
<div class="d-flex flex-column align-items-start">
<button
type="button"
class="btn btn-primary btn-sm"
id="btnDownloadMultiple"
disabled
>
<i class="ti ti-download me-1"></i>
Download Terpilih
</button>
<span
id="selectedCount"
class="small text-muted mt-1"
>
0 dipilih
</span>
</div>
<!-- Tambah Dokumen -->
<button
type="button"
class="btn btn-success btn-sm"
data-bs-toggle="modal"
data-bs-target="#modalCreateFile"
>
<i class="ti ti-plus me-1"></i>
Tambah Dokumen
</button>
</div>
</div>
<div class="d-flex flex-column flex-md-row align-items-md-center gap-2 mb-3">
<div class="d-flex flex-column flex-md-row align-items-md-center gap-2 flex-grow-1">
<input type="search" id="tableSearch" class="form-control"
placeholder="Cari Dokumen / Folder" autocomplete="off">
</div>
<div class="d-flex align-items-center gap-2">
</div>
</div>
<div class="table-responsive" style="max-height: 70vh; overflow-y:auto;">
<table class="table table-sm table-hover align-middle mb-0 table-fixed" id="lastUpdatedTable">
<thead>
<tr>
<th class="text-center" style="width: 45px;">
<input type="checkbox" id="checkAllRows" class="form-check-input">
</th>
<th style="width: 80px;">Aksi</th>
<th style="width: 40%;">Nama Dokumen / Folder</th>
<th>Tipe</th>
<th>Tgl Unggah</th>
</tr>
</thead>
<tbody id="tableDataAkreditasi">
<!-- data dari fetch masuk sini -->
</tbody>
</table>
</div>
<div class="d-flex flex-wrap align-items-center gap-2 mt-3">
<div class="d-flex align-items-center gap-1">
<span class="small text-muted">Tampilkan</span>
<select id="tablePageSize" class="form-select form-select-sm" style="width: 80px;">
<option value="5">5</option>
<option value="10" selected>10</option>
<option value="20">20</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
<span class="small text-muted">data</span>
</div>
<div id="paginationControls" class="ms-auto"></div>
<div class="small text-muted text-nowrap" id="tableSummary">
Memuat data...
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@include('dataUnit.modal.create')
<script>
// === STATE MANAGEMENT ===
let currentData = [];
let selectedIds = [];
let expandedFolders = new Set();
let searchTimer;
const btn = document.getElementById('btnDownloadMultiple');
// === INITIALIZATION ===
document.addEventListener('DOMContentLoaded', () => {
fetchData();
initEventListeners();
});
// === EVENT LISTENERS ===
function initEventListeners() {
// Search dengan Debounce
document.getElementById('tableSearch').addEventListener('input', (e) => {
clearTimeout(searchTimer);
searchTimer = setTimeout(() => {
fetchData(e.target.value, 1);
}, 500);
});
// Per Page Change
document.getElementById('tablePageSize').addEventListener('change', (e) => {
const keyword = document.getElementById('tableSearch').value;
fetchData(keyword,1);
});
// Check All Rows
document.getElementById('checkAllRows').addEventListener('change', function() {
const checkboxes = document.querySelectorAll('.row-checkbox');
selectedIds = [];
checkboxes.forEach(cb => {
cb.checked = this.checked;
if (this.checked) selectedIds.push(cb.value);
});
updateDownloadButton();
});
}
// === CORE FUNCTIONS ===
/**
* Mengambil data dari server
*/
function fetchData(keyword = '', page = 1) {
const tbody = document.getElementById('tableDataAkreditasi');
const perPage = document.getElementById('tablePageSize').value;
tbody.innerHTML = '<tr><td colspan="6" class="text-center">Memuat data...</td></tr>';
fetch(`/datatable-akreditasi?keyword=${encodeURIComponent(keyword)}&per_page=${perPage}&page=${page}`)
.then(res => res.json())
.then(response => {
if (response.status) {
currentData = response.data || [];
renderTable(keyword);
updateSummary(response.pagination.total);
} else {
tbody.innerHTML = `<tr><td colspan="6" class="text-center text-danger">${response.message}</td></tr>`;
}
})
.catch(err => {
console.error('Error fetching data:', err);
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-danger">Gagal memuat data dari server.</td></tr>';
});
}
/**
* Merender tabel berdasarkan mode (Folder vs Search)
*/
function renderTable(keyword = '') {
const tbody = document.getElementById('tableDataAkreditasi');
tbody.innerHTML = '';
if (currentData.length === 0) {
tbody.innerHTML = '<tr><td colspan="6" class="text-center">Tidak ada data ditemukan</td></tr>';
return;
}
// MODE 1: SEARCH VIEW (Flat View - Langsung tampilkan semua file)
if (keyword.length > 0) {
currentData.forEach(item => renderFileRow(tbody, item));
return;
}
// MODE 2: TREE VIEW (All folders & files)
const tree = buildTree(currentData);
if (expandedFolders.size === 0) {
expandAllFolders(tree, '');
}
renderTree(tbody, tree, '', 0);
}
/**
* Render Baris File (Digunakan di kedua mode)
*/
function renderFileRow(targetTbody, item) {
const fileName = item.file ? item.file.split('/').pop() : 'Unknown';
const typeDok = fileName.split('.').pop().toUpperCase();
const isChecked = selectedIds.includes(String(item.file_directory_id)) ? 'checked' : '';
targetTbody.innerHTML += `
<tr>
<td class="text-center">
<input type="checkbox" class="form-check-input row-checkbox"
value="${item.file_directory_id}" ${isChecked} onchange="handleRowCheck(this)">
</td>
<td>
<div class="btn-group">
<a href="/file-download/${item.file_directory_id}" target="_blank"
class="btn btn-sm btn-outline-primary" download title="Download">
<i class="ti ti-download"></i>
</a>
</div>
</td>
<td>
<div class="d-flex flex-column">
<strong>${item.nama_dokumen}</strong>
<small class="text-muted" style="font-size: 10px;">Path: ${item.file}</small>
</div>
</td>
<td><span class="badge bg-light text-primary border">${typeDok}</span></td>
<td>${item.entry_at || '-'}</td>
</tr>`;
}
// === TREE FUNCTIONS ===
function buildTree(items) {
const root = { folders: {}, files: [] };
(items || []).forEach(item => {
const path = (item.file || '').split('/').filter(Boolean);
if (path.length === 0) {
root.files.push(item);
return;
}
const fileName = path.pop();
let node = root;
path.forEach(part => {
if (!node.folders[part]) {
node.folders[part] = { folders: {}, files: [] };
}
node = node.folders[part];
});
node.files.push({ ...item, __fileName: fileName });
});
return root;
}
function expandAllFolders(node, basePath) {
Object.keys(node.folders || {}).forEach(folder => {
const folderPath = basePath ? `${basePath}/${folder}` : folder;
expandedFolders.add(folderPath);
expandAllFolders(node.folders[folder], folderPath);
});
}
function renderTree(tbody, node, basePath, level) {
const folderNames = Object.keys(node.folders || {}).sort();
folderNames.forEach(folder => {
const folderPath = basePath ? `${basePath}/${folder}` : folder;
const isExpanded = expandedFolders.has(folderPath);
const folderNode = node.folders[folder];
let lines = '';
for (let i = 0; i < level; i++) {
lines += '<span class="tree-line"></span>';
}
tbody.innerHTML += `
<tr class="tree-folder" onclick="toggleFolder('${folderPath}')">
<td></td> <td></td> <td>
<div class="indent-container">
${lines}
<span class="folder-toggle me-1">
<i class="ti ${isExpanded ? 'ti-chevron-down' : 'ti-chevron-right'} text-muted" style="font-size: 12px;"></i>
</span>
<i class="ti ti-folder-filled folder-icon me-2"></i>
<strong class="text-dark">${folder}</strong>
</div>
</td>
<td><span class="badge bg-light text-muted border-0">FOLDER</span></td>
<td class="text-muted small">-</td> <td class="text-muted small">-</td> </tr>
`;
if (isExpanded) {
renderTree(tbody, folderNode, folderPath, level + 1);
}
});
// Bagian ini akan memanggil renderTreeFileRow yang menampilkan tanggal
(node.files || []).forEach(item => {
renderTreeFileRow(tbody, item, level);
});
}
function getFolderMaxDate(node) {
let dates = [];
// Ambil tanggal dari file di level ini
(node.files || []).forEach(f => {
if (f.entry_at) dates.push(new Date(f.entry_at));
});
// Ambil tanggal dari sub-folder (rekursif)
Object.values(node.folders || {}).forEach(subNode => {
const subDate = getFolderMaxDate(subNode);
if (subDate) dates.push(new Date(subDate));
});
if (dates.length === 0) return null;
// Cari tanggal paling besar (terbaru)
const maxDate = new Date(Math.max(...dates));
// Format balik ke YYYY-MM-DD
return maxDate.toISOString().split('T')[0];
}
function renderTreeFileRow(targetTbody, item, level){
const fileName = item.__fileName || (item.file ? item.file.split('/').pop() : 'Unknown');
const typeDok = fileName.split('.').pop().toUpperCase();
const isChecked = selectedIds.includes(String(item.file_directory_id)) ? 'checked' : '';
let lines = '';
for (let i = 0; i < level; i++) {
lines += '<span class="tree-line"></span>';
}
targetTbody.innerHTML += `
<tr class="tree-file">
<td class="text-center">
<input type="checkbox" class="form-check-input row-checkbox"
value="${item.file_directory_id}" ${isChecked} onchange="handleRowCheck(this)">
</td>
<td>
<a href="/file-download/${item.file_directory_id}" target="_blank"
class="btn btn-sm btn-primary text-white border">
<i class="ti ti-download"></i>
</a>
</td>
<td>
<div class="indent-container">
${lines}
<span style="width: 24px;"></span> <i class="ti ti-file-text file-icon me-2"></i>
<div class="d-flex flex-column">
<span class="text-dark fw-medium">${item.nama_dokumen}</span>
<small class="text-muted" style="font-size: 10px;">${fileName}</small>
</div>
</div>
</td>
<td><span class="badge bg-danger">${typeDok}</span></td>
<td class="text-muted small">${formatDateTime(item.entry_at)}</td>
</tr>`;
}
// Tambahkan fungsi ini di dalam <script>
function toggleFolder(folderPath) {
if (expandedFolders.has(folderPath)) {
expandedFolders.delete(folderPath);
} else {
expandedFolders.add(folderPath);
}
renderTable(document.getElementById('tableSearch').value || '');
}
// === UTILITY FUNCTIONS ===
function handleRowCheck(checkbox) {
const val = String(checkbox.value);
if (checkbox.checked) {
if (!selectedIds.includes(val)) selectedIds.push(val);
} else {
selectedIds = selectedIds.filter(id => id !== val);
document.getElementById('checkAllRows').checked = false;
}
updateDownloadButton();
}
function updateDownloadButton() {
const countLabel = document.getElementById('selectedCount');
btn.disabled = selectedIds.length === 0;
countLabel.innerText = `${selectedIds.length} dipilih`;
}
function updateSummary(total) {
const summary = document.getElementById('tableSummary');
if (summary) summary.innerText = `Total: ${total} data`;
}
const downloadBtn = document.getElementById('btnDownloadMultiple');
if (downloadBtn) {
downloadBtn.addEventListener('click', function(){
if(selectedIds.length === 0){
return;
}
const payload = { ids: Array.from(selectedIds) };
downloadBtn.disabled = true;
downloadBtn.textContent = 'Menyiapkan...';
fetch('/download-multiple', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify(payload)
})
.then(async (res) => {
const contentType = res.headers.get('content-type') || '';
if(!res.ok || contentType.includes('application/json')){
const err = await res.json().catch(() => ({}));
throw new Error(err?.message || 'Gagal download file');
}
const blob = await res.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
const disposition = res.headers.get('content-disposition') || '';
const match = disposition.match(/filename="?([^"]+)"?/);
a.href = url;
a.download = match?.[1] || 'files.zip';
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
})
.catch(err => {
Swal.fire({ icon: 'error', title: 'Gagal', text: err.message || 'Gagal download file' });
})
.finally(() => {
downloadBtn.disabled = selectedIds.length === 0;
downloadBtn.textContent = 'Download Terpilih';
});
});
}
document.addEventListener('click', function(e){
const toggle = e.target.closest('.folder-toggle');
if (!toggle) return;
const folderPath = toggle.getAttribute('data-folder');
if (!folderPath) return;
if (expandedFolders.has(folderPath)) {
expandedFolders.delete(folderPath);
} else {
expandedFolders.add(folderPath);
}
renderTable(document.getElementById('tableSearch').value || '');
});
function formatDateTime(value) {
if (!value) return '—';
const d = new Date(value);
if (isNaN(d)) return value;
const pad = n => String(n).padStart(2, '0');
return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} `
+ `${pad(d.getHours())}:${pad(d.getMinutes())}`;
}
const katDok = @json($katDok);
const formCreate = document.getElementById('formFile');
const modalCreate = document.getElementById('modalCreateFile');
let colCount = 1;
document.addEventListener('change', function(e){
if(!e.target.classList.contains('toggle-expired')) return;
const targetId = e.target.getAttribute('data-target');
if(!targetId) return;
const fieldWrap = document.getElementById(targetId);
const input = fieldWrap?.querySelector('input');
if (input) input.disabled = !e.target.checked;
});
let akreData = [];
let akreLoaded = false;
let akreFlat = [];
function loadAkreData(){
if(akreLoaded) return Promise.resolve(akreData);
return fetch('/json/akreditasi.json')
.then(res => res.json())
.then(data => {
akreData = Array.isArray(data) ? data : [];
akreFlat = [];
akreLoaded = true;
return akreData;
})
.catch(() => {
akreData = [];
akreFlat = [];
akreLoaded = true;
return akreData;
});
}
function getAkreFlat(){
if(akreFlat.length) return akreFlat;
akreFlat = (akreData || []).flatMap(type => {
const segments = Array.isArray(type.segment) ? type.segment : [];
return segments.flatMap(seg => {
const children = Array.isArray(seg.turunan) ? seg.turunan : [];
return children.map(child => ({
value: `${type.name}/${seg.name}/${child.name}`,
label: `${type.name} / ${child.name}`,
type: type.name,
segment: seg.name,
item: child.name
}));
});
});
return akreFlat;
}
function fillAkreSelect(selectEl){
if(!selectEl) return;
selectEl.innerHTML = '<option value="">Pilih Instrumen</option>';
getAkreFlat().forEach(optData => {
const opt = document.createElement('option');
opt.value = optData.value;
opt.textContent = optData.label;
selectEl.appendChild(opt);
});
}
function setKategoriRequired(index, isRequired){
const katSelect = document.getElementById(`select_kategori_${index}`);
if (!katSelect) return;
if (isRequired) {
katSelect.setAttribute('required', 'required');
} else {
katSelect.removeAttribute('required');
}
}
function resetAkreFields(index){
const selectEl = document.getElementById(`akre_select_${index}`);
const typeInput = document.getElementById(`akre_type_${index}`);
const segmentInput = document.getElementById(`akre_segment_${index}`);
const itemInput = document.getElementById(`akre_item_${index}`);
if(selectEl){
selectEl.value = '';
if(window.$ && $.fn.select2) $(selectEl).val(null).trigger('change');
}
if(typeInput) typeInput.value = '';
if(segmentInput) segmentInput.value = '';
if(itemInput) itemInput.value = '';
setKategoriRequired(index, false);
}
function enableAkreFields(index){
const selectEl = document.getElementById(`akre_select_${index}`);
setKategoriRequired(index, false);
loadAkreData().then(() => {
fillAkreSelect(selectEl);
if(window.$ && $.fn.select2){
$(selectEl).select2({
dropdownParent: $('#modalCreateFile'),
placeholder: 'Pilih Instrumen',
allowClear: true
});
}
});
}
function initKategoriSelect2(index){
if(!window.$ || !$.fn.select2) return;
const katSelect = $(`#select_kategori_${index}`);
const hukumSelect = $(`#select_kategori_hukum_${index}`);
if(katSelect.length){
katSelect.select2({
dropdownParent: $('#modalCreateFile'),
placeholder:'Pilih Kategori',
allowClear: true
});
}
if(hukumSelect.length){
hukumSelect.select2({
dropdownParent: $('#modalCreateFile'),
placeholder:'Pilih Kategori Hukum',
allowClear:true
});
}
}
function selectOptionUnitKerjaV1(localCol){
const selectUnit = $(`#select_id_unit_kerja_${localCol}`);
const selectSubUnit = $(`#select_id_sub_unit_kerja_${localCol}`);
selectUnit.select2({
placeholder: '-- Pilih Unit Kerja --',
allowClear:true,
width: '100%',
dropdownParent: selectUnit.parent(),
ajax:{
url : '/select-unit-kerja-mapping',
dataType: 'json',
delay: 250,
data: function(params){
return { q: params.term }
},
processResults: function(data){
return {
results : (data?.data || []).map(item => ({
id: item.id+'/'+item.name,
text: item.name,
sub_units: item.sub_unit_kerja
}))
}
},
cache: true,
},
minimumInputLength: 0,
});
selectSubUnit.select2({
placeholder: '-- Pilih Sub Unit Kerja --',
allowClear: true,
width: '100%',
dropdownParent: selectSubUnit.parent()
});
selectUnit.on('select2:select', function (e) {
const data = e.params.data;
selectSubUnit.empty().append('<option value="" disabled selected>-- Pilih Sub Unit Kerja --</option>');
if (data.sub_units && data.sub_units.length > 0) {
data.sub_units.forEach(sub => {
selectSubUnit.append(`<option value="${sub.id}/${sub.name}">${sub.name}</option>`);
});
}
});
}
window.addFormV2 = function(){
const col = document.getElementById('col_add_fileV2');
if (!col) return;
const katOptions = (Array.isArray(katDok) ? katDok : [])
.map(k => `<option value="${k.master_kategori_directory_id}/${k.nama_kategori_directory}">${k.nama_kategori_directory}</option>`)
.join('');
let html = `
<div class="row g-3 align-items-start" id="col-${colCount}">
<hr class="my-3" />
<div class="col-12 d-flex justify-content-end">
<button type="button"
class="btn btn-sm btn-danger"
onclick="removeCol(${colCount})">
<i class="fa-solid fa-trash"></i> Hapus
</button>
</div>
<div class="col-md-6">
<label class="form-label fw-semibold">Unit <span class="text-danger">*</span></label>
<select class="form-select"
name="data[${colCount}][id_unit_kerja]"
id="select_id_unit_kerja_${colCount}"
required>
<option value="" disabled selected>Pilih Unit</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label fw-semibold">Sub Unit <span class="text-danger">*</span></label>
<select class="form-select"
name="data[${colCount}][id_sub_unit_kerja]"
id="select_id_sub_unit_kerja_${colCount}"
required>
<option value="" disabled selected>Pilih Sub Unit</option>
</select>
</div>
<div class="col-md-4">
<label class="form-label fw-semibold">Nomor Dokumen</label>
<div class="input-group">
<span class="input-group-text">#</span>
<input type="text"
class="form-control"
name="data[${colCount}][no_dokumen]"
placeholder="Contoh: 001/RS/IT/I/2026">
</div>
</div>
<div class="col-md-4">
<label class="form-label fw-semibold">Nama Dokumen<span class="text-danger">*</span></label>
<input type="text"
class="form-control"
name="data[${colCount}][nama_dokumen]"
placeholder="Contoh: Panduan Mencuci Tangan" required>
</div>
<div class="col-md-4">
<label class="form-label fw-semibold">Tanggal Terbit</label>
<input class="form-control"
type="date"
name="data[${colCount}][date_active]">
</div>
<div class="col-md-3">
<div class="form-check">
<input class="form-check-input toggle-expired"
type="checkbox"
id="hasExpired_${colCount}"
data-target="expiredField_${colCount}">
<label class="form-check-label" for="hasExpired_${colCount}">Masa Berlaku Dokumen?</label>
</div>
</div>
<div class="col-md-5" id="expiredField_${colCount}">
<label class="form-label fw-semibold">Tanggal Kedaluwarsa Dokumen</label>
<input class="form-control"
type="date"
name="data[${colCount}][tgl_expired]" disabled>
</div>
<div class="col-md-4">
<label class="form-label fw-semibold">Boleh dilihat unit lain? <span class="text-danger">*</span></label>
<div class="border rounded-3 p-2 bg-light">
<div class="form-check">
<input class="form-check-input"
type="radio"
name="data[${colCount}][is_permission]"
id="perm_yes_${colCount}"
value="1"
required>
<label class="form-check-label" for="perm_yes_${colCount}">Iya</label>
</div>
<div class="form-check mt-1">
<input class="form-check-input"
type="radio"
name="data[${colCount}][is_permission]"
id="perm_no_${colCount}"
value="2"
required>
<label class="form-check-label" for="perm_no_${colCount}">Tidak</label>
</div>
</div>
</div>
<div class="col-md-4">
<label class="form-label fw-semibold">Instrumen Akreditasi </label>
<select class="form-select akre-select" id="akre_select_${colCount}" name="data[${colCount}][akre]" style="width: 350px;">
<option value="">Pilih Instrumen</option>
</select>
<div class="form-text text-muted">Isi form ini bila dokumen yang diunggah merupakan dokumen <strong>akreditasi</strong>.</div>
</div>
<div class="col-md-4">
<label class="form-label fw-semibold">Kategori Hukum</label>
<select class="form-select select-kat-hukum" name="data[${colCount}][kategori_hukum]" id="select_kategori_hukum_${colCount}" style="width: 350px;">
<option value="">Pilih Kategori Hukum</option>
<option value="Pedir - Kebijakan">Kebijakan - Peraturan Direktur</option>
<option value="Keputusan Dirut - Kebijakan">Kebijakan - Keputusan Direktur Utama</option>
<option value="Surat Edaran - Kebijakan">Kebijakan - Surat Edaran</option>
<option value="Pengumuman - Kebijakan">Pengumuman - Kebijakan</option>
<option value="Pelayanan Kesehatan - Kerjasama">Pelayanan Kesehatan - Kerjasama</option>
<option value="Management - Kerjasama">Management - Kerjasama</option>
<option value="Pemeliharan - Kerjasama">Pemeliharan - Kerjasama</option>
<option value="iklat - Kerjasama">Diklat - Kerjasama</option>
<option value="Luar Negeri - Kerjasama">Luar Negeri - Kerjasama</option>
<option value="Area Bisnis - Kerjasama">Area Bisnis - Kerjasama</option>
<option value="Pendidikan - Kerjasama">Pendidikan - Kerjasama</option>
<option value="Pengampuan KIA">Pengampuan KIA</option>
</select>
<div class="form-text text-muted">Isi form ini bila dokumen yang diunggah merupakan dokumen <strong>akreditasi</strong>.</div>
</div>
<div class="col-md-4">
<label class="form-label fw-semibold">Kategori Lainnya</label>
<select class="form-select"
name="data[${colCount}][master_kategori_directory_id]"
id="select_kategori_${colCount}" style="width: 350px;">
<option value="">Pilih Kategori</option>
${katOptions}
</select>
</div>
<div class="col-md-12">
<label for="fileUpload_${colCount}" class="form-label fw-semibold">📂 Upload Dokumen (PDF)</label>
<div class="border rounded-3 p-3 bg-white shadow-sm">
<input class="form-control"
type="file"
id="fileUpload_${colCount}"
accept=".pdf"
name="data[${colCount}][file]">
<div class="mt-2 text-success fw-semibold d-none file-name" id="fileName_${colCount}"></div>
</div>
<div class="form-text text-muted">Format yang didukung: <b>PDF</b>.</div>
</div>
</div>`;
col.insertAdjacentHTML('beforeend', html);
selectOptionUnitKerjaV1(colCount);
initKategoriSelect2(colCount);
enableAkreFields(colCount);
setKategoriRequired(colCount, false);
colCount++;
}
window.removeCol = function(count){
const el = document.getElementById(`col-${count}`);
if (el) el.remove();
}
function resetCreateForm(){
colCount = 1;
const colAdd = document.getElementById('col_add_fileV2');
if (colAdd) colAdd.innerHTML = '';
if (formCreate) {
formCreate.reset();
$(formCreate).find('select').val(null).trigger('change');
$(formCreate).find('input[type="file"]').val('');
$(formCreate).find('.file-name').addClass('d-none').text('');
}
resetAkreFields(0);
enableAkreFields(0);
}
if (formCreate) {
const select0 = $('#select_id_unit_kerja_0');
if (select0.length) selectOptionUnitKerjaV1(0);
initKategoriSelect2(0);
enableAkreFields(0);
formCreate.addEventListener('submit', (e) => {
e.preventDefault();
const submitBtn = formCreate.querySelector('button[type="submit"]');
if (submitBtn) submitBtn.disabled = true;
if (submitBtn) submitBtn.textContent = 'menyimpan...';
const formData = new FormData(formCreate);
fetch(`/uploadv2`, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': document.querySelector('input[name="_token"]')?.value || ''
},
body: formData
}).then(async(res) => {
const responseData = await res.json();
if(responseData.status){
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: responseData.message || 'Data berhasil disimpan.',
timer: 1500,
showConfirmButton: false
});
const modalInstance = bootstrap.Modal.getInstance(modalCreate);
modalInstance?.hide();
resetCreateForm();
fetchData();
} else {
throw new Error(responseData.message || 'Terjadi kesalahan saat menyimpan data.');
}
}).catch(err => {
Swal.fire({
icon: 'error',
title: 'Gagal',
text: err.message || 'Terjadi kesalahan.'
});
}).finally(() => {
if (submitBtn) submitBtn.disabled = false;
if (submitBtn) submitBtn.textContent = 'Simpan';
});
});
}
document.addEventListener('change', function(e){
if(e.target.classList.contains('akre-select')){
const id = e.target.id || '';
const idx = id.split('_').pop();
const [typeVal = '', segmentVal = '', itemVal = ''] = (e.target.value || '').split('/');
const typeInput = document.getElementById(`akre_type_${idx}`);
const segmentInput = document.getElementById(`akre_segment_${idx}`);
const itemInput = document.getElementById(`akre_item_${idx}`);
if(typeInput) typeInput.value = typeVal;
if(segmentInput) segmentInput.value = segmentVal;
if(itemInput) itemInput.value = itemVal;
}
});
</script>
@endsection