progress
This commit is contained in:
parent
b1c0251586
commit
ecc950f19d
@ -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,
|
||||
|
||||
@ -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(),
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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']);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user