forked from mahdahar/crm-summit
507 lines
24 KiB
PHP
507 lines
24 KiB
PHP
<?= $this->extend('layouts/main.php') ?>
|
|
|
|
<?= $this->section('content') ?>
|
|
|
|
<div class="page-wrapper">
|
|
<div class="container-fluid">
|
|
<div class="row page-titles">
|
|
<div class="col-md-5 align-self-center">
|
|
<h4 class="text-themecolor">Certificates Maintenance Management</h4>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="row mb-3">
|
|
<div class="col-12 mb-3">
|
|
<div class="input-group input-group-sm">
|
|
<span class="input-group-text"><i class="fas fa-search"></i></span>
|
|
<input type="text" id="searchInput" class="form-control"
|
|
placeholder="Search certificates by name, product, type, vendor, or dates...">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3 mb-2">
|
|
<select id="productFilter" class="form-select form-select-sm">
|
|
<option value="">--Product Filter--</option>
|
|
<option value="tms">TMS</option>
|
|
<option value="jokoh">Jokoh</option>
|
|
<option value="mindray">Mindray</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3 mb-2">
|
|
<select id="statusFilter" class="form-select form-select-sm">
|
|
<option value="">--Status Filter--</option>
|
|
<option value="active">Active</option>
|
|
<option value="expired">Expired</option>
|
|
<option value="expiring">Expiring Soon</option>
|
|
<option value="not">Not Available</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3 mb-2">
|
|
<select id="validationFilter" class="form-select form-select-sm">
|
|
<option value="">--Validation Filter--</option>
|
|
<option value="valid">Validated</option>
|
|
<option value="unval">Unvalidated</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3 mb-2">
|
|
<button onclick="resetFilters()" class="btn btn-secondary btn-sm w-100">
|
|
<i class="fas fa-redo"></i> Reset Filters
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- TABLE -->
|
|
<div class="table-responsive" style="width: 100%;">
|
|
<table id="certificatesTable" class="table table-striped table-hover border" style="width: 100%;">
|
|
<thead class="table-primary">
|
|
<tr>
|
|
<!-- <th class="text-center" style="width: 3%">No</th> -->
|
|
<th style="width: 23%">Certificate</th>
|
|
<th style="width: 28%">Act Report</th>
|
|
<th style="width: 10%">Issue Date</th>
|
|
<th style="width: 10%">Expiry Date</th>
|
|
<th style="width: 9%">Status</th>
|
|
<th style="width: 8%">Validation</th>
|
|
<th class="text-center" style="width: 12%">Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Validate Modal -->
|
|
<div class="modal fade" id="validateModal" tabindex="-1" aria-labelledby="validateModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-xl modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header bg-warning text-dark" id='modalHeader'>
|
|
<h5 class="modal-title" id="validateModalLabel">
|
|
Validate Maintenance Certificate
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"
|
|
aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="row border-bottom mb-3 pb-2">
|
|
<div class="col-md-8">
|
|
<!-- <label class="form-label small mb-0"><i class="fa-solid fa-certificate me-2"></i>Certificate Name</label> -->
|
|
<label class="form-label "><i class="fa-solid fa-certificate me-2"></i>Certificate Name</label>
|
|
<h5 id="modalCertName" class="mt-2 fw-bolder">-</h5>
|
|
</div>
|
|
<div class="col-md-4 text-md-end">
|
|
<h5 id="modalValidation">-</h5>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label "><i class="fa-solid fa-book-medical me-2"></i>Certificate Number</label>
|
|
<p id="modalCertNumber" class="form-control-plaintext border-bottom fw-bolder">-</p>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label "><i class="fa-solid fa-microchip me-2"></i>Product/Equipment</label>
|
|
<p id="modalProductName" class="form-control-plaintext border-bottom fw-bolder">-</p>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label "><i class="fa-solid fa-hashtag me-2"></i>Serial Number</label>
|
|
<p id="modalProductNumber" class="form-control-plaintext border-bottom fw-bolder">-</p>
|
|
</div>
|
|
<div class="col-md-6 mb-3">
|
|
<label class="form-label "><i class="fa-regular fa-hospital me-2"></i>Site</label>
|
|
<p id="modalSiteName" class="form-control-plaintext border-bottom fw-bolder">-</p>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label "><i class="fa-solid fa-calendar-check me-2"></i>Issue Date</label>
|
|
<p id="modalIssueDate" class="form-control-plaintext border-bottom fw-bolder">-</p>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label "><i class="fa-solid fa-calendar-xmark me-2"></i>Expiry Date</label>
|
|
<p id="modalExpiryDate" class="form-control-plaintext border-bottom fw-bolder">-</p>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label">Expired Status</label>
|
|
<p id="modalExpiredStatus" class="form-control-plaintext border-bottom">-</p>
|
|
</div>
|
|
|
|
<div class="col-md-12 mb-4">
|
|
<label class="form-label "><i class="fa-solid fa-file-lines me-2"></i>Activity Report Reference</label>
|
|
<a href="javascript:void(0)" id="modalActivityLink" class="activity-report-link text-decoration-none fw-bolder" style="color:#d43215b0;">
|
|
<div class="p-2 border rounded bg-light">
|
|
<i class="fa-solid fa-up-right-from-square me-2"></i>
|
|
<span id="modalActivity">-</span>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label"><i class="fa-solid fa-user me-2"></i>Owner Validation</label>
|
|
<p id="modalValOwner" class="form-control-plaintext border-bottom fw-bolder">-</p>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label"><i class="fa-solid fa-user me-2"></i></i>SPV Validation</label>
|
|
<p id="modalValSpv" class="form-control-plaintext border-bottom fw-bolder">-</p>
|
|
</div>
|
|
<div class="col-md-4 mb-3">
|
|
<label class="form-label"><i class="fa-solid fa-user me-2"></i>Manager Validation</label>
|
|
<p id="modalValManager" class="form-control-plaintext border-bottom fw-bolder">-</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div id='modalInfo'></div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-12 mb-3">
|
|
<div class="border rounded p-2" style="height: 500px; overflow: hidden;">
|
|
<iframe id="certificatePreview" src="" style="width: 100%; height: 100%; border: none;"></iframe>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
|
|
<i class="fa-solid fa-times"></i> Cancel
|
|
</button>
|
|
<button type="button" class="btn btn-warning text-dark" id="confirmValidateBtn">
|
|
<i class="fa-solid fa-check"></i> Validate Certificate
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?= $this->endSection() ?>
|
|
|
|
<?= $this->section('style') ?>
|
|
<?= $this->endSection() ?>
|
|
|
|
<?= $this->section('script') ?>
|
|
<script>
|
|
$(function () {
|
|
|
|
let table = $('#certificatesTable').DataTable({
|
|
order: [[5, 'asc'], [4, 'asc']],
|
|
pageLength: 25,
|
|
dom: '<"row"<"col-md-6"l>>rtip',
|
|
responsive: true,
|
|
serverSide: false,
|
|
autoWidth: false,
|
|
ajax: function (data, callback, settings) {
|
|
fetch('<?= base_url('certificates/api/getindexmaintenance') ?>')
|
|
.then(response => response.json())
|
|
.then(result => {
|
|
callback({
|
|
data: result.map((cert, index) => {
|
|
|
|
// console.log(result);
|
|
let certid = cert.cert_id || '';
|
|
let certname = cert.cert_name || '-';
|
|
let certnumber = cert.cert_number || '';
|
|
let actid = cert.actid || '';
|
|
let issuedateRaw = cert.issued_date || '';
|
|
let expirydateRaw = cert.expired_date || '';
|
|
let managerValidation = cert.manager_validation_at || null;
|
|
let spvValidation = cert.spv_validation_at || null;
|
|
let userValidation = cert.user_validation_at || null;
|
|
let status = cert.status;
|
|
let fullname = cert.fullname;
|
|
let activity_subject = cert.activity_subject;
|
|
|
|
let issuedate = '-';
|
|
let expirydate = '-';
|
|
// let statusBadge = '<span class="badge bg-warning text-dark">need validation</span>';
|
|
let validationBadge = '';
|
|
|
|
if (issuedateRaw && issuedateRaw !== '0000-00-00') {
|
|
let date = new Date(issuedateRaw);
|
|
let day = String(date.getDate()).padStart(2, '0');
|
|
let month = date.toLocaleString('en-US', { month: 'short' });
|
|
let year = date.getFullYear();
|
|
issuedate = `${day} ${month} ${year}`;
|
|
}
|
|
|
|
if (expirydateRaw && expirydateRaw !== '0000-00-00') {
|
|
let date = new Date(expirydateRaw);
|
|
let day = String(date.getDate()).padStart(2, '0');
|
|
let month = date.toLocaleString('en-US', { month: 'short' });
|
|
let year = date.getFullYear();
|
|
expirydate = `${day} ${month} ${year}`;
|
|
let today = new Date();
|
|
let expiryDate = new Date(expirydateRaw);
|
|
let days = Math.ceil((expiryDate - today) / (1000 * 60 * 60 * 24));
|
|
if (days < 0) {
|
|
statusBadge = '<div class=""><span class="badge bg-danger">Expired</span></div>';
|
|
} else if (days <= 30) {
|
|
statusBadge = '<div class=""><span class="badge bg-info">Expiring Soon</span></div>';
|
|
} else {
|
|
statusBadge = '<div class=""><span class="badge bg-success">Active</span></div>';
|
|
}
|
|
} else {
|
|
statusBadge = '<div class=""><span class="badge bg-dark">Not Available</span></div>';
|
|
}
|
|
|
|
if (status == 'unvalidated') {
|
|
|
|
validationBadge = '<div class=""><span class="badge bg-warning text-dark">unvalidated</span></div>';
|
|
} else {
|
|
validationBadge = '<div class=""><span class="badge bg-success">validated</span></div>';
|
|
}
|
|
|
|
return [
|
|
// index + 1,
|
|
`<strong>${certname}</strong><br><small class="text-muted">Cert# : ${certnumber}</small>`,
|
|
`<a href="javascript:void(0)" class="activity-report-link text-decoration-none" data-certid="${certid}" data-actid="${actid}" style="color:#d43215b0;">#${actid} - ${activity_subject} <i class="fa-solid fa-up-right-from-square"></i></strong><br><small class="text-muted">Owner : ${fullname}</small></a>`,
|
|
issuedate,
|
|
expirydate,
|
|
statusBadge,
|
|
validationBadge,
|
|
status == 'unvalidated'
|
|
? `<div class="text-center"><button type="button" class="btn btn-sm btn-warning text-dark btn-validate-modal" data-certid="${certid}"><i class="fa-solid fa-triangle-exclamation me-2"></i>Need Validation</button></div>`
|
|
: `<div class="text-center mb-1"><button type="button" class="btn btn-sm btn-info btn-validate-modal" data-certid="${certid}"><i class="fa-regular fa-eye me-2"></i>Detail</button></div>
|
|
<div class="text-center"><button type="button" class="btn btn-sm btn-success btn-view" data-certnumber="${certnumber}"><i class="fa-regular fa-file-pdf me-2"></i>Generated PDF</button></div>`
|
|
];
|
|
})
|
|
});
|
|
})
|
|
.catch(error => {
|
|
console.error('Error fetching data:', error);
|
|
callback({ data: [] });
|
|
});
|
|
},
|
|
columnDefs: [
|
|
{
|
|
// Kondisi untuk Kolom 4 (Status Masa Berlaku)
|
|
targets: 4,
|
|
render: function (data, type) {
|
|
if (type === 'sort') {
|
|
let val = data.toLowerCase();
|
|
if (val.includes('not available')) return 1;
|
|
if (val.includes('expired')) return 2; // Merah - Paling kritis
|
|
if (val.includes('expiring soon')) return 3; // Biru/Kuning
|
|
// if (val.includes('active')) return 4; // Hijau
|
|
return 4;
|
|
}
|
|
return data;
|
|
}
|
|
},
|
|
{
|
|
// Kondisi untuk Kolom 5 (Validation)
|
|
targets: 5,
|
|
render: function (data, type) {
|
|
if (type === 'sort') {
|
|
let val = data.toLowerCase();
|
|
if (val.includes('unvalidated')) return 1;
|
|
if (val.includes('validated')) return 2;
|
|
return 3;
|
|
}
|
|
return data;
|
|
}
|
|
},
|
|
{
|
|
// Hapus angka 4 dari sini agar kolom 4 bisa di-klik untuk sorting
|
|
targets: [6],
|
|
orderable: false
|
|
},
|
|
]
|
|
});
|
|
|
|
$('#certificatesTable_filter').hide();
|
|
|
|
// Search
|
|
$('#searchInput').on('keyup', function () {
|
|
table.search(this.value).draw();
|
|
});
|
|
|
|
// Type filter using DataTables column filter
|
|
$('#productFilter').on('change', function () {
|
|
let type = $(this).val();
|
|
|
|
// Filter by type column (index 1 - Certificate Name contains type info)
|
|
if (type === '') {
|
|
table.column(0).search('').draw();
|
|
} else {
|
|
// Capitalize first letter for search
|
|
let typeText = type.charAt(0).toUpperCase() + type.slice(1);
|
|
table.column(0).search(typeText).draw();
|
|
}
|
|
});
|
|
|
|
$('#statusFilter').on('change', function () { // OK
|
|
let status = $(this).val();
|
|
|
|
if (status === '') {
|
|
table.column(4).search('').draw();
|
|
} else if (status === 'active') {
|
|
table.column(4).search('active').draw();
|
|
} else if (status === 'expired') {
|
|
table.column(4).search('expired').draw();
|
|
} else if (status === 'expiring') {
|
|
table.column(4).search('expiring soon').draw();
|
|
} else if (status === 'not') {
|
|
table.column(4).search('not available').draw();
|
|
}
|
|
});
|
|
|
|
$('#validationFilter').on('change', function () { // OK
|
|
let validation = $(this).val();
|
|
if (validation === '') {
|
|
table.column(5).search('').draw();
|
|
} else if (validation === 'unval') {
|
|
table.column(5).search('unv').draw();
|
|
} else if (validation === 'valid') {
|
|
table.column(5).search('^validated$', true, false).draw();
|
|
}
|
|
});
|
|
|
|
// Reset
|
|
window.resetFilters = function () {
|
|
$('#searchInput, #statusFilter, #productFilter, #validationFilter').val('');
|
|
table.search('').columns().search('').draw();
|
|
};
|
|
|
|
// View PDF
|
|
$(document).on('click', '.btn-view', function () {
|
|
let certnumber = $(this).data('certnumber');
|
|
window.open('<?= base_url('certificates/number/') ?>' + certnumber, '_blank');
|
|
});
|
|
|
|
// Activity report
|
|
$(document).on('click', '.activity-report-link', function () {
|
|
let actid = $(this).data('actid');
|
|
window.open(
|
|
'<?= base_url('activities/detail/') ?>' + actid,
|
|
'_blank',
|
|
'width=1200,height=800,scrollbars=yes,resizable=yes'
|
|
);
|
|
});
|
|
|
|
// Open modal
|
|
$(document).on('click', '.btn-validate-modal', function () {
|
|
|
|
let btn = $(this);
|
|
let certid = btn.data('certid');
|
|
|
|
// POST API call to fetch certificate data
|
|
$.post(
|
|
'<?= base_url('certificates/api/showmaintenance') ?>',
|
|
{ certid },
|
|
function (data) {
|
|
// console.log(data);
|
|
// Populate modal with data
|
|
$('#modalCertName').text(data.cert_name || '-');
|
|
$('#modalCertNumber').text(data.cert_number || '-');
|
|
$('#modalProductName').text(data.productname || '-');
|
|
$('#modalProductNumber').text(data.productnumber || '-');
|
|
$('#modalIssueDate').text(data.issued_date || '-');
|
|
|
|
const formattedDate = data.expired_date
|
|
? new Date(data.expired_date).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })
|
|
: '-';
|
|
$('#modalExpiryDate').text(formattedDate);
|
|
|
|
$('#modalSiteName').text(data.sitename || '-');
|
|
|
|
// 1. Cek status validasi
|
|
const isValid = data.status === 'validated';
|
|
// 2. Siapkan variabel dinamis berdasarkan status
|
|
const theme = isValid ? 'success' : 'warning';
|
|
const icon = isValid ? 'fa-regular fa-circle-check' : 'fa-solid fa-triangle-exclamation';
|
|
const note = isValid
|
|
? 'Sertifikat Sudah Divalidasi'
|
|
: 'Review data berikut dengan teliti, setelah divalidasi maka sertifikat akan dinyatakan <strong>Valid</strong> dan <strong>sah secara sistem.</strong>';
|
|
// 3. Terapkan ke dalam HTML (struktur HTML ditulis satu kali saja)
|
|
$('#modalHeader').removeClass('bg-warning bg-success').addClass(`bg-${theme} text-dark`);
|
|
$('#modalValidation').html(
|
|
`<span class="badge bg-${theme} text-dark py-2 px-3">
|
|
<i class="${icon} me-2"></i>${data.status || '-'}
|
|
</span>`
|
|
);
|
|
|
|
$('#modalInfo').html(
|
|
`<div class="alert alert-${theme} border-0 shadow-sm d-flex align-items-center">
|
|
<i class="${icon} fs-4 me-3"></i>
|
|
<div>
|
|
<strong>Validation Note:</strong><br>
|
|
${note}
|
|
</div>
|
|
</div>`
|
|
);
|
|
|
|
let badge = '';
|
|
const today = new Date();
|
|
today.setHours(0, 0, 0, 0);
|
|
if (data.expired_date == null) {
|
|
badge = '<span class="badge bg-dark">Not Available</span>';
|
|
} else {
|
|
const expiryDate = new Date(data.expired_date);
|
|
expiryDate.setHours(0, 0, 0, 0);
|
|
const diffTime = expiryDate - today;
|
|
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
if (diffDays < 0) {
|
|
badge = '<span class="badge bg-danger">Expired</span>';
|
|
}
|
|
else if (diffDays <= 30) {
|
|
badge = '<span class="badge bg-warning text-dark">Expiring Soon</span>';
|
|
} else {
|
|
badge = '<span class="badge bg-success">Active</span>';
|
|
}
|
|
}
|
|
|
|
$('#modalExpiredStatus').html(badge);
|
|
$('#modalActivity').text(data.actid ? `#${data.actid} - ${data.subject || '-'}` : '-');
|
|
$('#modalActivityLink').data('actid', data.actid || '');
|
|
$('#modalValOwner').html(data.user_validation_at ? `${data.username} - ${data.user_validation_at}<i class="fa-solid fa-circle-check text-success ms-2"></i>` : '-');
|
|
$('#modalValSpv').html(data.spv_validation_at ? `${data.spvname} - ${data.spv_validation_at}<i class="fa-solid fa-circle-check text-success ms-2"></i>` : '-');
|
|
$('#modalValManager').html(data.manager_validation_at ? `${data.managername} - ${data.manager_validation_at}<i class="fa-solid fa-circle-check text-success ms-2"></i>` : '-');
|
|
},
|
|
'json'
|
|
).fail(function () {
|
|
console.error('Error fetching certificate data');
|
|
});
|
|
|
|
// INI JANGAN DIUBAH
|
|
$('#confirmValidateBtn').data('certid', certid);
|
|
$('#certificatePreview').attr('src','<?= base_url('certificates/maintenance/show/') ?>' + certid);
|
|
|
|
$('#validateModal').modal('show');
|
|
});
|
|
|
|
// Confirm validate
|
|
$('#confirmValidateBtn').on('click', function () {
|
|
|
|
let certid = $(this).data('certid');
|
|
let certificateType = 'maintenance';
|
|
if (!confirm('Are you sure?')) return;
|
|
|
|
$.post(
|
|
'<?= base_url('certificates/api/validatecertificate') ?>',
|
|
{ certid, certificateType},
|
|
function (response) {
|
|
|
|
if (response.success) {
|
|
$('#validateModal').modal('hide');
|
|
alert(response.message);
|
|
location.reload();
|
|
} else {
|
|
alert(response.message || 'Validation failed');
|
|
}
|
|
|
|
}, 'json'
|
|
).fail(function (xhr) {
|
|
console.log(xhr);
|
|
alert(xhr.responseText);
|
|
});
|
|
});
|
|
|
|
});
|
|
</script>
|
|
<?= $this->endSection() ?>
|