crm-summit/app/Views/certificate_maintenance_index.php

507 lines
24 KiB
PHP
Raw Normal View History

2026-02-19 08:53:25 +07:00
<?= $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>
2026-03-08 22:40:36 +07:00
<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">
2026-02-19 08:53:25 +07:00
<select id="statusFilter" class="form-select form-select-sm">
<option value="">--Status Filter--</option>
2026-02-19 08:53:25 +07:00
<option value="active">Active</option>
<option value="expired">Expired</option>
<option value="expiring">Expiring Soon</option>
2026-03-08 22:40:36 +07:00
<option value="not">Not Available</option>
2026-02-19 08:53:25 +07:00
</select>
</div>
<div class="col-md-3 mb-2">
2026-03-08 22:40:36 +07:00
<select id="validationFilter" class="form-select form-select-sm">
<option value="">--Validation Filter--</option>
2026-03-08 22:40:36 +07:00
<option value="valid">Validated</option>
<option value="unval">Unvalidated</option>
2026-02-19 08:53:25 +07:00
</select>
</div>
<div class="col-md-3 mb-2">
2026-02-19 08:53:25 +07:00
<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>
2026-03-08 22:40:36 +07:00
<!-- <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>
2026-03-08 22:40:36 +07:00
<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>
2026-02-19 08:53:25 +07:00
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Validate Modal -->
<div class="modal fade" id="validateModal" tabindex="-1" aria-labelledby="validateModalLabel" aria-hidden="true">
2026-02-19 08:53:25 +07:00
<div class="modal-dialog modal-xl modal-dialog-centered">
<div class="modal-content">
2026-03-08 22:40:36 +07:00
<div class="modal-header bg-warning text-dark" id='modalHeader'>
<h5 class="modal-title" id="validateModalLabel">
2026-03-08 22:40:36 +07:00
Validate Maintenance Certificate
2026-02-19 08:53:25 +07:00
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"
2026-02-19 08:53:25 +07:00
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">
2026-03-08 22:40:36 +07:00
<h5 id="modalValidation">-</h5>
</div>
</div>
<div class="row">
2026-02-25 09:35:41 +07:00
<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">
2026-03-08 22:40:36 +07:00
<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">
2026-03-08 22:40:36 +07:00
<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>
2026-03-08 22:40:36 +07:00
<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>
2026-03-08 22:40:36 +07:00
<div id='modalInfo'></div>
2026-02-19 08:53:25 +07:00
<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>
2026-02-19 08:53:25 +07:00
</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>
2026-02-19 08:53:25 +07:00
</div>
</div>
</div>
<?= $this->endSection() ?>
<?= $this->section('style') ?>
<?= $this->endSection() ?>
2026-02-19 08:53:25 +07:00
<?= $this->section('script') ?>
<script>
$(function () {
let table = $('#certificatesTable').DataTable({
2026-03-08 22:40:36 +07:00
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) {
2026-03-08 22:40:36 +07:00
statusBadge = '<div class=""><span class="badge bg-danger">Expired</span></div>';
} else if (days <= 30) {
2026-03-08 22:40:36 +07:00
statusBadge = '<div class=""><span class="badge bg-info">Expiring Soon</span></div>';
} else {
2026-03-08 22:40:36 +07:00
statusBadge = '<div class=""><span class="badge bg-success">Active</span></div>';
}
} else {
2026-03-08 22:40:36 +07:00
statusBadge = '<div class=""><span class="badge bg-dark">Not Available</span></div>';
}
if (status == 'unvalidated') {
2026-03-08 22:40:36 +07:00
validationBadge = '<div class=""><span class="badge bg-warning text-dark">unvalidated</span></div>';
} else {
2026-03-08 22:40:36 +07:00
validationBadge = '<div class=""><span class="badge bg-success">validated</span></div>';
}
return [
2026-03-08 22:40:36 +07:00
// 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} &nbsp;<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>`
2026-03-08 22:40:36 +07:00
: `<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: [
{
2026-03-08 22:40:36 +07:00
// Kondisi untuk Kolom 4 (Status Masa Berlaku)
targets: 4,
render: function (data, type) {
if (type === 'sort') {
2026-03-08 22:40:36 +07:00
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;
}
},
{
2026-03-08 22:40:36 +07:00
// 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;
}
},
{
2026-03-08 22:40:36 +07:00
// Hapus angka 4 dari sini agar kolom 4 bisa di-klik untuk sorting
targets: [6],
orderable: false
2026-03-08 22:40:36 +07:00
},
]
});
$('#certificatesTable_filter').hide();
// Search
$('#searchInput').on('keyup', function () {
table.search(this.value).draw();
});
2026-02-25 09:35:41 +07:00
// Type filter using DataTables column filter
2026-03-08 22:40:36 +07:00
$('#productFilter').on('change', function () {
2026-02-25 09:35:41 +07:00
let type = $(this).val();
// Filter by type column (index 1 - Certificate Name contains type info)
if (type === '') {
2026-03-08 22:40:36 +07:00
table.column(0).search('').draw();
2026-02-25 09:35:41 +07:00
} else {
// Capitalize first letter for search
let typeText = type.charAt(0).toUpperCase() + type.slice(1);
2026-03-08 22:40:36 +07:00
table.column(0).search(typeText).draw();
2026-02-25 09:35:41 +07:00
}
});
2026-03-08 22:40:36 +07:00
$('#statusFilter').on('change', function () { // OK
let status = $(this).val();
if (status === '') {
2026-03-08 22:40:36 +07:00
table.column(4).search('').draw();
} else if (status === 'active') {
2026-03-08 22:40:36 +07:00
table.column(4).search('active').draw();
} else if (status === 'expired') {
2026-03-08 22:40:36 +07:00
table.column(4).search('expired').draw();
} else if (status === 'expiring') {
2026-03-08 22:40:36 +07:00
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 () {
2026-03-08 22:40:36 +07:00
$('#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 || '-');
2026-03-08 22:40:36 +07:00
// 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>`
);
2026-03-08 22:40:36 +07:00
let badge = '';
const today = new Date();
today.setHours(0, 0, 0, 0);
2026-03-08 22:40:36 +07:00
if (data.expired_date == null) {
badge = '<span class="badge bg-dark">Not Available</span>';
} else {
2026-03-08 22:40:36 +07:00
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>';
}
}
2026-03-08 22:40:36 +07:00
$('#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') ?>',
2026-02-25 09:35:41 +07:00
{ certid, certificateType},
function (response) {
if (response.success) {
$('#validateModal').modal('hide');
2026-02-25 09:35:41 +07:00
alert(response.message);
location.reload();
} else {
alert(response.message || 'Validation failed');
}
}, 'json'
).fail(function (xhr) {
console.log(xhr);
alert(xhr.responseText);
});
});
2026-02-19 08:53:25 +07:00
});
2026-02-19 08:53:25 +07:00
</script>
<?= $this->endSection() ?>