done approval add mutu & turt -> review
This commit is contained in:
parent
353efd16b0
commit
f8c3816ca7
@ -1991,7 +1991,7 @@ class DashboardController extends Controller
|
||||
}
|
||||
|
||||
$paginated = $query->paginate($perPage);
|
||||
$data = $paginated->getCollection()->map(function($item) use($objectpegawaifk){
|
||||
$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,
|
||||
@ -3369,6 +3369,9 @@ class DashboardController extends Controller
|
||||
'no_dokumen',
|
||||
'file',
|
||||
'entry_at',
|
||||
'tanggal_terbit',
|
||||
'permission_file',
|
||||
'pegawai_id_entry',
|
||||
'id_unit_kerja',
|
||||
'is_akre',
|
||||
])
|
||||
|
||||
@ -19,6 +19,9 @@ 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');
|
||||
const modalCreate = document.getElementById('modalCreateFile');
|
||||
let colCount = 1;
|
||||
@ -29,6 +32,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
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) {
|
||||
@ -493,6 +497,41 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
updateSelectionUI();
|
||||
}
|
||||
|
||||
function updatePendingPersetujuanIndicators(total){
|
||||
const count = Number(total || 0);
|
||||
if (pendingPersetujuanTabBadge) {
|
||||
pendingPersetujuanTabBadge.textContent = String(count);
|
||||
pendingPersetujuanTabBadge.classList.toggle('d-none', count <= 0);
|
||||
}
|
||||
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;
|
||||
|
||||
const params = new URLSearchParams({
|
||||
page: 1,
|
||||
per_page: 1,
|
||||
keyword: '',
|
||||
start_date: '',
|
||||
end_date: '',
|
||||
mode: 'persetujuan'
|
||||
});
|
||||
|
||||
fetch(`/datatable/pengajuan-file?${params.toString()}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
updatePendingPersetujuanIndicators(data?.pagination?.total || 0);
|
||||
})
|
||||
.catch(() => {
|
||||
updatePendingPersetujuanIndicators(0);
|
||||
});
|
||||
}
|
||||
|
||||
if (tabsEl) {
|
||||
tabsEl.addEventListener('click', (e) => {
|
||||
const btn = e.target.closest('[data-mode]');
|
||||
@ -584,6 +623,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
activeState.lastPage = data?.pagination?.last_page || 1;
|
||||
activeState.total = data?.pagination?.total || 0;
|
||||
renderTable();
|
||||
fetchPendingPersetujuanCount();
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
</style>
|
||||
@section('body_main')
|
||||
<div class="row">
|
||||
@ -167,6 +210,7 @@
|
||||
</div>
|
||||
|
||||
@include('dataUnit.modal.create')
|
||||
@include('dataUnit.modal.view')
|
||||
|
||||
@if(Auth::guard('admin')->check())
|
||||
<div class="modal fade" id="modalAkreInstrumen" tabindex="-1" aria-hidden="true">
|
||||
@ -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 @@
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-secondary btn-preview-file"
|
||||
data-id="${item.file_directory_id}"
|
||||
data-name="${escapeAttribute(item.nama_dokumen || fileName)}"
|
||||
data-no="${escapeAttribute(item.no_dokumen || '-')}"
|
||||
data-date="${escapeAttribute(formatPreviewDate(item.tanggal_terbit || item.entry_at))}"
|
||||
data-permission="${item.permission_file === true ? '1' : '0'}"
|
||||
title="Preview">
|
||||
<i class="ti ti-eye"></i>
|
||||
</button>
|
||||
<a href="/file-download/${item.file_directory_id}" target="_blank"
|
||||
class="btn btn-sm btn-outline-primary" download title="Download">
|
||||
<i class="ti ti-download"></i>
|
||||
@ -356,9 +411,20 @@
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex flex-column">
|
||||
<strong>${item.nama_dokumen}</strong>
|
||||
<small class="text-muted" style="font-size: 10px;">Path: ${item.file}</small>
|
||||
<div class="file-row-content">
|
||||
<i class="ti ti-file-type-pdf file-icon text-danger"></i>
|
||||
<div class="d-flex flex-column file-text-wrap">
|
||||
<button type="button"
|
||||
class="btn btn-link btn-sm p-0 text-start text-decoration-none btn-preview-file file-name-trigger"
|
||||
data-id="${item.file_directory_id}"
|
||||
data-name="${escapeAttribute(item.nama_dokumen || fileName)}"
|
||||
data-no="${escapeAttribute(item.no_dokumen || '-')}"
|
||||
data-date="${escapeAttribute(formatPreviewDate(item.tanggal_terbit || item.entry_at))}"
|
||||
data-permission="${item.permission_file === true ? '1' : '0'}">
|
||||
<strong>${escapeHtml(item.nama_dokumen || fileName)}</strong>
|
||||
</button>
|
||||
<small class="text-muted file-meta-path" style="font-size: 10px;">Path: ${item.file}</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="badge bg-light text-primary border">${typeDok}</span></td>
|
||||
@ -495,18 +561,40 @@
|
||||
value="${item.file_directory_id}" ${isChecked} onchange="handleRowCheck(this)">
|
||||
</td>
|
||||
<td>
|
||||
<a href="/file-download/${item.file_directory_id}" target="_blank"
|
||||
class="btn btn-sm btn-primary text-white border">
|
||||
<i class="ti ti-download"></i>
|
||||
</a>
|
||||
<div class="btn-group">
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-outline-secondary btn-preview-file"
|
||||
data-id="${item.file_directory_id}"
|
||||
data-name="${escapeAttribute(item.nama_dokumen || fileName)}"
|
||||
data-no="${escapeAttribute(item.no_dokumen || '-')}"
|
||||
data-date="${escapeAttribute(formatPreviewDate(item.tanggal_terbit || item.entry_at))}"
|
||||
data-permission="${item.permission_file === true ? '1' : '0'}"
|
||||
title="Preview">
|
||||
<i class="ti ti-eye"></i>
|
||||
</button>
|
||||
<a href="/file-download/${item.file_directory_id}" target="_blank"
|
||||
class="btn btn-sm btn-primary text-white border">
|
||||
<i class="ti ti-download"></i>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="indent-container">
|
||||
${lines}
|
||||
<span style="width: 24px;"></span> <i class="ti ti-file-text file-icon me-2"></i>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="text-dark fw-medium">${item.nama_dokumen}</span>
|
||||
<small class="text-muted" style="font-size: 10px;">${fileName}</small>
|
||||
<span style="width: 24px;"></span>
|
||||
<div class="file-row-content">
|
||||
<i class="ti ti-file-text file-icon text-danger"></i>
|
||||
<div class="d-flex flex-column file-text-wrap">
|
||||
<button type="button"
|
||||
class="btn btn-link btn-sm p-0 text-start text-decoration-none text-dark fw-medium btn-preview-file file-name-trigger"
|
||||
data-id="${item.file_directory_id}"
|
||||
data-name="${escapeAttribute(item.nama_dokumen || fileName)}"
|
||||
data-no="${escapeAttribute(item.no_dokumen || '-')}"
|
||||
data-date="${escapeAttribute(formatPreviewDate(item.tanggal_terbit || item.entry_at))}"
|
||||
data-permission="${item.permission_file === true ? '1' : '0'}">
|
||||
${escapeHtml(item.nama_dokumen || fileName)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@ -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, '"')
|
||||
.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 = `<div id="pdfWrap" style="height:500px; overflow:auto; background:#f7f7f7; padding:8px;">
|
||||
<div id="pdfPages"></div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
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');
|
||||
|
||||
@ -408,6 +408,30 @@
|
||||
return null;
|
||||
}
|
||||
|
||||
function escapeHtml(value){
|
||||
return String(value ?? '')
|
||||
.replace(/&/g, '&')
|
||||
.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 `
|
||||
<div class="mt-1 px-2 py-1 rounded border border-warning-subtle bg-warning-subtle text-warning-emphasis" style="font-size:12px; line-height:1.35;">
|
||||
Dokumen ini direkomendasikan dihapus oleh ${escapeHtml(actorRole)}${actorName}. Catatan: ${note}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function buildRow(item){
|
||||
const parts = (item.file || '').split('/');
|
||||
const fileName = parts.pop() || '-';
|
||||
@ -520,6 +544,7 @@
|
||||
${expiryBadge}
|
||||
|
||||
</div>
|
||||
${renderDeleteRecommendation(item)}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
|
||||
@ -649,6 +649,30 @@
|
||||
return null;
|
||||
}
|
||||
|
||||
function escapeHtml(value){
|
||||
return String(value ?? '')
|
||||
.replace(/&/g, '&')
|
||||
.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 `
|
||||
<div class="mt-1 px-2 py-1 rounded border border-warning-subtle bg-warning-subtle text-warning-emphasis" style="font-size:12px; line-height:1.35;">
|
||||
Dokumen ini direkomendasikan dihapus oleh ${escapeHtml(actorRole)}${actorName}. Catatan: ${note}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function buildRow(item){
|
||||
const parts = (item.file || '').split('/');
|
||||
const fileName = parts.pop() || '-';
|
||||
@ -748,6 +772,7 @@
|
||||
</a>
|
||||
${akreBadge}${expiryBadge}
|
||||
</div>
|
||||
${renderDeleteRecommendation(item)}
|
||||
</td>
|
||||
<td>
|
||||
${item.nama_kategori || '-'}
|
||||
|
||||
@ -66,7 +66,7 @@
|
||||
})->exists();
|
||||
$isRoleApprover = in_array(51, $userUnitIds, true) || in_array(22, $userUnitIds, true);
|
||||
@endphp
|
||||
@if($isAtasan || $isRoleApprover)
|
||||
@if($isAtasan)
|
||||
@if(!Auth::guard('admin')->check())
|
||||
<li class="sidebar-item">
|
||||
<a class="sidebar-link d-flex align-items-center justify-content-between"
|
||||
@ -91,6 +91,10 @@
|
||||
<i class="ti ti-clock"></i>
|
||||
<span class="hide-menu">Pengajuan</span>
|
||||
</div>
|
||||
|
||||
@if($isRoleApprover)
|
||||
<span class="badge bg-danger rounded-pill d-none" id="pengajuanPendingCountBadge">0</span>
|
||||
@endif
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
@ -204,10 +208,11 @@
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const badge = document.getElementById('pendingCountBadge');
|
||||
const pendingBadge = document.getElementById('pendingCountBadge');
|
||||
const pengajuanBadge = document.getElementById('pengajuanPendingCountBadge');
|
||||
|
||||
async function countData() {
|
||||
if (!badge) return;
|
||||
if (!pendingBadge && !pengajuanBadge) return;
|
||||
|
||||
try {
|
||||
const res = await fetch('/data/count-pending', {
|
||||
@ -219,11 +224,19 @@
|
||||
const data = await res.json();
|
||||
const count = Number(data?.count ?? 0);
|
||||
|
||||
badge.textContent = count;
|
||||
badge.classList.toggle('d-none', count <= 0);
|
||||
if (pendingBadge) {
|
||||
pendingBadge.textContent = count;
|
||||
pendingBadge.classList.toggle('d-none', count <= 0);
|
||||
}
|
||||
|
||||
if (pengajuanBadge) {
|
||||
pengajuanBadge.textContent = count;
|
||||
pengajuanBadge.classList.toggle('d-none', count <= 0);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
badge.classList.add('d-none');
|
||||
if (pendingBadge) pendingBadge.classList.add('d-none');
|
||||
if (pengajuanBadge) pengajuanBadge.classList.add('d-none');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -48,13 +48,24 @@
|
||||
</li>
|
||||
@if($isKomiteMutu || $isTurt || $isAtasan)
|
||||
<li class="nav-item">
|
||||
<button class="nav-link" type="button" data-mode="persetujuan">Data Persetujuan</button>
|
||||
<button class="nav-link d-flex align-items-center gap-2" type="button" data-mode="persetujuan">
|
||||
<span>Data Persetujuan</span>
|
||||
<span class="badge bg-danger rounded-pill d-none" id="pendingPersetujuanTabBadge">0</span>
|
||||
</button>
|
||||
</li>
|
||||
@endif
|
||||
<li class="nav-item">
|
||||
<button class="nav-link" type="button" data-mode="history">Log History</button>
|
||||
</li>
|
||||
</ul>
|
||||
@if(($isKomiteMutu || $isTurt) && !$isAtasan)
|
||||
<div class="alert alert-warning d-none align-items-center justify-content-between gap-3 py-2" id="pendingPersetujuanNotice" role="alert">
|
||||
<div>
|
||||
Ada <strong id="pendingPersetujuanNoticeCount">0</strong> data yang menunggu persetujuan Anda.
|
||||
</div>
|
||||
<a href="{{ url('/pending-file') }}" class="btn btn-sm btn-outline-warning">Lihat Persetujuan</a>
|
||||
</div>
|
||||
@endif
|
||||
<div class="d-flex flex-column flex-md-row align-items-md-center gap-2 mb-3 flex-wrap">
|
||||
<div class="input-group input-group-sm flex-grow-1" style="max-width:320px;">
|
||||
<span class="input-group-text bg-white border-end-0">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user