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())
@@ -214,6 +258,7 @@
let currentData = [];
let selectedIds = [];
let expandedFolders = new Set();
+ let currentPreviewId = null;
let searchTimer;
const isAdminUser = @json(Auth::guard('admin')->check());
const btn = document.getElementById('btnDownloadMultiple');
@@ -349,6 +394,16 @@
|
-
- ${item.nama_dokumen}
- Path: ${item.file}
+
+
+
+
+ Path: ${item.file}
+
|
${typeDok} |
@@ -495,18 +561,40 @@
value="${item.file_directory_id}" ${isChecked} onchange="handleRowCheck(this)">
-
-
-
+
|
${lines}
-
-
- ${item.nama_dokumen}
- ${fileName}
+
+
+
+
+
+
|
@@ -651,6 +739,18 @@
}
document.addEventListener('click', function(e){
+ const previewTrigger = e.target.closest('.btn-preview-file');
+ if (previewTrigger) {
+ openDocumentPreview({
+ id: previewTrigger.getAttribute('data-id'),
+ name: previewTrigger.getAttribute('data-name'),
+ noDokumen: previewTrigger.getAttribute('data-no'),
+ tanggalTerbit: previewTrigger.getAttribute('data-date'),
+ permissionFile: previewTrigger.getAttribute('data-permission')
+ });
+ return;
+ }
+
const toggle = e.target.closest('.folder-toggle');
if (!toggle) return;
const folderPath = toggle.getAttribute('data-folder');
@@ -671,11 +771,84 @@
return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} `
+ `${pad(d.getHours())}:${pad(d.getMinutes())}`;
}
+
+ function formatPreviewDate(value) {
+ if (!value) return '-';
+ const d = new Date(value);
+ if (isNaN(d)) return value;
+ const pad = n => String(n).padStart(2, '0');
+ return `${pad(d.getDate())}/${pad(d.getMonth()+1)}/${d.getFullYear()}`;
+ }
+
+ function isPublic(permissionVal){
+ return permissionVal === true || permissionVal === 'true' || permissionVal === 1 || permissionVal === '1';
+ }
+
+ function escapeHtml(value) {
+ return String(value ?? '')
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ }
+
+ function escapeAttribute(value) {
+ return escapeHtml(value);
+ }
+
+ function openDocumentPreview({ id, name, noDokumen, tanggalTerbit, permissionFile }) {
+ currentPreviewId = id;
+
+ const titleEl = document.getElementById('confirm_preview_file');
+ if (titleEl) titleEl.textContent = name || '-';
+
+ const noEl = document.getElementById('confirm-upload-dokumen');
+ if (noEl) noEl.textContent = noDokumen || '-';
+
+ const tglEl = document.getElementById('confirm-time-dokumen');
+ if (tglEl) tglEl.textContent = tanggalTerbit || '-';
+
+ const permEl = document.getElementById('confirm-permission');
+ if (permEl) {
+ const publicDoc = isPublic(permissionFile);
+ permEl.textContent = publicDoc ? 'Umum' : 'Internal Unit';
+ permEl.className = 'badge ' + (publicDoc ? 'bg-success' : 'bg-secondary');
+ }
+
+ const downloadBtn = document.getElementById('btn-download');
+ if (downloadBtn) {
+ downloadBtn.setAttribute('href', `/file-download/${id}`);
+ }
+
+ const deleteWrapper = document.getElementById('deleteData');
+ if (deleteWrapper) {
+ deleteWrapper.classList.add('d-none');
+ }
+
+ const previewBox = document.getElementById('file-preview');
+ if (previewBox) {
+ previewBox.innerHTML = ` `;
+ }
+
+ openPreview(id);
+ $("#previewModal").modal('show');
+ }
+
const katDok = @json($katDok);
const formCreate = document.getElementById('formFile');
const modalCreate = document.getElementById('modalCreateFile');
let colCount = 1;
+ document.addEventListener('click', function(e){
+ if(e.target.closest('#btn-view-full')){
+ if (!currentPreviewId) return;
+ window.open(`/full-preview/${currentPreviewId}`, '_blank');
+ }
+ });
+
document.addEventListener('change', function(e){
if(!e.target.classList.contains('toggle-expired')) return;
const targetId = e.target.getAttribute('data-target');
diff --git a/resources/views/dataUmum/index.blade.php b/resources/views/dataUmum/index.blade.php
index cffe564..2e2d397 100644
--- a/resources/views/dataUmum/index.blade.php
+++ b/resources/views/dataUmum/index.blade.php
@@ -200,8 +200,8 @@
Tampilkan
@@ -226,7 +226,8 @@
@endsection
diff --git a/resources/views/dataUnit/index.blade.php b/resources/views/dataUnit/index.blade.php
index 863afbc..864e97e 100644
--- a/resources/views/dataUnit/index.blade.php
+++ b/resources/views/dataUnit/index.blade.php
@@ -198,8 +198,8 @@
Tampilkan
@@ -262,13 +262,117 @@
return [value];
}
+ 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;
+ }
+
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');
- 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);
});
if(pageSizeSelect){
@@ -308,7 +412,7 @@
cache: true
}
});
-
+
}
if (kategoriSelect) {
$('#tableKategori').select2({
@@ -349,6 +453,7 @@
formCreate.find('select').val(null).trigger('change');
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);
}
@@ -544,6 +649,31 @@
return null;
}
+ function escapeHtml(value){
+ return String(value ?? '')
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ }
+
+ function renderDeleteRecommendation(item){
+ const recommendation = item?.delete_recommendation;
+
+ if (!recommendation?.visible || !recommendation?.note) return '';
+
+ const actorRole = recommendation.actor_role || 'TURT';
+ const actorName = recommendation.actor_name ? ` (${escapeHtml(recommendation.actor_name)})` : '';
+ const note = escapeHtml(recommendation.note);
+
+ return `
+
+ Dokumen ini direkomendasikan dihapus oleh ${escapeHtml(actorRole)}${actorName}. Catatan: ${note}
+
+ `;
+ }
+
function buildRow(item){
const parts = (item.file || '').split('/');
const fileName = parts.pop() || '-';
@@ -607,6 +737,23 @@
>
+ ${authUnitKerja?.id === 22 ? `
+
+ ` : ''}
+
${item.no_dokumen || '-'} |
@@ -626,6 +773,7 @@
${akreBadge}${expiryBadge}
+ ${renderDeleteRecommendation(item)}
${item.nama_kategori || '-'}
@@ -1011,6 +1159,7 @@
selectOptionUnitKerjaV1(0);
initKategoriSelect2(0);
enableAkreFields(0);
+ document.querySelectorAll('.masa-berlaku-select').forEach(syncMasaBerlakuField);
});
function loadSubUnitKerja(unitId){
@@ -1020,6 +1169,8 @@
url: `/select-sub-unit-kerja-mapping/${unitId}`,
method: 'GET',
success: function(response) {
+ console.log(response);
+
if (response?.data) {
response.data.forEach(unit => {
let selected = (authSubUnitKerja && unit.id === authSubUnitKerja.objectsubunitkerjapegawaifk);
@@ -1089,28 +1240,39 @@
placeholder="Contoh: Panduan Mencuci Tangan" required>
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ 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 @@
+
+ @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())
| |