450 lines
15 KiB
PHP
450 lines
15 KiB
PHP
@extends('partials.main_auth')
|
|
|
|
@section('custom_css')
|
|
<style>
|
|
.table td, .table th {
|
|
vertical-align: middle !important;
|
|
}
|
|
.progress {
|
|
background-color: #e9ecef;
|
|
}
|
|
.container-fluid {
|
|
padding: 0 !important;
|
|
}
|
|
|
|
.left-panel {
|
|
background: linear-gradient(135deg, #eef2f7, #f8f9fb);
|
|
min-height: 100vh;
|
|
padding: 30px;
|
|
}
|
|
|
|
.right-panel {
|
|
background: #ffffff;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 24px;
|
|
}
|
|
|
|
.card-custom {
|
|
border-radius: 12px;
|
|
box-shadow: 0 8px 25px rgba(0,0,0,0.08);
|
|
border: none;
|
|
}
|
|
|
|
.login-card {
|
|
width: 100%;
|
|
max-width: 400px;
|
|
}
|
|
|
|
.progress {
|
|
border-radius: 10px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.table thead th {
|
|
font-size: 12px;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
@media (max-width: 575.98px) {
|
|
.hide-xs {
|
|
display: none !important;
|
|
}
|
|
.right-panel {
|
|
min-height: auto;
|
|
padding: 20px 16px;
|
|
align-items: flex-start;
|
|
}
|
|
.left-panel {
|
|
min-height: auto;
|
|
padding: 16px;
|
|
}
|
|
.login-hero-title {
|
|
font-size: 22px;
|
|
line-height: 1.25;
|
|
}
|
|
.login-monitor-card {
|
|
border-radius: 12px;
|
|
box-shadow: 0 8px 25px rgba(0,0,0,0.06);
|
|
border: none;
|
|
}
|
|
#searchAllKaryawan {
|
|
width: 100% !important;
|
|
}
|
|
}
|
|
|
|
@media (min-width: 576px) {
|
|
.right-panel {
|
|
min-height: 100vh;
|
|
}
|
|
}
|
|
</style>
|
|
@endsection
|
|
|
|
@section('content')
|
|
<div class="container-fluid">
|
|
<div class="row g-0">
|
|
<div class="col-12 col-md-4 right-panel order-1 order-md-2">
|
|
<div class="w-100" style="max-width: 440px;">
|
|
<div class="text-center mb-8">
|
|
<img src="{{ asset('assets/img/logo-fullname.png') }}" alt="Logo" class="img-fluid" style="max-height: 72px;">
|
|
<h1 class="mt-6 mb-0 fw-bold login-hero-title">Login Admin Pra Akreditasi</h1>
|
|
<div class="text-muted mt-2">Masuk untuk mengelola penilaian dan monitoring.</div>
|
|
</div>
|
|
<div class="card card-custom">
|
|
<div class="card-body p-8 p-md-10">
|
|
<form id="form_login" class="form" action="/login" autocomplete="off" method="POST">
|
|
@csrf
|
|
<div class="fv-row mb-7">
|
|
<label class="required fw-semibold fs-6 mb-2">Username</label>
|
|
<input type="text" name="namauser" class="form-control mb-3 mb-lg-0" placeholder="" value="" />
|
|
</div>
|
|
<div class="fv-row mb-7">
|
|
<label class="required fw-semibold fs-6 mb-2">Password</label>
|
|
<div class="input-group mb-3">
|
|
<input type="password" name="password" id="password" class="form-control" placeholder="Password">
|
|
<button class="btn btn-primary" type="button" id="togglePassword">
|
|
<i class="fa fa-eye"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<button id="form_permintaan_submit" type="submit" class="btn btn-primary w-100 mt-10">
|
|
<span class="indicator-label">
|
|
Login
|
|
</span>
|
|
<span class="indicator-progress">
|
|
Please wait... <span class="spinner-border spinner-border-sm align-middle ms-2"></span>
|
|
</span>
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-12 col-md-8 left-panel order-2 order-md-1 d-flex align-items-start align-items-md-center justify-content-center">
|
|
<div class="w-100" style="max-width: 980px;">
|
|
<div class="card login-monitor-card">
|
|
<div class="card-body table-responsive">
|
|
<div class="d-flex flex-column flex-sm-row justify-content-between align-items-start align-items-sm-center gap-3 mb-4">
|
|
<h4 class="fw-bold mb-0">Monitoring Pra Akreditasi</h4>
|
|
<input type="text" id="searchAllKaryawan"
|
|
class="form-control w-250px"
|
|
placeholder="Cari karyawan...">
|
|
</div>
|
|
<table id="tblAllKaryawan" class="table align-middle table-row-dashed fs-6 gy-3 w-100">
|
|
<thead>
|
|
<tr class="text-muted fw-semibold fs-7 text-uppercase gs-0">
|
|
<th>Nama</th>
|
|
<th>Unit / Tipe</th>
|
|
<th style="width: 320px">Progress</th>
|
|
<th>Belum dikerjakan</th>
|
|
<th class="text-end">Jenis</th>
|
|
<th class="text-end">Aksi</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="fw-semibold text-gray-800"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endsection
|
|
|
|
<div class="modal fade" id="modalAllDetail" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Detail Progress</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="text-muted mb-4" id="allDetailNama"></div>
|
|
<div class="table-responsive">
|
|
<table class="table align-middle table-row-dashed fs-6 gy-3">
|
|
<thead>
|
|
<tr class="text-muted fw-semibold fs-7 text-uppercase gs-0">
|
|
<th>Nama Step</th>
|
|
<th>Nilai</th>
|
|
<th class="text-end">Waktu</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="fw-semibold text-gray-800" id="allDetailTable">
|
|
<tr>
|
|
<td colspan="4" class="text-center text-muted py-6">Memuat data...</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="modalBelum" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">PitStop Belum Dikerjakan</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="text-dark fw-semibold mb-4" id="belumNama"></div>
|
|
<ul id="belumList" class="mb-0 ps-5"></ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@section('custom_js')
|
|
<script>
|
|
@if (session('error'))
|
|
toastr.error("{{ session('error') }}");
|
|
@endif
|
|
function escapeHtml(text) {
|
|
return $('<div>').text(text).html();
|
|
}
|
|
$(document).ready(function () {
|
|
|
|
// =========================
|
|
// TOGGLE PASSWORD
|
|
// =========================
|
|
$('#togglePassword').on('click', function () {
|
|
let input = $('#password');
|
|
let icon = $(this).find('i');
|
|
|
|
if (input.attr('type') === 'password') {
|
|
input.attr('type', 'text');
|
|
icon.removeClass('fa-eye').addClass('fa-eye-slash');
|
|
} else {
|
|
input.attr('type', 'password');
|
|
icon.removeClass('fa-eye-slash').addClass('fa-eye');
|
|
}
|
|
});
|
|
|
|
// =========================
|
|
// TOTAL STEP
|
|
// =========================
|
|
const totalSteps = Number(@json((int) ($totalSteps ?? 0)));
|
|
|
|
// =========================
|
|
// ESCAPE HTML (WAJIB ADA)
|
|
// =========================
|
|
|
|
|
|
// =========================
|
|
// FORM VALIDATION (FIXED)
|
|
// =========================
|
|
const form = document.getElementById('form_login');
|
|
|
|
var validator = FormValidation.formValidation(form, {
|
|
fields: {
|
|
'namauser': {
|
|
validators: {
|
|
notEmpty: {
|
|
message: 'Username masih kosong'
|
|
}
|
|
}
|
|
},
|
|
'password': {
|
|
validators: {
|
|
notEmpty: {
|
|
message: 'Password masih kosong'
|
|
}
|
|
}
|
|
}
|
|
},
|
|
plugins: {
|
|
trigger: new FormValidation.plugins.Trigger(),
|
|
bootstrap: new FormValidation.plugins.Bootstrap5({
|
|
rowSelector: '.fv-row'
|
|
})
|
|
}
|
|
});
|
|
|
|
$('#form_permintaan_submit').on('click', function (e) {
|
|
e.preventDefault();
|
|
|
|
validator.validate().then(function (status) {
|
|
if (status === 'Valid') {
|
|
form.submit();
|
|
}
|
|
});
|
|
});
|
|
|
|
// =========================
|
|
// DATATABLE INIT (FIXED)
|
|
// =========================
|
|
const allTable = $('#tblAllKaryawan').DataTable({
|
|
processing: true,
|
|
serverSide: true,
|
|
searchDelay: 350,
|
|
dom: 'rtip',
|
|
pageLength: 10,
|
|
order: [[2, 'desc']],
|
|
ajax: {
|
|
url: '/data/progress-all-karyawan-ghost',
|
|
type: 'GET',
|
|
},
|
|
columns: [
|
|
{
|
|
data: null,
|
|
render: function (row) {
|
|
const nama = escapeHtml(row.nama ?? '-');
|
|
// const idt = escapeHtml(row.identitas ?? '-');
|
|
// const label = row.tipe_karyawan === 'luar' ? 'NIK' : 'NIP';
|
|
|
|
return `
|
|
<div>
|
|
<div class="fw-bold">${nama}</div>
|
|
</div>
|
|
`;
|
|
}
|
|
},
|
|
{
|
|
data: 'unit_name',
|
|
render: data => `<span>${escapeHtml(data ?? '-')}</span>`
|
|
},
|
|
{
|
|
data: null,
|
|
orderable: false,
|
|
render: function (row) {
|
|
const pct = Number(row.pct ?? 0);
|
|
|
|
return `
|
|
<div>
|
|
<div class="d-flex justify-content-between small text-muted">
|
|
<span>${row.lulus_count ?? 0} / ${totalSteps}</span>
|
|
<span>${pct}%</span>
|
|
</div>
|
|
<div class="progress mt-1" style="height:6px;">
|
|
<div class="progress-bar bg-success" style="width:${pct}%"></div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
},
|
|
{
|
|
data: null,
|
|
className: 'text-center',
|
|
orderable: false,
|
|
render: function (data, type, row) {
|
|
const c = Number(row.belum_count ?? 0);
|
|
if (c <= 0) return '<span class="text-muted">-</span>';
|
|
return `<a href="#" class="showBelum" data-nama="${escapeHtml(row.nama)}" data-steps="${escapeHtml(row.belum_steps ?? '')}"><span class="badge badge-warning text-dark">${c} pitstop</span></a>`;
|
|
},
|
|
},
|
|
{
|
|
data: 'tipe_karyawan',
|
|
className: 'text-end',
|
|
render: function (data) {
|
|
return data === 'luar'
|
|
? '<span class="badge bg-warning text-dark">Eksternal</span>'
|
|
: '<span class="badge bg-primary text-white">Internal</span>';
|
|
}
|
|
},
|
|
{
|
|
data: null,
|
|
className: 'text-end',
|
|
orderable: false,
|
|
render: function (row) {
|
|
return `
|
|
<button class="btn btn-sm btn-primary viewDetailAll" data-id="${row.id}" data-nama="${row.nama}">
|
|
Detail
|
|
</button>
|
|
`;
|
|
}
|
|
}
|
|
]
|
|
});
|
|
// =========================
|
|
// SEARCH FIX (DEBOUNCE)
|
|
// =========================
|
|
let timer = null;
|
|
|
|
$('#searchAllKaryawan').on('keyup', function () {
|
|
const val = $(this).val();
|
|
|
|
clearTimeout(timer);
|
|
timer = setTimeout(() => {
|
|
allTable.search(val).draw();
|
|
}, 300);
|
|
});
|
|
|
|
$(document).on('click', '.viewDetailAll', function (e) {
|
|
e.preventDefault();
|
|
|
|
const id = Number($(this).data('id'));
|
|
const tipe = String($(this).data('tipe') ?? 'internal');
|
|
const nama = String($(this).data('nama') ?? '-');
|
|
|
|
$('#allDetailNama').text(`${nama}`);
|
|
$('#allDetailTable').html('<tr><td colspan="4" class="text-center text-muted py-6">Memuat data...</td></tr>');
|
|
|
|
const url = tipe === 'luar' ? '/pitstop/progress-detail-external-ghost' : '/pitstop/progress-detail-ghost';
|
|
$.get(url, { pegawai_id: id })
|
|
.done(function (res) {
|
|
renderDetailRows(res?.data ?? []);
|
|
})
|
|
.fail(function () {
|
|
$('#allDetailTable').html('<tr><td colspan="4" class="text-center text-muted py-6">Gagal memuat data</td></tr>');
|
|
});
|
|
|
|
new bootstrap.Modal(document.getElementById('modalAllDetail')).show();
|
|
});
|
|
|
|
const renderDetailRows = (rows) => {
|
|
if (!rows.length) {
|
|
$('#allDetailTable').html('<tr><td colspan="4" class="text-center text-muted py-6">Belum ada data</td></tr>');
|
|
return;
|
|
}
|
|
|
|
const html = rows
|
|
.map((r) => {
|
|
const status = String(r.status ?? '-');
|
|
const badge =
|
|
status === 'lulus'
|
|
? '<span class="badge badge-light-success">Lulus</span>'
|
|
: status === 'tidak_lulus'
|
|
? '<span class="badge badge-light-danger">Tidak Lulus</span>'
|
|
: `<span class="badge badge-light">${escapeHtml(status)}</span>`;
|
|
|
|
const waktu = r.created_at ? String(r.created_at) : '-';
|
|
|
|
return `
|
|
<tr>
|
|
<td>${escapeHtml(r.step_nama ?? '-')}</td>
|
|
<td>${r.nilai}</td>
|
|
<td class="text-end">${escapeHtml(waktu)}</td>
|
|
</tr>`;
|
|
})
|
|
.join('');
|
|
|
|
$('#allDetailTable').html(html);
|
|
};
|
|
|
|
});
|
|
$(document).on('click', '.showBelum', function (e) {
|
|
e.preventDefault();
|
|
const nama = $(this).data('nama');
|
|
const nip = $(this).data('nip');
|
|
const stepsRaw = String($(this).data('steps') ?? '').trim();
|
|
|
|
$('#belumNama').text(nama + ' (NIP ' + nip +')');
|
|
|
|
if (!stepsRaw) {
|
|
$('#belumList').html('<li class="text-muted">Tidak ada data.</li>');
|
|
} else {
|
|
const steps = stepsRaw.split(',').map((s) => s.trim()).filter(Boolean);
|
|
const html = steps.map((s) => `<li><span class="fw-semibold">${escapeHtml(s)}</span></li>`).join('');
|
|
$('#belumList').html(html || '<li class="text-muted">Tidak ada data.</li>');
|
|
}
|
|
|
|
new bootstrap.Modal(document.getElementById('modalBelum')).show();
|
|
});
|
|
|
|
</script>
|
|
@endsection
|