fixing bug di approval bertahap dokumen

This commit is contained in:
JokoPrasetio 2026-06-22 16:00:56 +07:00
parent f8c3816ca7
commit ca4dbf0486
8 changed files with 310 additions and 167 deletions

View File

@ -12,6 +12,7 @@ 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;
@ -88,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();
@ -131,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;
@ -287,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];
@ -1219,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)) {
@ -1398,18 +1435,18 @@ class DashboardController extends Controller
];
$fd = FileDirectory::create($payload);
$disk = Storage::disk('s3');
// $disk = Storage::disk('s3');
$imageName = $this->buildStoredFileName(
$fd->nama_dokumen ?? null,
$fd->file_directory_id,
$uploadedFile->getClientOriginalExtension()
);
$disk->putFileAs(
$path,
$uploadedFile,
$imageName,
['visibility' => 'private']
);
// $disk->putFileAs(
// $path,
// $uploadedFile,
// $imageName,
// ['visibility' => 'private']
// );
$fd->update([
'file' => $path . '/' . $imageName
]);
@ -1933,14 +1970,14 @@ class DashboardController extends Controller
$needTurt->whereNotNull('master_kategori_directory_id')
->where(function ($pendingTurt) {
$pendingTurt->whereNull('status_turt')
->orWhere('status_turt', 'rejected');
->orWhereIn('status_turt', ['rejected Turt', 'revised Turt']);
});
})
->orWhere(function ($needMutu) {
$needMutu->where('is_akre', true)
->where(function ($pendingMutu) {
$pendingMutu->whereNull('status_mutu')
->orWhere('status_mutu', 'rejected');
->orWhereIn('status_mutu', ['rejected Mutu', 'revised Mutu']);
});
});
});
@ -1959,7 +1996,7 @@ class DashboardController extends Controller
$sub->whereNotNull('master_kategori_directory_id')
->where(function ($pendingTurt) {
$pendingTurt->whereNull('status_turt')
->orWhere('status_turt', 'rejected');
->orWhereIn('status_turt', ['rejected Turt', 'revised Turt']);
});
});
}
@ -1969,7 +2006,7 @@ class DashboardController extends Controller
$sub->where('is_akre', true)
->where(function ($pendingMutu) {
$pendingMutu->whereNull('status_mutu')
->orWhere('status_mutu', 'rejected');
->orWhereIn('status_mutu', ['rejected Mutu', 'revised Mutu']);
});
});
}
@ -2393,7 +2430,7 @@ class DashboardController extends Controller
$sub->whereNotNull('master_kategori_directory_id')
->where(function ($pendingTurt) {
$pendingTurt->whereNull('status_turt')
->orWhere('status_turt', 'rejected');
->orWhereIn('status_turt', ['revised Turt']);
});
});
}
@ -2403,7 +2440,7 @@ class DashboardController extends Controller
$sub->where('is_akre', true)
->where(function ($pendingMutu) {
$pendingMutu->whereNull('status_mutu')
->orWhere('status_mutu', 'rejected');
->orWhereIn('status_mutu', ['revised Mutu']);
});
});
}
@ -2846,7 +2883,7 @@ class DashboardController extends Controller
$q->whereNotNull('master_kategori_directory_id')
->where(function ($pendingTurt) {
$pendingTurt->whereNull('status_turt')
->orWhere('status_turt', 'rejected');
->orWhereIn('status_turt', ['rejected Turt', 'revised Turt']);
});
}
if ($isKomiteMutu) {
@ -2855,7 +2892,7 @@ class DashboardController extends Controller
$sub->where('is_akre', true)
->where(function ($pendingMutu) {
$pendingMutu->whereNull('status_mutu')
->orWhere('status_mutu', 'rejected');
->orWhereIn('status_mutu', ['rejected Mutu', 'revised Mutu']);
});
});
}
@ -2873,14 +2910,14 @@ class DashboardController extends Controller
$needTurt->whereNotNull('master_kategori_directory_id')
->where(function ($pendingTurt) {
$pendingTurt->whereNull('status_turt')
->orWhere('status_turt', 'rejected');
->orWhereIn('status_turt', ['rejected Turt', 'revised Turt']);
});
})
->orWhere(function ($needMutu) {
$needMutu->where('is_akre', true)
->where(function ($pendingMutu) {
$pendingMutu->whereNull('status_mutu')
->orWhere('status_mutu', 'rejected');
->orWhereIn('status_mutu', ['rejected Mutu', 'revised Mutu']);
});
});
});
@ -2912,14 +2949,14 @@ class DashboardController extends Controller
$needTurt->whereNotNull('master_kategori_directory_id')
->where(function ($pendingTurt) {
$pendingTurt->whereNull('status_turt')
->orWhere('status_turt', 'rejected');
->orWhere('status_turt', ['revised Turt', 'rejected Turt']);
});
})
->orWhere(function ($needMutu) {
$needMutu->where('is_akre', true)
->where(function ($pendingMutu) {
$pendingMutu->whereNull('status_mutu')
->orWhere('status_mutu', 'rejected');
->orWhereIn('status_mutu', ['revised Mutu', 'rejected Mutu']);
});
});
});
@ -3124,34 +3161,32 @@ class DashboardController extends Controller
}
}
$targetRoleNotifications = [];
$resetMutuApproval = $data->status_mutu === 'rejected';
$resetTurtApproval = $data->status_turt === 'rejected';
$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['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['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['revision'] = null;
$payload['status_action'] = 'revised';
$payload['action_at'] = null;
$payload['action_by'] = null;
}
if (!empty($payload)) {
$data->update($payload);
}
@ -3940,14 +3975,11 @@ class DashboardController extends Controller
], 422);
}
$data->update([
'status_mutu' => 'rejected',
'status_mutu' => 'rejected Mutu',
'action_mutu_at' => now(),
'action_mutu_by' => auth()->user()->dataUser->id,
'mutu_revision' => $revision,
'revision' => 'Komite Mutu: ' . $revision,
'status_action' => 'rejected',
'action_at' => now(),
'action_by' => auth()->user()->objectpegawaifk,
'revision' => 'Komite Mutu: ' . $revision
]);
$parts = array_values(array_filter(explode('/', $data->file)));
@ -4020,14 +4052,11 @@ class DashboardController extends Controller
], 422);
}
$data->update([
'status_turt' => 'rejected',
'status_turt' => 'rejected Turt',
'action_turt_at' => now(),
'action_turt_by' => auth()->user()->dataUser->id,
'turt_revision' => $revision,
'revision' => 'TURT: ' . $revision,
'status_action' => 'rejected',
'action_at' => now(),
'action_by' => auth()->user()->objectpegawaifk,
]);
$parts = array_values(array_filter(explode('/', $data->file)));

