This commit is contained in:
JokoPrasetio 2026-04-30 19:51:30 +07:00
parent 98f7cf437f
commit 25d77a8875
4 changed files with 163 additions and 49 deletions

View File

@ -19,10 +19,10 @@ class PitStopController extends Controller
return view('pitstop.index', $data);
}
public function progress()
public function progress(Request $request)
{
$q = trim((string) request()->query('q', ''));
$perPage = (int) request()->query('per_page', 20);
$q = trim((string) $request->query('q', ''));
$perPage = (int) $request->query('per_page', 20);
if ($perPage < 1) $perPage = 20;
if ($perPage > 100) $perPage = 100;
@ -63,9 +63,26 @@ class PitStopController extends Controller
->select(DB::raw('count(distinct (p.pegawai_id, m.id)) as c'))
->value('c');
if ($request->boolean('ajax')) {
return response()->json([
'error' => 0,
'data' => [
'totalUsers' => $totalUsers,
'table_html' => view('pitstop.partials.progress_rows', [
'users' => $users,
'totalSteps' => $totalSteps,
])->render(),
'pager_html' => view('pitstop.partials.progress_pager', [
'users' => $users,
])->render(),
],
]);
}
return view('pitstop.progress', [
'title' => 'Dashboard Progress',
'q' => $q,
'perPage' => $perPage,
'totalSteps' => $totalSteps,
'totalUsers' => $totalUsers,
'totalSelesai' => $totalSelesai,

View File

@ -0,0 +1,51 @@
<div class="d-flex flex-wrap justify-content-between align-items-center mt-5 gap-3">
<div class="text-muted fs-7">
Halaman {{ $users->currentPage() }} dari {{ $users->lastPage() }} ({{ $users->total() }} data)
</div>
@php
$current = $users->currentPage();
$last = $users->lastPage();
$start = max(1, $current - 2);
$end = min($last, $current + 2);
@endphp
<div class="d-flex align-items-center gap-2" id="progressPager">
@if ($users->onFirstPage())
<button type="button" class="btn btn-sm btn-light" disabled>Sebelumnya</button>
@else
<a class="btn btn-sm btn-light" href="{{ $users->previousPageUrl() }}">Sebelumnya</a>
@endif
<div class="d-flex align-items-center gap-1">
@if ($start > 1)
<a class="btn btn-sm btn-light" href="{{ $users->url(1) }}">1</a>
@if ($start > 2)
<span class="px-2 text-muted">...</span>
@endif
@endif
@for ($i = $start; $i <= $end; $i++)
@if ($i === $current)
<button type="button" class="btn btn-sm btn-primary" disabled>{{ $i }}</button>
@else
<a class="btn btn-sm btn-light" href="{{ $users->url($i) }}">{{ $i }}</a>
@endif
@endfor
@if ($end < $last)
@if ($end < $last - 1)
<span class="px-2 text-muted">...</span>
@endif
<a class="btn btn-sm btn-light" href="{{ $users->url($last) }}">{{ $last }}</a>
@endif
</div>
@if ($users->hasMorePages())
<a class="btn btn-sm btn-light" href="{{ $users->nextPageUrl() }}">Berikutnya</a>
@else
<button type="button" class="btn btn-sm btn-light" disabled>Berikutnya</button>
@endif
</div>
</div>

View File

@ -0,0 +1,31 @@
@forelse ($users as $row)
@php
$done = (int) $row->lulus_count;
$pct = $totalSteps > 0 ? round(($done / $totalSteps) * 100) : 0;
@endphp
<tr>
<td>
<a href="#" class="text-gray-800 text-hover-primary viewDetail" data-id="{{ $row->id }}" data-nama="{{ e($row->nama) }}">
{{ $row->nama }}
</a>
</td>
<td>
<div class="progress bg-light" style="height: 18px;">
<div
class="progress-bar bg-success d-flex align-items-center justify-content-center fw-semibold"
role="progressbar"
style="width: {{ $pct }}%; font-size: 12px;"
aria-valuenow="{{ $pct }}"
aria-valuemin="0"
aria-valuemax="100"
>
{{ $done }} / {{ $totalSteps }}
</div>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="2" class="text-center text-muted py-6">Belum ada data</td>
</tr>
@endforelse

View File

@ -25,7 +25,7 @@
<div class="card bg-light-info border-0 h-100">
<div class="card-body">
<div class="fw-semibold text-muted">Total Karyawan</div>
<div class="fs-2hx fw-bold mt-2">{{ $totalUsers }}</div>
<div class="fs-2hx fw-bold mt-2" id="totalUsersVal">{{ $totalUsers }}</div>
</div>
</div>
</div>
@ -49,13 +49,18 @@
<h3 class="mb-0 fw-bold">Progress per Karyawan</h3>
</div>
<div class="card-toolbar">
<form method="get" class="d-flex align-items-center gap-2">
<form method="get" class="d-flex align-items-center gap-2" id="filterForm">
<div class="d-flex align-items-center position-relative my-1">
<span class="svg-icon svg-icon-1 position-absolute ms-4">
<i class="fa-solid fa-magnifying-glass"></i>
</span>
<input type="text" name="q" value="{{ $q ?? '' }}" class="form-control form-control-solid w-250px ps-12" placeholder="Cari nama..." />
</div>
<select name="per_page" class="form-select form-select-solid w-125px">
@foreach ([10, 20, 50, 100] as $n)
<option value="{{ $n }}" @selected(((int) ($perPage ?? 20)) === $n)>{{ $n }}/page</option>
@endforeach
</select>
<button type="submit" class="btn btn-sm btn-primary">Cari</button>
</form>
</div>
@ -67,57 +72,16 @@
<tr class="text-muted fw-semibold fs-7 text-uppercase gs-0">
<th>Nama</th>
<th style="width: 320px">Progress</th>
<th class="text-end">Step Lulus</th>
</tr>
</thead>
<tbody class="fw-semibold text-gray-800" id="tableUsers">
@forelse ($users as $row)
@php
$done = (int) $row->lulus_count;
$pct = $totalSteps > 0 ? round(($done / $totalSteps) * 100) : 0;
@endphp
<tr>
<td>
<a href="#" class="text-gray-800 text-hover-primary viewDetail" data-id="{{ $row->id }}" data-nama="{{ e($row->nama) }}">
{{ $row->nama }}
</a>
</td>
<td>
<div class="d-flex align-items-center">
<div class="progress h-6px w-100 me-3 bg-light">
<div class="progress-bar bg-success" role="progressbar" style="width: {{ $pct }}%"></div>
</div>
<div class="text-muted fw-bold">{{ $pct }}%</div>
</div>
</td>
<td class="text-end">{{ $done }} / {{ $totalSteps }}</td>
</tr>
@empty
<tr>
<td colspan="3" class="text-center text-muted py-6">Belum ada data</td>
</tr>
@endforelse
@include('pitstop.partials.progress_rows', ['users' => $users, 'totalSteps' => $totalSteps])
</tbody>
</table>
</div>
<div class="d-flex flex-wrap justify-content-between align-items-center mt-5">
<div class="text-muted fs-7">
Halaman {{ $users->currentPage() }} dari {{ $users->lastPage() }} ({{ $users->total() }} data)
</div>
<div class="d-flex gap-2">
@if ($users->onFirstPage())
<button type="button" class="btn btn-sm btn-light" disabled>Sebelumnya</button>
@else
<a class="btn btn-sm btn-light" href="{{ $users->previousPageUrl() }}">Sebelumnya</a>
@endif
@if ($users->hasMorePages())
<a class="btn btn-sm btn-light" href="{{ $users->nextPageUrl() }}">Berikutnya</a>
@else
<button type="button" class="btn btn-sm btn-light" disabled>Berikutnya</button>
@endif
</div>
<div id="pagerWrap">
@include('pitstop.partials.progress_pager', ['users' => $users])
</div>
</div>
</div>
@ -157,6 +121,11 @@
@section('custom_js')
<script>
$(document).ready(function () {
const $filterForm = $('#filterForm');
const $pagerWrap = $('#pagerWrap');
const $tableUsers = $('#tableUsers');
const $totalUsersVal = $('#totalUsersVal');
const escapeHtml = (str) => String(str ?? '').replace(/[&<>"']/g, (m) => ({
'&': '&amp;',
'<': '&lt;',
@ -165,6 +134,52 @@
"'": '&#39;',
}[m]));
const withAjaxParam = (href) => {
const url = new URL(href, window.location.origin);
url.searchParams.set('ajax', '1');
return url.toString();
};
const setLoading = () => {
$tableUsers.html('<tr><td colspan="2" class="text-center text-muted py-6">Memuat data...</td></tr>');
};
const loadPage = (href, push = true) => {
setLoading();
$.get(withAjaxParam(href))
.done(function (res) {
const data = res?.data ?? {};
$totalUsersVal.text(data.totalUsers ?? 0);
$tableUsers.html(data.table_html ?? '');
$pagerWrap.html(data.pager_html ?? '');
if (push) {
const urlNoAjax = new URL(href, window.location.origin);
urlNoAjax.searchParams.delete('ajax');
window.history.pushState({}, '', urlNoAjax.toString());
}
})
.fail(function () {
$tableUsers.html('<tr><td colspan="2" class="text-center text-muted py-6">Gagal memuat data</td></tr>');
});
};
$filterForm.on('submit', function (e) {
e.preventDefault();
const href = `${window.location.pathname}?${$filterForm.serialize()}`;
loadPage(href, true);
});
$(document).on('click', '#progressPager a', function (e) {
e.preventDefault();
loadPage($(this).attr('href'), true);
});
window.addEventListener('popstate', function () {
loadPage(window.location.href, false);
});
const renderDetail = (rows) => {
if (!rows.length) {
$('#detailTable').html('<tr><td colspan="4" class="text-center text-muted py-6">Belum ada data</td></tr>');