diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index f569764..073f857 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -628,7 +628,6 @@ class DashboardController extends Controller 'pegawai_id_entry' => auth()->user()->dataUser->id ?? 1, 'pegawai_nama_entry' => auth()->user()->dataUser->namalengkap ?? 'tes', ]; - dd($payload); if($file){ $imageName = $file->getClientOriginalName(); $path = "{$nama_unit_kerja}/{$nama_sub_unit_kerja}/{$nama_kategori}/{$nama_klasifikasi}"; @@ -714,85 +713,138 @@ class DashboardController extends Controller ], 200); } - public function deleteFile(string $id){ + public function deleteFile(string $id) + { DB::connection('dbDirectory')->beginTransaction(); try { - $mapping = MappingUnitKerjaPegawai::where('statusenabled', true) - ->where('objectpegawaifk', auth()->user()?->dataUser?->id) - ->first(); + $mapping = $this->getDeleteNotificationMapping(); $data = FileDirectory::where('file_directory_id', $id)->first(); - if(!$data){ + + if (!$data) { return response()->json([ 'success' => false, 'message' => 'File tidak ditemukan' - ]); - } - $fileInfo = pathinfo($data->file); - $newFileName = $fileInfo['filename'] . '_deleted.' . ($fileInfo['extension'] ?? ''); - $dirName = $fileInfo['dirname'] ?? ''; - $newPath = ($dirName && $dirName !== '.') ? ($dirName . '/' . $newFileName) : $newFileName; - $disk = Storage::disk('s3'); - if ($disk->exists($data->file)) { - // S3 tidak bisa rename langsung, jadi copy lalu delete - $disk->copy($data->file, $newPath); - $disk->delete($data->file); + ], 404); } - $data->update([ - 'statusenabled' => false, - 'file' => $newPath - ]); + $this->deleteFileRecord($data, $mapping); - $uploaderName = auth()->user()?->dataUser?->namalengkap ?? 'Pengguna'; - $docNumber = $data->no_dokumen ? 'nomor '. $data->no_dokumen : $data->nama_dokumen; - $payloadNotification = [ - 'created_at' => now(), - 'text_notifikasi' => "Dokumen {$docNumber} . dihapus oleh {$uploaderName}.", - 'url' => '/pending-file', - 'is_read' => false, - 'pegawai_id' => $mapping?->objectatasanlangsungfk, - ]; - - Notifkasi::create($payloadNotification); - if($mapping->objectpejabatpenilaifk){ - $payloadNotification = [ - 'created_at' => now(), - 'text_notifikasi' => "Dokumen {$docNumber}. dihapus oleh {$uploaderName}.", - 'url' => '/pending-file', - 'is_read' => false, - 'pegawai_id' => $mapping?->objectpejabatpenilaifk, - ]; - Notifkasi::create($payloadNotification); - } - - $payloadLog = [ - 'file_directory_id' => $data->file_directory_id, - 'pegawai_id_entry' => $data->pegawai_id_entry, - 'pegawai_nama_entry' => $data->pegawai_nama_entry, - 'entry_at' => now(), - 'file' => $data->file, - 'statusenabled' => true, - 'nama_dokumen' => $data->nama_dokumen ?? null, - 'no_dokumen' => $data->no_dokumen ?? null, - 'mod_change' => null, - 'id_unit_kerja' => $data ? $data->id_unit_kerja : null, - 'id_sub_unit_kerja' => $data ? $data->id_sub_unit_kerja : null, - 'action_type' => 'Menghapus Dokumen', - ]; - LogActivity::create($payloadLog); DB::connection('dbDirectory')->commit(); + return response()->json([ 'success' => true, 'message' => 'Berhasil menghapus data' ]); } catch (\Throwable $th) { DB::connection('dbDirectory')->rollBack(); - return response()->json([ + return response()->json([ 'success' => false, 'message' => 'Gagal menghapus data' + ], 500); + } + } + + public function deleteFiles(Request $request) + { + $ids = collect($request->input('ids', [])) + ->filter(fn ($id) => filled($id)) + ->map(fn ($id) => (string) $id) + ->unique() + ->values(); + + if ($ids->isEmpty()) { + return response()->json([ + 'success' => false, + 'message' => 'Tidak ada dokumen yang dipilih' + ], 422); + } + + DB::connection('dbDirectory')->beginTransaction(); + try { + $mapping = $this->getDeleteNotificationMapping(); + $files = FileDirectory::whereIn('file_directory_id', $ids)->get()->keyBy('file_directory_id'); + + foreach ($ids as $id) { + $data = $files->get($id); + if (!$data) { + throw new \RuntimeException("File {$id} tidak ditemukan"); + } + + $this->deleteFileRecord($data, $mapping); + } + + DB::connection('dbDirectory')->commit(); + + return response()->json([ + 'success' => true, + 'message' => 'Berhasil menghapus ' . $ids->count() . ' dokumen' + ]); + } catch (\Throwable $th) { + DB::connection('dbDirectory')->rollBack(); + return response()->json([ + 'success' => false, + 'message' => 'Gagal menghapus dokumen' + ], 500); + } + } + + private function getDeleteNotificationMapping(): ?MappingUnitKerjaPegawai + { + return MappingUnitKerjaPegawai::where('statusenabled', true) + ->where('objectpegawaifk', auth()->user()?->dataUser?->id) + ->first(); + } + + private function deleteFileRecord(FileDirectory $data, ?MappingUnitKerjaPegawai $mapping): void + { + $fileInfo = pathinfo((string) $data->file); + $extension = $fileInfo['extension'] ?? ''; + $newFileName = ($fileInfo['filename'] ?? 'dokumen') . '_deleted' . ($extension !== '' ? '.' . $extension : ''); + $dirName = $fileInfo['dirname'] ?? ''; + $newPath = ($dirName && $dirName !== '.') ? ($dirName . '/' . $newFileName) : $newFileName; + $disk = Storage::disk('s3'); + + if ($disk->exists($data->file)) { + $disk->copy($data->file, $newPath); + $disk->delete($data->file); + } + + $data->update([ + 'statusenabled' => false, + 'file' => $newPath + ]); + + $uploaderName = auth()->user()?->dataUser?->namalengkap ?? 'Pengguna'; + $docNumber = $data->no_dokumen ? 'nomor ' . $data->no_dokumen : $data->nama_dokumen; + $notifiedPegawaiIds = collect([ + $mapping?->objectatasanlangsungfk, + $mapping?->objectpejabatpenilaifk, + ])->filter()->unique(); + + foreach ($notifiedPegawaiIds as $pegawaiId) { + Notifkasi::create([ + 'created_at' => now(), + 'text_notifikasi' => "Dokumen {$docNumber}. dihapus oleh {$uploaderName}.", + 'url' => '/pending-file', + 'is_read' => false, + 'pegawai_id' => $pegawaiId, ]); } + LogActivity::create([ + 'file_directory_id' => $data->file_directory_id, + 'pegawai_id_entry' => $data->pegawai_id_entry, + 'pegawai_nama_entry' => $data->pegawai_nama_entry, + 'entry_at' => now(), + 'file' => $data->file, + 'statusenabled' => true, + 'nama_dokumen' => $data->nama_dokumen ?? null, + 'no_dokumen' => $data->no_dokumen ?? null, + 'mod_change' => null, + 'id_unit_kerja' => $data->id_unit_kerja, + 'id_sub_unit_kerja' => $data->id_sub_unit_kerja, + 'action_type' => 'Menghapus Dokumen', + ]); } public function optionSubUnitKerja(string $id){ @@ -969,68 +1021,76 @@ class DashboardController extends Controller public function downloadFile(string $id) { - $data = FileDirectory::where('file_directory_id', $id) + try { + $data = FileDirectory::where('file_directory_id', $id) ->where('statusenabled', true) ->first(); - if (!$data) { - abort(404, 'File tidak ditemukan'); - } - $disk = Storage::disk('s3'); - if (!$data->file || !$disk->exists($data->file)) { - abort(404, 'File tidak ditemukan'); - } - - $user = auth()->user()->dataUser; - if($user){ - $mapping = MappingUnitKerjaPegawai::where('statusenabled', true) - ->where('objectpegawaifk', $user->id)->where('isprimary', true) - ->first(); - LogActivity::create([ - 'file_directory_id' => $id, - 'pegawai_id_entry' => $user->id, - 'pegawai_nama_entry' => $user->namalengkap, - 'entry_at' => now(), - 'action_type' => 'Download Dokumen', - 'no_dokumen' => $data->no_dokumen, - 'file' => $data->file, - 'statusenabled' => true, - 'mod_change' => null, - 'id_unit_kerja' => $mapping ? $mapping->objectunitkerjapegawaifk : null, - 'id_sub_unit_kerja' => $mapping ? $mapping->objectsubunitkerjapegawaifk : null, - ]); - } - - - $tempDir = storage_path('app/temp'); - if (!is_dir($tempDir)) { - @mkdir($tempDir, 0777, true); - } - $ext = pathinfo((string) $data->file, PATHINFO_EXTENSION); - $ext = $ext ? strtolower($ext) : 'bin'; - $localPath = $tempDir . '/' . uniqid('s3_') . '.' . $ext; - file_put_contents($localPath, $disk->get($data->file)); - - $tempFiles = []; - $fileToSend = $this->prepareFileWithWatermark($localPath, $tempFiles); - $downloadName = basename($data->file); - - $response = response()->download($fileToSend, $downloadName); - $response->deleteFileAfterSend(true); - $cleanup = array_merge($tempFiles, [$localPath]); - $cleanup = array_values(array_unique(array_filter($cleanup))); - // cleanup after response is sent - register_shutdown_function(function () use ($cleanup, $fileToSend) { - foreach ($cleanup as $file) { - if ($file === $fileToSend) { - continue; - } - if (is_string($file) && $file !== '' && file_exists($file)) { - @unlink($file); - } + if (!$data) { + abort(404, 'File tidak ditemukan'); + } + $disk = Storage::disk('s3'); + if (!$data->file || !$disk->exists($data->file)) { + abort(404, 'File tidak ditemukan'); } - }); - return $response; + $user = auth()->user()->dataUser; + if($user){ + $mapping = MappingUnitKerjaPegawai::where('statusenabled', true) + ->where('objectpegawaifk', $user->id)->where('isprimary', true) + ->first(); + LogActivity::create([ + 'file_directory_id' => $id, + 'pegawai_id_entry' => $user->id, + 'pegawai_nama_entry' => $user->namalengkap, + 'entry_at' => now(), + 'action_type' => 'Download Dokumen', + 'no_dokumen' => $data->no_dokumen, + 'file' => $data->file, + 'statusenabled' => true, + 'mod_change' => null, + 'id_unit_kerja' => $mapping ? $mapping->objectunitkerjapegawaifk : null, + 'id_sub_unit_kerja' => $mapping ? $mapping->objectsubunitkerjapegawaifk : null, + ]); + } + + + $tempDir = storage_path('app/temp'); + if (!is_dir($tempDir)) { + @mkdir($tempDir, 0777, true); + } + $ext = pathinfo((string) $data->file, PATHINFO_EXTENSION); + $ext = $ext ? strtolower($ext) : 'bin'; + $localPath = $tempDir . '/' . uniqid('s3_') . '.' . $ext; + file_put_contents($localPath, $disk->get($data->file)); + + $tempFiles = []; + $fileToSend = $this->prepareFileWithWatermark($localPath, $tempFiles); + $downloadName = basename($data->file); + + $response = response()->download($fileToSend, $downloadName); + $response->deleteFileAfterSend(true); + $cleanup = array_merge($tempFiles, [$localPath]); + $cleanup = array_values(array_unique(array_filter($cleanup))); + // cleanup after response is sent + register_shutdown_function(function () use ($cleanup, $fileToSend) { + foreach ($cleanup as $file) { + if ($file === $fileToSend) { + continue; + } + if (is_string($file) && $file !== '' && file_exists($file)) { + @unlink($file); + } + } + }); + + return $response; + } catch (\Throwable $th) { + return back()->with([ + 'status' => false, + 'message' => 'terdapat kesalahan coba lagi nanti' + ]); + } + } public function dataUmum(){ @@ -2002,6 +2062,76 @@ class DashboardController extends Controller } } + public function recommendDeleteFile(Request $request, string $id) + { + try { + DB::connection('dbDirectory')->beginTransaction(); + $data = FileDirectory::where('file_directory_id', $id) + ->where('statusenabled', true) + ->first(); + + if (!$data) { + return response()->json([ + 'status' => false, + 'message' => 'Data tidak ditemukan' + ], 404); + } + + $note = trim((string) $request->input('note', '')); + if ($note === '') { + return response()->json([ + 'status' => false, + 'message' => 'Catatan rekomendasi wajib diisi' + ], 422); + } + + $actionName = auth()->user()?->dataUser?->namalengkap ?? 'Pengguna'; + $docNumber = $data->no_dokumen ? 'nomor ' . $data->no_dokumen : ($data->nama_dokumen ?? 'dokumen'); + + Notifkasi::create([ + 'created_at' => now(), + 'text_notifikasi' => "Dokumen {$docNumber} direkomendasikan untuk dihapus oleh {$actionName}. Catatan: {$note}", + 'url' => '/pengajuan-file', + 'is_read' => false, + 'pegawai_id' => $data->pegawai_id_entry, + ]); + + LogActivity::create([ + 'file_directory_id' => $data->file_directory_id, + 'pegawai_id_entry' => $data->pegawai_id_entry, + 'pegawai_nama_entry' => $data->pegawai_nama_entry, + 'entry_at' => now(), + 'file' => $data->file, + 'statusenabled' => true, + 'nama_dokumen' => $data->nama_dokumen ?? null, + 'no_dokumen' => $data->no_dokumen ?? null, + 'id_unit_kerja' => $data->id_unit_kerja, + 'id_sub_unit_kerja' => $data->id_sub_unit_kerja, + 'action_type' => 'Rekomendasi Hapus Dokumen', + ]); + + $data->update([ + 'hapus_turt' => true, + 'note_hapus_turt' => $note, + 'turt_by' => auth()->user()->objectpegawaifk, + 'turt_at' => now() + ]); + + DB::connection('dbDirectory')->commit(); + + return response()->json([ + 'status' => true, + 'message' => 'Rekomendasi hapus berhasil dikirim' + ], 200); + } catch (\Throwable $th) { + DB::connection('dbDirectory')->rollBack(); + return response()->json([ + 'status' => false, + 'message' => $th->getMessage() + ], 500); + } + } + public function countDataPending(){ try { $objectpegawaifk = MappingUnitKerjaPegawai::query() diff --git a/app/Http/Middleware/EnsureMasterPersetujuan.php b/app/Http/Middleware/EnsureMasterPersetujuan.php index 0bc99f1..877ab6f 100644 --- a/app/Http/Middleware/EnsureMasterPersetujuan.php +++ b/app/Http/Middleware/EnsureMasterPersetujuan.php @@ -12,7 +12,6 @@ class EnsureMasterPersetujuan { $user = $request->user(); $hasAccess = $user && $user->masterPersetujuan; - if (!$hasAccess) { abort(403, 'Tidak memiliki akses.'); } diff --git a/app/Models/SubUnitKerja.php b/app/Models/SubUnitKerja.php index f70ddc6..5f3509a 100644 --- a/app/Models/SubUnitKerja.php +++ b/app/Models/SubUnitKerja.php @@ -11,9 +11,9 @@ class SubUnitKerja extends Model public $timestamps = false; protected $primaryKey = 'id'; protected $guarded = ['id']; - protected $with = ['fileDirectory']; + // protected $with = ['fileDirectory']; - public function fileDirectory(){ - return $this->hasMany(FileDirectory::class, 'id_sub_unit_kerja', 'id')->where('statusenabled', true); - } + // public function fileDirectory(){ + // return $this->hasMany(FileDirectory::class, 'id_sub_unit_kerja', 'id')->where('statusenabled', true); + // } } diff --git a/app/Models/UnitKerja.php b/app/Models/UnitKerja.php index 3019e71..a3e1d44 100644 --- a/app/Models/UnitKerja.php +++ b/app/Models/UnitKerja.php @@ -11,10 +11,10 @@ class UnitKerja extends Model public $timestamps = false; protected $primaryKey = 'id'; protected $guarded = ['id']; - protected $with = ['subUnitKerja']; + // protected $with = ['subUnitKerja']; - public function subUnitKerja(){ - return $this->hasMany(SubUnitKerja::class, 'objectunitkerjapegawaifk', 'id')->where('statusenabled', true)->select('id', 'objectunitkerjapegawaifk', 'name'); - } + // public function subUnitKerja(){ + // return $this->hasMany(SubUnitKerja::class, 'objectunitkerjapegawaifk', 'id')->where('statusenabled', true)->select('id', 'objectunitkerjapegawaifk', 'name'); + // } } diff --git a/public/js/pengajuanFile/index.js b/public/js/pengajuanFile/index.js index bb095c5..eb22d4a 100644 --- a/public/js/pengajuanFile/index.js +++ b/public/js/pengajuanFile/index.js @@ -13,6 +13,12 @@ document.addEventListener('DOMContentLoaded', () => { const titleEl = document.getElementById('pengajuanTitle'); const tabPengajuanEl = document.getElementById('tabPengajuan'); const tabHistoryEl = document.getElementById('tabHistory'); + const selectAllCheckbox = document.getElementById('selectAllPengajuan'); + const bulkDeleteBtn = document.getElementById('bulkDeleteBtn'); + const clearSelectionBtn = document.getElementById('clearSelectionBtn'); + const selectedCountEl = document.getElementById('selectedCount'); + const bulkActionsEl = document.getElementById('pengajuanBulkActions'); + const deleteDataEl = document.getElementById('deleteData'); const formCreate = document.getElementById('formFile'); const modalCreate = document.getElementById('modalCreateFile'); let colCount = 1; @@ -20,6 +26,7 @@ document.addEventListener('DOMContentLoaded', () => { const editForm = document.getElementById('formEditPengajuanFile'); const editUnitSelect = $('#edit_id_unit_kerja'); const editSubUnitSelect = $('#edit_id_sub_unit_kerja'); + const selectedIds = new Set(); if (pageSizeSelect) { const initialSize = parseInt(pageSizeSelect.value); @@ -61,6 +68,41 @@ document.addEventListener('DOMContentLoaded', () => { }); } + function getSelectableIdsOnPage(){ + return (tableState.data || []).map((row) => String(row.file_directory_id)); + } + + function updateSelectAllState(){ + if (!selectAllCheckbox) return; + if (tableState.mode === 'history') { + selectAllCheckbox.checked = false; + selectAllCheckbox.indeterminate = false; + selectAllCheckbox.disabled = true; + return; + } + + const pageIds = getSelectableIdsOnPage(); + if (pageIds.length === 0) { + selectAllCheckbox.checked = false; + selectAllCheckbox.indeterminate = false; + selectAllCheckbox.disabled = true; + return; + } + + selectAllCheckbox.disabled = false; + const selectedOnPage = pageIds.filter((id) => selectedIds.has(id)).length; + selectAllCheckbox.checked = selectedOnPage > 0 && selectedOnPage === pageIds.length; + selectAllCheckbox.indeterminate = selectedOnPage > 0 && selectedOnPage < pageIds.length; + } + + function updateSelectionUI(){ + const count = selectedIds.size; + if (selectedCountEl) selectedCountEl.textContent = String(count); + if (bulkDeleteBtn) bulkDeleteBtn.disabled = count === 0 || tableState.mode === 'history'; + if (clearSelectionBtn) clearSelectionBtn.disabled = count === 0 || tableState.mode === 'history'; + updateSelectAllState(); + } + function buildRow(item){ let tanggal = item.entry_at ? formatTanggal(item.entry_at) : '-'; let tanggalExp = item.tgl_expired ? formatTanggal(item.tgl_expired) : '-'; @@ -69,6 +111,8 @@ document.addEventListener('DOMContentLoaded', () => { const isRejected = item?.status_action === 'rejected'; const showEdit = !isApproved; const showInfo = isRejected; + const id = String(item.file_directory_id); + const checked = selectedIds.has(id); const aksi = `
| + + | Aksi | No Dokumen | Status | diff --git a/routes/web.php b/routes/web.php index 1fedf25..5ff1369 100644 --- a/routes/web.php +++ b/routes/web.php @@ -51,7 +51,9 @@ Route::middleware(['auth:admin,web'])->group(function(){ Route::get('/select-sub-unit-kerja-mapping/{id}', [DashboardController::class, 'optionSubUnitKerjaByMapping']); + Route::delete('/delete-file/bulk', [DashboardController::class, 'deleteFiles']); Route::delete('/delete-file/{id}', [DashboardController::class, 'deleteFile']); + Route::post('/recommend-delete-file/{id}', [DashboardController::class, 'recommendDeleteFile']); // Route::get('/getFile/{id_unit_kerja}/{id_sub_unit_kerja}/{master_kategori_directory_id}', [DashboardController::class, 'getFile']); Route::post('/download-multiple', [DashboardController::class, 'downloadDataMultiple']);
|---|