View File

@ -66,30 +66,49 @@ document.addEventListener('DOMContentLoaded', () => {
}
function approvalBadge(status, label){
if (status === 'approved') return `<span class="badge bg-success">Approved ${label}</span>`;
if (status === 'rejected') return `<span class="badge bg-danger">Rejected ${label}</span>`;
const normalized = String(status || '').toLowerCase();
if (normalized === 'approved') return `<span class="badge bg-success">Approved ${label}</span>`;
if (normalized.startsWith('rejected')) return `<span class="badge bg-danger">Rejected ${label}</span>`;
if (normalized.startsWith('revised')) return `<span class="badge bg-info">Revised ${label}</span>`;
return `<span class="badge bg-warning text-dark">Pending ${label}</span>`;
}
function getWorkflowStatusBadges(item){
if (item?.status_mutu === 'rejected') return [`<span class="badge bg-danger">Rejected Mutu</span>`];
if (item?.status_turt === 'rejected') return [`<span class="badge bg-danger">Rejected TURT</span>`];
if (item?.status_action === 'rejected') return [`<span class="badge bg-danger">Rejected Atasan</span>`];
if (item?.status_action === 'revised') return [`<span class="badge bg-info">Revised</span>`];
const actionStatus = String(item?.status_action || '').toLowerCase();
const mutuStatus = String(item?.status_mutu || '').toLowerCase();
const turtStatus = String(item?.status_turt || '').toLowerCase();
if (item?.status_action !== 'approved') {
if (actionStatus.startsWith('revised')) {
return [`<span class="badge bg-info">Revised</span>`];
}
if (actionStatus.startsWith('rejected')) {
return [`<span class="badge bg-danger">Rejected Atasan</span>`];
}
if (actionStatus !== 'approved') {
return [`<span class="badge bg-warning text-dark">Pending Atasan</span>`];
}
const statuses = [];
if (item?.is_akre && item?.status_mutu !== 'approved') {
statuses.push(`<span class="badge bg-warning text-dark">Pending Mutu</span>`);
}
if (item?.master_kategori_directory_id && item?.status_turt !== 'approved') {
statuses.push(`<span class="badge bg-warning text-dark">Pending TURT</span>`);
if (item?.is_akre) {
if (mutuStatus.startsWith('rejected')) {
statuses.push(`<span class="badge bg-danger">Rejected Mutu</span>`);
} else if (mutuStatus !== 'approved') {
statuses.push(`<span class="badge bg-warning text-dark">Pending Mutu</span>`);
}
}
if (statuses.length) {
if (item?.master_kategori_directory_id) {
if (turtStatus.startsWith('rejected')) {
statuses.push(`<span class="badge bg-danger">Rejected TURT</span>`);
} else if (turtStatus !== 'approved') {
statuses.push(`<span class="badge bg-warning text-dark">Pending TURT</span>`);
}
}
if (statuses.length > 0) {
return statuses;
}
@ -108,11 +127,18 @@ document.addEventListener('DOMContentLoaded', () => {
}
function isRejected(item){
return item?.status_action === 'rejected' || item?.status_mutu === 'rejected' || item?.status_turt === '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 item?.status_action === 'approved';
return String(item?.status_action || '').toLowerCase() === 'approved';
}
function canProcessRoleStatus(status){
const normalized = String(status || '').toLowerCase();
return normalized === '' || normalized.startsWith('revised');
}
function canApproveAsAtasan(item){
@ -123,6 +149,62 @@ document.addEventListener('DOMContentLoaded', () => {
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, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function buildRevisionNotesHtml(notes){
if (!notes.length) {
return '<div class="text-muted">Tidak ada catatan revisi.</div>';
}
return `
<div class="text-start" style="line-height:1.6;">
${notes.map(note => `
<div class="mb-2">
<div><strong>${escapeHtml(note.label)}</strong></div>
<div>${escapeHtml(note.text)}</div>
</div>
`).join('')}
</div>
`;
}
function getItemById(id){
return (tableState.data || []).find((row) => String(row.file_directory_id) === String(id));
}
function getSelectableIdsOnPage(){
return (tableState.data || [])
.filter((item) => !isRejected(item) && !isAtasanApproved(item) && canApproveAsAtasan(item))
@ -169,6 +251,9 @@ document.addEventListener('DOMContentLoaded', () => {
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 actions = [
`<button class="btn btn-sm btn-primary" onclick="infoDok(this)"
@ -187,20 +272,14 @@ document.addEventListener('DOMContentLoaded', () => {
actions.push(`<button class="btn btn-sm btn-success" onclick="approvePending('${item.file_directory_id}', '${item.nama_dokumen || ''}')"><i class="fa-solid fa-check"></i></button>`);
actions.push(`<button class="btn btn-sm btn-danger" onclick="rejectPending('${item.file_directory_id}', '${item.nama_dokumen || ''}')"><i class="fa-solid fa-xmark"></i></button>`);
}
if (atasanApproved && isKomiteMutu && item.is_akre && item.status_mutu !== 'approved') {
if (canMutuAct) {
actions.push(`<button class="btn btn-sm btn-success" onclick="approvePendingRole('mutu','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-check"></i></button>`);
actions.push(`<button class="btn btn-sm btn-danger" onclick="rejectPendingRole('mutu','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-xmark"></i></button>`);
}
if (atasanApproved && isTurt && item.master_kategori_directory_id && item.status_turt !== 'approved') {
actions.push(`<button class="btn btn-sm btn-success" onclick="approvePendingRole('turt','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-check-double"></i></button>`);
if (canTurtAct) {
actions.push(`<button class="btn btn-sm btn-success" onclick="approvePendingRole('turt','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-check"></i></button>`);
actions.push(`<button class="btn btn-sm btn-danger" onclick="rejectPendingRole('turt','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-ban"></i></button>`);
}
if (item.status_mutu === 'rejected' && item.mutu_revision) {
actions.push(`<button class="btn btn-sm btn-info" onclick="infoRejectRole('Komite Mutu', ${JSON.stringify('' + (item.mutu_revision || ''))})"><i class="fa-solid fa-circle-info"></i></button>`);
}
if (item.status_turt === 'rejected' && item.turt_revision) {
actions.push(`<button class="btn btn-sm btn-info" onclick="infoRejectRole('TURT', ${JSON.stringify('' + (item.turt_revision || ''))})"><i class="fa-solid fa-circle-info"></i></button>`);
}
} else {
if (!atasanApproved) {
actions.push(`<button class="btn btn-sm btn-success" onclick="approvePending('${item.file_directory_id}', '${item.nama_dokumen || ''}')"><i class="fa-solid fa-check"></i></button>`);
@ -208,9 +287,14 @@ document.addEventListener('DOMContentLoaded', () => {
}
}
if (rejected) {
if (rejected && atasanRejected && !canMutuAct && !canTurtAct) {
actions.length = 1;
actions.push(`<button class="btn btn-sm btn-info" onclick="infoRejectPending('${item.file_directory_id}')"><i class="fa-solid fa-circle-info"></i></button>`);
}
if (rejected) {
if (hasRevisionInfo(item)) {
actions.push(`<button class="btn btn-sm btn-info" onclick="infoRejectPending('${item.file_directory_id}')"><i class="fa-solid fa-circle-info"></i></button>`);
}
if (canEditSubmission(item)) {
actions.push(`<button class="btn btn-sm btn-primary" onclick="editRejectedFromPending('${item.file_directory_id}')"><i class="fa-solid fa-pen-to-square"></i></button>`);
}
@ -546,31 +630,11 @@ document.addEventListener('DOMContentLoaded', () => {
});
}
window.infoRejectRole = function(label, revision){
Swal.fire({
title: `Catatan ${label}`,
text: revision || 'Tidak ada catatan revisi.',
icon: 'info',
confirmButtonText: 'Tutup'
});
}
window.infoRejectPending = function(id){
const item = getItemById(id);
const notes = [];
if (item?.revision) {
notes.push(`Atasan: ${String(item.revision)}`);
}
if (item?.mutu_revision) {
notes.push(`Komite Mutu: ${String(item.mutu_revision)}`);
}
if (item?.turt_revision) {
notes.push(`TURT: ${String(item.turt_revision)}`);
}
Swal.fire({
title: 'Catatan Revisi',
text: notes.length ? notes.join('\n\n') : 'Tidak ada catatan revisi.',
html: buildRevisionNotesHtml(collectRevisionNotes(item)),
icon: 'info',
confirmButtonText: 'Tutup'
});

View File

@ -19,7 +19,6 @@ document.addEventListener('DOMContentLoaded', () => {
const selectedCountEl = document.getElementById('selectedCount');
const bulkActionsEl = document.getElementById('pengajuanBulkActions');
const deleteDataEl = document.getElementById('deleteData');
const pendingPersetujuanTabBadge = document.getElementById('pendingPersetujuanTabBadge');
const pendingPersetujuanNotice = document.getElementById('pendingPersetujuanNotice');
const pendingPersetujuanNoticeCount = document.getElementById('pendingPersetujuanNoticeCount');
const formCreate = document.getElementById('formFile');
@ -111,55 +110,137 @@ document.addEventListener('DOMContentLoaded', () => {
}
function approvalBadge(status, label){
if (status === 'approved') return `<span class="badge bg-success">Approved ${label}</span>`;
if (status === 'rejected') return `<span class="badge bg-danger">Rejected ${label}</span>`;
const normalized = String(status || '').toLowerCase();
if (normalized === 'approved') return `<span class="badge bg-success">Approved ${label}</span>`;
if (normalized.startsWith('rejected')) return `<span class="badge bg-danger">Rejected ${label}</span>`;
if (normalized.startsWith('revised')) return `<span class="badge bg-info">Revised ${label}</span>`;
return `<span class="badge bg-warning text-dark">Pending ${label}</span>`;
}
function getPengajuanStatusBadge(item){
if (item?.status_mutu === 'rejected') {
return `<span class="badge bg-danger">Rejected Mutu</span>`;
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 (item?.status_turt === 'rejected') {
return `<span class="badge bg-danger">Rejected TURT</span>`;
if (mutuRevision) {
notes.push({ label: 'Komite Mutu', text: mutuRevision });
}
if (item?.status_action === 'rejected') {
return `<span class="badge bg-danger">Rejected Atasan</span>`;
if (turtRevision) {
notes.push({ label: 'TURT', text: turtRevision });
}
if (item?.status_action === 'revised') {
return notes;
}
function escapeHtml(value){
return String(value ?? '')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function buildRevisionNotesHtml(notes){
if (!notes.length) {
return '<div class="text-muted">Tidak ada catatan revisi.</div>';
}
return `
<div class="text-start" style="line-height:1.6;">
${notes.map(note => `
<div class="mb-2">
<div><strong>${escapeHtml(note.label)}</strong></div>
<div>${escapeHtml(note.text)}</div>
</div>
`).join('')}
</div>
`;
}
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 `<span class="badge bg-info">Revised</span>`;
}
if (item?.status_action !== 'approved') {
if (actionStatus.startsWith('rejected')) {
return `<span class="badge bg-danger">Rejected Atasan</span>`;
}
if (actionStatus !== 'approved') {
return `<span class="badge bg-warning text-dark">Pending Atasan</span>`;
}
const pendingApprovals = [];
if (item?.is_akre && item?.status_mutu !== 'approved') {
pendingApprovals.push('Pending Mutu');
}
if (item?.master_kategori_directory_id && item?.status_turt !== 'approved') {
pendingApprovals.push('Pending TURT');
const statuses = [];
if (item?.is_akre) {
if (mutuStatus.startsWith('rejected mutu')) {
statuses.push(`<span class="badge bg-danger">Rejected Mutu</span>`);
} else if (mutuStatus === 'revised mutu') {
statuses.push(`<span class="badge bg-info text-white">Revisi Mutu</span>`);
} else if(mutuStatus === 'approved'){
statuses.push(`<span class="badge bg-success text-white">Approved Mutu</span>`);
}else{
statuses.push(`<span class="badge bg-warning text-dark">Pending Mutu</span>`);
}
}
if (pendingApprovals.length) {
return pendingApprovals.map((label) => `<span class="badge bg-warning text-dark">${label}</span>`).join(' ');
}
if (item?.status_action === 'approved') {
return `<span class="badge bg-success">Approved</span>`;
if (item?.master_kategori_directory_id) {
if (turtStatus.startsWith('rejected turt')) {
statuses.push(`<span class="badge bg-danger">Rejected TURT</span>`);
} else if (turtStatus === 'revised turt') {
statuses.push(`<span class="badge bg-info text-white">Revisi TURT</span>`);
} else if(turtStatus === 'approved'){
statuses.push(`<span class="badge bg-success text-white">Approved TURT</span>`);
} else{
statuses.push(`<span class="badge bg-warning text-dark">Pending TURT</span>`);
}
}
return `<span class="badge bg-warning text-dark">Pending Atasan</span>`;
if (statuses.length > 0) {
return statuses.join(' ');
}
return `<span class="badge bg-success">Approved</span>`;
}
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' || item?.status_mutu === 'rejected' || item?.status_turt === 'rejected';
const showEdit = !isApproved || isRejected;
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 = `
@ -195,7 +276,7 @@ document.addEventListener('DOMContentLoaded', () => {
</td>
<td>${aksi}</td>
<td>${item.no_dokumen || '-'}</td>
<td>${getPengajuanStatusBadge(item)}</td>
<td><div class="d-flex gap-1 flex-wrap">${getPengajuanStatusBadges(item)}</div></td>
<td>${aksesBadge(item?.permission_file)}</td>
<td><a href="#" class="file-link"
data-file="${item.file}"
@ -234,19 +315,16 @@ document.addEventListener('DOMContentLoaded', () => {
data-permission_file="${item.permission_file || '-'}"><i class="fa-solid fa-eye"></i></button>`
];
if (showMutu && item.status_mutu !== 'approved') {
if (showMutu && canProcessRoleStatus(item.status_mutu)) {
actions.push(`<button class="btn btn-sm btn-success" onclick="approvePersetujuan('mutu','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-check"></i></button>`);
actions.push(`<button class="btn btn-sm btn-danger" onclick="rejectPersetujuan('mutu','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-xmark"></i></button>`);
}
if (showTurt && item.status_turt !== 'approved') {
actions.push(`<button class="btn btn-sm btn-success" onclick="approvePersetujuan('turt','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-check-double"></i></button>`);
if (showTurt && canProcessRoleStatus(item.status_turt)) {
actions.push(`<button class="btn btn-sm btn-success" onclick="approvePersetujuan('turt','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-check"></i></button>`);
actions.push(`<button class="btn btn-sm btn-danger" onclick="rejectPersetujuan('turt','${item.file_directory_id}','${item.nama_dokumen || ''}')"><i class="fa-solid fa-ban"></i></button>`);
}
if (item.status_mutu === 'rejected' && item.mutu_revision) {
actions.push(`<button class="btn btn-sm btn-info" onclick="infoRejectPersetujuan('Komite Mutu', ${JSON.stringify('' + (item.mutu_revision || ''))})"><i class="fa-solid fa-circle-info"></i></button>`);
}
if (item.status_turt === 'rejected' && item.turt_revision) {
actions.push(`<button class="btn btn-sm btn-info" onclick="infoRejectPersetujuan('TURT', ${JSON.stringify('' + (item.turt_revision || ''))})"><i class="fa-solid fa-circle-info"></i></button>`);
if (hasRevisionInfo(item)) {
actions.push(`<button class="btn btn-sm btn-info" onclick="infoReject('${item.file_directory_id}')"><i class="fa-solid fa-circle-info"></i></button>`);
}
const statusParts = [];
@ -499,10 +577,8 @@ document.addEventListener('DOMContentLoaded', () => {
function updatePendingPersetujuanIndicators(total){
const count = Number(total || 0);
if (pendingPersetujuanTabBadge) {
pendingPersetujuanTabBadge.textContent = String(count);
pendingPersetujuanTabBadge.classList.toggle('d-none', count <= 0);
}
console.log(pendingPersetujuanNoticeCount);
if (pendingPersetujuanNotice && pendingPersetujuanNoticeCount) {
pendingPersetujuanNoticeCount.textContent = String(count);
pendingPersetujuanNotice.classList.toggle('d-none', count <= 0);
@ -513,19 +589,14 @@ document.addEventListener('DOMContentLoaded', () => {
function fetchPendingPersetujuanCount(){
if (!(isKomiteMutu || isTurt || isAtasan)) return;
const params = new URLSearchParams({
page: 1,
per_page: 1,
keyword: '',
start_date: '',
end_date: '',
mode: 'persetujuan'
});
fetch(`/datatable/pengajuan-file?${params.toString()}`)
fetch('/data/count-pending', {
headers: {
'Accept': 'application/json'
}
})
.then(res => res.json())
.then(data => {
updatePendingPersetujuanIndicators(data?.pagination?.total || 0);
updatePendingPersetujuanIndicators(data?.count || 0);
})
.catch(() => {
updatePendingPersetujuanIndicators(0);
@ -633,29 +704,9 @@ document.addEventListener('DOMContentLoaded', () => {
window.infoReject = function(id){
const item = getItemById(id);
const notes = [];
if (item?.revision) {
notes.push(`Atasan/Bagian Terkait: ${String(item.revision)}`);
}
if (item?.mutu_revision) {
notes.push(`Komite Mutu: ${String(item.mutu_revision)}`);
}
if (item?.turt_revision) {
notes.push(`TURT: ${String(item.turt_revision)}`);
}
const revision = notes.length ? notes.join('\n\n') : 'Tidak ada catatan revisi.';
Swal.fire({
title: 'Catatan Revisi',
text: revision,
icon: 'info',
confirmButtonText: 'Tutup'
});
}
window.infoRejectPersetujuan = function(label, revision){
Swal.fire({
title: `Catatan ${label}`,
text: revision || 'Tidak ada catatan revisi.',
html: buildRevisionNotesHtml(collectRevisionNotes(item)),
icon: 'info',
confirmButtonText: 'Tutup'
});

View File

@ -660,6 +660,7 @@
function renderDeleteRecommendation(item){
const recommendation = item?.delete_recommendation;
if (!recommendation?.visible || !recommendation?.note) return '';
const actorRole = recommendation.actor_role || 'TURT';
@ -1255,7 +1256,7 @@
data-input-target="expiredInput_${colCount}"
data-base-target="dateActive_${colCount}"
data-preview-target="expiredPreview_${colCount}">
<option value="">Tidak ada</option>
<option value="">Selamanya</option>
<option value="1">1 Tahun</option>
<option value="2">2 Tahun</option>
<option value="3">3 Tahun</option>

View File

@ -57,7 +57,7 @@
data-input-target="expiredInput_0"
data-base-target="dateActive_0"
data-preview-target="expiredPreview_0">
<option value="">Tidak ada</option>
<option value="">Selamanya</option>
<option value="1">1 Tahun</option>
<option value="2">2 Tahun</option>
<option value="3">3 Tahun</option>

View File

@ -58,7 +58,7 @@
<div class="small text-muted ms-md-auto" id="tableSummary"></div>
</div>
<div id="tabPengajuan">
<div class="table-responsive" style="max-height: 55vh; overflow-y:auto; overflow-x:auto;">
<div class="table-responsive" style="max-height: 75vh; overflow-y:auto; overflow-x:auto;">
<table class="table table-sm table-hover table-striped align-middle mb-0 pending-table" id="lastUpdatedTable">
<thead>
<tr>

View File

@ -135,7 +135,7 @@
<div class="d-flex flex-column flex-md-row align-items-md-center justify-content-between gap-2 mt-3" id="paginationPengajuan"></div>
</div>
<div id="tabHistory" class="d-none">
<div class="table-responsive" style="max-height: 55vh; overflow-y:auto;">
<div class="table-responsive" style="max-height: 75vh; overflow-y:auto;">
<table class="table table-sm table-hover align-middle mb-0" id="tableHistory">
<thead>
<tr>
@ -145,8 +145,6 @@
<th>Kategori</th>
<th>Unit</th>
<th>Aksi</th>
<th>Tanggal Terbit</th>
<th>Tanggal Kedaluwarsa Dokumen</th>
<th>Tanggal Upload</th>
</tr>
</thead>

View File

@ -56,7 +56,7 @@
data-input-target="edit_tgl_expired"
data-base-target="edit_tanggal_terbit"
data-preview-target="edit_expired_preview">
<option value="">Tidak ada</option>
<option value="">Selamanya</option>
<option value="1">1 Tahun</option>
<option value="2">2 Tahun</option>
<option value="3">3 Tahun</option>