add detail data path akreditasi

This commit is contained in:
JokoPrasetio 2026-06-11 11:53:45 +07:00
parent 80edf77280
commit d3b9cc3425
3 changed files with 167 additions and 1 deletions

View File

@ -2833,6 +2833,57 @@ class DashboardController extends Controller
]); ]);
} }
public function dataAkreditasiRecap()
{
$rows = FileDirectory::query()
->select(['file'])
->where('statusenabled', true)
->where('status_action', 'approved')
->where('is_akre', true)
->get();
$folderCounts = [];
$totalFiles = 0;
foreach ($rows as $row) {
$filePath = trim((string) ($row->file ?? ''));
if ($filePath === '' || !str_contains($filePath, '/')) {
continue;
}
$folderPath = preg_replace('~/[^/]+$~', '', $filePath);
$folderPath = trim((string) $folderPath, '/');
if ($folderPath === '') {
continue;
}
$folderCounts[$folderPath] = ($folderCounts[$folderPath] ?? 0) + 1;
$totalFiles++;
}
$detailRows = collect($folderCounts)
->map(fn ($count, $path) => [
'path' => $path,
'total' => $count,
])
->sortBy([
['total', 'desc'],
['path', 'asc'],
])
->values()
->all();
return response()->json([
'status' => true,
'message' => 'Berhasil mendapatkan rekap akreditasi',
'summary' => [
'total_files' => $totalFiles,
'total_folders' => count($folderCounts),
],
'data' => $detailRows,
]);
}
public function expDokumen(){ public function expDokumen(){
$katDok = MasterKategori::where('statusenabled', true)->select('master_kategori_directory_id', 'nama_kategori_directory')->get(); $katDok = MasterKategori::where('statusenabled', true)->select('master_kategori_directory_id', 'nama_kategori_directory')->get();
$authMapping = auth()->user()?->dataUser?->mappingUnitKerjaPegawai[0]; $authMapping = auth()->user()?->dataUser?->mappingUnitKerjaPegawai[0];

View File

@ -305,6 +305,19 @@
.akre-selection-status { .akre-selection-status {
color: #7b8aa0; color: #7b8aa0;
} }
.akre-recap-bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 10px 12px;
border-top: 1px solid #edf2f7;
color: #64748b;
font-size: 12px;
}
.akre-recap-bar strong {
color: #1e293b;
}
.akre-parent-chip { .akre-parent-chip {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@ -397,7 +410,7 @@
<div class="col-lg-4 akre-column"> <div class="col-lg-4 akre-column">
<div class="akre-tree-panel"> <div class="akre-tree-panel">
<div class="akre-tree-header"> <div class="akre-tree-header">
<h5 class="mb-1">Struktur Akreditasi</h5> <h5 class="mb-1">Struktur Dokumen</h5>
<p class="text-muted small mb-0">Pilih folder di kiri untuk memfilter dokumen di panel kanan.</p> <p class="text-muted small mb-0">Pilih folder di kiri untuk memfilter dokumen di panel kanan.</p>
</div> </div>
<div class="akre-tree-body" id="akreTreeContainer"> <div class="akre-tree-body" id="akreTreeContainer">
@ -489,6 +502,12 @@
<div class="akre-browser-empty">Pilih folder pada struktur kiri untuk melihat isinya.</div> <div class="akre-browser-empty">Pilih folder pada struktur kiri untuk melihat isinya.</div>
</div> </div>
</div> </div>
<div class="akre-recap-bar">
<span id="akreRecapSummary">Memuat ringkasan dokumen akreditasi...</span>
<button type="button" class="btn btn-outline-secondary btn-sm" id="btnAkreRecapDetail">
Detail
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -542,6 +561,35 @@
</div> </div>
@endif @endif
<div class="modal fade" id="modalAkreRecapDetail" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5">Detail Rekap Dokumen Akreditasi</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="small text-muted mb-3" id="akreRecapModalSummary">Memuat detail rekap...</div>
<div class="table-responsive">
<table class="table table-sm table-hover align-middle mb-0">
<thead class="table-light">
<tr>
<th style="width: 70%;">Path</th>
<th class="text-center" style="width: 30%;">Total File</th>
</tr>
</thead>
<tbody id="akreRecapDetailBody">
<tr>
<td colspan="2" class="text-center text-muted py-4">Memuat data...</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script> <script>
// === STATE MANAGEMENT === // === STATE MANAGEMENT ===
let currentData = []; let currentData = [];
@ -562,6 +610,7 @@
initEventListeners(); initEventListeners();
initTooltips(); initTooltips();
configureAkreModal(''); configureAkreModal('');
fetchAkreRecap();
}); });
function initTooltips() { function initTooltips() {
@ -623,6 +672,15 @@
clearSelectedFiles(); clearSelectedFiles();
}); });
} }
const recapDetailBtn = document.getElementById('btnAkreRecapDetail');
if (recapDetailBtn) {
recapDetailBtn.addEventListener('click', function() {
const modalEl = document.getElementById('modalAkreRecapDetail');
if (!modalEl) return;
bootstrap.Modal.getOrCreateInstance(modalEl).show();
});
}
} }
// === CORE FUNCTIONS === // === CORE FUNCTIONS ===
@ -824,11 +882,66 @@
let activeAkreNodeKey = null; let activeAkreNodeKey = null;
let activeAkrePath = ''; let activeAkrePath = '';
let pendingAkreFocusPath = ''; let pendingAkreFocusPath = '';
let akreRecapRows = [];
function loadAkreData(){ function loadAkreData(){
return Promise.resolve(akreData); return Promise.resolve(akreData);
} }
function renderAkreRecapSummary(summary = {}) {
const summaryEl = document.getElementById('akreRecapSummary');
const modalSummaryEl = document.getElementById('akreRecapModalSummary');
const totalFiles = Number(summary.total_files || 0);
const totalFolders = Number(summary.total_folders || 0);
const html = totalFiles || totalFolders
? `Total <strong>${totalFiles}</strong> file di <strong>${totalFolders}</strong> folder akreditasi.`
: 'Belum ada file akreditasi yang tersimpan.';
if (summaryEl) summaryEl.innerHTML = html;
if (modalSummaryEl) {
modalSummaryEl.textContent = totalFiles || totalFolders
? `Total ${totalFiles} file di ${totalFolders} folder akreditasi.`
: 'Belum ada file akreditasi yang tersimpan.';
}
}
function renderAkreRecapDetailRows(rows = []) {
const tbody = document.getElementById('akreRecapDetailBody');
if (!tbody) return;
if (!rows.length) {
tbody.innerHTML = '<tr><td colspan="2" class="text-center text-muted py-4">Belum ada data rekap akreditasi.</td></tr>';
return;
}
tbody.innerHTML = rows.map(row => `
<tr>
<td>${e(row.path || '-')}</td>
<td class="text-center fw-semibold">${Number(row.total || 0)}</td>
</tr>
`).join('');
}
function fetchAkreRecap() {
const summaryEl = document.getElementById('akreRecapSummary');
const tbody = document.getElementById('akreRecapDetailBody');
if (summaryEl) summaryEl.textContent = 'Memuat ringkasan dokumen akreditasi...';
if (tbody) tbody.innerHTML = '<tr><td colspan="2" class="text-center text-muted py-4">Memuat data...</td></tr>';
return fetch('/data-akreditasi/recap')
.then(res => res.json())
.then(data => {
akreRecapRows = Array.isArray(data?.data) ? data.data : [];
renderAkreRecapSummary(data?.summary || {});
renderAkreRecapDetailRows(akreRecapRows);
})
.catch(() => {
akreRecapRows = [];
if (summaryEl) summaryEl.textContent = 'Gagal memuat ringkasan dokumen akreditasi.';
if (tbody) tbody.innerHTML = '<tr><td colspan="2" class="text-center text-danger py-4">Gagal memuat data.</td></tr>';
});
}
function refreshAkreData(){ function refreshAkreData(){
akreFlat = []; akreFlat = [];
return fetch('/json/akreditasi.jff') return fetch('/json/akreditasi.jff')
@ -1645,6 +1758,7 @@
} else { } else {
renderAkreSubtree(); renderAkreSubtree();
} }
fetchAkreRecap();
} else { } else {
throw new Error(responseData.message || 'Terjadi kesalahan saat menyimpan data.'); throw new Error(responseData.message || 'Terjadi kesalahan saat menyimpan data.');
} }

View File

@ -19,6 +19,7 @@ Route::middleware(['auth:admin,web'])->group(function(){
Route::get('/datatable-umum', [DashboardController::class, 'datatableDataUmum']); Route::get('/datatable-umum', [DashboardController::class, 'datatableDataUmum']);
Route::get('/data-akreditasi', [DashboardController::class, 'dataAkreditasi']); Route::get('/data-akreditasi', [DashboardController::class, 'dataAkreditasi']);
Route::get('/datatable-akreditasi', [DashboardController::class, 'dataTableAkreditasi']); Route::get('/datatable-akreditasi', [DashboardController::class, 'dataTableAkreditasi']);
Route::get('/data-akreditasi/recap', [DashboardController::class, 'dataAkreditasiRecap']);
// Kelola "folder/instrumen" akreditasi via file: public/json/akreditasi.jff (tanpa database) // Kelola "folder/instrumen" akreditasi via file: public/json/akreditasi.jff (tanpa database)
Route::get('/akreditasi/instrumen', [AkreditasiInstrumenController::class, 'index']); Route::get('/akreditasi/instrumen', [AkreditasiInstrumenController::class, 'index']);