From 8c0aecd4aa1dfe68329e6b6119d984bc7dad6291 Mon Sep 17 00:00:00 2001 From: JokoPrasetio Date: Mon, 20 Apr 2026 13:02:16 +0700 Subject: [PATCH] add expired dokumen --- app/Http/Controllers/DashboardController.php | 308 +++++++++++- .../views/layout/partials/topnav.blade.php | 441 ++++++++++++++---- routes/web.php | 3 + 3 files changed, 648 insertions(+), 104 deletions(-) diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index d0280d1..625af0b 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -144,16 +144,40 @@ class DashboardController extends Controller public function dataUnitInternal(){ $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) + $user = auth()->user(); + $isAdmin = auth('admin')->check(); + $pegawaiId = $user?->dataUser?->id; + + $unitIds = []; + if (!$isAdmin) { + if (!$pegawaiId) { + return response()->json([ + 'status' => false, + 'message' => 'Pegawai tidak ditemukan', + 'data' => [], + 'kategori_list' => [], + 'pagination' => [ + 'current_page' => 1, + 'next_page' => null, + 'has_more' => false, + 'last_page' => 1, + 'per_page' => $perPage, + 'total' => 0, + ], + ], 404); + } + + $mapping = MappingUnitKerjaPegawai::where('statusenabled', true) + ->where('objectpegawaifk', $pegawaiId) ->get(['objectunitkerjapegawaifk']); - $unitIds = $mapping->pluck('objectunitkerjapegawaifk') - ->filter() // buang null - ->unique() - ->values() - ->toArray(); + + $unitIds = $mapping->pluck('objectunitkerjapegawaifk') + ->filter(fn ($v) => $v !== null && $v !== '') + ->map(fn ($v) => (int) $v) + ->unique() + ->values() + ->toArray(); + } $keyword = request('keyword'); $kategori = request('kategori'); $kategoriHeader = request('kategori_header'); @@ -202,11 +226,14 @@ class DashboardController extends Controller ->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); + if (!$isAdmin) { + if (in_array(22, $unitIds, true)) { + // akses semua unit + } elseif (($user?->username === 'admin.turt')) { + // akses semua unit (exception user) + } else { + $baseQuery = $baseQuery->whereIn('id_unit_kerja', $unitIds); + } } $query = (clone $baseQuery) @@ -235,6 +262,7 @@ class DashboardController extends Controller }); }) ->when($keyword, function ($q) use ($keyword) { + $q->where(function ($sub) use ($keyword) { $sub->where('nama_dokumen', 'ILIKE', "%{$keyword}%") ->orWhere('no_dokumen', 'ILIKE', "%{$keyword}%"); @@ -1312,10 +1340,17 @@ class DashboardController extends Controller 'data' => $file ]); } - $userUnitIds = auth()->user() - ->dataUser - ->mappingUnitKerjaPegawai - ->pluck('objectunitkerjapegawaifk'); + + $user = auth()->user(); + $isAdmin = auth('admin')->check(); + if ($isAdmin || ($user?->username === 'admin.turt')) { + return view('pdf.index', [ + 'id' => $id, + 'data' => $file + ]); + } + + $userUnitIds = $user?->dataUser?->mappingUnitKerjaPegawai?->pluck('objectunitkerjapegawaifk') ?? collect(); // cek apakah unit file termasuk unit user if ($userUnitIds->contains($file->id_unit_kerja)) { @@ -1962,6 +1997,14 @@ class DashboardController extends Controller public function notifkasiList() { try { + if (auth('admin')->check()) { + return response()->json([ + 'status' => true, + 'data' => [], + 'unread' => 0 + ]); + } + $pegawaiId = auth()->user()?->dataUser?->id; if (!$pegawaiId) { return response()->json([ @@ -1996,6 +2039,13 @@ class DashboardController extends Controller public function notifkasiMarkRead() { try { + if (auth('admin')->check()) { + return response()->json([ + 'status' => true, + 'message' => 'Notifikasi ditandai dibaca' + ]); + } + $pegawaiId = auth()->user()?->dataUser?->id; if (!$pegawaiId) { return response()->json([ @@ -2020,6 +2070,228 @@ class DashboardController extends Controller } } + public function expiredNotifkasiList() + { + try { + $user = auth()->user(); + $pegawaiId = $user?->dataUser?->id; + $isAdmin = auth('admin')->check(); + $unitIds = []; + if (!$isAdmin) { + // if (!$pegawaiId) { + // return response()->json([ + // 'status' => false, + // 'message' => 'Pegawai tidak ditemukan', + // 'data' => [], + // 'unread' => 0 + // ], 404); + // } + + $mapping = MappingUnitKerjaPegawai::where('statusenabled', true) + ->where('objectpegawaifk', $pegawaiId) + ->get(['objectunitkerjapegawaifk']); + + $unitIds = $mapping->pluck('objectunitkerjapegawaifk') + ->filter(fn ($v) => $v !== null && $v !== '') + ->map(fn ($v) => (int) $v) + ->unique() + ->values() + ->toArray(); + } + + $baseQuery = FileDirectory::query() + ->where('statusenabled', true) + ->where('status_action', 'approved') + ->whereNotNull('tgl_expired') + ->whereBetween('tgl_expired', [ + now()->startOfDay(), + now()->addMonth()->endOfDay() + ]); + + if (!$isAdmin && !in_array(22, $unitIds, true) && ($user?->username !== 'admin.turt')) { + $baseQuery->whereIn('id_unit_kerja', $unitIds); + } + + $seenAtRaw = session('expired_notif_seen_at'); + $seenAt = $seenAtRaw ? Carbon::parse($seenAtRaw) : null; + $unreadQuery = (clone $baseQuery); + if ($seenAt) { + $unreadQuery->whereDate('tgl_expired', '>=', $seenAt->toDateString()); + } + $unread = (int) $unreadQuery->count(); + + $rows = (clone $baseQuery) + ->orderBy('tgl_expired', 'desc') + ->orderBy('entry_at', 'desc') + ->limit(20) + ->get([ + 'file_directory_id', + 'nama_dokumen', + 'no_dokumen', + 'tgl_expired', + 'file', + ]); + $items = $rows->map(function ($row) use ($seenAt) { + $expiredAt = $row->tgl_expired ? Carbon::parse($row->tgl_expired)->format('d/m/Y') : '-'; + $namaDokumen = $row->nama_dokumen ?: ($row->no_dokumen ?: 'Dokumen'); + $text = "Dokumen '{$namaDokumen}' expired pada {$expiredAt}"; + $isRead = false; + if ($seenAt && $row->tgl_expired) { + $isRead = Carbon::parse($row->tgl_expired)->toDateString() < $seenAt->toDateString(); + } + + return [ + 'id' => $row->file_directory_id, + 'text_notifikasi' => $text, + 'url' => url("/file-preview/{$row->file_directory_id}"), + 'is_read' => $isRead, + 'created_at' => $expiredAt, + 'meta' => [ + 'no_dokumen' => $row->no_dokumen, + 'nama_dokumen' => $row->nama_dokumen, + 'tgl_expired' => $row->tgl_expired, + 'file' => $row->file, + ], + ]; + }); + + return response()->json([ + 'status' => true, + 'data' => $items, + 'unread' => $unread, + ]); + } catch (\Throwable $th) { + return response()->json([ + 'status' => false, + 'message' => 'Terdapat kesalahan!', + 'data' => [], + 'unread' => 0 + ], 500); + } + } + + public function expiredNotifkasiDetail(Request $request) + { + try { + $user = auth()->user(); + $pegawaiId = $user?->dataUser?->id; + $isAdmin = auth('admin')->check(); + + $unitIds = []; + if (!$isAdmin) { + $mapping = MappingUnitKerjaPegawai::where('statusenabled', true) + ->where('objectpegawaifk', $pegawaiId) + ->get(['objectunitkerjapegawaifk']); + + $unitIds = $mapping->pluck('objectunitkerjapegawaifk') + ->filter(fn ($v) => $v !== null && $v !== '') + ->map(fn ($v) => (int) $v) + ->unique() + ->values() + ->toArray(); + } + + $docId = (int) $request->query('doc_id', 0); + $daysLeftMinRaw = $request->query('days_left_min'); + $daysLeftMaxRaw = $request->query('days_left_max'); + + $page = max(1, (int) $request->query('page', 1)); + $perPage = (int) $request->query('per_page', 10); + $perPage = max(5, min(50, $perPage)); + + $today = Carbon::now()->startOfDay(); + $query = FileDirectory::query() + ->where('statusenabled', true) + ->where('status_action', 'approved') + ->whereNotNull('tgl_expired') + ->whereDate('tgl_expired', '>=', $today->toDateString()); + + if (!$isAdmin && !in_array(22, $unitIds, true) && ($user?->username !== 'admin.turt')) { + $query->whereIn('id_unit_kerja', $unitIds); + } + + if ($docId > 0) { + $query->where('file_directory_id', $docId); + } + + $daysLeftMin = null; + if ($daysLeftMinRaw !== null && $daysLeftMinRaw !== '') { + $daysLeftMin = max(0, (int) $daysLeftMinRaw); + $query->whereDate('tgl_expired', '>=', (clone $today)->addDays($daysLeftMin)->toDateString()); + } + + $daysLeftMax = null; + if ($daysLeftMaxRaw !== null && $daysLeftMaxRaw !== '') { + $daysLeftMax = max(0, (int) $daysLeftMaxRaw); + $query->whereDate('tgl_expired', '<=', (clone $today)->addDays($daysLeftMax)->toDateString()); + } + + if ($daysLeftMin === null && $daysLeftMax === null && $docId <= 0) { + $query->whereDate('tgl_expired', '<=', (clone $today)->addMonth()->endOfDay()->toDateString()); + } + $total = (clone $query)->count(); + $rows = $query + ->orderBy('tgl_expired', 'asc') + ->orderBy('entry_at', 'desc') + ->forPage($page, $perPage) + ->get([ + 'file_directory_id', + 'nama_dokumen', + 'no_dokumen', + 'tgl_expired', + 'id_unit_kerja' + ]); + $data = $rows->map(function ($row) use ($today) { + $expiredAt = $row->tgl_expired ? Carbon::parse($row->tgl_expired)->startOfDay() : null; + $daysLeft = $expiredAt ? $today->diffInDays($expiredAt, false) : null; + + return [ + 'id' => $row->file_directory_id, + 'no_dokumen' => $row->no_dokumen, + 'nama_dokumen' => $row->nama_dokumen, + 'unit_name' => $row->unit?->name, + 'tgl_expired' => $expiredAt ? $expiredAt->toDateString() : null, + 'tgl_expired_label' => $expiredAt ? $expiredAt->format('d/m/Y') : '-', + 'days_left' => $daysLeft, + 'preview_url' => url("/full-preview/{$row->file_directory_id}"), + ]; + })->values(); + + return response()->json([ + 'status' => true, + 'data' => $data, + 'meta' => [ + 'page' => $page, + 'per_page' => $perPage, + 'total' => $total, + ], + ]); + } catch (\Throwable $th) { + return response()->json([ + 'status' => false, + 'message' => 'Terdapat kesalahan!', + 'data' => [], + ], 500); + } + } + + public function expiredNotifkasiMarkRead() + { + try { + session(['expired_notif_seen_at' => now()->toISOString()]); + + return response()->json([ + 'status' => true, + 'message' => 'Notifikasi expired ditandai dibaca' + ]); + } catch (\Throwable $th) { + return response()->json([ + 'status' => false, + 'message' => 'Terdapat kesalahan!' + ], 500); + } + } + public function pengajuanFile(){ $katDok = MasterKategori::where('statusenabled', true) ->select('master_kategori_directory_id', 'nama_kategori_directory') diff --git a/resources/views/layout/partials/topnav.blade.php b/resources/views/layout/partials/topnav.blade.php index 6111e12..15a40ec 100644 --- a/resources/views/layout/partials/topnav.blade.php +++ b/resources/views/layout/partials/topnav.blade.php @@ -1,41 +1,50 @@ -
@@ -50,7 +59,27 @@
- + } + + function attachMarkRead(toggleEl, els, endpoint) { + if (!toggleEl) return; + + toggleEl.addEventListener('show.bs.dropdown', () => { + fetch(endpoint, { + method: 'POST', + headers: { + 'X-CSRF-TOKEN': csrfToken, + 'X-Requested-With': 'XMLHttpRequest' + } + }).then(() => { + if (els.dotEl) els.dotEl.classList.add('d-none'); + }).catch(() => {}); + }); + } + + // 🔴 EXPIRED + const expiredEls = { + listEl: document.getElementById('expiredNotifList'), + dotEl: document.getElementById('expiredDot'), + itemClass: 'js-expired-notif-item' + }; + + hydrateNotification('/data/expired-notifications', expiredEls, 'Gagal memuat notifikasi expired'); + attachMarkRead( + document.getElementById('dropExpired'), + expiredEls, + '/data/expired-notifications/read' + ); + + // 🔔 NOTIF + const notifEls = { + listEl: document.getElementById('notifList'), + dotEl: document.getElementById('notifDot') + }; + + hydrateNotification('/data/notifications', notifEls, 'Gagal memuat notifikasi'); + attachMarkRead( + document.getElementById('dropNotif'), + notifEls, + '/data/notifications/read' + ); + + // 📋 DETAIL MODAL (EXPIRED) + const expiredDetailModalEl = document.getElementById('expiredDetailModal'); + const expiredDetailModal = (expiredDetailModalEl && window.bootstrap?.Modal) + ? new bootstrap.Modal(expiredDetailModalEl) + : null; + + const expiredState = { + docId: null, + page: 1, + perPage: 10, + }; + + const expiredDetailTbody = document.getElementById('expiredDetailTbody'); + const expiredDetailInfo = document.getElementById('expiredDetailInfo'); + const expiredDetailPageText = document.getElementById('expiredDetailPageText'); + + function setExpiredLoading(text = 'Memuat...') { + if (!expiredDetailTbody) return; + expiredDetailTbody.innerHTML = ` + + ${text} + + `; + } + + function buildExpiredDetailParams() { + const daysMax = document.getElementById('expiredFilterDaysMax')?.value ?? ''; + + const params = new URLSearchParams(); + params.set('page', String(expiredState.page)); + params.set('per_page', String(expiredState.perPage)); + + if (expiredState.docId) params.set('doc_id', String(expiredState.docId)); + if (daysMax !== '' && daysMax !== null) params.set('days_left_max', String(daysMax)); + + return params; + } + + function renderExpiredDetail(rows, meta) { + const total = Number(meta?.total || 0); + const perPage = Number(meta?.per_page || expiredState.perPage); + const page = Number(meta?.page || expiredState.page); + const maxPage = total ? Math.ceil(total / perPage) : 1; + + if (expiredDetailPageText) expiredDetailPageText.textContent = String(page); + if (expiredDetailInfo) { + const start = total ? ((page - 1) * perPage + 1) : 0; + const end = Math.min(page * perPage, total); + expiredDetailInfo.textContent = total ? `Menampilkan ${start}-${end} dari ${total} data` : 'Tidak ada data'; + } + + const prevBtn = document.getElementById('expiredDetailPrevBtn'); + const nextBtn = document.getElementById('expiredDetailNextBtn'); + if (prevBtn) prevBtn.disabled = page <= 1; + if (nextBtn) nextBtn.disabled = page >= maxPage; + + if (!expiredDetailTbody) return; + if (!rows?.length) { + expiredDetailTbody.innerHTML = ` + + Tidak ada data + + `; + return; + } + + expiredDetailTbody.innerHTML = rows.map((row, idx) => { + const no = (page - 1) * perPage + (idx + 1); + const unit = row.unit_name || '-'; + const noDok = row.no_dokumen || '-'; + const nama = row.nama_dokumen || '-'; + const tgl = row.tgl_expired_label || '-'; + const daysLeft = (row.days_left === null || row.days_left === undefined) ? '-' : String(row.days_left); + const previewUrl = row.preview_url || '#'; + return ` + + ${no} + ${unit} + ${noDok} + ${nama} + ${tgl} + ${daysLeft} + + Preview + + + `; + }).join(''); + } + + async function loadExpiredDetail() { + setExpiredLoading(); + const params = buildExpiredDetailParams(); + + try { + const r = await fetch(`/data/expired-notifications/detail?${params.toString()}`, { + headers: { 'X-Requested-With': 'XMLHttpRequest' } + }); + const res = await r.json(); + if (!res?.status) { + setExpiredLoading(res?.message || 'Gagal memuat data'); + return; + } + renderExpiredDetail(res.data || [], res.meta || {}); + } catch (e) { + setExpiredLoading('Gagal memuat data'); + } + } + + function openExpiredDetail(docId = null) { + expiredState.docId = docId ? Number(docId) : null; + expiredState.page = 1; + + if (expiredState.docId) { + const daysMaxEl = document.getElementById('expiredFilterDaysMax'); + if (daysMaxEl) daysMaxEl.value = ''; + } + + if (expiredDetailModal) expiredDetailModal.show(); + loadExpiredDetail(); + } + + document.getElementById('expiredOpenDetailBtn')?.addEventListener('click', () => openExpiredDetail(null)); + + document.getElementById('expiredNotifList')?.addEventListener('click', (e) => { + const item = e.target.closest('a.js-expired-notif-item'); + if (!item) return; + e.preventDefault(); + openExpiredDetail(item.dataset.notifId || null); + }); + + document.getElementById('expiredApplyFilterBtn')?.addEventListener('click', () => { + expiredState.page = 1; + loadExpiredDetail(); + }); + + document.getElementById('expiredResetFilterBtn')?.addEventListener('click', () => { + const daysMaxEl = document.getElementById('expiredFilterDaysMax'); + if (daysMaxEl) daysMaxEl.value = '30'; + expiredState.docId = null; + expiredState.page = 1; + loadExpiredDetail(); + }); + + document.getElementById('expiredDetailPrevBtn')?.addEventListener('click', () => { + if (expiredState.page <= 1) return; + expiredState.page -= 1; + loadExpiredDetail(); + }); + + document.getElementById('expiredDetailNextBtn')?.addEventListener('click', () => { + expiredState.page += 1; + loadExpiredDetail(); + }); + }); + diff --git a/routes/web.php b/routes/web.php index 455b1bc..4e3e842 100644 --- a/routes/web.php +++ b/routes/web.php @@ -76,6 +76,9 @@ Route::middleware(['auth:admin,web'])->group(function(){ // }); Route::get('/data/notifications', [DashboardController::class, 'notifkasiList']); Route::post('/data/notifications/read', [DashboardController::class, 'notifkasiMarkRead']); + Route::get('/data/expired-notifications', [DashboardController::class, 'expiredNotifkasiList']); + Route::post('/data/expired-notifications/read', [DashboardController::class, 'expiredNotifkasiMarkRead']); + Route::get('/data/expired-notifications/detail', [DashboardController::class, 'expiredNotifkasiDetail']); Route::get('/data/log-dokumen', [DashboardController::class, 'logDokumen']);