add expired menu
This commit is contained in:
parent
a0b32672b4
commit
1b07699476
@ -195,6 +195,10 @@ class DashboardController extends Controller
|
||||
->when(!empty($unitFilter), function($q) use($unitFilter){
|
||||
$q->whereIn('id_unit_kerja', $unitFilter);
|
||||
})
|
||||
->where(function ($q) {
|
||||
$q->whereNull('tgl_expired')
|
||||
->orWhereDate('tgl_expired', '>=', now()->toDateString());
|
||||
})
|
||||
->where('statusenabled', true)
|
||||
->where('status_action', 'approved');
|
||||
|
||||
@ -1041,6 +1045,10 @@ class DashboardController extends Controller
|
||||
}
|
||||
$baseQuery = FileDirectory::with('kategori')
|
||||
->where('statusenabled', true)->where('status_action', 'approved')
|
||||
->where(function ($q) {
|
||||
$q->whereNull('tgl_expired')
|
||||
->orWhereDate('tgl_expired', '>=', now()->toDateString());
|
||||
})
|
||||
->when(!empty($unitIds), function ($q) use ($unitIds) {
|
||||
$q->whereIn('id_unit_kerja', $unitIds);
|
||||
});
|
||||
@ -1560,7 +1568,6 @@ class DashboardController extends Controller
|
||||
]
|
||||
]);
|
||||
} catch (\Throwable $th) {
|
||||
dd($th);
|
||||
return response()->json([
|
||||
'status' => false,
|
||||
'message' => 'Gagal! mendapatkan data'
|
||||
@ -1571,6 +1578,10 @@ class DashboardController extends Controller
|
||||
private function buildRecapData(array $unitIds, string $keyword = ''): array
|
||||
{
|
||||
$rows = FileDirectory::where('statusenabled', true)
|
||||
->where(function ($q) {
|
||||
$q->whereNull('tgl_expired')
|
||||
->orWhereDate('tgl_expired', '>=', now()->toDateString());
|
||||
})
|
||||
->whereNotNull('status_action')->where('status_action', 'approved');
|
||||
if(in_array(22, $unitIds)){
|
||||
$rows = $rows->pluck('file');
|
||||
@ -2455,4 +2466,249 @@ class DashboardController extends Controller
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function expDokumen(){
|
||||
$katDok = MasterKategori::where('statusenabled', true)->select('master_kategori_directory_id', 'nama_kategori_directory')->get();
|
||||
$authMapping = auth()->user()?->dataUser?->mappingUnitKerjaPegawai[0];
|
||||
$authUnitKerja = $authMapping->objectunitkerjapegawaifk ?? null;
|
||||
$authSubUnitKerja = $authMapping->objectsubunitkerjapegawaifk ?? null;
|
||||
$data = [
|
||||
'title' => 'Dashboard',
|
||||
'katDok' => $katDok,
|
||||
'authUnitKerja' => $authUnitKerja,
|
||||
'authSubUnitKerja' => $authSubUnitKerja,
|
||||
];
|
||||
return view('expDokumen.index', $data);
|
||||
}
|
||||
|
||||
public function dataUnitExp(){
|
||||
$perPage = (int) request('per_page', 10);
|
||||
// $authUnitId = auth()->user()->dataUser?->mappingUnitKerjaPegawai[0]?->objectunitkerjapegawaifk;
|
||||
$userId = auth()->user()->dataUser->id ?? 937;
|
||||
$mapping = MappingUnitKerjaPegawai::where('statusenabled', true)
|
||||
->where('objectpegawaifk', $userId)
|
||||
->get(['objectunitkerjapegawaifk']);
|
||||
$unitIds = $mapping->pluck('objectunitkerjapegawaifk')
|
||||
->filter() // buang null
|
||||
->unique()
|
||||
->values()
|
||||
->toArray();
|
||||
$keyword = request('keyword');
|
||||
$kategori = request('kategori');
|
||||
$kategoriHeader = request('kategori_header');
|
||||
$unitFilter = request('unit');
|
||||
$kategoriValues = is_array($kategori)
|
||||
? array_values(array_filter($kategori))
|
||||
: array_values(array_filter(explode(',', (string) $kategori)));
|
||||
$kategoriHeaderValues = is_array($kategoriHeader)
|
||||
? array_values(array_filter($kategoriHeader))
|
||||
: array_values(array_filter(explode(',', (string) $kategoriHeader)));
|
||||
$allKategoriValues = array_values(array_filter(array_merge($kategoriValues, $kategoriHeaderValues)));
|
||||
$kategoriTypes = [];
|
||||
$kategoriIds = [];
|
||||
$kategoriHukumValues = [];
|
||||
foreach ($allKategoriValues as $val) {
|
||||
$lower = strtolower(trim((string) $val));
|
||||
if (str_starts_with($lower, 'hukum:')) {
|
||||
$hukumVal = trim(substr((string) $val, strlen('hukum:')));
|
||||
if ($hukumVal !== '') {
|
||||
$kategoriHukumValues[] = $hukumVal;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (in_array($lower, ['akreditasi', 'akre'], true)) {
|
||||
$kategoriTypes[] = 'akreditasi';
|
||||
continue;
|
||||
}
|
||||
if ($lower === 'hukum') {
|
||||
$kategoriTypes[] = 'hukum';
|
||||
continue;
|
||||
}
|
||||
if ($lower === 'lainnya') {
|
||||
$kategoriTypes[] = 'lainnya';
|
||||
continue;
|
||||
}
|
||||
$kategoriIds[] = $val;
|
||||
}
|
||||
$baseQuery = FileDirectory::with('kategori')
|
||||
->when(!empty($unitFilter), function($q) use($unitFilter){
|
||||
$q->whereIn('id_unit_kerja', $unitFilter);
|
||||
})
|
||||
->whereNotNull('tgl_expired')
|
||||
->whereDate('tgl_expired', '<', now()->toDateString())
|
||||
->where('statusenabled', true)
|
||||
->where('status_action', 'approved');
|
||||
|
||||
if(in_array(22, $unitIds)){
|
||||
}elseif(auth()->user()->username === "admin.turt"){
|
||||
|
||||
}else{
|
||||
$baseQuery = $baseQuery->whereIn('id_unit_kerja', $unitIds);
|
||||
}
|
||||
|
||||
$query = (clone $baseQuery)
|
||||
->when(!empty($kategoriIds) || !empty($kategoriTypes) || !empty($kategoriHukumValues), function ($q) use ($kategoriIds, $kategoriTypes, $kategoriHukumValues) {
|
||||
$q->where(function ($sub) use ($kategoriIds, $kategoriTypes, $kategoriHukumValues) {
|
||||
$hasClause = false;
|
||||
if (!empty($kategoriIds)) {
|
||||
$sub->whereIn('master_kategori_directory_id', $kategoriIds);
|
||||
$hasClause = true;
|
||||
}
|
||||
if (!empty($kategoriHukumValues)) {
|
||||
$hasClause ? $sub->orWhereIn('kategori_hukum', $kategoriHukumValues) : $sub->whereIn('kategori_hukum', $kategoriHukumValues);
|
||||
$hasClause = true;
|
||||
}
|
||||
if (in_array('akreditasi', $kategoriTypes, true)) {
|
||||
$sub->where('is_akre', true);
|
||||
$hasClause = true;
|
||||
}
|
||||
if (in_array('hukum', $kategoriTypes, true)) {
|
||||
$hasClause ? $sub->orWhereNotNull('kategori_hukum') : $sub->whereNotNull('kategori_hukum');
|
||||
$hasClause = true;
|
||||
}
|
||||
if (in_array('lainnya', $kategoriTypes, true)) {
|
||||
$hasClause ? $sub->orWhereNotNull('master_kategori_directory_id') : $sub->whereNotNull('master_kategori_directory_id');
|
||||
}
|
||||
});
|
||||
})
|
||||
->when($keyword, function ($q) use ($keyword) {
|
||||
$q->where(function ($sub) use ($keyword) {
|
||||
$sub->where('nama_dokumen', 'ILIKE', "%{$keyword}%")
|
||||
->orWhere('no_dokumen', 'ILIKE', "%{$keyword}%");
|
||||
});
|
||||
});
|
||||
|
||||
$data = $query->orderBy('entry_at', 'desc')
|
||||
->paginate($perPage);
|
||||
|
||||
$items = collect($data->items())->map(function($item){
|
||||
$item->nama_kategori = $item->kategori->nama_kategori_directory ?? $item->kategori_hukum ?? null;
|
||||
return $item;
|
||||
});
|
||||
$kategoriList = (clone $baseQuery)->get()->map(function($item){
|
||||
if (!empty($item->kategori_hukum)) {
|
||||
return ['id' => 'hukum:' . $item->kategori_hukum, 'label' => $item->kategori_hukum];
|
||||
}
|
||||
$label = $item->kategori->nama_kategori_directory ?? $item->nama_kategori_directory ?? 'Kategori';
|
||||
$id = $item->master_kategori_directory_id ?? $label;
|
||||
return ['id' => (string) $id, 'label' => $label];
|
||||
})->unique('id')->values();
|
||||
|
||||
$payload = [
|
||||
'status' => true,
|
||||
'message' => 'Berhasil mendapatkan data',
|
||||
'data' => $items,
|
||||
'kategori_list' => $kategoriList,
|
||||
'pagination' => [
|
||||
'current_page' => $data->currentPage(),
|
||||
'next_page' => $data->hasMorePages() ? $data->currentPage() + 1 : null,
|
||||
'has_more' => $data->hasMorePages(),
|
||||
'last_page' => $data->lastPage(),
|
||||
'per_page' => $data->perPage(),
|
||||
'total' => $data->total(),
|
||||
]
|
||||
];
|
||||
return response()->json($payload);
|
||||
}
|
||||
|
||||
public function recapDataExp(){
|
||||
try {
|
||||
$perPage = (int) request('per_page', 10);
|
||||
$page = max(1, (int) request('page', 1));
|
||||
$keyword = strtolower(request('keyword', ''));
|
||||
// $authUnit = auth()->user()->masterPersetujuan->details->pluck('unit_pegawai_id')->unique()->toArray();
|
||||
$user = auth()->user()->dataUser;
|
||||
$unitIds = [];
|
||||
if($user){
|
||||
$mapping = MappingUnitKerjaPegawai::where('statusenabled', true)
|
||||
->where('objectpegawaifk', $user->id)
|
||||
->get(['objectunitkerjapegawaifk', 'objectsubunitkerjapegawaifk']);
|
||||
$unitIds = $mapping->pluck('objectunitkerjapegawaifk')
|
||||
->filter() // buang null
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
$result = $this->buildRecapDataExp($unitIds, $keyword);
|
||||
// paginate manually
|
||||
$total = count($result);
|
||||
$chunks = array_chunk($result, $perPage);
|
||||
$currentData = $chunks[$page-1] ?? [];
|
||||
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
'data' => $currentData,
|
||||
'message' => 'Berhasil mendapatkan data',
|
||||
'pagination' => [
|
||||
'current_page' => $page,
|
||||
'per_page' => $perPage,
|
||||
'total' => $total,
|
||||
'last_page' => max(1, ceil($total / $perPage)),
|
||||
'has_more' => $page < max(1, ceil($total / $perPage)),
|
||||
]
|
||||
]);
|
||||
} catch (\Throwable $th) {
|
||||
return response()->json([
|
||||
'status' => false,
|
||||
'message' => 'Gagal! mendapatkan data'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function buildRecapDataExp(array $unitIds, string $keyword = ''): array
|
||||
{
|
||||
$rows = FileDirectory::where('statusenabled', true)
|
||||
->whereNotNull('tgl_expired')
|
||||
->whereDate('tgl_expired', '<', now()->toDateString())
|
||||
->whereNotNull('status_action')->where('status_action', 'approved');
|
||||
if(in_array(22, $unitIds)){
|
||||
$rows = $rows->pluck('file');
|
||||
}elseif(auth()->user()->username === "admin.turt"){
|
||||
$rows = $rows->pluck('file');
|
||||
}else{
|
||||
$rows = $rows->whereIn('id_unit_kerja', $unitIds)->pluck('file');
|
||||
}
|
||||
$grouped = [];
|
||||
foreach ($rows as $path) {
|
||||
$parts = array_values(array_filter(explode('/', $path)));
|
||||
if (count($parts) < 4) {
|
||||
continue;
|
||||
}
|
||||
$unit = $parts[0];
|
||||
$folder = $parts[2];
|
||||
|
||||
if ($keyword) {
|
||||
$hit = str_contains(strtolower($unit), $keyword) || str_contains(strtolower($folder), $keyword);
|
||||
if (!$hit) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!isset($grouped[$unit])) {
|
||||
$grouped[$unit] = [];
|
||||
}
|
||||
if (!isset($grouped[$unit][$folder])) {
|
||||
$grouped[$unit][$folder] = 0;
|
||||
}
|
||||
$grouped[$unit][$folder]++;
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($grouped as $unitName => $folders) {
|
||||
$data = [];
|
||||
foreach ($folders as $folder => $count) {
|
||||
$data[] = [
|
||||
'folder' => $folder,
|
||||
'count' => $count,
|
||||
];
|
||||
}
|
||||
usort($data, fn($a, $b) => $b['count'] <=> $a['count']);
|
||||
$result[] = [
|
||||
'unit' => $unitName,
|
||||
'data' => $data,
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -542,7 +542,7 @@
|
||||
if(pageData.length === 0){
|
||||
tbody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="7" class="text-center text-muted py-4">
|
||||
<td colspan="8" class="text-center text-muted py-4">
|
||||
Tidak ada data yang cocok
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -664,7 +664,7 @@
|
||||
if(pageData.length === 0){
|
||||
tbody.innerHTML = `
|
||||
<tr>
|
||||
<td colspan="7" class="text-center text-muted py-4">
|
||||
<td colspan="8" class="text-center text-muted py-4">
|
||||
Tidak ada data yang cocok
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
1450
resources/views/expDokumen/index.blade.php
Normal file
1450
resources/views/expDokumen/index.blade.php
Normal file
File diff suppressed because it is too large
Load Diff
152
resources/views/expDokumen/section/recap.blade.php
Normal file
152
resources/views/expDokumen/section/recap.blade.php
Normal file
@ -0,0 +1,152 @@
|
||||
@php($showRecapTitle = $showRecapTitle ?? true)
|
||||
<div class="d-flex flex-column flex-md-row align-items-md-center gap-2 mb-3">
|
||||
@if ($showRecapTitle)
|
||||
<div>
|
||||
<h4 class="mb-0">Rekap Dokumen Expired</h4>
|
||||
<small class="text-muted">Ringkasan jumlah file per Unit dan Kategori</small>
|
||||
</div>
|
||||
@endif
|
||||
<div class="{{ $showRecapTitle ? 'ms-md-auto' : '' }} d-flex gap-2 align-items-center">
|
||||
<div class="input-group input-group-sm" style="max-width:320px;">
|
||||
<span class="input-group-text bg-white border-end-0">
|
||||
<i class="fa fa-search text-muted"></i>
|
||||
</span>
|
||||
<input type="search" id="recapSearch" class="form-control border-start-0" placeholder="Cari unit atau folder" oninput="debouncedRecapSearch(this.value)">
|
||||
</div>
|
||||
<select id="recapPerPage" class="form-select form-select-sm" style="width:auto;" onchange="changePerPage(this.value)">
|
||||
<option value="5">5</option>
|
||||
<option value="10" selected>10</option>
|
||||
<option value="20">20</option>
|
||||
<option value="50">50</option>
|
||||
</select>
|
||||
<button class="btn btn-outline-secondary btn-sm d-flex align-items-center gap-1" onclick="fetchRecap()">
|
||||
<i class="fa fa-rotate"></i>
|
||||
<span>Refresh</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-responsive" style="max-height: 55vh; overflow-y:auto;">
|
||||
<table class="table table-sm table-hover align-middle">
|
||||
<thead class="table-light shadow-sm">
|
||||
<tr>
|
||||
<th style="width:5%;" class="text-center">#</th>
|
||||
<th style="width:30%;">Unit / Akreditasi</th>
|
||||
<th style="width:20%;">Kategori</th>
|
||||
<th style="width:15%;" class="text-center">Jumlah File</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="recapBody">
|
||||
<tr>
|
||||
<td colspan="4" class="text-center text-muted py-4">Memuat data...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex flex-column flex-md-row align-items-center justify-content-between gap-2 mt-3" id="recapPagination"></div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => fetchRecap());
|
||||
|
||||
let recapDebounce;
|
||||
const recapState = { page:1, perPage:10, keyword:'', lastPage:1 };
|
||||
|
||||
function debouncedRecapSearch(val){
|
||||
clearTimeout(recapDebounce);
|
||||
recapDebounce = setTimeout(() => {
|
||||
recapState.keyword = val;
|
||||
recapState.page = 1;
|
||||
fetchRecap();
|
||||
}, 250);
|
||||
}
|
||||
|
||||
function changePerPage(val){
|
||||
recapState.perPage = parseInt(val) || 10;
|
||||
recapState.page = 1;
|
||||
fetchRecap();
|
||||
}
|
||||
|
||||
function fetchRecap(){
|
||||
const tbody = document.getElementById('recapBody');
|
||||
const pager = document.getElementById('recapPagination');
|
||||
if(!tbody) return;
|
||||
tbody.innerHTML = `<tr><td colspan="4" class="text-center text-muted py-4">Memuat data...</td></tr>`;
|
||||
if(pager) pager.innerHTML = '';
|
||||
|
||||
const params = new URLSearchParams({
|
||||
page: recapState.page,
|
||||
per_page: recapState.perPage,
|
||||
keyword: recapState.keyword || ''
|
||||
});
|
||||
fetch('/data/recapExp?' + params.toString())
|
||||
.then(res => res.json())
|
||||
.then(json => {
|
||||
const rows = json?.data || [];
|
||||
recapState.lastPage = json?.pagination?.last_page || 1;
|
||||
if(!rows.length){
|
||||
tbody.innerHTML = `<tr><td colspan="4" class="text-center text-muted py-4">Tidak ada data</td></tr>`;
|
||||
return;
|
||||
}
|
||||
let grandTotal = 0;
|
||||
const html = rows.map((row, idx) => {
|
||||
const folderRows = (row.data || []).map((f, i) => `
|
||||
<tr>
|
||||
${i === 0 ? `<td rowspan="${row.data.length}" class="text-center align-middle fw-semibold">${idx+1}</td>` : ''}
|
||||
${i === 0 ? `<td rowspan="${row.data.length}" class="fw-semibold">${row.unit || '-'}</td>` : ''}
|
||||
<td>${f.folder || '-'}</td>
|
||||
<td class="text-center fw-bold">${f.count || 0}</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
(row.data || []).forEach(f => { grandTotal += (parseInt(f.count, 10) || 0); });
|
||||
return folderRows;
|
||||
}).join('');
|
||||
tbody.innerHTML = html + `
|
||||
<tr class="table-light">
|
||||
<td colspan="3" class="text-end fw-semibold">Total File</td>
|
||||
<td class="text-center fw-bold">${grandTotal}</td>
|
||||
</tr>
|
||||
`;
|
||||
renderRecapPagination();
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
tbody.innerHTML = `<tr><td colspan="4" class="text-center text-danger py-4">Gagal memuat data</td></tr>`;
|
||||
});
|
||||
}
|
||||
|
||||
function renderRecapPagination(){
|
||||
const pager = document.getElementById('recapPagination');
|
||||
if(!pager) return;
|
||||
if(recapState.lastPage <= 1){
|
||||
pager.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
const maxButtons = 5;
|
||||
let start = Math.max(1, recapState.page - Math.floor(maxButtons/2));
|
||||
let end = Math.min(recapState.lastPage, start + maxButtons - 1);
|
||||
start = Math.max(1, end - maxButtons + 1);
|
||||
|
||||
let buttons = '';
|
||||
buttons += `<button class="btn btn-outline-secondary btn-sm" data-page="prev" ${recapState.page === 1 ? 'disabled' : ''}>‹</button>`;
|
||||
for(let i=start; i<=end; i++){
|
||||
buttons += `<button class="btn btn-sm ${i === recapState.page ? 'btn-primary' : 'btn-outline-secondary'}" data-page="${i}">${i}</button>`;
|
||||
}
|
||||
buttons += `<button class="btn btn-outline-secondary btn-sm" data-page="next" ${recapState.page === recapState.lastPage ? 'disabled' : ''}>›</button>`;
|
||||
|
||||
pager.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 ${recapState.page} dari ${recapState.lastPage}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
pager.querySelectorAll('button[data-page]').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const page = btn.getAttribute('data-page');
|
||||
if(page === 'prev' && recapState.page > 1) recapState.page--;
|
||||
else if(page === 'next' && recapState.page < recapState.lastPage) recapState.page++;
|
||||
else if(!isNaN(parseInt(page))) recapState.page = parseInt(page);
|
||||
fetchRecap();
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@ -106,6 +106,13 @@
|
||||
</li> --}}
|
||||
|
||||
{{-- MASTER --}}
|
||||
<li class="nav-small-cap"><span class="hide-menu">History</span></li>
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link" href="{{ url('/expired-dokumen') }}" aria-expanded="false">
|
||||
<i class="ti ti-clock"></i>
|
||||
<span class="hide-menu">Expired Dokumen</span>
|
||||
</a>
|
||||
</li>
|
||||
@if(!Auth::guard('admin')->check())
|
||||
@if(auth()->user()->dataUser->mappingUnitKerjaPegawai()->where('objectunitkerjapegawaifk', 43)->exists())
|
||||
<li class="nav-small-cap"><span class="hide-menu">Master</span></li>
|
||||
|
||||
@ -78,6 +78,10 @@ Route::middleware(['auth:admin,web'])->group(function(){
|
||||
Route::post('/data/notifications/read', [DashboardController::class, 'notifkasiMarkRead']);
|
||||
|
||||
Route::get('/data/log-dokumen', [DashboardController::class, 'logDokumen']);
|
||||
|
||||
Route::get('/expired-dokumen', [DashboardController::class, 'expDokumen']);
|
||||
Route::get('/data/expired-dokumen', [DashboardController::class, 'dataUnitExp']);
|
||||
Route::get('/data/recapExp', [DashboardController::class, 'recapDataExp']);
|
||||
});
|
||||
|
||||
Route::get('/login', [AuthController::class, 'index'])->name('login');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user