diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index f569764..9385045 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -12,11 +12,13 @@ use App\Models\MasterKlasifikasi; use App\Models\Notifkasi; use App\Models\SubUnitKerja; use App\Models\UnitKerja; +use App\Models\DataUser; use Carbon\Carbon; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Str; +use Mockery\Matcher\Not; use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use setasign\Fpdi\Fpdi; @@ -87,6 +89,41 @@ class PdfFpdiWithAlpha extends Fpdi class DashboardController extends Controller { + private function attachDeleteRecommendationMeta($items, $viewerPegawaiId) + { + $collection = collect($items); + $recommenderIds = $collection->pluck('turt_by') + ->filter(fn ($id) => !empty($id)) + ->map(fn ($id) => (int) $id) + ->unique() + ->values(); + + $recommenderNames = $recommenderIds->isEmpty() + ? collect() + : DataUser::whereIn('id', $recommenderIds->all())->pluck('namalengkap', 'id'); + + return $collection->map(function ($item) use ($viewerPegawaiId, $recommenderNames) { + $viewerId = $viewerPegawaiId ? (int) $viewerPegawaiId : null; + $entryId = !empty($item->pegawai_id_entry) ? (int) $item->pegawai_id_entry : null; + $recommenderId = !empty($item->turt_by) ? (int) $item->turt_by : null; + + $canViewRecommendation = (bool) ($item->hapus_turt ?? false) + && !empty($item->note_hapus_turt) + && $viewerId !== null + && ($viewerId === $entryId || $viewerId === $recommenderId); + + $item->delete_recommendation = $canViewRecommendation ? [ + 'visible' => true, + 'note' => $item->note_hapus_turt, + 'actor_name' => $recommenderNames->get($recommenderId) ?? 'TURT', + 'actor_role' => 'TURT', + 'created_at' => $item->turt_at, + ] : null; + + return $item; + }); + } + // public function index(){ // $katDok = MasterKategori::where('statusenabled', true)->select('master_kategori_directory_id', 'nama_kategori_directory')->get(); // $klasifikasiDok = MasterKlasifikasi::where('statusenabled', true)->select('master_klasifikasi_directory_id', 'nama_klasifikasi_directory')->get(); @@ -130,9 +167,7 @@ class DashboardController extends Controller public function index(){ $katDok = MasterKategori::where('statusenabled', true)->select('master_kategori_directory_id', 'nama_kategori_directory')->get(); - $authMapping = auth()->user()?->dataUser?->mappingUnitKerjaPegawai() - ->where('isprimary', true) - ->first(); + $authMapping = auth()->user()?->dataUser?->mappingUnitKerjaPegawai()->where('isprimary', true)->first(); // dd(auth()->user()?->dataUser); $authUnitKerja = $authMapping->objectunitkerjapegawaifk ?? null; $authSubUnitKerja = $authMapping->objectsubunitkerjapegawaifk ?? null; @@ -226,6 +261,15 @@ class DashboardController extends Controller $q->whereNull('tgl_expired') ->orWhereDate('tgl_expired', '>=', now()->toDateString()); }) + ->where(function ($q) { + $q->whereNull('master_kategori_directory_id') + ->orWhere('status_turt', 'approved'); + }) + ->where(function ($q) { + $q->where('is_akre', '!=', true) + ->orWhereNull('is_akre') + ->orWhere('status_mutu', 'approved'); + }) ->where('statusenabled', true) ->where('status_action', 'approved'); @@ -277,6 +321,7 @@ class DashboardController extends Controller $item->nama_kategori = $item->kategori->nama_kategori_directory ?? $item->kategori_hukum ?? null; return $item; }); + $items = $this->attachDeleteRecommendationMeta($items, $pegawaiId); $kategoriList = (clone $baseQuery)->get()->map(function($item){ if (!empty($item->kategori_hukum)) { return ['id' => 'hukum:' . $item->kategori_hukum, 'label' => $item->kategori_hukum]; @@ -628,7 +673,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 +758,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 +1066,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(){ @@ -1098,6 +1203,15 @@ class DashboardController extends Controller $q->whereNull('tgl_expired') ->orWhereDate('tgl_expired', '>=', now()->toDateString()); }) + ->where(function ($q) { + $q->whereNull('master_kategori_directory_id') + ->orWhere('status_turt', 'approved'); + }) + ->where(function ($q) { + $q->where('is_akre', '!=', true) + ->orWhereNull('is_akre') + ->orWhere('status_mutu', 'approved'); + }) ->when(!empty($unitIds), function ($q) use ($unitIds) { $q->whereIn('id_unit_kerja', $unitIds); }); @@ -1140,10 +1254,12 @@ class DashboardController extends Controller $data = $query->orderBy('entry_at', 'desc') ->paginate($perPage); + $pegawaiId = auth()->user()?->dataUser?->id; $items = collect($data->items())->map(function($item){ $item->nama_kategori = $item->kategori->nama_kategori_directory ?? $item->kategori_hukum ?? null; return $item; }); + $items = $this->attachDeleteRecommendationMeta($items, $pegawaiId); $kategoriList = (clone $baseQuery)->get()->map(function($item){ if (!empty($item->kategori_hukum)) { @@ -1252,20 +1368,36 @@ class DashboardController extends Controller $isAtasan = MappingUnitKerjaPegawai::where('statusenabled', true)->where(function($q){ $q->where('objectatasanlangsungfk', auth()->user()?->dataUser?->id)->orWhere('objectpejabatpenilaifk', auth()->user()->objectpegawaifk); })->exists(); + + $objectUnit = MappingUnitKerjaPegawai::query() + ->where('statusenabled', true) + ->where('objectpegawaifk', auth()->user()->objectpegawaifk) + ->pluck('objectunitkerjapegawaifk') + ->unique() + ->values() + ->all(); + $isKomiteMutu = in_array(51, $objectUnit); + $isTurt = in_array(22, $objectUnit); foreach ($datas as $index => $data) { - list($id_unit_kerja, $nama_unit_kerja) = explode('/', $data['id_unit_kerja'],2); + if(isset(explode('/', $data['id_unit_kerja'],2)[1])){ + list($id_unit_kerja, $nama_unit_kerja) = explode('/', $data['id_unit_kerja'],2); + }else{ + $nama_unit_kerja = UnitKerja::where('id', $data['id_unit_kerja'])->first()->name; + $id_unit_kerja = $data['id_unit_kerja']; + } list($id_sub_unit_kerja, $nama_sub_unit_kerja) = explode('/', $data['id_sub_unit_kerja'],2); + if(isset($data['master_kategori_directory_id'])){ + list($master_kategori_directory_id, $nama_kategori) = explode('/', $data['master_kategori_directory_id'],2); + } if(isset($data['akre'])){ $path = $data['akre']; }else{ if(isset($data['master_kategori_directory_id'])){ - list($master_kategori_directory_id, $nama_kategori) = explode('/', $data['master_kategori_directory_id'],2); $path = "{$nama_unit_kerja}/{$nama_sub_unit_kerja}/{$nama_kategori}"; }else{ $path = "{$nama_unit_kerja}/{$nama_sub_unit_kerja}"; } } - $uploadedFile = request()->file("data.$index.file"); if(!$uploadedFile){ throw new \RuntimeException('File wajib diunggah pada segment ke-' . ($index+1)); @@ -1273,8 +1405,11 @@ class DashboardController extends Controller if (!$uploadedFile->isValid()) { throw new \RuntimeException('Upload file gagal pada segment ke-' . ($index+1)); } - $status = $isAtasan ? 'approved' : null; - + $requiresMutuApproval = isset($data['akre']); + $requiresTurtApproval = !empty($master_kategori_directory_id); + $mutuStatus = $isKomiteMutu && $requiresMutuApproval ? 'approved' : null; + $turtStatus = $isTurt && $requiresTurtApproval ? 'approved' : null; + $status = $isAtasan ? 'approved' : null; $payload = [ 'id_unit_kerja' => $id_unit_kerja, 'id_sub_unit_kerja' => $id_sub_unit_kerja, @@ -1290,7 +1425,13 @@ class DashboardController extends Controller 'action_by' => $status && $status === "approved" ? auth()->user()->objectpegawaifk : null, 'action_at' => $status && $status === "approved" ? now() : null, 'is_akre' => isset($data['akre']), - 'kategori_hukum' => isset($data['kategori_hukum']) ? $data['kategori_hukum'] : null + 'kategori_hukum' => isset($data['kategori_hukum']) ? $data['kategori_hukum'] : null, + 'status_mutu' => $mutuStatus, + 'action_mutu_at' => $mutuStatus === 'approved' ? now() : null, + 'action_mutu_by' => $mutuStatus === 'approved' ? auth()->user()->objectpegawaifk : null, + 'status_turt' => $turtStatus, + 'action_turt_at' => $turtStatus === 'approved' ? now() : null, + 'action_turt_by' => $turtStatus === 'approved' ? auth()->user()->objectpegawaifk : null, ]; $fd = FileDirectory::create($payload); @@ -1309,9 +1450,9 @@ class DashboardController extends Controller $fd->update([ 'file' => $path . '/' . $imageName ]); + $docNumber = $fd->no_dokumen ? 'nomor '. $fd->no_dokumen : $data['nama_dokumen']; if(!$isAtasan){ $uploaderName = auth()->user()?->dataUser?->namalengkap ?? 'Pengguna'; - $docNumber = $fd->no_dokumen ? 'nomor '. $fd->no_dokumen : $imageName; $payloadNotification = [ 'created_at' => now(), 'text_notifikasi' => "Dokumen {$docNumber} memerlukan persetujuan. Diunggah oleh {$uploaderName}.", @@ -1334,15 +1475,39 @@ class DashboardController extends Controller } } + if($isAtasan){ + if($fd->is_akre && $fd->status_mutu !== 'approved'){ + $payloadNotificationMutu = [ + 'created_at' => now(), + 'text_notifikasi' => "Dokumen {$docNumber} memerlukan persetujuan. Diunggah oleh " . auth()->user()->dataUser->namalengkap, + 'url' => '/pending-file', + 'is_read' => false, + 'unit_id' => 51, + ]; + + Notifkasi::create($payloadNotificationMutu); + } + if($fd->master_kategori_directory_id && $fd->status_turt !== 'approved'){ + $payloadNotificationTurt = [ + 'created_at' => now(), + 'text_notifikasi' => "Dokumen {$docNumber} memerlukan persetujuan. Diunggah oleh " . auth()->user()->dataUser->namalengkap, + 'url' => '/pending-file', + 'is_read' => false, + 'unit_id' => 22, + ]; + + Notifkasi::create($payloadNotificationTurt); + } + } } + DB::connection('dbDirectory')->commit(); return response()->json([ 'status' => true, 'message' => 'Data berhasil disimpan', - 'status_action' => $isAtasan ? 'approved' : null + 'status_action' => $status ], 200); } catch (\Throwable $th) { - // dd($th); DB::connection('dbDirectory')->rollback(); return response()->json([ 'status' => false, @@ -1653,11 +1818,21 @@ 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'); + ->where(function ($q) { + $q->whereNull('tgl_expired') + ->orWhereDate('tgl_expired', '>=', now()->toDateString()); + }) + ->where(function ($q) { + $q->whereNull('master_kategori_directory_id') + ->orWhere('status_turt', 'approved'); + }) + ->where(function ($q) { + $q->where('is_akre', '!=', true) + ->orWhereNull('is_akre') + ->orWhere('status_mutu', 'approved'); + }) + ->whereNotNull('status_action')->where('status_action', 'approved'); + // if(in_array(22, $unitIds)){ // $rows = $rows->pluck('file'); // }elseif(auth()->user()->username === "admin.turt"){ @@ -1734,7 +1909,19 @@ class DashboardController extends Controller } public function pendingFile(){ - return view('pendingFile.index', ['title' => 'Data Persetujuan']); + $objectUnit = MappingUnitKerjaPegawai::query() + ->where('statusenabled', true) + ->where('objectpegawaifk', auth()->user()->objectpegawaifk) + ->pluck('objectunitkerjapegawaifk') + ->unique() + ->values() + ->all(); + + return view('pendingFile.index', [ + 'title' => 'Data Persetujuan', + 'isKomiteMutu' => in_array(51, $objectUnit), + 'isTurt' => in_array(22, $objectUnit), + ]); } public function dataPendingFile(){ @@ -1742,29 +1929,97 @@ class DashboardController extends Controller $keyword = request('keyword'); $start = request('start_date'); $end = request('end_date'); + $pegawaiId = auth()->user()->objectpegawaifk; $objectpegawaifk = MappingUnitKerjaPegawai::query() ->where('statusenabled', true) ->where(fn($q) => - $q->where('objectatasanlangsungfk', auth()->user()->objectpegawaifk) - ->orWhere('objectpejabatpenilaifk', auth()->user()->objectpegawaifk) + $q->where('objectatasanlangsungfk', $pegawaiId) + ->orWhere('objectpejabatpenilaifk', $pegawaiId) ) ->pluck('objectpegawaifk') ->unique() ->values() ->all(); + if (!in_array($pegawaiId, $objectpegawaifk, true)) { + $objectpegawaifk[] = $pegawaiId; + } + $objectUnit = MappingUnitKerjaPegawai::query() + ->where('statusenabled', true) + ->where('objectpegawaifk', $pegawaiId) + ->pluck('objectunitkerjapegawaifk') + ->unique() + ->values() + ->all(); $keyword = request('keyword'); - $query = FileDirectory::where('statusenabled', true) - ->where(function($qsa){ - $qsa->where('status_action', '!=', 'approved')->orWhereNull('status_action'); - }) - ->whereIn('pegawai_id_entry', $objectpegawaifk) - ->when($keyword, function ($q) use ($keyword) { + $isKomiteMutu = in_array(51, $objectUnit); + $isTurt = in_array(22, $objectUnit); + $hasAtasanScope = !empty($objectpegawaifk); + // 51 / Komite Mutu + // 22 / turt + $query = FileDirectory::where('statusenabled', true); + $query->where(function ($root) use ($isKomiteMutu, $isTurt, $hasAtasanScope, $objectpegawaifk) { + if ($hasAtasanScope) { + $root->where(function ($atasanQuery) use ($objectpegawaifk, $hasAtasanScope) { + $atasanQuery->whereIn('pegawai_id_entry', $objectpegawaifk)->where(function ($qsa) { + $qsa->where('status_action', '!=', 'approved') + ->orWhereNull('status_action') + ->orWhere(function ($approvedButPending) { + $approvedButPending->where(function ($needFurtherApproval) { + $needFurtherApproval + ->where(function ($needTurt) { + $needTurt->whereNotNull('master_kategori_directory_id') + ->where(function ($pendingTurt) { + $pendingTurt->whereNull('status_turt') + ->orWhereIn('status_turt', ['rejected Turt', 'revised Turt']); + }); + }) + ->orWhere(function ($needMutu) { + $needMutu->where('is_akre', true) + ->where(function ($pendingMutu) { + $pendingMutu->whereNull('status_mutu') + ->orWhereIn('status_mutu', ['rejected Mutu', 'revised Mutu']); + }); + }); + }); + }); + }); + }); + } + + if ($isKomiteMutu || $isTurt) { + $method = $hasAtasanScope ? 'orWhere' : 'where'; + $root->{$method}(function ($roleQuery) use ($isKomiteMutu, $isTurt) { + $roleQuery->where('status_action', 'approved') + ->where(function ($q) use ($isKomiteMutu, $isTurt) { + if ($isTurt) { + $q->where(function ($sub) { + $sub->whereNotNull('master_kategori_directory_id') + ->where(function ($pendingTurt) { + $pendingTurt->whereNull('status_turt') + ->orWhereIn('status_turt', ['rejected Turt', 'revised Turt']); + }); + }); + } + if ($isKomiteMutu) { + $innerMethod = $isTurt ? 'orWhere' : 'where'; + $q->{$innerMethod}(function ($sub) { + $sub->where('is_akre', true) + ->where(function ($pendingMutu) { + $pendingMutu->whereNull('status_mutu') + ->orWhereIn('status_mutu', ['rejected Mutu', 'revised Mutu']); + }); + }); + } + }); + }); + } + }); + $query->when($keyword, function ($q) use ($keyword) { $q->where(function ($sub) use ($keyword) { $sub->where('nama_dokumen', 'ILIKE', "%{$keyword}%") ->orWhere('no_dokumen', 'ILIKE', "%{$keyword}%"); }); }); - if($start){ $query->whereDate('entry_at','>=',$start); } @@ -1773,7 +2028,7 @@ class DashboardController extends Controller } $paginated = $query->paginate($perPage); - $data = $paginated->getCollection()->map(function($item){ + $data = $paginated->getCollection()->map(function($item) use($objectpegawaifk, $pegawaiId){ $dataSlice = array_values(array_filter(explode('/', $item->file))); return [ 'file_directory_id' => $item->file_directory_id, @@ -1789,9 +2044,17 @@ class DashboardController extends Controller 'tanggal_terbit' => $item->tanggal_terbit, 'permission_file' => $item->permission_file, 'status_action' => $item->status_action, + 'status_mutu' => $item->status_mutu, + 'status_turt' => $item->status_turt, + 'mutu_revision' => $item->mutu_revision, + 'turt_revision' => $item->turt_revision, 'id_unit_kerja' => $item->id_unit_kerja, 'id_sub_unit_kerja' => $item->id_sub_unit_kerja, - 'master_kategori_directory_id' => $item->master_kategori_directory_id + 'master_kategori_directory_id' => $item->master_kategori_directory_id, + 'is_akre' => $item->is_akre, + 'can_approve_atasan' => in_array($item->pegawai_id_entry, $objectpegawaifk), + 'can_edit_submission' => (int) $item->pegawai_id_entry === (int) $pegawaiId, + 'revision' => $item->revision, ]; }); return response()->json([ @@ -1819,10 +2082,29 @@ class DashboardController extends Controller 'message' => 'Data tidak ditemukan' ], 404); } + $objectUnit = MappingUnitKerjaPegawai::query() + ->where('statusenabled', true) + ->where('objectpegawaifk', auth()->user()->objectpegawaifk) + ->pluck('objectunitkerjapegawaifk') + ->unique() + ->values() + ->all(); + $requiresMutuApproval = $data->is_akre; + $requiresTurtApproval = $data->master_kategori_directory_id; + $isKomiteMutu = in_array(51, $objectUnit); + $isTurt = in_array(22, $objectUnit); + $mutuStatus = $isKomiteMutu && $requiresMutuApproval ? 'approved' : null; + $turtStatus = $isTurt && $requiresTurtApproval ? 'approved' : null; $data->update([ 'status_action' => 'approved', 'action_at' => now(), 'action_by' => auth()->user()->dataUser->id, + 'status_mutu' => $mutuStatus, + 'action_mutu_at' => $mutuStatus === 'approved' ? now() : null, + 'action_mutu_by' => $mutuStatus === 'approved' ? auth()->user()->objectpegawaifk : null, + 'status_turt' => $turtStatus, + 'action_turt_at' => $turtStatus === 'approved' ? now() : null, + 'action_turt_by' => $turtStatus === 'approved' ? auth()->user()->objectpegawaifk : null, ]); $parts = array_values(array_filter(explode('/', $data->file))); @@ -1854,6 +2136,29 @@ class DashboardController extends Controller 'action_type' => 'Approved Dokumen', ]; LogActivity::create($payloadLog); + if($data->is_akre){ + $payloadNotificationMutu = [ + 'created_at' => now(), + 'text_notifikasi' => "Dokumen {$docNumber} memerlukan persetujuan. Diunggah oleh {$data->pegawai_nama_entry}.", + 'url' => '/pending-file', + 'is_read' => false, + 'unit_id' => 51, + ]; + + Notifkasi::create($payloadNotificationMutu); + } + if($data->master_kategori_directory_id){ + $payloadNotificationTurt = [ + 'created_at' => now(), + 'text_notifikasi' => "Dokumen {$docNumber} memerlukan persetujuan. Diunggah oleh {$data->pegawai_nama_entry}.", + 'url' => '/pending-file', + 'is_read' => false, + 'unit_id' => 22, + ]; + + Notifkasi::create($payloadNotificationTurt); + } + DB::connection('dbDirectory')->commit(); return response()->json([ 'status' => true, @@ -2002,25 +2307,149 @@ 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 { + $pegawaiId = auth()->user()->objectpegawaifk; $objectpegawaifk = MappingUnitKerjaPegawai::query() ->where('statusenabled', true) ->where(fn($q) => - $q->where('objectatasanlangsungfk', auth()->user()->objectpegawaifk) - ->orWhere('objectpejabatpenilaifk', auth()->user()->objectpegawaifk) + $q->where('objectatasanlangsungfk', $pegawaiId) + ->orWhere('objectpejabatpenilaifk', $pegawaiId) ) ->pluck('objectpegawaifk') ->unique() ->values() ->all(); - $count = FileDirectory::where('statusenabled', true)->whereIn('pegawai_id_entry', $objectpegawaifk) - ->where(function($q){ - $q->whereNotIn('status_action', ['approved', 'rejected'])->orWhereNull('status_action'); - })->count(); - // $authUnit = auth()->user()->masterPersetujuan->details->pluck('unit_pegawai_id')->unique()->toArray(); - // $count= $query->whereIn('id_unit_kerja', $authUnit)->count(); - // $count = $query-> + if (!in_array($pegawaiId, $objectpegawaifk, true)) { + $objectpegawaifk[] = $pegawaiId; + } + $objectUnit = MappingUnitKerjaPegawai::query() + ->where('statusenabled', true) + ->where('objectpegawaifk', $pegawaiId) + ->pluck('objectunitkerjapegawaifk') + ->unique() + ->values() + ->all(); + $isKomiteMutu = in_array(51, $objectUnit); + $isTurt = in_array(22, $objectUnit); + $hasAtasanScope = !empty($objectpegawaifk); + + $query = FileDirectory::where('statusenabled', true); + $query->where(function ($root) use ($isKomiteMutu, $isTurt, $hasAtasanScope, $objectpegawaifk) { + if ($hasAtasanScope) { + $root->where(function ($atasanQuery) use ($objectpegawaifk) { + $atasanQuery->whereIn('pegawai_id_entry', $objectpegawaifk) + ->where(function ($qsa) { + $qsa->where('status_action', '!=', 'approved') + ->where(function ($notRejected) { + $notRejected->where('status_action', '!=', 'rejected') + ->orWhereNull('status_action'); + }) + ->orWhereNull('status_action'); + }); + }); + } + + if ($isKomiteMutu || $isTurt) { + $method = $hasAtasanScope ? 'orWhere' : 'where'; + $root->{$method}(function ($roleQuery) use ($isKomiteMutu, $isTurt) { + $roleQuery->where('status_action', 'approved') + ->where(function ($q) use ($isKomiteMutu, $isTurt) { + if ($isTurt) { + $q->where(function ($sub) { + $sub->whereNotNull('master_kategori_directory_id') + ->where(function ($pendingTurt) { + $pendingTurt->whereNull('status_turt') + ->orWhereIn('status_turt', ['revised Turt']); + }); + }); + } + if ($isKomiteMutu) { + $innerMethod = $isTurt ? 'orWhere' : 'where'; + $q->{$innerMethod}(function ($sub) { + $sub->where('is_akre', true) + ->where(function ($pendingMutu) { + $pendingMutu->whereNull('status_mutu') + ->orWhereIn('status_mutu', ['revised Mutu']); + }); + }); + } + }); + }); + } + }); + + $count = $query->count(); return response()->json([ 'status' => true, 'count' => $count, @@ -2066,7 +2495,16 @@ class DashboardController extends Controller ]); } - $pegawaiId = auth()->user()?->dataUser?->id; + $pegawaiId = auth()->user()?->objectpegawaifk; + $objectUnit = MappingUnitKerjaPegawai::query() + ->where('statusenabled', true) + ->where('objectpegawaifk', auth()->user()->objectpegawaifk) + ->pluck('objectunitkerjapegawaifk') + ->unique() + ->values() + ->all(); + $isKomiteMutu = in_array(51, $objectUnit); + $isTurt = in_array(22, $objectUnit); if (!$pegawaiId) { return response()->json([ 'status' => false, @@ -2075,8 +2513,17 @@ class DashboardController extends Controller 'unread' => 0 ], 404); } - - $query = Notifkasi::where('pegawai_id', $pegawaiId); + $query = Notifkasi::query(); + $query->where(function($q) use($pegawaiId, $objectUnit, $isKomiteMutu, $isTurt){ + if($isKomiteMutu || $isTurt){ + $q->where('pegawai_id', $pegawaiId) + ->orWhere(function($sub) use($objectUnit){ + $sub->whereNull('pegawai_id')->whereIn('unit_id', $objectUnit); + }); + }else{ + $q->where('pegawai_id', $pegawaiId); + } + }); $unread = (clone $query)->where('is_read', false)->count(); $items = $query->orderBy('created_at', 'desc') ->limit(20) @@ -2107,7 +2554,16 @@ class DashboardController extends Controller ]); } - $pegawaiId = auth()->user()?->dataUser?->id; + $pegawaiId = auth()->user()?->objectpegawaifk; + $objectUnit = MappingUnitKerjaPegawai::query() + ->where('statusenabled', true) + ->where('objectpegawaifk', auth()->user()->objectpegawaifk) + ->pluck('objectunitkerjapegawaifk') + ->unique() + ->values() + ->all(); + $isKomiteMutu = in_array(51, $objectUnit); + $isTurt = in_array(22, $objectUnit); if (!$pegawaiId) { return response()->json([ 'status' => false, @@ -2115,9 +2571,18 @@ class DashboardController extends Controller ], 404); } - Notifkasi::where('pegawai_id', $pegawaiId) - ->where('is_read', false) - ->update(['is_read' => true]); + $query = Notifkasi::query(); + $query->where(function($q) use($pegawaiId, $isKomiteMutu, $isTurt, $objectUnit){ + if($isKomiteMutu || $isTurt){ + $q->where('pegawai_id', $pegawaiId) + ->orWhere(function($sub) use($objectUnit){ + $sub->whereNull('pegawai_id')->whereIn('unit_id', $objectUnit); + }); + }else{ + $q->where('pegawai_id', $pegawaiId); + } + })->where('is_read', false) + ->update(['is_read' => true]); return response()->json([ 'status' => true, @@ -2357,9 +2822,29 @@ class DashboardController extends Controller $katDok = MasterKategori::where('statusenabled', true) ->select('master_kategori_directory_id', 'nama_kategori_directory') ->get(); + $pegawaiId = auth()->user()->objectpegawaifk; + $objectUnit = MappingUnitKerjaPegawai::query() + ->where('statusenabled', true) + ->where('objectpegawaifk', $pegawaiId) + ->pluck('objectunitkerjapegawaifk') + ->unique() + ->values() + ->all(); + $isKomiteMutu = in_array(51, $objectUnit); + $isTurt = in_array(22, $objectUnit); + $isAtasan = MappingUnitKerjaPegawai::query() + ->where('statusenabled', true) + ->where(function ($q) use ($pegawaiId) { + $q->where('objectatasanlangsungfk', $pegawaiId) + ->orWhere('objectpejabatpenilaifk', $pegawaiId); + }) + ->exists(); return view('pengajuanFile.index', [ 'title' => 'Data Persetujuan', - 'katDok' => $katDok + 'katDok' => $katDok, + 'isKomiteMutu' => $isKomiteMutu, + 'isTurt' => $isTurt, + 'isAtasan' => $isAtasan, ]); } @@ -2369,21 +2854,123 @@ class DashboardController extends Controller $start = request('start_date'); $end = request('end_date'); $isHistory = filter_var(request('history'), FILTER_VALIDATE_BOOLEAN); - $query = FileDirectory::with('kategori')->where('statusenabled', true) - ->where('pegawai_id_entry', auth()->user()->objectpegawaifk) - ->when($isHistory, function($q){ - $q->where('status_action', 'approved'); - }, function($q){ - $q->where(function($sub){ - $sub->where('status_action', '!=', 'approved') - ->orWhereNull('status_action'); - }); + $mode = (string) request('mode', 'pengajuan'); + $pegawaiId = auth()->user()->objectpegawaifk; + $objectUnit = MappingUnitKerjaPegawai::query() + ->where('statusenabled', true) + ->where('objectpegawaifk', $pegawaiId) + ->pluck('objectunitkerjapegawaifk') + ->unique() + ->values() + ->all(); + $isKomiteMutu = in_array(51, $objectUnit); + $isTurt = in_array(22, $objectUnit); + $isAtasan = MappingUnitKerjaPegawai::query() + ->where('statusenabled', true) + ->where(function ($q) use ($pegawaiId) { + $q->where('objectatasanlangsungfk', $pegawaiId) + ->orWhere('objectpejabatpenilaifk', $pegawaiId); }) - ->orderBy('entry_at','desc'); + ->exists(); + + $query = FileDirectory::with('kategori')->where('statusenabled', true); + + if ($mode === 'persetujuan' && ($isKomiteMutu || $isTurt || $isAtasan)) { + $query->where(function ($root) use ($isKomiteMutu, $isTurt, $isAtasan, $pegawaiId) { + if ($isKomiteMutu || $isTurt) { + $root->where(function ($q) use ($isKomiteMutu, $isTurt) { + if ($isTurt) { + $q->whereNotNull('master_kategori_directory_id') + ->where(function ($pendingTurt) { + $pendingTurt->whereNull('status_turt') + ->orWhereIn('status_turt', ['rejected Turt', 'revised Turt']); + }); + } + if ($isKomiteMutu) { + $method = $isTurt ? 'orWhere' : 'where'; + $q->{$method}(function ($sub) { + $sub->where('is_akre', true) + ->where(function ($pendingMutu) { + $pendingMutu->whereNull('status_mutu') + ->orWhereIn('status_mutu', ['rejected Mutu', 'revised Mutu']); + }); + }); + } + })->where('status_action', 'approved'); + } + + if ($isAtasan) { + $method = ($isKomiteMutu || $isTurt) ? 'orWhere' : 'where'; + $root->{$method}(function ($ownSubmission) use ($pegawaiId) { + $ownSubmission->where('pegawai_id_entry', $pegawaiId) + ->where('status_action', 'approved') + ->where(function ($needFurtherApproval) { + $needFurtherApproval + ->where(function ($needTurt) { + $needTurt->whereNotNull('master_kategori_directory_id') + ->where(function ($pendingTurt) { + $pendingTurt->whereNull('status_turt') + ->orWhereIn('status_turt', ['rejected Turt', 'revised Turt']); + }); + }) + ->orWhere(function ($needMutu) { + $needMutu->where('is_akre', true) + ->where(function ($pendingMutu) { + $pendingMutu->whereNull('status_mutu') + ->orWhereIn('status_mutu', ['rejected Mutu', 'revised Mutu']); + }); + }); + }); + }); + } + }); + } else { + $query->where('pegawai_id_entry', $pegawaiId) + ->when($isHistory, function($q){ + $q->where('status_action', 'approved') + ->where(function ($sub) { + $sub->whereNull('master_kategori_directory_id') + ->orWhere('status_turt', 'approved'); + }) + ->where(function ($sub) { + $sub->where('is_akre', '!=', true) + ->orWhereNull('is_akre') + ->orWhere('status_mutu', 'approved'); + }); + }, function($q){ + $q->where(function($sub){ + $sub->where('status_action', '!=', 'approved') + ->orWhereNull('status_action') + ->orWhere(function ($approvedButPending) { + $approvedButPending->where('status_action', 'approved') + ->where(function ($needFurtherApproval) { + $needFurtherApproval + ->where(function ($needTurt) { + $needTurt->whereNotNull('master_kategori_directory_id') + ->where(function ($pendingTurt) { + $pendingTurt->whereNull('status_turt') + ->orWhere('status_turt', ['revised Turt', 'rejected Turt']); + }); + }) + ->orWhere(function ($needMutu) { + $needMutu->where('is_akre', true) + ->where(function ($pendingMutu) { + $pendingMutu->whereNull('status_mutu') + ->orWhereIn('status_mutu', ['revised Mutu', 'rejected Mutu']); + }); + }); + }); + }); + }); + }); + } + + $query->orderBy('entry_at','desc'); if($keyword){ $query->where(function($q) use ($keyword){ $q->where('file', 'ILIKE', "%{$keyword}%") - ->orWhere('no_dokumen', 'ILIKE', "%{$keyword}%"); + ->orWhere('no_dokumen', 'ILIKE', "%{$keyword}%") + ->orWhere('nama_dokumen', 'ILIKE', "%{$keyword}%"); }); } if($start){ @@ -2410,6 +2997,10 @@ class DashboardController extends Controller 'permission_file' => $item->permission_file, 'status_action' => $item->status_action, 'revision' => $item->revision, + 'status_mutu' => $item->status_mutu, + 'status_turt' => $item->status_turt, + 'mutu_revision' => $item->mutu_revision, + 'turt_revision' => $item->turt_revision, 'id_unit_kerja' => $item->id_unit_kerja, 'id_sub_unit_kerja' => $item->id_sub_unit_kerja, 'master_kategori_directory_id' => $item->master_kategori_directory_id, @@ -2569,39 +3160,71 @@ class DashboardController extends Controller } } } + $targetRoleNotifications = []; + $resetMutuApproval = $data->status_mutu === 'rejected Mutu'; + $resetTurtApproval = $data->status_turt === 'rejected Turt'; + $resetAtasanApproval = $data->status_action === 'rejected'; + // dd($resetTurtApproval, $resetMutuApproval); + if ($resetMutuApproval) { + $payload['status_mutu'] = 'revised Mutu'; + // $payload['mutu_revision'] = null; + $payload['action_mutu_at'] = null; + $payload['action_mutu_by'] = null; + $targetRoleNotifications[] = 51; + } + if ($resetTurtApproval) { + $payload['status_turt'] = 'revised Turt'; + // $payload['turt_revision'] = null; + $payload['action_turt_at'] = null; + $payload['action_turt_by'] = null; + $targetRoleNotifications[] = 22; + } + if($resetAtasanApproval){ + // $payload['revision'] = null; $payload['status_action'] = 'revised'; + $payload['action_at'] = null; + $payload['action_by'] = null; + } if (!empty($payload)) { $data->update($payload); } + $data->refresh(); $mapping = MappingUnitKerjaPegawai::where('statusenabled', true) ->where('objectpegawaifk', auth()->user()?->dataUser?->id) ->first(); $parts = array_values(array_filter(explode('/', $data->file))); $uploaderName = auth()->user()?->dataUser?->namalengkap ?? 'Pengguna'; $docNumber = $data->no_dokumen ? 'nomor '. $data->no_dokumen : end($parts);; - $payloadNotification = [ - 'created_at' => now(), - 'text_notifikasi' => "Dokumen {$docNumber} telah direvisi dan memerlukan persetujuan ulang. ". "Direvisi oleh {$uploaderName}.", - 'url' => '/pending-file', - 'is_read' => false, - 'pegawai_id' => $mapping?->objectatasanlangsungfk, - ]; + $notifText = "Dokumen {$docNumber} telah direvisi dan memerlukan persetujuan ulang. Direvisi oleh {$uploaderName}."; + $targetRoleNotifications = array_values(array_unique($targetRoleNotifications)); - - Notifkasi::create($payloadNotification); - - if($mapping->objectpejabatpenilaifk){ - $payloadNotification = [ + if (!empty($targetRoleNotifications)) { + foreach ($targetRoleNotifications as $unitId) { + Notifkasi::create([ 'created_at' => now(), - 'text_notifikasi' => "Dokumen {$docNumber} telah direvisi dan memerlukan persetujuan ulang. ". "Direvisi oleh {$uploaderName}.", + 'text_notifikasi' => $notifText, + 'url' => '/pengajuan-file', + 'is_read' => false, + 'unit_id' => $unitId, + ]); + } + } else { + $pegawaiTargets = array_filter([ + $mapping?->objectatasanlangsungfk, + $mapping?->objectpejabatpenilaifk, + ]); + + foreach (array_unique($pegawaiTargets) as $pegawaiId) { + Notifkasi::create([ + 'created_at' => now(), + 'text_notifikasi' => $notifText, 'url' => '/pending-file', 'is_read' => false, - 'pegawai_id' => $mapping?->objectpejabatpenilaifk, - ]; - - Notifkasi::create($payloadNotification); + 'pegawai_id' => $pegawaiId, + ]); + } } $payloadLog = [ @@ -2613,7 +3236,6 @@ class DashboardController extends Controller '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' => 'Revisi Dokumen', @@ -2771,6 +3393,7 @@ class DashboardController extends Controller public function dataTableAkreditasi(Request $request) { $keyword = $request->query('keyword'); + $page = max(1, (int) $request->query('page', 1)); $perPage = (int) $request->query('per_page', 10); $perPage = max(1, min(100, $perPage)); @@ -2781,12 +3404,24 @@ class DashboardController extends Controller 'no_dokumen', 'file', 'entry_at', + 'tanggal_terbit', + 'permission_file', + 'pegawai_id_entry', 'id_unit_kerja', 'is_akre', ]) ->with(['unit:id,name']) ->where('statusenabled', true) ->where('status_action', 'approved') + ->where(function ($q) { + $q->whereNull('master_kategori_directory_id') + ->orWhere('status_turt', 'approved'); + }) + ->where(function ($q) { + $q->where('is_akre', '!=', true) + ->orWhereNull('is_akre') + ->orWhere('status_mutu', 'approved'); + }) ->where('is_akre', true); // Logic Pencarian @@ -2797,28 +3432,131 @@ class DashboardController extends Controller ->orWhere('file', 'ILIKE', "%{$keyword}%"); // Cari juga berdasarkan nama file di path }); }); - $data = $query - ->orderBy('entry_at', 'desc') - ->paginate($perPage); + $items = $query->get(); + $sortedItems = $this->sortAkreditasiRowsByJffOrder($items->all()); + $total = count($sortedItems); + $offset = ($page - 1) * $perPage; + $pageItems = array_slice($sortedItems, $offset, $perPage); + $lastPage = max(1, (int) ceil($total / $perPage)); return response()->json([ 'status' => true, 'message' => 'Berhasil mendapatkan data', - 'data' => $data->items(), + 'data' => array_values($pageItems), 'pagination' => [ - 'current_page' => $data->currentPage(), - 'next_page' => $data->hasMorePages() ? $data->currentPage() + 1 : null, - 'prev_page' => $data->currentPage() > 1 ? $data->currentPage() - 1 : null, - 'total' => $data->total(), - 'per_page' => $data->perPage(), - 'last_page' => $data->lastPage(), - 'has_more' => $data->hasMorePages(), - 'from' => $data->firstItem(), - 'to' => $data->lastItem(), + 'current_page' => $page, + 'next_page' => $page < $lastPage ? $page + 1 : null, + 'prev_page' => $page > 1 ? $page - 1 : null, + 'total' => $total, + 'per_page' => $perPage, + 'last_page' => $lastPage, + 'has_more' => $page < $lastPage, + 'from' => $total > 0 ? $offset + 1 : 0, + 'to' => $offset + count($pageItems), ] ]); } + private function sortAkreditasiRowsByJffOrder(array $items): array + { + $orderMap = $this->buildAkreditasiOrderMap(); + + usort($items, function ($left, $right) use ($orderMap) { + $leftPath = $this->getAkreditasiFolderPathFromFile($left->file ?? ''); + $rightPath = $this->getAkreditasiFolderPathFromFile($right->file ?? ''); + + $pathCompare = $this->compareAkreditasiPaths($leftPath, $rightPath, $orderMap); + if ($pathCompare !== 0) { + return $pathCompare; + } + + $leftName = Str::lower((string) ($left->nama_dokumen ?? $left->file ?? '')); + $rightName = Str::lower((string) ($right->nama_dokumen ?? $right->file ?? '')); + + return strnatcasecmp($leftName, $rightName); + }); + + return $items; + } + + private function buildAkreditasiOrderMap(): array + { + $path = public_path('json/akreditasi.jff'); + if (!is_file($path)) { + return []; + } + + $raw = @file_get_contents($path); + $data = json_decode($raw ?: '[]', true); + if (!is_array($data)) { + return []; + } + + $orderMap = []; + foreach ($data as $typeIndex => $typeItem) { + $typeName = trim((string) ($typeItem['name'] ?? '')); + if ($typeName === '') { + continue; + } + $orderMap[$typeName] = [$typeIndex, -1, -1]; + + $segments = isset($typeItem['segment']) && is_array($typeItem['segment']) + ? $typeItem['segment'] + : []; + + foreach ($segments as $segmentIndex => $segmentItem) { + $segmentName = trim((string) ($segmentItem['name'] ?? '')); + if ($segmentName === '') { + continue; + } + + $segmentPath = $typeName . '/' . $segmentName; + $orderMap[$segmentPath] = [$typeIndex, $segmentIndex, -1]; + + $children = isset($segmentItem['turunan']) && is_array($segmentItem['turunan']) + ? $segmentItem['turunan'] + : []; + + foreach ($children as $childIndex => $childItem) { + $childName = trim((string) ($childItem['name'] ?? '')); + if ($childName === '') { + continue; + } + + $orderMap[$segmentPath . '/' . $childName] = [$typeIndex, $segmentIndex, $childIndex]; + } + } + } + + return $orderMap; + } + + private function getAkreditasiFolderPathFromFile(?string $filePath): string + { + $parts = array_values(array_filter(explode('/', (string) $filePath))); + if (count($parts) <= 1) { + return ''; + } + + array_pop($parts); + return implode('/', $parts); + } + + private function compareAkreditasiPaths(string $leftPath, string $rightPath, array $orderMap): int + { + $fallback = [PHP_INT_MAX, PHP_INT_MAX, PHP_INT_MAX]; + $leftOrder = $orderMap[$leftPath] ?? $fallback; + $rightOrder = $orderMap[$rightPath] ?? $fallback; + + for ($i = 0; $i < 3; $i++) { + if ($leftOrder[$i] !== $rightOrder[$i]) { + return $leftOrder[$i] <=> $rightOrder[$i]; + } + } + + return strnatcasecmp($leftPath, $rightPath); + } + public function expDokumen(){ $katDok = MasterKategori::where('statusenabled', true)->select('master_kategori_directory_id', 'nama_kategori_directory')->get(); $authMapping = auth()->user()?->dataUser?->mappingUnitKerjaPegawai[0]; @@ -3063,4 +3801,334 @@ class DashboardController extends Controller return $result; } + public function approvePendingFileMutu(string $id){ + try { + DB::connection('dbDirectory')->beginTransaction(); + if (!$this->hasApprovalRole(51)) { + return response()->json([ + 'status' => false, + 'message' => 'Anda tidak memiliki akses approval Komite Mutu' + ], 403); + } + $data = FileDirectory::where('file_directory_id', $id)->first(); + if(!$data){ + return response()->json([ + 'status' => false, + 'message' => 'Data tidak ditemukan' + ], 404); + } + if ($data->status_action !== 'approved') { + return response()->json([ + 'status' => false, + 'message' => 'Dokumen belum disetujui atasan.' + ], 422); + } + $data->update([ + 'status_mutu' => 'approved', + 'action_mutu_at' => now(), + 'action_mutu_by' => auth()->user()->objectpegawaifk, + ]); + $this->syncFinalApprovalStatus($data->fresh()); + + $parts = array_values(array_filter(explode('/', $data->file))); + $actionName = auth()->user()?->dataUser?->namalengkap ?? 'Pengguna'; + $docNumber = $data->no_dokumen ? 'nomor '. $data->no_dokumen : end($parts); + $payloadNotification = [ + 'created_at' => now(), + 'text_notifikasi' => "Dokumen {$docNumber} telah disetujui oleh {$actionName} Komite Mutu.", + 'url' => '/', + 'is_read' => false, + 'pegawai_id' => $data->pegawai_id_entry, + ]; + + 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' => 'Approved Dokumen Mutu', + ]; + LogActivity::create($payloadLog); + + DB::connection('dbDirectory')->commit(); + return response()->json([ + 'status' => true, + 'message' => 'File berhasil di-approve' + ], 200); + } catch (\Throwable $th) { + DB::connection('dbDirectory')->rollBack(); + return response()->json([ + 'status' => false, + 'message' => $th->getMessage() + ], 500); + } + } + + public function approvePendingFileTurt(string $id){ + try { + DB::connection('dbDirectory')->beginTransaction(); + if (!$this->hasApprovalRole(22)) { + return response()->json([ + 'status' => false, + 'message' => 'Anda tidak memiliki akses approval TURT' + ], 403); + } + $data = FileDirectory::where('file_directory_id', $id)->first(); + if(!$data){ + return response()->json([ + 'status' => false, + 'message' => 'Data tidak ditemukan' + ], 404); + } + if ($data->status_action !== 'approved') { + return response()->json([ + 'status' => false, + 'message' => 'Dokumen belum disetujui atasan.' + ], 422); + } + $data->update([ + 'status_turt' => 'approved', + 'action_turt_at' => now(), + 'action_turt_by' => auth()->user()->objectpegawaifk, + ]); + $this->syncFinalApprovalStatus($data->fresh()); + + $parts = array_values(array_filter(explode('/', $data->file))); + $actionName = auth()->user()?->dataUser?->namalengkap ?? 'Pengguna'; + $docNumber = $data->no_dokumen ? 'nomor '. $data->no_dokumen : end($parts); + $payloadNotification = [ + 'created_at' => now(), + 'text_notifikasi' => "Dokumen {$docNumber} telah disetujui oleh {$actionName}, Tim Kerja Tata Usaha dan Rumah Tangga.", + 'url' => '/', + 'is_read' => false, + 'pegawai_id' => $data->pegawai_id_entry, + ]; + + 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' => 'Approved Dokumen Turt', + ]; + LogActivity::create($payloadLog); + + DB::connection('dbDirectory')->commit(); + return response()->json([ + 'status' => true, + 'message' => 'File berhasil di-approve' + ], 200); + } catch (\Throwable $th) { + DB::connection('dbDirectory')->rollBack(); + return response()->json([ + 'status' => false, + 'message' => $th->getMessage() + ], 500); + } + } + + public function rejectPendingFileMutu(Request $request, string $id){ + try { + DB::connection('dbDirectory')->beginTransaction(); + if (!$this->hasApprovalRole(51)) { + return response()->json([ + 'status' => false, + 'message' => 'Anda tidak memiliki akses approval Komite Mutu' + ], 403); + } + $data = FileDirectory::where('file_directory_id', $id)->first(); + if(!$data){ + return response()->json([ + 'status' => false, + 'message' => 'Data tidak ditemukan' + ], 404); + } + if ($data->status_action !== 'approved') { + return response()->json([ + 'status' => false, + 'message' => 'Dokumen belum disetujui atasan.' + ], 422); + } + $revision = trim((string) $request->input('revision', '')); + if ($revision === '') { + return response()->json([ + 'status' => false, + 'message' => 'Catatan revisi wajib diisi' + ], 422); + } + $data->update([ + 'status_mutu' => 'rejected Mutu', + 'action_mutu_at' => now(), + 'action_mutu_by' => auth()->user()->dataUser->id, + 'mutu_revision' => $revision, + 'revision' => 'Komite Mutu: ' . $revision + ]); + + $parts = array_values(array_filter(explode('/', $data->file))); + $actionName = auth()->user()?->dataUser?->namalengkap ?? 'Pengguna'; + $docNumber = $data->no_dokumen ? 'nomor '. $data->no_dokumen : end($parts); + $payloadNotification = [ + 'created_at' => now(), + 'text_notifikasi' => "Dokumen {$docNumber} telah ditolak oleh {$actionName}, Komite Mutu. ". "Silakan periksa dokumen untuk melakukan perbaikan.", + 'url' => '/pengajuan-file', + 'is_read' => false, + // 'pegawai_id' => $mapping?->objectatasanlangsungfk, + 'pegawai_id' => $data->pegawai_id_entry, + ]; + + 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, + 'id_unit_kerja' => $data ? $data->id_unit_kerja : null, + 'id_sub_unit_kerja' => $data ? $data->id_sub_unit_kerja : null, + 'action_type' => 'Rejected Dokumen Mutu', + ]; + LogActivity::create($payloadLog); + DB::connection('dbDirectory')->commit(); + return response()->json([ + 'status' => true, + 'message' => 'File berhasil di-reject' + ], 200); + } catch (\Throwable $th) { + DB::connection('dbDirectory')->rollBack(); + return response()->json([ + 'status' => false, + 'message' => $th->getMessage() + ], 500); + } + } + public function rejectPendingFileTurt(Request $request, string $id){ + try { + DB::connection('dbDirectory')->beginTransaction(); + if (!$this->hasApprovalRole(22)) { + return response()->json([ + 'status' => false, + 'message' => 'Anda tidak memiliki akses approval TURT' + ], 403); + } + $data = FileDirectory::where('file_directory_id', $id)->first(); + if(!$data){ + return response()->json([ + 'status' => false, + 'message' => 'Data tidak ditemukan' + ], 404); + } + if ($data->status_action !== 'approved') { + return response()->json([ + 'status' => false, + 'message' => 'Dokumen belum disetujui atasan.' + ], 422); + } + $revision = trim((string) $request->input('revision', '')); + if ($revision === '') { + return response()->json([ + 'status' => false, + 'message' => 'Catatan revisi wajib diisi' + ], 422); + } + $data->update([ + 'status_turt' => 'rejected Turt', + 'action_turt_at' => now(), + 'action_turt_by' => auth()->user()->dataUser->id, + 'turt_revision' => $revision, + 'revision' => 'TURT: ' . $revision, + ]); + + $parts = array_values(array_filter(explode('/', $data->file))); + $actionName = auth()->user()?->dataUser?->namalengkap ?? 'Pengguna'; + $docNumber = $data->no_dokumen ? 'nomor '. $data->no_dokumen : end($parts); + $payloadNotification = [ + 'created_at' => now(), + 'text_notifikasi' => "Dokumen {$docNumber} telah ditolak oleh {$actionName}, Tim Kerja Tata Usaha dan Rumah Tangga. ". "Silakan periksa dokumen untuk melakukan perbaikan.", + 'url' => '/pengajuan-file', + 'is_read' => false, + // 'pegawai_id' => $mapping?->objectatasanlangsungfk, + 'pegawai_id' => $data->pegawai_id_entry, + ]; + + 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' => 'Rejected Dokumen Turt', + ]; + LogActivity::create($payloadLog); + DB::connection('dbDirectory')->commit(); + return response()->json([ + 'status' => true, + 'message' => 'File berhasil di-reject' + ], 200); + } catch (\Throwable $th) { + DB::connection('dbDirectory')->rollBack(); + return response()->json([ + 'status' => false, + 'message' => $th->getMessage() + ], 500); + } + } + + private function syncFinalApprovalStatus(FileDirectory $data): void + { + $mustApproveMutu = (bool) $data->is_akre; + $mustApproveTurt = !empty($data->master_kategori_directory_id); + + if ($data->status_action !== 'approved') { + return; + } + + $mutuApproved = !$mustApproveMutu || $data->status_mutu === 'approved'; + $turtApproved = !$mustApproveTurt || $data->status_turt === 'approved'; + + if ($mutuApproved && $turtApproved) { + $data->update([ + 'status_action' => 'approved', + 'action_at' => now(), + 'action_by' => auth()->user()->objectpegawaifk, + ]); + } + } + + private function hasApprovalRole(int $unitId): bool + { + return MappingUnitKerjaPegawai::query() + ->where('statusenabled', true) + ->where('objectpegawaifk', auth()->user()->objectpegawaifk) + ->where('objectunitkerjapegawaifk', $unitId) + ->exists(); + } + } 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/app/Models/User.php b/app/Models/User.php index 9a0e454..a62c864 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -45,7 +45,7 @@ class User extends Authenticatable 'katasandi' => 'hashed', ]; } - protected $with = ['dataUser', 'masterPersetujuan', 'akses']; + protected $with = ['dataUser']; public function dataUser(){ return $this->belongsTo(DataUser::class, 'objectpegawaifk', 'id')->select('id', 'namalengkap'); } diff --git a/public/assets/css/styles.min.css b/public/assets/css/styles.min.css index da87999..db728db 100644 --- a/public/assets/css/styles.min.css +++ b/public/assets/css/styles.min.css @@ -15904,14 +15904,14 @@ body { border-radius: 20px; } .body-wrapper .body-wrapper-inner { - min-height: calc(100vh - 110px); + min-height: calc(130vh - 180px); } .body-wrapper .container-fluid, .body-wrapper .container-sm, .body-wrapper .container-md, .body-wrapper .container-lg, .body-wrapper .container-xl, .body-wrapper .container-xxl { - max-width: 1300px; + max-width: 1600px; margin: 0 auto; - padding: 24px; + padding: 10px; transition: 0.2s ease-in; - padding-top: 120px; + padding-top: 90px; } @media (max-width: 991.98px) { .body-wrapper .container-fluid, .body-wrapper .container-sm, .body-wrapper .container-md, .body-wrapper .container-lg, .body-wrapper .container-xl, .body-wrapper .container-xxl { diff --git a/public/js/pendingFile/index.js b/public/js/pendingFile/index.js index f15d17d..3765049 100644 --- a/public/js/pendingFile/index.js +++ b/public/js/pendingFile/index.js @@ -19,6 +19,8 @@ document.addEventListener('DOMContentLoaded', () => { const titleEl = document.getElementById('pendingTitle'); const tabPendingEl = document.getElementById('tabPengajuan'); const tabHistoryEl = document.getElementById('tabHistory'); + const isKomiteMutu = !!window.isKomiteMutu; + const isTurt = !!window.isTurt; const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content'); const selectedIds = new Set(); @@ -63,10 +65,54 @@ document.addEventListener('DOMContentLoaded', () => { }); } - function statusBadge(status){ - if (status === 'rejected') return 'Rejected'; - if (status === 'revised') return 'Revised'; - return 'Pending'; + function approvalBadge(status, label){ + const normalized = String(status || '').toLowerCase(); + if (normalized === 'approved') return `Approved ${label}`; + if (normalized.startsWith('rejected')) return `Rejected ${label}`; + if (normalized.startsWith('revised')) return `Revised ${label}`; + return `Pending ${label}`; + } + + function getWorkflowStatusBadges(item){ + const actionStatus = String(item?.status_action || '').toLowerCase(); + const mutuStatus = String(item?.status_mutu || '').toLowerCase(); + const turtStatus = String(item?.status_turt || '').toLowerCase(); + + if (actionStatus.startsWith('revised')) { + return [`Revised`]; + } + + if (actionStatus.startsWith('rejected')) { + return [`Rejected Atasan`]; + } + + if (actionStatus !== 'approved') { + return [`Pending Atasan`]; + } + + const statuses = []; + + if (item?.is_akre) { + if (mutuStatus.startsWith('rejected')) { + statuses.push(`Rejected Mutu`); + } else if (mutuStatus !== 'approved') { + statuses.push(`Pending Mutu`); + } + } + + if (item?.master_kategori_directory_id) { + if (turtStatus.startsWith('rejected')) { + statuses.push(`Rejected TURT`); + } else if (turtStatus !== 'approved') { + statuses.push(`Pending TURT`); + } + } + + if (statuses.length > 0) { + return statuses; + } + + return [`Approved`]; } function aksesBadge(akses){ @@ -81,12 +127,87 @@ document.addEventListener('DOMContentLoaded', () => { } function isRejected(item){ - return item?.status_action === 'rejected'; + return String(item?.status_action || '').toLowerCase().startsWith('rejected') + || String(item?.status_mutu || '').toLowerCase().startsWith('rejected') + || String(item?.status_turt || '').toLowerCase().startsWith('rejected'); + } + + function isAtasanApproved(item){ + return String(item?.status_action || '').toLowerCase() === 'approved'; + } + + function canProcessRoleStatus(status){ + const normalized = String(status || '').toLowerCase(); + return normalized === '' || normalized.startsWith('revised'); + } + + function canApproveAsAtasan(item){ + return item?.can_approve_atasan === true || item?.can_approve_atasan === 1 || item?.can_approve_atasan === '1'; + } + + function canEditSubmission(item){ + return item?.can_edit_submission === true || item?.can_edit_submission === 1 || item?.can_edit_submission === '1'; + } + + function hasRevisionInfo(item){ + return !!(item?.revision || item?.mutu_revision || item?.turt_revision); + } + + function collectRevisionNotes(item){ + const notes = []; + const genericRevision = String(item?.revision || '').trim(); + const mutuRevision = String(item?.mutu_revision || '').trim(); + const turtRevision = String(item?.turt_revision || '').trim(); + + if (genericRevision) { + const lowerGeneric = genericRevision.toLowerCase(); + if (!lowerGeneric.startsWith('komite mutu:') && !lowerGeneric.startsWith('turt:')) { + notes.push({ label: 'Atasan', text: genericRevision }); + } + } + if (mutuRevision) { + notes.push({ label: 'Komite Mutu', text: mutuRevision }); + } + if (turtRevision) { + notes.push({ label: 'TURT', text: turtRevision }); + } + + return notes; + } + + function escapeHtml(value){ + return String(value ?? '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + function buildRevisionNotesHtml(notes){ + if (!notes.length) { + return '
Tidak ada catatan revisi.
'; + } + + return ` +
+ ${notes.map(note => ` +
+
${escapeHtml(note.label)}
+
${escapeHtml(note.text)}
+
+ `).join('')} +
+ `; + } + + function getItemById(id){ + return (tableState.data || []).find((row) => String(row.file_directory_id) === String(id)); } function getSelectableIdsOnPage(){ return (tableState.data || []) - .filter((item) => !isRejected(item)) + .filter((item) => !isRejected(item) && !isAtasanApproved(item) && canApproveAsAtasan(item)) .map((item) => String(item.file_directory_id)); } @@ -114,9 +235,11 @@ document.addEventListener('DOMContentLoaded', () => { function updateSelectionUI(){ const count = selectedIds.size; + const selectableIds = getSelectableIdsOnPage(); + const canBulkApprove = tableState.mode !== 'history' && selectableIds.length > 0; if (selectedCountEl) selectedCountEl.textContent = String(count); - if (bulkApproveBtn) bulkApproveBtn.disabled = count === 0 || tableState.mode === 'history'; - if (clearSelectionBtn) clearSelectionBtn.disabled = count === 0 || tableState.mode === 'history'; + if (bulkApproveBtn) bulkApproveBtn.disabled = count === 0 || !canBulkApprove; + if (clearSelectionBtn) clearSelectionBtn.disabled = count === 0 || !canBulkApprove; updateSelectAllState(); } @@ -126,26 +249,60 @@ document.addEventListener('DOMContentLoaded', () => { const tanggalTerbit = item.tanggal_terbit ? formatTanggal(item.tanggal_terbit) : '-'; const id = String(item.file_directory_id); const rejected = isRejected(item); + const atasanApproved = isAtasanApproved(item); + const canAtasanApprove = canApproveAsAtasan(item); + const atasanRejected = String(item?.status_action || '').toLowerCase().startsWith('rejected'); + const canMutuAct = atasanApproved && isKomiteMutu && item.is_akre && canProcessRoleStatus(item.status_mutu); + const canTurtAct = atasanApproved && isTurt && item.master_kategori_directory_id && canProcessRoleStatus(item.status_turt); const checked = selectedIds.has(id); - const aksi = ` -
- - - -
- `; + const actions = [ + `` + ]; + + if (isKomiteMutu || isTurt) { + if (canAtasanApprove && !atasanApproved) { + actions.push(``); + actions.push(``); + } + if (canMutuAct) { + actions.push(``); + actions.push(``); + } + if (canTurtAct) { + actions.push(``); + actions.push(``); + } + } else { + if (!atasanApproved) { + actions.push(``); + actions.push(``); + } + } + + if (rejected && atasanRejected && !canMutuAct && !canTurtAct) { + actions.length = 1; + } + + if (rejected) { + if (hasRevisionInfo(item)) { + actions.push(``); + } + if (canEditSubmission(item)) { + actions.push(``); + } + } + + const statusContent = (isKomiteMutu || isTurt) + ? getWorkflowStatusBadges(item).join(' ') + : getWorkflowStatusBadges(item).join(' '); return ` @@ -153,11 +310,11 @@ document.addEventListener('DOMContentLoaded', () => { class="form-check-input row-select" data-id="${id}" ${checked ? 'checked' : ''} - ${rejected ? 'disabled' : ''}> + ${(rejected || atasanApproved || !canAtasanApprove) ? 'disabled' : ''}> - ${rejected ? '' : aksi} +
${actions.join('')}
${safeText(item.no_dokumen)} - ${statusBadge(item?.status_action)} + ${statusContent} ${aksesBadge(item?.permission_file)} { tabHistoryEl.classList.toggle('d-none', tableState.mode !== 'history'); } if (pendingBulkActionsEl) { - pendingBulkActionsEl.classList.toggle('d-none', tableState.mode === 'history'); + pendingBulkActionsEl.classList.toggle('d-none', tableState.mode === 'history' || isKomiteMutu || isTurt); } updateSelectionUI(); } @@ -404,7 +561,7 @@ document.addEventListener('DOMContentLoaded', () => { showConfirmButton: false }); selectedIds.delete(String(id)); - countData(); + if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount(); fetchData(); }).catch((err) => { Swal.fire({ @@ -461,7 +618,127 @@ document.addEventListener('DOMContentLoaded', () => { showConfirmButton: false }); selectedIds.delete(String(id)); - countData(); + if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount(); + fetchData(); + }).catch((err) => { + Swal.fire({ + icon: 'error', + title: 'Gagal', + text: err.message || 'Terjadi kesalahan.' + }); + }); + }); + } + + window.infoRejectPending = function(id){ + const item = getItemById(id); + Swal.fire({ + title: 'Catatan Revisi', + html: buildRevisionNotesHtml(collectRevisionNotes(item)), + icon: 'info', + confirmButtonText: 'Tutup' + }); + } + + window.editRejectedFromPending = function(id){ + const targetUrl = new URL('/pengajuan-file', window.location.origin); + targetUrl.searchParams.set('edit', id); + window.location.href = targetUrl.toString(); + } + + window.approvePendingRole = function(type, id, fileName){ + const endpoint = type === 'mutu' + ? `/pending-file/${id}/approve-mutu` + : `/pending-file/${id}/approve-turt`; + const label = type === 'mutu' ? 'Komite Mutu' : 'TURT'; + + Swal.fire({ + title: `Approve ${label}?`, + text: fileName || 'Dokumen akan disetujui.', + icon: 'question', + showCancelButton: true, + confirmButtonText: 'Approve', + cancelButtonText: 'Batal', + }).then((result) => { + if (!result.isConfirmed) return; + fetch(endpoint, { + method: 'POST', + headers: { + 'X-CSRF-TOKEN': csrfToken, + 'Content-Type': 'application/json' + }, + }).then(async(res) => { + const data = await res.json(); + if (!res.ok || !data?.status) { + throw new Error(data?.message || 'Gagal approve.'); + } + Swal.fire({ + icon: 'success', + title: 'Berhasil', + text: data.message || 'Dokumen disetujui.', + timer: 1500, + showConfirmButton: false + }); + if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount(); + fetchData(); + }).catch((err) => { + Swal.fire({ + icon: 'error', + title: 'Gagal', + text: err.message || 'Terjadi kesalahan.' + }); + }); + }); + } + + window.rejectPendingRole = function(type, id, fileName){ + const endpoint = type === 'mutu' + ? `/pending-file/${id}/reject-mutu` + : `/pending-file/${id}/reject-turt`; + const label = type === 'mutu' ? 'Komite Mutu' : 'TURT'; + + Swal.fire({ + title: `Reject ${label}?`, + text: fileName || 'Dokumen akan ditolak.', + icon: 'warning', + showCancelButton: true, + confirmButtonText: 'Reject', + cancelButtonText: 'Batal', + input: 'textarea', + inputLabel: 'Catatan', + inputPlaceholder: 'Tulis alasan atau catatan revisi...', + inputAttributes: { + 'aria-label': 'Catatan' + }, + preConfirm: (value) => { + const revision = (value || '').trim(); + if (!revision) { + Swal.showValidationMessage('Catatan revisi wajib diisi.'); + } + return revision; + } + }).then((result) => { + if (!result.isConfirmed) return; + fetch(endpoint, { + method: 'POST', + headers: { + 'X-CSRF-TOKEN': csrfToken, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ revision: result.value }) + }).then(async(res) => { + const data = await res.json(); + if (!res.ok || !data?.status) { + throw new Error(data?.message || 'Gagal reject.'); + } + Swal.fire({ + icon: 'success', + title: 'Berhasil', + text: data.message || 'Dokumen ditolak.', + timer: 1500, + showConfirmButton: false + }); + if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount(); fetchData(); }).catch((err) => { Swal.fire({ @@ -572,7 +849,7 @@ document.addEventListener('DOMContentLoaded', () => { timer: 1500, showConfirmButton: false }); - countData(); + if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount(); fetchData(); }).catch((err) => { Swal.fire({ diff --git a/public/js/pengajuanFile/index.js b/public/js/pengajuanFile/index.js index bb095c5..403ac15 100644 --- a/public/js/pengajuanFile/index.js +++ b/public/js/pengajuanFile/index.js @@ -13,6 +13,14 @@ 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 pendingPersetujuanNotice = document.getElementById('pendingPersetujuanNotice'); + const pendingPersetujuanNoticeCount = document.getElementById('pendingPersetujuanNoticeCount'); const formCreate = document.getElementById('formFile'); const modalCreate = document.getElementById('modalCreateFile'); let colCount = 1; @@ -20,6 +28,11 @@ 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(); + const isKomiteMutu = !!window.isKomiteMutu; + const isTurt = !!window.isTurt; + const isAtasan = !!window.isAtasan; + let editFromQueryId = new URLSearchParams(window.location.search).get('edit'); if (pageSizeSelect) { const initialSize = parseInt(pageSizeSelect.value); @@ -61,14 +74,175 @@ document.addEventListener('DOMContentLoaded', () => { }); } + function getSelectableIdsOnPage(){ + return (tableState.data || []).map((row) => String(row.file_directory_id)); + } + + function updateSelectAllState(){ + if (!selectAllCheckbox) return; + if (tableState.mode === 'history' || tableState.mode === 'persetujuan') { + 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' || tableState.mode === 'persetujuan'; + if (clearSelectionBtn) clearSelectionBtn.disabled = count === 0 || tableState.mode === 'history' || tableState.mode === 'persetujuan'; + updateSelectAllState(); + } + + function approvalBadge(status, label){ + const normalized = String(status || '').toLowerCase(); + if (normalized === 'approved') return `Approved ${label}`; + if (normalized.startsWith('rejected')) return `Rejected ${label}`; + if (normalized.startsWith('revised')) return `Revised ${label}`; + return `Pending ${label}`; + } + + function hasRevisionInfo(item){ + return !!(item?.revision || item?.mutu_revision || item?.turt_revision); + } + + function collectRevisionNotes(item){ + const notes = []; + const genericRevision = String(item?.revision || '').trim(); + const mutuRevision = String(item?.mutu_revision || '').trim(); + const turtRevision = String(item?.turt_revision || '').trim(); + + if (genericRevision) { + const lowerGeneric = genericRevision.toLowerCase(); + if (!lowerGeneric.startsWith('komite mutu:') && !lowerGeneric.startsWith('turt:')) { + notes.push({ label: 'Atasan/Bagian Terkait', text: genericRevision }); + } + } + if (mutuRevision) { + notes.push({ label: 'Komite Mutu', text: mutuRevision }); + } + if (turtRevision) { + notes.push({ label: 'TURT', text: turtRevision }); + } + + return notes; + } + + function escapeHtml(value){ + return String(value ?? '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + function buildRevisionNotesHtml(notes){ + if (!notes.length) { + return '
Tidak ada catatan revisi.
'; + } + + return ` +
+ ${notes.map(note => ` +
+
${escapeHtml(note.label)}
+
${escapeHtml(note.text)}
+
+ `).join('')} +
+ `; + } + + function isRejectedStatus(item){ + return String(item?.status_action || '').toLowerCase().startsWith('rejected') + || String(item?.status_mutu || '').toLowerCase().startsWith('rejected') + || String(item?.status_turt || '').toLowerCase().startsWith('rejected'); + } + + function isApprovedStatus(status){ + return String(status || '').toLowerCase() === 'approved'; + } + + function canProcessRoleStatus(status){ + const normalized = String(status || '').toLowerCase(); + return normalized === '' || normalized.startsWith('revised'); + } + + function getPengajuanStatusBadges(item){ + const actionStatus = String(item?.status_action || '').toLowerCase(); + const mutuStatus = String(item?.status_mutu || '').toLowerCase(); + const turtStatus = String(item?.status_turt || '').toLowerCase(); + + if (actionStatus.startsWith('revised')) { + return `Revised`; + } + + if (actionStatus.startsWith('rejected')) { + return `Rejected Atasan`; + } + + if (actionStatus !== 'approved') { + return `Pending Atasan`; + } + + const statuses = []; + if (item?.is_akre) { + if (mutuStatus.startsWith('rejected mutu')) { + statuses.push(`Rejected Mutu`); + } else if (mutuStatus === 'revised mutu') { + statuses.push(`Revisi Mutu`); + } else if(mutuStatus === 'approved'){ + statuses.push(`Approved Mutu`); + }else{ + statuses.push(`Pending Mutu`); + } + } + + if (item?.master_kategori_directory_id) { + if (turtStatus.startsWith('rejected turt')) { + statuses.push(`Rejected TURT`); + } else if (turtStatus === 'revised turt') { + statuses.push(`Revisi TURT`); + } else if(turtStatus === 'approved'){ + statuses.push(`Approved TURT`); + } else{ + statuses.push(`Pending TURT`); + } + } + + if (statuses.length > 0) { + return statuses.join(' '); + } + + return `Approved`; + } + function buildRow(item){ let tanggal = item.entry_at ? formatTanggal(item.entry_at) : '-'; let tanggalExp = item.tgl_expired ? formatTanggal(item.tgl_expired) : '-'; let tanggalTerbit = item.tanggal_terbit ? formatTanggal(item.tanggal_terbit) : '-'; - const isApproved = item?.status_action === 'approved'; - const isRejected = item?.status_action === 'rejected'; - const showEdit = !isApproved; - const showInfo = isRejected; + const isApproved = isApprovedStatus(item?.status_action); + const isRejected = isRejectedStatus(item); + const showEdit = isRejected; + const showInfo = isRejected && hasRevisionInfo(item); + const id = String(item.file_directory_id); + const checked = selectedIds.has(id); const aksi = `
`; return ` - + + + + ${aksi} ${item.no_dokumen || '-'} - - - +
${getPengajuanStatusBadges(item)}
${aksesBadge(item?.permission_file)}
{ `; } + function buildRowPersetujuan(item){ + const tanggal = item.entry_at ? formatTanggal(item.entry_at) : '-'; + const tanggalExp = item.tgl_expired ? formatTanggal(item.tgl_expired) : '-'; + const tanggalTerbit = item.tanggal_terbit ? formatTanggal(item.tanggal_terbit) : '-'; + const showMutu = isKomiteMutu && !!item.is_akre; + const showTurt = isTurt; + const actions = [ + `` + ]; + + if (showMutu && canProcessRoleStatus(item.status_mutu)) { + actions.push(``); + actions.push(``); + } + if (showTurt && canProcessRoleStatus(item.status_turt)) { + actions.push(``); + actions.push(``); + } + if (hasRevisionInfo(item)) { + actions.push(``); + } + + const statusParts = []; + if (item.is_akre) { + statusParts.push(approvalBadge(item.status_mutu, 'Mutu')); + } + if (item.master_kategori_directory_id) { + statusParts.push(approvalBadge(item.status_turt, 'TURT')); + } + + return ` + + +
${actions.join('')}
+ ${item.no_dokumen || '-'} +
${statusParts.join('')}
+ ${aksesBadge(item?.permission_file)} +
${item.nama_dokumen} + ${item.is_akre ? `Dokumen akreditasi` : `${item.name_kategori || '-'}${item.name_unit || '-'}`} + ${tanggalTerbit} + ${tanggalExp} + ${tanggal} + + `; + } + function buildHistoryRow(item){ const tanggal = item.entry_at ? formatTanggal(item.entry_at) : '-'; const tanggalExp = item.tgl_expired ? formatTanggal(item.tgl_expired) : '-'; @@ -149,8 +376,6 @@ document.addEventListener('DOMContentLoaded', () => { ${item.folder || '-'} ${item.part || '-'} ${item.action_type} - ${tanggalTerbit} - ${tanggalExp} ${tanggal} `; @@ -168,6 +393,41 @@ document.addEventListener('DOMContentLoaded', () => { return (tableState.data || []).find((row) => String(row.file_directory_id) === String(id)); } + function deleteDocuments(ids, successMessage){ + return fetch('/delete-file/bulk', { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': csrfToken + }, + body: JSON.stringify({ ids }) + }).then(async(res) => { + const data = await res.json(); + if (!res.ok || !data?.success) { + throw new Error(data?.message || 'Gagal menghapus dokumen.'); + } + + ids.forEach((id) => selectedIds.delete(String(id))); + if (window.idDirectory && ids.includes(String(window.idDirectory))) { + window.idDirectory = null; + window.currentFile = null; + if (deleteDataEl) deleteDataEl.classList.add('d-none'); + const modalEl = document.getElementById('previewModal'); + const modalInstance = modalEl ? bootstrap.Modal.getInstance(modalEl) : null; + modalInstance?.hide(); + } + + Swal.fire({ + icon: 'success', + title: 'Berhasil', + text: data.message || successMessage, + timer: 1500, + showConfirmButton: false + }); + fetchData(); + }); + } + function renderPagination(totalPages){ const activeState = tableState.mode === 'history' ? historyState : tableState; const paginationEl = tableState.mode === 'history' ? paginationHistory : paginationPengajuan; @@ -215,11 +475,14 @@ document.addEventListener('DOMContentLoaded', () => { function renderTable(){ const isHistoryMode = tableState.mode === 'history'; + const isPersetujuan = tableState.mode === 'persetujuan'; + const activeState = isHistoryMode ? historyState : tableState; + const pageData = activeState.data || []; const targetBody = isHistoryMode ? tbodyHistory : tbodyPengajuan; - const colSpan = isHistoryMode ? 9 : 10; - const rowBuilder = isHistoryMode ? buildHistoryRow : buildRow; + const colSpan = isHistoryMode ? 9 : 11; + const rowBuilder = isHistoryMode ? buildHistoryRow : (isPersetujuan ? buildRowPersetujuan : buildRow); if (pageData.length === 0) { targetBody.innerHTML = ` @@ -239,6 +502,20 @@ document.addEventListener('DOMContentLoaded', () => { } renderPagination(activeState.lastPage || 1); + updateSelectionUI(); + maybeOpenEditFromQuery(); + } + + function maybeOpenEditFromQuery(){ + if (!editFromQueryId || tableState.mode !== 'pengajuan') return; + const item = getItemById(editFromQueryId); + if (!item) return; + const cleanUrl = new URL(window.location.href); + cleanUrl.searchParams.delete('edit'); + window.history.replaceState({}, '', cleanUrl.toString()); + const pendingId = editFromQueryId; + editFromQueryId = null; + window.editFileReject(pendingId); } let searchDebounce; @@ -264,6 +541,7 @@ document.addEventListener('DOMContentLoaded', () => { document.getElementById('tableSearch').value = ''; startDateInput.value = ''; endDateInput.value = ''; + selectedIds.clear(); tableState.page = 1; historyState.page = 1; fetchData(); @@ -276,12 +554,51 @@ document.addEventListener('DOMContentLoaded', () => { }); } if (titleEl) { - titleEl.textContent = tableState.mode === 'history' ? 'Log History' : 'Data Pengajuan'; + titleEl.textContent = tableState.mode === 'history' + ? 'Log History' + : (tableState.mode === 'persetujuan' ? 'Data Persetujuan' : 'Data Pengajuan'); } + document.body.dataset.pengajuanMode = tableState.mode; + if (tabPengajuanEl && tabHistoryEl) { tabPengajuanEl.classList.toggle('d-none', tableState.mode === 'history'); tabHistoryEl.classList.toggle('d-none', tableState.mode !== 'history'); } + if (bulkActionsEl) { + bulkActionsEl.classList.toggle('d-none', tableState.mode === 'history' || tableState.mode === 'persetujuan'); + } + if (deleteDataEl && (tableState.mode === 'history' || tableState.mode === 'persetujuan')) { + deleteDataEl.classList.add('d-none'); + } + updateSelectionUI(); + } + + function updatePendingPersetujuanIndicators(total){ + const count = Number(total || 0); + console.log(pendingPersetujuanNoticeCount); + + if (pendingPersetujuanNotice && pendingPersetujuanNoticeCount) { + pendingPersetujuanNoticeCount.textContent = String(count); + pendingPersetujuanNotice.classList.toggle('d-none', count <= 0); + pendingPersetujuanNotice.classList.toggle('d-flex', count > 0); + } + } + + function fetchPendingPersetujuanCount(){ + if (!(isKomiteMutu || isTurt || isAtasan)) return; + + fetch('/data/count-pending', { + headers: { + 'Accept': 'application/json' + } + }) + .then(res => res.json()) + .then(data => { + updatePendingPersetujuanIndicators(data?.count || 0); + }) + .catch(() => { + updatePendingPersetujuanIndicators(0); + }); } if (tabsEl) { @@ -297,6 +614,63 @@ document.addEventListener('DOMContentLoaded', () => { }); } + if (tbodyPengajuan) { + tbodyPengajuan.addEventListener('change', (e) => { + const target = e.target; + if (!target.classList.contains('row-select')) return; + const id = target.getAttribute('data-id'); + if (!id) return; + if (target.checked) selectedIds.add(id); + else selectedIds.delete(id); + const row = target.closest('tr'); + if (row) row.classList.toggle('table-active', target.checked); + updateSelectionUI(); + }); + } + + if (selectAllCheckbox) { + selectAllCheckbox.addEventListener('change', () => { + const pageIds = getSelectableIdsOnPage(); + if (selectAllCheckbox.checked) { + pageIds.forEach((id) => selectedIds.add(id)); + } else { + pageIds.forEach((id) => selectedIds.delete(id)); + } + renderTable(); + }); + } + + if (clearSelectionBtn) { + clearSelectionBtn.addEventListener('click', () => { + selectedIds.clear(); + renderTable(); + }); + } + + if (bulkDeleteBtn) { + bulkDeleteBtn.addEventListener('click', () => { + if (selectedIds.size === 0) return; + const ids = Array.from(selectedIds); + Swal.fire({ + title: 'Hapus dokumen terpilih?', + text: `Total ${ids.length} dokumen akan dihapus.`, + icon: 'warning', + showCancelButton: true, + confirmButtonText: 'Hapus', + cancelButtonText: 'Batal' + }).then((result) => { + if (!result.isConfirmed) return; + deleteDocuments(ids, 'Dokumen berhasil dihapus.').catch((err) => { + Swal.fire({ + icon: 'error', + title: 'Gagal', + text: err.message || 'Terjadi kesalahan.' + }); + }); + }); + }); + } + function fetchData(){ if (summaryEl) summaryEl.textContent = 'Memuat data...'; const activeState = tableState.mode === 'history' ? historyState : tableState; @@ -305,7 +679,8 @@ document.addEventListener('DOMContentLoaded', () => { per_page: activeState.pageSize, keyword: activeState.search || '', start_date: activeState.startDate || '', - end_date: activeState.endDate || '' + end_date: activeState.endDate || '', + mode: tableState.mode }); const endpoint = tableState.mode === 'history' ? `/data/log-dokumen?${params.toString()}` @@ -317,6 +692,7 @@ document.addEventListener('DOMContentLoaded', () => { activeState.lastPage = data?.pagination?.last_page || 1; activeState.total = data?.pagination?.total || 0; renderTable(); + fetchPendingPersetujuanCount(); }) .catch(err => { console.error(err); @@ -326,14 +702,119 @@ document.addEventListener('DOMContentLoaded', () => { window.infoReject = function(id){ const item = getItemById(id); - const revision = item?.revision ? String(item.revision) : 'Tidak ada catatan revisi.'; Swal.fire({ title: 'Catatan Revisi', - text: revision, + html: buildRevisionNotesHtml(collectRevisionNotes(item)), icon: 'info', confirmButtonText: 'Tutup' }); } + + window.approvePersetujuan = function(type, id, fileName){ + const endpoint = type === 'mutu' + ? `/pengajuan-file/${id}/approve-mutu` + : `/pengajuan-file/${id}/approve-turt`; + const label = type === 'mutu' ? 'Komite Mutu' : 'TURT'; + + Swal.fire({ + title: `Approve ${label}?`, + text: fileName || 'Dokumen akan disetujui.', + icon: 'question', + showCancelButton: true, + confirmButtonText: 'Approve', + cancelButtonText: 'Batal', + }).then((result) => { + if (!result.isConfirmed) return; + fetch(endpoint, { + method: 'POST', + headers: { + 'X-CSRF-TOKEN': csrfToken, + 'Content-Type': 'application/json' + }, + }).then(async(res) => { + const data = await res.json(); + if (!res.ok || !data?.status) { + throw new Error(data?.message || 'Gagal approve.'); + } + Swal.fire({ + icon: 'success', + title: 'Berhasil', + text: data.message || 'Dokumen disetujui.', + timer: 1500, + showConfirmButton: false + }); + if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount(); + fetchData(); + }).catch((err) => { + Swal.fire({ + icon: 'error', + title: 'Gagal', + text: err.message || 'Terjadi kesalahan.' + }); + }); + }); + } + + window.rejectPersetujuan = function(type, id, fileName){ + const endpoint = type === 'mutu' + ? `/pengajuan-file/${id}/reject-mutu` + : `/pengajuan-file/${id}/reject-turt`; + const label = type === 'mutu' ? 'Komite Mutu' : 'TURT'; + + Swal.fire({ + title: `Reject ${label}?`, + text: fileName || 'Dokumen akan ditolak.', + icon: 'warning', + showCancelButton: true, + confirmButtonText: 'Reject', + cancelButtonText: 'Batal', + input: 'textarea', + inputLabel: 'Catatan', + inputPlaceholder: 'Tulis alasan atau catatan revisi...', + inputAttributes: { + 'aria-label': 'Catatan' + }, + preConfirm: (value) => { + const revision = (value || '').trim(); + if (!revision) { + Swal.showValidationMessage('Catatan revisi wajib diisi.'); + } + return revision; + } + }).then((result) => { + if (!result.isConfirmed) return; + fetch(endpoint, { + method: 'POST', + headers: { + 'X-CSRF-TOKEN': csrfToken, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + revision: result.value + }) + }).then(async(res) => { + const data = await res.json(); + if (!res.ok || !data?.status) { + throw new Error(data?.message || 'Gagal reject.'); + } + Swal.fire({ + icon: 'success', + title: 'Berhasil', + text: data.message || 'Dokumen ditolak.', + timer: 1500, + showConfirmButton: false + }); + if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount(); + fetchData(); + }).catch((err) => { + Swal.fire({ + icon: 'error', + title: 'Gagal', + text: err.message || 'Terjadi kesalahan.' + }); + }); + }); + } window.infoDok = function(e){ let fileUrl = $(e).data('file'); let noDokumen = $(e).data('no_dokumen') @@ -360,6 +841,9 @@ document.addEventListener('DOMContentLoaded', () => { permEl.textContent = publicDoc ? 'Umum' : 'Internal Unit'; permEl.className = 'badge ' + (publicDoc ? 'bg-success' : 'bg-secondary'); } + if (deleteDataEl) { + deleteDataEl.classList.remove('d-none'); + } let previewBox = document.getElementById('file-preview'); previewBox.innerHTML = `
`; @@ -404,6 +888,105 @@ document.addEventListener('DOMContentLoaded', () => { if (window.$ && $.fn.select2) $(selectEl).trigger('change'); } + function formatDateForInput(date) { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${year}-${month}-${day}`; + } + + function formatDateDisplay(dateValue) { + if (!dateValue) return ''; + const date = new Date(`${dateValue}T00:00:00`); + if (Number.isNaN(date.getTime())) return ''; + + const day = String(date.getDate()).padStart(2, '0'); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const year = date.getFullYear(); + return `${day}/${month}/${year}`; + } + + function addYearsToDate(dateValue, years) { + if (!dateValue || !years) return ''; + const baseDate = new Date(`${dateValue}T00:00:00`); + if (Number.isNaN(baseDate.getTime())) return ''; + + const originalDay = baseDate.getDate(); + baseDate.setFullYear(baseDate.getFullYear() + Number(years)); + if (baseDate.getDate() !== originalDay) { + baseDate.setDate(0); + } + + return formatDateForInput(baseDate); + } + + function syncMasaBerlakuField(selectEl) { + if (!selectEl) return; + const fieldWrap = document.getElementById(selectEl.dataset.dateTarget); + const input = document.getElementById(selectEl.dataset.inputTarget); + const preview = document.getElementById(selectEl.dataset.previewTarget); + const baseInput = document.getElementById(selectEl.dataset.baseTarget); + if (!fieldWrap || !input) return; + + if (selectEl.value === 'custom') { + fieldWrap.classList.remove('d-none'); + input.disabled = false; + if (preview) preview.textContent = ''; + return; + } + + fieldWrap.classList.add('d-none'); + input.disabled = true; + input.value = ''; + + if (!preview) return; + if (!selectEl.value) { + preview.textContent = ''; + return; + } + + if (!baseInput || !baseInput.value) { + preview.textContent = 'Pilih Tanggal Terbit untuk melihat hasil masa berlaku.'; + return; + } + + const expiredDate = addYearsToDate(baseInput.value, selectEl.value); + preview.textContent = expiredDate ? `Tanggal kedaluwarsa: ${formatDateDisplay(expiredDate)}` : ''; + } + + function prepareExpiryInputs(formEl) { + const selects = formEl.querySelectorAll('.masa-berlaku-select'); + for (const selectEl of selects) { + const option = selectEl.value; + const dateInput = document.getElementById(selectEl.dataset.inputTarget); + const baseInput = document.getElementById(selectEl.dataset.baseTarget); + if (!dateInput) continue; + + if (!option) { + dateInput.value = ''; + dateInput.disabled = true; + continue; + } + + if (option === 'custom') { + if (!dateInput.value) { + return 'Tanggal kedaluwarsa wajib diisi saat memilih opsi Lainnya.'; + } + dateInput.disabled = false; + continue; + } + + if (!baseInput || !baseInput.value) { + return 'Tanggal Terbit wajib diisi saat memilih masa berlaku 1, 2, atau 3 tahun.'; + } + + dateInput.value = addYearsToDate(baseInput.value, option); + dateInput.disabled = false; + } + + return null; + } + function arrayFilterEmpty(arr){ return (arr || []).filter((val) => val !== null && val !== undefined && String(val).trim() !== ''); } @@ -563,9 +1146,11 @@ document.addEventListener('DOMContentLoaded', () => { setChecked(byId('edit_perm_yes'), isPublic); setChecked(byId('edit_perm_no'), !isPublic); - const hasExpired = !!item.tgl_expired; - setChecked(byId('edit_has_expired'), hasExpired); - syncEditExpiredField(); + const editMasaBerlaku = byId('edit_masa_berlaku_option'); + if (editMasaBerlaku) { + editMasaBerlaku.value = item.tgl_expired ? 'custom' : ''; + syncMasaBerlakuField(editMasaBerlaku); + } const displayName = item.fileName || (item.file ? String(item.file).split('/').pop() : ''); setTextValue(byId('edit_current_file'), displayName ? `File saat ini: ${displayName}` : ''); @@ -592,19 +1177,18 @@ document.addEventListener('DOMContentLoaded', () => { $("#modalEditPengajuanFile").modal('show'); } - function syncEditExpiredField() { - const editHasExpired = document.getElementById('edit_has_expired'); - const editExpiredInput = document.getElementById('edit_tgl_expired'); - if (!editHasExpired || !editExpiredInput) return; - editExpiredInput.disabled = !editHasExpired.checked; - if (!editHasExpired.checked) { - editExpiredInput.value = ''; - } + const editMasaBerlaku = document.getElementById('edit_masa_berlaku_option'); + if (editMasaBerlaku) { + editMasaBerlaku.addEventListener('change', function() { + syncMasaBerlakuField(editMasaBerlaku); + }); } - const editHasExpired = document.getElementById('edit_has_expired'); - if (editHasExpired) { - editHasExpired.addEventListener('change', syncEditExpiredField); + const editTanggalTerbit = document.getElementById('edit_tanggal_terbit'); + if (editTanggalTerbit) { + editTanggalTerbit.addEventListener('change', function() { + if (editMasaBerlaku) syncMasaBerlakuField(editMasaBerlaku); + }); } if (editForm) { @@ -615,6 +1199,11 @@ document.addEventListener('DOMContentLoaded', () => { Swal.fire({ icon: 'error', title: 'Gagal', text: 'ID dokumen tidak ditemukan.' }); return; } + const expiryError = prepareExpiryInputs(editForm); + if (expiryError) { + Swal.fire({ icon: 'warning', title: 'Validasi form', text: expiryError }); + return; + } const formData = new FormData(editForm); fetch(`/pengajuan-file/${id}/update`, { @@ -636,6 +1225,7 @@ document.addEventListener('DOMContentLoaded', () => { showConfirmButton: false }); $("#modalEditPengajuanFile").modal('hide'); + if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount(); fetchData(); }).catch((err) => { Swal.fire({ @@ -654,12 +1244,15 @@ document.addEventListener('DOMContentLoaded', () => { // ===== Upload baru (seperti dataUnit) ===== 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; + if(!e.target.classList.contains('masa-berlaku-select')) return; + syncMasaBerlakuField(e.target); + }); + + document.addEventListener('change', function(e){ + if (!e.target.matches('input[type="date"][id^="dateActive_"]')) return; + const index = e.target.id.replace('dateActive_', ''); + const selectEl = document.querySelector(`.masa-berlaku-select[data-index="${index}"]`); + if (selectEl) syncMasaBerlakuField(selectEl); }); function resetCreateForm(){ @@ -672,6 +1265,7 @@ document.addEventListener('DOMContentLoaded', () => { $(formCreate).find('input[type="file"]').val(''); $(formCreate).find('.file-name').addClass('d-none').text(''); } + document.querySelectorAll('.masa-berlaku-select').forEach(syncMasaBerlakuField); resetAkreFields(0); enableAkreFields(0); } @@ -901,22 +1495,33 @@ document.addEventListener('DOMContentLoaded', () => { + name="data[${colCount}][date_active]" + id="dateActive_${colCount}">
-
-
- - -
+
+ + +
Opsi 1, 2, dan 3 tahun dihitung dari Tanggal Terbit.
+
-
+
@@ -997,6 +1602,8 @@ document.addEventListener('DOMContentLoaded', () => { initKategoriSelect2(colCount); enableAkreFields(colCount); setKategoriRequired(colCount, false); + const insertedSelect = document.querySelector(`.masa-berlaku-select[data-index="${colCount}"]`); + if (insertedSelect) syncMasaBerlakuField(insertedSelect); colCount++; } @@ -1010,12 +1617,22 @@ document.addEventListener('DOMContentLoaded', () => { if (select0.length) selectOptionUnitKerjaV1(0); initKategoriSelect2(0); enableAkreFields(0); + document.querySelectorAll('.masa-berlaku-select').forEach(syncMasaBerlakuField); formCreate.addEventListener('submit', (e) => { e.preventDefault(); const submitBtn = formCreate.querySelector('button[type="submit"]'); if (submitBtn) submitBtn.disabled = true; if (submitBtn) submitBtn.textContent = 'menyimpan...'; + + const expiryError = prepareExpiryInputs(formCreate); + if (expiryError) { + Swal.fire({ icon: 'warning', title: 'Validasi form', text: expiryError }); + if (submitBtn) submitBtn.disabled = false; + if (submitBtn) submitBtn.textContent = 'Simpan'; + return; + } + const formData = new FormData(formCreate); fetch(`/uploadv2`, { @@ -1037,6 +1654,7 @@ document.addEventListener('DOMContentLoaded', () => { const modalInstance = bootstrap.Modal.getInstance(modalCreate); modalInstance?.hide(); resetCreateForm(); + if (typeof window.refreshPendingCount === 'function') window.refreshPendingCount(); fetchData(); if(responseData.status_action === null || responseData.status_action === undefined){ Swal.fire({ @@ -1076,6 +1694,7 @@ document.addEventListener('DOMContentLoaded', () => { return; } }); + }); document.addEventListener('click', function(e){ if(e.target.matches('.file-link')){ @@ -1105,6 +1724,10 @@ document.addEventListener('click', function(e){ const publicDoc = isPublic(permissionFile); permEl.textContent = publicDoc ? 'Umum' : 'Internal Unit'; permEl.className = 'badge ' + (publicDoc ? 'bg-success' : 'bg-secondary'); + } + const deleteDataEl = document.getElementById('deleteData'); + if (deleteDataEl) { + deleteDataEl.classList.toggle('d-none', document.body.dataset.pengajuanMode !== 'pengajuan'); } let previewBox = document.getElementById('file-preview'); previewBox.innerHTML = `
@@ -1119,6 +1742,56 @@ document.addEventListener('click', function(e){ if(e.target.matches('#btn-view-full')){ window.open(`/full-preview/${idDirectory}`, '_blank'); } + + if(e.target.matches('#delete-file')){ + if(!idDirectory){ + Swal.fire({ + icon: 'warning', + title: 'Pilih dokumen', + text: 'Dokumen belum dipilih.' + }); + return; + } + + Swal.fire({ + title: 'Hapus dokumen ini?', + text: 'Dokumen yang dihapus tidak akan tampil lagi.', + icon: 'warning', + showCancelButton: true, + confirmButtonText: 'Hapus', + cancelButtonText: 'Batal' + }).then((result) => { + if (!result.isConfirmed) return; + fetch('/delete-file/bulk', { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '' + }, + body: JSON.stringify({ ids: [String(idDirectory)] }) + }).then(async(res) => { + const data = await res.json(); + if (!res.ok || !data?.success) { + throw new Error(data?.message || 'Gagal menghapus dokumen.'); + } + Swal.fire({ + icon: 'success', + title: 'Berhasil', + text: data.message || 'Dokumen berhasil dihapus.', + timer: 1500, + showConfirmButton: false + }).then(() => { + window.location.reload(); + }); + }).catch((err) => { + Swal.fire({ + icon: 'error', + title: 'Gagal', + text: err.message || 'Terjadi kesalahan.' + }); + }); + }); + } }) function isPublic(permissionVal){ if(permissionVal === null || permissionVal === undefined) return false; diff --git a/resources/views/dataAkreditasi/index.blade.php b/resources/views/dataAkreditasi/index.blade.php index cbe0a44..a17468e 100644 --- a/resources/views/dataAkreditasi/index.blade.php +++ b/resources/views/dataAkreditasi/index.blade.php @@ -56,6 +56,49 @@ line-height: 1.2; border-radius: 999px; } + + #lastUpdatedTable { + table-layout: fixed; + } + + #lastUpdatedTable td, + #lastUpdatedTable th { + vertical-align: middle; + } + + .file-row-content { + display: flex; + align-items: flex-start; + gap: 8px; + min-width: 0; + width: 100%; + } + + .file-text-wrap { + min-width: 0; + flex: 1 1 auto; + } + + .file-name-trigger { + display: block; + width: 100%; + overflow: hidden; + line-height: 1.3; + white-space: normal; + word-break: break-word; + overflow-wrap: anywhere; + } + + .file-name-trigger strong { + display: block; + white-space: normal; + word-break: break-word; + overflow-wrap: anywhere; + } + + .file-meta-path { + display: none; + } @section('body_main')
@@ -167,6 +210,7 @@
@include('dataUnit.modal.create') + @include('dataUnit.modal.view') @if(Auth::guard('admin')->check()) -
- - -
-
-
- - -
-
+
+ + +
+
+ + +
Opsi 1, 2, dan 3 tahun dihitung dari Tanggal Terbit.
+
+
-
- - -
+
+ + +
@@ -1228,7 +1390,7 @@ processResults: function(data){ return { results : data?.data.map(item => ({ - id: item.id+'/'+item.name, + id: item.id, text: item.name, sub_units: item.sub_unit_kerja // kirim ke front })) @@ -1378,6 +1540,18 @@ e.preventDefault(); const submitBtn = $(this).find('button[type="submit"]'); submitBtn.prop('disabled', true).text('menyimpan...') + + const expiryError = prepareExpiryInputs(this); + if (expiryError) { + Swal.fire({ + icon: 'warning', + title: 'Validasi form', + text: expiryError + }); + submitBtn.prop('disabled', false).text('Simpan'); + return; + } + const formData = new FormData(this); console.log(formData); @@ -1482,5 +1656,67 @@ } $("#previewModal").modal('show') } + + function rekomendasiDelete(button){ + const id = $(button).data('id'); + const fileName = $(button).data('filename') || 'dokumen'; + const noDokumen = $(button).data('no_dokumen') || '-'; + + Swal.fire({ + title: 'Rekomendasi hapus dokumen?', + text: `${fileName} (${noDokumen})`, + icon: 'warning', + input: 'textarea', + inputLabel: 'Catatan rekomendasi', + inputPlaceholder: 'Tulis alasan kenapa dokumen ini direkomendasikan untuk dihapus...', + inputAttributes: { + 'aria-label': 'Catatan rekomendasi' + }, + showCancelButton: true, + confirmButtonText: 'Kirim rekomendasi', + cancelButtonText: 'Batal', + preConfirm: (value) => { + const note = (value || '').trim(); + if (!note) { + Swal.showValidationMessage('Catatan rekomendasi wajib diisi.'); + } + return note; + } + }).then((result) => { + if (!result.isConfirmed) return; + + fetch(`/recommend-delete-file/${id}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content + }, + body: JSON.stringify({ + note: result.value + }) + }) + .then(res => res.json()) + .then(data => { + if (data.status) { + Swal.fire({ + icon: 'success', + title: 'Berhasil', + text: data.message || 'Rekomendasi hapus berhasil dikirim.', + timer: 1800, + showConfirmButton: false + }); + } else { + throw new Error(data.message || 'Gagal mengirim rekomendasi hapus.'); + } + }) + .catch(err => { + Swal.fire({ + icon: 'error', + title: 'Gagal', + text: err.message || 'Terjadi kesalahan.' + }); + }); + }); + } @endsection diff --git a/resources/views/dataUnit/modal/create.blade.php b/resources/views/dataUnit/modal/create.blade.php index e0c3524..d259817 100644 --- a/resources/views/dataUnit/modal/create.blade.php +++ b/resources/views/dataUnit/modal/create.blade.php @@ -46,17 +46,29 @@
- +
-
- - -
+ + +
Opsi 1, 2, dan 3 tahun dihitung dari Tanggal Terbit.
+
-
+
- +
diff --git a/resources/views/layout/partials/sidenav.blade.php b/resources/views/layout/partials/sidenav.blade.php index 0aaeebb..018f4dc 100644 --- a/resources/views/layout/partials/sidenav.blade.php +++ b/resources/views/layout/partials/sidenav.blade.php @@ -52,7 +52,19 @@ {{-- AKTIVITAS --}} @php - $isAtasan = \App\Models\MappingUnitKerjaPegawai::where('statusenabled', true)->where('objectatasanlangsungfk', auth()->user()->objectpegawaifk)->exists(); + $pegawaiId = auth()->user()->objectpegawaifk; + $userUnitIds = auth()->user()->dataUser->mappingUnitKerjaPegawai() + ->where('statusenabled', true) + ->pluck('objectunitkerjapegawaifk') + ->unique() + ->values() + ->all(); + $isAtasan = \App\Models\MappingUnitKerjaPegawai::where('statusenabled', true) + ->where(function ($q) use ($pegawaiId) { + $q->where('objectatasanlangsungfk', $pegawaiId) + ->orWhere('objectpejabatpenilaifk', $pegawaiId); + })->exists(); + $isRoleApprover = in_array(51, $userUnitIds, true) || in_array(22, $userUnitIds, true); @endphp @if($isAtasan) @if(!Auth::guard('admin')->check()) @@ -79,6 +91,10 @@ Pengajuan
+ + @if($isRoleApprover) + 0 + @endif @endif @@ -114,7 +130,7 @@ @if(!Auth::guard('admin')->check()) - @if(auth()->user()->dataUser->mappingUnitKerjaPegawai()->where('objectunitkerjapegawaifk', 43)->exists()) + @if(auth()->user()->dataUser->mappingUnitKerjaPegawai()->whereIn('objectunitkerjapegawaifk', [43, 22])->exists()) + @if($isKomiteMutu || $isTurt || $isAtasan) + + @endif + @if(($isKomiteMutu || $isTurt) && !$isAtasan) + + @endif
@@ -78,6 +94,14 @@
+
+ + +
@@ -88,6 +112,9 @@ + @@ -108,7 +135,7 @@
-
+
+ + Aksi No Dokumen Status
@@ -118,9 +145,7 @@ - - - + @@ -140,6 +165,9 @@ @include('dataUnit.modal.create') @endsection diff --git a/resources/views/pengajuanFile/modal/edit.blade.php b/resources/views/pengajuanFile/modal/edit.blade.php index aa83807..948e631 100644 --- a/resources/views/pengajuanFile/modal/edit.blade.php +++ b/resources/views/pengajuanFile/modal/edit.blade.php @@ -47,12 +47,25 @@
-
- - -
+ + +
Opsi 1, 2, dan 3 tahun dihitung dari Tanggal Terbit.
+
-
+
diff --git a/routes/web.php b/routes/web.php index 1fedf25..eb79ea2 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,7 +1,7 @@ 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']); @@ -69,6 +71,10 @@ Route::middleware(['auth:admin,web'])->group(function(){ Route::get('/pengajuan-file', [DashboardController::class, 'pengajuanFile']); Route::get('/datatable/pengajuan-file', [DashboardController::class, 'dataPengajuanFile']); Route::post('/pengajuan-file/{id}/update', [DashboardController::class, 'updatePengajuanFile']); + Route::post('/pengajuan-file/{id}/approve-mutu', [DashboardController::class, 'approvePendingFileMutu']); + Route::post('/pengajuan-file/{id}/approve-turt', [DashboardController::class, 'approvePendingFileTurt']); + Route::post('/pengajuan-file/{id}/reject-mutu', [DashboardController::class, 'rejectPendingFileMutu']); + Route::post('/pengajuan-file/{id}/reject-turt', [DashboardController::class, 'rejectPendingFileTurt']); // Route::middleware(['master.persetujuan'])->group(function () { Route::get('/pending-file', [DashboardController::class, 'pendingFile']); @@ -76,6 +82,10 @@ Route::middleware(['auth:admin,web'])->group(function(){ Route::post('/pending-file/{id}/approve', [DashboardController::class, 'approvePendingFile']); Route::post('/pending-file/approve-multiple', [DashboardController::class, 'approvePendingFileMultiple']); Route::post('/pending-file/{id}/reject', [DashboardController::class, 'rejectPendingFile']); + Route::post('/pending-file/{id}/approve-mutu', [DashboardController::class, 'approvePendingFileMutu']); + Route::post('/pending-file/{id}/approve-turt', [DashboardController::class, 'approvePendingFileTurt']); + Route::post('/pending-file/{id}/reject-mutu', [DashboardController::class, 'rejectPendingFileMutu']); + Route::post('/pending-file/{id}/reject-turt', [DashboardController::class, 'rejectPendingFileTurt']); Route::get('/data/count-pending', [DashboardController::class, 'countDataPending']); Route::get('/data/count-rejected', [DashboardController::class, 'countRejectedPengajuan']); // });
Kategori Unit AksiTanggal TerbitTanggal Kedaluwarsa DokumenTanggal UploadTanggal Aksi