300 lines
8.6 KiB
PHP
300 lines
8.6 KiB
PHP
@extends('partials.main')
|
|
|
|
@section('custom_css')
|
|
<style>
|
|
#resultList {
|
|
max-height: 420px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
/* Catatan: styling <option> tergantung browser, tapi ini membantu di banyak kasus */
|
|
#step option[data-locked='1'] {
|
|
color: var(--bs-success) !important;
|
|
-webkit-text-fill-color: var(--bs-success);
|
|
}
|
|
</style>
|
|
@endsection
|
|
|
|
@section('content')
|
|
<div class="card card-flush">
|
|
<div class="card-header pt-7">
|
|
<div class="card-title">
|
|
<h2 class="mb-0 fw-bold">PitStop Pra Akreditasi</h2>
|
|
</div>
|
|
<div class="card-toolbar">
|
|
<span class="text-muted">Cari karyawan untuk input status per step</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-body pt-0">
|
|
<div class="w-100">
|
|
<label for="searchKaryawan" class="form-label fw-semibold">Cari Karyawan</label>
|
|
<input
|
|
type="text"
|
|
id="searchKaryawan"
|
|
class="form-control"
|
|
placeholder="Ketik nama / NIP..."
|
|
autocomplete="off"
|
|
aria-label="Cari karyawan"
|
|
/>
|
|
<ul class="list-group mt-3" id="resultList"></ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="modalPitstop" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<form id="formPitstop">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Input PitStop</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
|
|
<div class="modal-body">
|
|
<input type="hidden" id="karyawan_id" name="karyawan_id" />
|
|
|
|
<div class="mb-4">
|
|
<label for="nama_karyawan" class="form-label">Nama Karyawan</label>
|
|
<input type="text" id="nama_karyawan" class="form-control" readonly />
|
|
</div>
|
|
|
|
<div class="notice d-flex bg-light-warning rounded border-warning border border-dashed p-4 mb-5">
|
|
<div class="d-flex flex-stack flex-grow-1">
|
|
<div class="fw-semibold">
|
|
<div class="text-gray-900 fw-bold">Info</div>
|
|
<div class="text-gray-700">Step akan terkunci jika sudah berstatus lulus.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<label for="step" class="form-label">Step</label>
|
|
<select id="step" name="step" class="form-select" required>
|
|
@forelse ($masterPitStop as $ps)
|
|
<option value="{{ $ps->id }}">{{ $ps->nama }}</option>
|
|
@empty
|
|
<option value="" disabled selected>Tidak ada data</option>
|
|
@endforelse
|
|
</select>
|
|
<div class="form-text" id="stepHint"></div>
|
|
</div>
|
|
|
|
<div class="mb-2">
|
|
<label class="form-label d-block">Status</label>
|
|
<div class="d-flex flex-wrap gap-6">
|
|
<label class="form-check form-check-sm form-check-custom form-check-solid">
|
|
<input class="form-check-input" type="radio" name="status" value="lulus" required />
|
|
<span class="form-check-label">Lulus</span>
|
|
</label>
|
|
<label class="form-check form-check-sm form-check-custom form-check-solid">
|
|
<input class="form-check-input" type="radio" name="status" value="tidak_lulus" required />
|
|
<span class="form-check-label">Tidak Lulus</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Batal</button>
|
|
<button type="submit" class="btn btn-primary">
|
|
<span class="indicator-label">Simpan</span>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endsection
|
|
|
|
@section('custom_js')
|
|
<script>
|
|
$(document).ready(function () {
|
|
let searchTimer = null;
|
|
let activeRequest = null;
|
|
|
|
const $search = $('#searchKaryawan');
|
|
const $resultList = $('#resultList');
|
|
const $form = $('#formPitstop');
|
|
const $step = $('#step');
|
|
const $stepHint = $('#stepHint');
|
|
|
|
const csrf = $('meta[name="csrf-token"]').attr('content');
|
|
|
|
const renderMessage = (message) => {
|
|
$resultList.html(`<li class="list-group-item text-muted">${message}</li>`);
|
|
};
|
|
|
|
const clearResults = () => {
|
|
$resultList.empty();
|
|
};
|
|
|
|
const searchKaryawan = (keyword) => {
|
|
if (activeRequest) activeRequest.abort();
|
|
|
|
renderMessage('Mencari...');
|
|
|
|
activeRequest = $.get('/list-karyawan', { search: keyword, per_page: 20 })
|
|
.done(function (res) {
|
|
const data = res?.data ?? [];
|
|
if (!data.length) {
|
|
renderMessage('Data tidak ditemukan.');
|
|
return;
|
|
}
|
|
|
|
const html = data
|
|
.map((item) => {
|
|
const nama = item?.namalengkap ?? '-';
|
|
return `
|
|
<li class="list-group-item list-group-item-action pilihKaryawan"
|
|
role="button"
|
|
data-id="${item.id}"
|
|
data-nama="${nama}"
|
|
>
|
|
${nama}
|
|
</li>`;
|
|
})
|
|
.join('');
|
|
|
|
$resultList.html(html);
|
|
})
|
|
.fail(function (xhr, status) {
|
|
if (status === 'abort') return;
|
|
renderMessage('Gagal mengambil data.');
|
|
});
|
|
};
|
|
|
|
const lockExistingSteps = (pegawaiId) => {
|
|
$stepHint.html('');
|
|
$step.find('option').each(function () {
|
|
const $opt = $(this);
|
|
const original = $opt.attr('data-original');
|
|
if (original) $opt.text(original);
|
|
$opt.prop('disabled', false).removeAttr('data-locked').removeAttr('data-original');
|
|
});
|
|
$form.find('button[type="submit"]').prop('disabled', false);
|
|
|
|
return $.get('/pitstop/pegawai-steps', { pegawai_id: pegawaiId })
|
|
.done(function (res) {
|
|
const lockedSteps = res?.data?.locked_steps ?? [];
|
|
const locked = new Set(lockedSteps.map((v) => String(v)));
|
|
|
|
$step.find('option').each(function () {
|
|
const val = String($(this).val());
|
|
if (locked.has(val)) {
|
|
const $opt = $(this);
|
|
$opt.attr('data-original', $opt.text());
|
|
$opt.text(`${$opt.text()} (Selesai)`);
|
|
$opt.prop('disabled', true).attr('data-locked', '1');
|
|
}
|
|
});
|
|
|
|
if ($step.find('option:selected').prop('disabled')) {
|
|
const $firstEnabled = $step.find('option:not([disabled])').first();
|
|
if ($firstEnabled.length) $step.val($firstEnabled.val());
|
|
}
|
|
|
|
const enabledCount = $step.find('option:not([disabled])').length;
|
|
if (!enabledCount) {
|
|
$form.find('button[type="submit"]').prop('disabled', true);
|
|
}
|
|
|
|
const selectedLocked = $step.find('option:selected').attr('data-locked') === '1';
|
|
if (selectedLocked) {
|
|
$stepHint.html('<span class="text-success fw-semibold">Selesai</span>');
|
|
}
|
|
})
|
|
.fail(function () {
|
|
// kalau gagal ambil data lock, tetap biarkan user submit (server akan validasi)
|
|
});
|
|
};
|
|
|
|
$step.on('change', function () {
|
|
const selectedLocked = $step.find('option:selected').attr('data-locked') === '1';
|
|
$stepHint.html(selectedLocked ? '<span class="text-success fw-semibold">Selesai</span>' : '');
|
|
});
|
|
|
|
$search.on('keyup', function () {
|
|
const keyword = String($(this).val() ?? '').trim();
|
|
|
|
if (keyword.length < 2) {
|
|
if (activeRequest) activeRequest.abort();
|
|
clearResults();
|
|
return;
|
|
}
|
|
|
|
clearTimeout(searchTimer);
|
|
searchTimer = setTimeout(() => searchKaryawan(keyword), 250);
|
|
});
|
|
|
|
$(document).on('click', '.pilihKaryawan', function () {
|
|
$form.trigger('reset');
|
|
$('#nama_karyawan').val($(this).data('nama'));
|
|
$('#karyawan_id').val($(this).data('id'));
|
|
lockExistingSteps($(this).data('id'));
|
|
new bootstrap.Modal(document.getElementById('modalPitstop')).show();
|
|
});
|
|
|
|
$form.on('submit', function (e) {
|
|
e.preventDefault();
|
|
|
|
if ($step.find('option:selected').attr('data-locked') === '1') {
|
|
Swal.fire({
|
|
toast: true,
|
|
position: 'top-end',
|
|
icon: 'info',
|
|
title: 'Step ini sudah lulus dan terkunci.',
|
|
showConfirmButton: false,
|
|
timer: 2200,
|
|
timerProgressBar: true,
|
|
});
|
|
return;
|
|
}
|
|
|
|
const payload = {
|
|
karyawan_id: $('#karyawan_id').val(),
|
|
step: $('#step').val(),
|
|
status: $('input[name="status"]:checked').val(),
|
|
};
|
|
|
|
$.ajax({
|
|
url: '/pitstop/submit',
|
|
method: 'POST',
|
|
headers: { 'X-CSRF-TOKEN': csrf },
|
|
data: payload,
|
|
})
|
|
.done(function () {
|
|
const modalEl = document.getElementById('modalPitstop');
|
|
const modal = bootstrap.Modal.getInstance(modalEl);
|
|
if (modal) modal.hide();
|
|
|
|
Swal.fire({
|
|
toast: true,
|
|
position: 'top-end',
|
|
icon: 'success',
|
|
title: 'Berhasil submit pitstop.',
|
|
showConfirmButton: false,
|
|
timer: 2000,
|
|
timerProgressBar: true,
|
|
});
|
|
})
|
|
.fail(function (xhr) {
|
|
const msg =
|
|
xhr?.responseJSON?.message ||
|
|
(xhr?.status === 422 ? 'Validasi gagal.' : 'Gagal menyimpan data.');
|
|
|
|
Swal.fire({
|
|
toast: true,
|
|
position: 'top-end',
|
|
icon: 'error',
|
|
title: msg,
|
|
showConfirmButton: false,
|
|
timer: 2600,
|
|
timerProgressBar: true,
|
|
});
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
@endsection
|