progress
This commit is contained in:
parent
98f7cf437f
commit
25d77a8875
@ -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,
|
||||
|
||||
51
resources/views/pitstop/partials/progress_pager.blade.php
Normal file
51
resources/views/pitstop/partials/progress_pager.blade.php
Normal 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>
|
||||
|
||||
31
resources/views/pitstop/partials/progress_rows.blade.php
Normal file
31
resources/views/pitstop/partials/progress_rows.blade.php
Normal 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
|
||||
@ -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) => ({
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
@ -165,6 +134,52 @@
|
||||
"'": ''',
|
||||
}[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>');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user