This commit is contained in:
Jokoprasetio 2026-01-30 01:06:11 +07:00
parent b1c0251586
commit ecc950f19d
5 changed files with 252 additions and 42 deletions

View File

@ -967,12 +967,30 @@ class DashboardController extends Controller
$keyword = request('keyword');
$start = request('start_date');
$end = request('end_date');
$authUnit = auth()->user()->masterPersetujuan->details->pluck('unit_pegawai_id')->unique()->toArray();
// $authUnit = auth()->user()->masterPersetujuan->details->pluck('unit_pegawai_id')->unique()->toArray();
// $query = FileDirectory::where('statusenabled', true)->where(function($q){
// $q->where('status_action', '!=', 'approved')
// ->orWhereNull('status_action');
// })->whereIn('id_unit_kerja', $authUnit)->orderBy('entry_at','desc');
$mapping = MappingUnitKerjaPegawai::where('statusenabled', true)
// ->where('objectatasanlangsungfk', auth()->user()->dataUser->id)
->where('objectatasanlangsungfk', 22924)
->get(['objectpegawaifk']);
$objectpegawaifk = $mapping->pluck('objectpegawaifk')
->values()
->all();
$keyword = request('keyword');
$query = FileDirectory::where('statusenabled', true)
->where('status_action', '!=', 'approved')
->whereIn('pegawai_id_entry', $objectpegawaifk)
->when($keyword, function ($q) use ($keyword) {
$q->where(function ($sub) use ($keyword) {
$sub->where('file', 'ILIKE', "%{$keyword}%")
->orWhere('no_dokumen', 'ILIKE', "%{$keyword}%");
});
});
$query = FileDirectory::where('statusenabled', true)->where(function($q){
$q->where('status_action', '!=', 'approved')
->orWhereNull('status_action');
})->whereIn('id_unit_kerja', $authUnit)->orderBy('entry_at','desc');
if($keyword){
$query->where(function($q) use ($keyword){
$q->where('file', 'ILIKE', "%{$keyword}%")
@ -1114,10 +1132,20 @@ class DashboardController extends Controller
public function countDataPending(){
try {
$query = FileDirectory::where('statusenabled', true)
->whereNull('status_action');
$authUnit = auth()->user()->masterPersetujuan->details->pluck('unit_pegawai_id')->unique()->toArray();
$count= $query->whereIn('id_unit_kerja', $authUnit)->count();
$mapping = MappingUnitKerjaPegawai::where('statusenabled', true)
// ->where('objectatasanlangsungfk', auth()->user()->dataUser->id)
->where('objectatasanlangsungfk', 22924)
->get(['objectpegawaifk']);
$objectpegawaifk = $mapping->pluck('objectpegawaifk')
->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->
return response()->json([
'status' => true,
'count' => $count,

View File

@ -3,7 +3,10 @@
namespace App\Http\Controllers;
use App\Models\LogActivity;
use App\Models\MappingUnitKerjaPegawai;
use App\Models\FileDirectory;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class LogActivityController extends Controller
{
@ -19,20 +22,25 @@ class LogActivityController extends Controller
$keyword = request('keyword');
$start = request('start_date');
$end = request('end_date');
$mapping = MappingUnitKerjaPegawai::where('statusenabled', true)
->where('objectpegawaifk', auth()->user()->dataUser->id)
->get(['objectunitkerjapegawaifk', 'objectsubunitkerjapegawaifk']);
$unitIds = $mapping->pluck('objectunitkerjapegawaifk')
->filter() // buang null
->unique()
->values()
->all();
$query = FileDirectory::withCount(['viewLogs as total_views' => function($q){
$q->select(DB::raw('COUNT(DISTINCT pegawai_id_entry)'));
}])
->where('statusenabled', true)
->where('status_action', 'approved')
->whereIn('id_unit_kerja', $unitIds)
->orderBy('entry_at','desc');
$query = LogActivity::query()
->orderBy('entry_at','desc');
if(auth()->user()->masterPersetujuan){
$authUnit = auth()->user()->masterPersetujuan->details->pluck('unit_pegawai_id')->unique()->toArray();
$query = $query->whereIn('id_unit_kerja', $authUnit);
}else{
$query = $query->where('pegawai_id_entry', auth()->user()->objectpegawaifk);
}
if($keyword){
$query->where(function($q) use ($keyword){
$q->where('pegawai_nama_entry', 'ILIKE', "%{$keyword}%")
->orWhere('action_type', 'ILIKE', "%{$keyword}%")
->orWhere('file', 'ILIKE', "%{$keyword}%")
$q->where('file', 'ILIKE', "%{$keyword}%")
->orWhere('no_dokumen', 'ILIKE', "%{$keyword}%");
});
}
@ -46,13 +54,16 @@ class LogActivityController extends Controller
$paginated = $query->paginate($perPage);
$data = $paginated->getCollection()->map(function($item){
$parts = array_values(array_filter(explode('/', $item->file)));
return [
'pegawai_nama_entry' => $item->pegawai_nama_entry,
'action_type' => $item->action_type,
'file' => $item->file,
'no_dokumen' => $item->no_dokumen ?? $item->fileDirectory->no_dokumen ?? '-',
'id' => $item->file_directory_id,
'file' => end($parts),
'no_dokumen' => $item->no_dokumen ?? '-',
'unit' => $parts[0],
'sub_unit' => $parts[1],
'kategori' => $parts[2],
'entry_at' => $item->entry_at,
'unit_name' => $item->unit_name ?? '-',
'total_views' => $item->total_views ?? 0,
];
});
@ -69,4 +80,40 @@ class LogActivityController extends Controller
]
]);
}
public function detailByFile($fileDirectoryId)
{
$perPage = max(1, (int) request('per_page', 10));
$keyword = request('keyword');
$query = LogActivity::select(
'pegawai_id_entry',
'pegawai_nama_entry',
DB::raw('COUNT(*) as total_open'),
DB::raw('MAX(entry_at) as last_open')
)
->where('file_directory_id', $fileDirectoryId)
->where('statusenabled', true)
->where('action_type', 'Membuka Dokumen')
->groupBy('pegawai_id_entry', 'pegawai_nama_entry')
->orderByDesc('total_open');
if($keyword){
$query->havingRaw('pegawai_nama_entry ILIKE ?', ["%{$keyword}%"]);
}
$paginated = $query->paginate($perPage);
$logs = $paginated->items();
return response()->json([
'status' => true,
'data' => $logs,
'pagination' => [
'current_page' => $paginated->currentPage(),
'last_page' => $paginated->lastPage(),
'per_page' => $paginated->perPage(),
'total' => $paginated->total(),
]
]);
}
}

View File

@ -3,6 +3,7 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Models\LogActivity;
class FileDirectory extends Model
{
@ -12,4 +13,11 @@ class FileDirectory extends Model
protected $primaryKey = 'file_directory_id';
protected $guarded = ['file_directory_id'];
public function viewLogs()
{
return $this->hasMany(LogActivity::class, 'file_directory_id', 'file_directory_id')
->where('statusenabled', true)
->where('action_type', 'Membuka Dokumen');
}
}

View File

@ -40,18 +40,19 @@
</button>
<div class="small text-muted ms-md-auto" id="tableSummary"></div>
</div>
<div class="table-responsive" style="max-height: 55vh; overflow-y:auto;">
<div class="table-responsive" style="max-height: 65vh; overflow-y:auto;">
<table class="table table-sm table-hover align-middle mb-0" id="lastUpdatedTable">
<thead>
<tr>
<th>Nama</th>
<th>Unit</th>
<th>Aktivitas</th>
<th>No Dokumen</th>
<th>Tanggal</th>
<th>File</th>
<th>Kategori</th>
<th>Unit</th>
<th>Sub Unit</th>
<th class="text-center">Jumlah Pegawai Melihat</th>
</tr>
</thead>
<tbody id="tableFolderLastUpdated">
<tbody id="tableLogAktivityDocument">
<!-- data dari fetch masuk sini -->
</tbody>
</table>
@ -67,7 +68,7 @@
<script>
document.addEventListener('DOMContentLoaded', () => {
const tableState = { data: [], page: 1, pageSize: 10, search: '', lastPage: 1, total: 0, startDate: '', endDate: '' };
const tbody = document.getElementById('tableFolderLastUpdated');
const tbody = document.getElementById('tableLogAktivityDocument');
const paginationEl = document.getElementById('paginationControls');
const summaryEl = document.getElementById('tableSummary');
const pageSizeSelect = document.getElementById('tablePageSize');
@ -95,20 +96,21 @@ document.addEventListener('DOMContentLoaded', () => {
}
function buildRow(item){
let unitKerja = item.file ? item.file.split('/')[0] : '-';
let tanggal = item.entry_at ? formatTanggal(item.entry_at) : '-'
const tanggal = item.entry_at ? formatTanggal(item.entry_at) : '-';
const totalViews = item.total_views ?? 0;
return `
<tr>
<td>${item.pegawai_nama_entry || '-'}</td>
<td>${unitKerja}</td>
<td>${item.action_type || '-'}</td>
<td>${item.no_dokumen || '-'}</td>
<td class="text-nowrap">${tanggal}</td>
<tr role="button" onclick='showLogDetail(${JSON.stringify(item.id)})'>
<td>${item.no_dokumen}</td>
<td>${item.file}</td>
<td>${item.kategori || '-'}</td>
<td>${item.unit || '-'}</td>
<td>${item.sub_unit || '-'}</td>
<td class="text-center fw-semibold">${totalViews}</td>
</tr>
`;
}
function formatTanggal(dateString) {
function formatTanggal(dateString) {
const d = new Date(dateString);
return d.toLocaleDateString('id-ID', {
day: '2-digit',
@ -119,6 +121,93 @@ document.addEventListener('DOMContentLoaded', () => {
});
}
window.showLogDetail = function(id){
if(!id) return;
const modalEl = document.getElementById('logDetailModal');
const modal = new bootstrap.Modal(modalEl);
modal.show();
renderLogModal(id, 1, '');
}
function renderLogModal(id, page, keyword){
const tbody = document.getElementById('logDetailTbody');
const searchInput = document.getElementById('logSearchInput');
const searchBtn = document.getElementById('logSearchBtn');
const paginationBtns = document.getElementById('logPaginationBtns');
const summaryText = document.getElementById('logSummaryText');
if(tbody) tbody.innerHTML = '<tr><td colspan="4" class="text-center text-muted py-3">Memuat...</td></tr>';
if(summaryText) summaryText.textContent = 'Memuat data...';
if(searchInput) searchInput.value = keyword || '';
const params = new URLSearchParams({ page, per_page: 10, keyword: keyword || '' });
fetch(`/datatable/log-activity/${id}?${params.toString()}`)
.then(res => res.json())
.then(resp => {
const logs = resp?.data || [];
const pagination = resp?.pagination || {};
const totalPages = pagination.last_page || 1;
const currentPage = pagination.current_page || 1;
if(searchInput) searchInput.value = keyword || '';
const rows = logs.map((row, idx) => `
<tr>
<td>${((currentPage - 1) * (pagination.per_page || 10)) + idx + 1}</td>
<td>${row.pegawai_nama_entry || '-'}</td>
<td>${row.total_open || 0}</td>
<td>${formatTanggal(row.last_open)}</td>
</tr>
`).join('');
const emptyState = logs.length === 0 ? '<tr><td colspan="4" class="text-center text-muted py-3">Belum ada aktivitas</td></tr>' : '';
if(tbody) tbody.innerHTML = logs.length ? rows : emptyState;
if(summaryText){
summaryText.textContent = `Menampilkan ${logs.length} dari ${pagination.total ?? logs.length} pegawai`;
}
if(paginationBtns){
let paginationButtons = '';
if(totalPages > 1){
paginationButtons += `<button class="btn btn-sm btn-outline-secondary" data-page="prev" ${currentPage===1?'disabled':''}></button>`;
for(let i=1;i<=totalPages;i++){
paginationButtons += `<button class="btn btn-sm ${i===currentPage?'btn-primary':'btn-outline-secondary'}" data-page="${i}">${i}</button>`;
}
paginationButtons += `<button class="btn btn-sm btn-outline-secondary" data-page="next" ${currentPage===totalPages?'disabled':''}></button>`;
}
paginationBtns.innerHTML = paginationButtons;
paginationBtns.onclick = (e) => {
const target = e.target;
const pageAttr = target.getAttribute('data-page');
if(!pageAttr) return;
let nextPage = currentPage;
if(pageAttr === 'prev') nextPage = Math.max(1, currentPage-1);
else if(pageAttr === 'next') nextPage = Math.min(totalPages, currentPage+1);
else nextPage = parseInt(pageAttr);
renderLogModal(id, nextPage, keyword);
};
}
if(searchBtn){
searchBtn.onclick = () => renderLogModal(id, 1, searchInput.value.trim());
}
if(searchInput){
searchInput.onkeypress = (e) => {
if(e.key === 'Enter') renderLogModal(id, 1, searchInput.value.trim());
};
}
})
.catch(() => {
if(tbody) tbody.innerHTML = '<tr><td colspan="4" class="text-center text-danger py-3">Gagal memuat data</td></tr>';
if(summaryText) summaryText.textContent = 'Gagal memuat data';
if(paginationBtns) paginationBtns.innerHTML = '';
});
}
function renderPagination(totalPages){
if(!paginationEl) return;
if(totalPages <= 1){
@ -164,7 +253,7 @@ document.addEventListener('DOMContentLoaded', () => {
if(pageData.length === 0){
tbody.innerHTML = `
<tr>
<td colspan="5" class="text-center text-muted py-4">
<td colspan="6" class="text-center text-muted py-4">
Tidak ada data
</td>
</tr>
@ -229,3 +318,40 @@ document.addEventListener('DOMContentLoaded', () => {
fetchData();
});
</script>
<!-- Modal Detail Log -->
<div class="modal fade" id="logDetailModal" tabindex="-1" aria-labelledby="logDetailLabel" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="logDetailLabel">Detail Aktivitas</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" id="logDetailBody">
<div class="d-flex align-items-center gap-2 mb-2">
<input type="search" class="form-control form-control-sm" id="logSearchInput" placeholder="Cari nama pegawai">
<button class="btn btn-sm btn-outline-secondary" id="logSearchBtn"><i class="fa fa-search"></i></button>
</div>
<div class="table-responsive">
<table class="table table-sm table-striped mb-0">
<thead class="table-light">
<tr>
<th>#</th>
<th>Nama</th>
<th>Jumlah Membuka</th>
<th>Terakhir Dilihat</th>
</tr>
</thead>
<tbody id="logDetailTbody">
<tr><td colspan="4" class="text-center text-muted py-3">Memuat...</td></tr>
</tbody>
</table>
</div>
<div class="d-flex align-items-center justify-content-between mt-2">
<div class="small text-muted" id="logSummaryText">Menampilkan 0 pegawai</div>
<div class="btn-group" role="group" id="logPaginationBtns"></div>
</div>
</div>
</div>
</div>
</div>

View File

@ -44,6 +44,7 @@ Route::middleware(['auth'])->group(function(){
Route::get('/log-activity', [LogActivityController::class, 'index']);
Route::get('/datatable/log-activity', [LogActivityController::class, 'datatable']);
Route::get('/datatable/log-activity/{fileDirectoryId}', [LogActivityController::class, 'detailByFile']);
Route::get('/recap', [DashboardController::class, 'recapView']);
Route::get('/data/recap', [DashboardController::class, 'recapData']);