Update Fix UI Certificate Maintenance Index

This commit is contained in:
mikael-zakaria 2026-02-24 08:41:04 +07:00
parent 6250a1394c
commit 7a0b79e73e
6 changed files with 805 additions and 1289 deletions

View File

@ -217,6 +217,13 @@ $routes->group('certificates', function($routes) {
$routes->get('calibration', 'Certificates::calibrateIndex');
$routes->get('maintenance', 'Certificates::maintenanceIndex');
$routes->get('api/getindexmaintenance', 'Certificates::getDataIndexMaintenance');
$routes->get('api/getindextraining', 'Certificates::getDataIndexTraining');
$routes->post('validate', 'Certificates::validateCertificate');
// Untuk View Cerificate
$routes->get('training/show/(:any)', 'Certificates::createTraining/$1');
$routes->get('calibration/show/(:any)/(:any)', 'Certificates::createCalibrate/$1/$2');

View File

@ -193,8 +193,6 @@ class Activities extends Controller {
$data['sql'] = $sql;
$data['tampildata'] = $result;
// dd($sql);
return view('activities_index', $data);
}
@ -366,6 +364,7 @@ class Activities extends Controller {
if( $this->request->getVar('siteid') != '' ) { $siteid = $this->request->getVar('siteid'); }
if ($this->request->getMethod() === 'POST') {
// $allPostData = $this->request->getPost();
$rules = [
'subject' => 'required',
'siteid' => 'required',

File diff suppressed because it is too large Load Diff

View File

@ -174,8 +174,8 @@ if(isset($data)) {
<div class="row mb-2">
<div class="col-12 col-md-6">
<div class="form-group">
<label for="type" class="form-label border-start border-5 border-primary ps-1">Activity Type</label>
<select name="acttypeid" id="type" class="form-select form-select-sm select2" required>
<label for="acttypeid" class="form-label border-start border-5 border-primary ps-1">Activity Type</label>
<select name="acttypeid" id="acttypeid" class="form-select form-select-sm select2" required>
<option value="" selected disabled>-- Choose one --</option>
<?php foreach($acttype as $data) {
$qacttypeid = $data['acttypeid'];
@ -206,6 +206,55 @@ if(isset($data)) {
</div>
</div>
</div>
<div class="row mb-2">
<div class="col-6">
<div class="form-group">
<label class="form-label border-start border-5 border-primary ps-1">Certificate</label>
<div class="row">
<div class="col-md-6 mb-2">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="calibration" id="calibration" value="1">
<label class="form-check-label" for="calibration">
Calibration
</label>
</div>
</div>
<div class="col-md-6 mb-2">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="installation" id="installation" value="1">
<label class="form-check-label" for="installation">
Installation Certificate
</label>
</div>
</div>
<div class="col-md-6 mb-2">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="maintenance" value="1" id="maintenance" >
<label class="form-check-label" for="maintenance">
Maintenance Certificate
</label>
</div>
</div>
<div class="col-md-6 mb-2">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="offreport" value="1" id="offreport" >
<label class="form-check-label" for="offreport">
Berita Acara Instalasi
</label>
</div>
</div>
<div class="col-md-6 mb-2">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="training" value="1" id="training" >
<label class="form-check-label" for="training">
User Training Certificate
</label>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row mb-2">
<div class="col-12 col-md-6">
<div class="form-group">
@ -244,6 +293,7 @@ if(isset($data)) {
</div>
</div>
</div>
<div class="row mb-2">
<div class="col-12 col-md-6">
<div class="form-group">
@ -315,6 +365,7 @@ if(isset($data)) {
</div>
</div>
</div>
</div>
</div>
</div>
@ -694,6 +745,13 @@ $('#status').change(function() {
else { $(".closedate").prop('disabled', false); $("#opendate").prop('disabled', false);}
})
$("#maintenance").prop('disabled', true); $("#calibration").prop('disabled', true); $("#installation").prop('disabled', true); $("#offreport").prop('disabled', true); $("#training").prop('disabled', true);
$('#acttypeid').change(function() {
if (this.value=='5'){ $("#maintenance").prop('disabled', false); $("#calibration").prop('disabled', true); $("#installation").prop('disabled', true); $("#offreport").prop('disabled', true); $("#training").prop('disabled', true);}
else if (this.value=='3'){ $("#maintenance").prop('disabled', true); $("#calibration").prop('disabled', false); $("#installation").prop('disabled', false); $("#offreport").prop('disabled', false); $("#training").prop('disabled', false); }
else { $("#maintenance").prop('disabled', true); $("#calibration").prop('disabled', true); $("#installation").prop('disabled', true); $("#offreport").prop('disabled', true); $("#training").prop('disabled', true);}
})
// reportdate change => opendate.value = reportdate.value
$('#reportdate').change(function() {
$('#opendate').val(this.value);

View File

@ -28,6 +28,7 @@
<option value="active">Active</option>
<option value="expired">Expired</option>
<option value="expiring">Expiring Soon</option>
<option value="isval">Need Validation</option>
</select>
</div>
<div class="col-md-4 mb-2">
@ -45,105 +46,23 @@
</div>
</div>
<div class="">
<button type="button" class="btn btn-info text-white btn-sm" data-bs-toggle="modal"
data-bs-target="#createModal">
<i class="fas fa-plus-circle"></i> Create
</button>
</div>
<div class="table-responsive">
<table id="certificatesTable" class="table table-striped table-hover border">
<thead class="table-primary">
<tr>
<th class="text-center" style="width: 5%;">No</th>
<th style="width: 15%;">Certificate Name</th>
<th style="width: 15%;">Product/Equipment</th>
<th style="width: 12%;">Issue Date</th>
<th style="width: 12%;">Expiry Date</th>
<th style="width: 10%;">Status</th>
<th style="width: 10%;">Vendor</th>
<th class="text-center" style="width: 9%;">Action</th>
</tr>
</thead>
<tbody>
<?php
if(isset($certificates) && !empty($certificates)) {
$no = 1;
foreach($certificates as $cert) {
$certid = $cert['certid'] ?? '';
$certname = $cert['certname'] ?? '-';
$productname = $cert['productname'] ?? '-';
$productnumber = $cert['productnumber'] ?? '';
$type = $cert['type'] ?? '-';
$issuedate = $cert['issuedate'] ?? '';
$expirydate = $cert['expirydate'] ?? '';
$vendor = $cert['vendor'] ?? '-';
// Format dates
if($issuedate && $issuedate != '0000-00-00') {
$issuedate = date('M d, Y', strtotime($issuedate));
} else {
$issuedate = '-';
}
if($expirydate && $expirydate != '0000-00-00') {
$expirydate = date('M d, Y', strtotime($expirydate));
// Check expiry status
$today = date('Y-m-d');
$expiryCheck = date('Y-m-d', strtotime($cert['expirydate']));
$daysUntilExpiry = (strtotime($expiryCheck) - strtotime($today)) / (60 * 60 * 24);
if($daysUntilExpiry < 0) {
$statusBadge = '<span class="badge bg-danger">Expired</span>';
$statusClass = 'expired';
} elseif($daysUntilExpiry <= 30) {
$statusBadge = '<span class="badge bg-warning text-dark">Expiring Soon</span>';
$statusClass = 'expiring';
} else {
$statusBadge = '<span class="badge bg-success">Active</span>';
$statusClass = 'active';
}
} else {
$expirydate = '-';
$statusBadge = '<span class="badge bg-secondary">N/A</span>';
$statusClass = '';
}
?>
<tr>
<td class="text-center"><?= $no++; ?></td>
<td>
<strong><?= htmlspecialchars($certname) ?></strong>
<br>
<small class="text-muted">ID: <?= $certid ?></small>
</td>
<td>
<?= htmlspecialchars($productname) ?>
<?php if($productnumber): ?>
<br><small class="text-muted">SN:
<?= htmlspecialchars($productnumber) ?></small>
<?php endif; ?>
</td>
<td><?= $issuedate ?></td>
<td><?= $expirydate ?></td>
<td><?= $statusBadge ?></td>
<td><?= htmlspecialchars($vendor) ?></td>
<td class="text-center">
<div class="btn-group btn-group-sm">
<button type="button" class="btn btn-success btn-view"
data-certid="<?= $certid ?>" data-certtype="<?= $type ?>"
title="View PDF">
<i class="fas fa-file-pdf"></i>
</button>
</div>
</td>
</tr>
<?php
}
}
?>
</tbody>
</table>
<!-- 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 style="width:20%">Certificate Name</th>
<th style="width:20%">Product/Equipment</th>
<th style="width:20%">Activity Report</th>
<th style="width:10%">Issue Date</th>
<th style="width:10%">Expiry Date</th>
<th style="width:10%">Status</th>
<th class="text-center" style="width:10%">Action</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
@ -152,210 +71,271 @@
</div>
</div>
<!-- Create Modal -->
<div class="modal fade" id="createModal" tabindex="-1" aria-labelledby="createModalLabel" aria-hidden="true">
<!-- 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-info text-white">
<h5 class="modal-title" id="createModalLabel">
<i class="fas fa-plus-circle"></i> &nbsp;Create Certificate Maintenance
<div class="modal-header bg-warning text-dark">
<h5 class="modal-title" id="validateModalLabel">
<i class="fa-solid fa-check-double"></i> &nbsp;Validate Certificate
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"
<button type="button" class="btn-close" data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<form id="createForm" action="<?= base_url('certificates/create') ?>" method="post">
<div class="modal-body">
<div class="row">
<div class="col-md-6 mb-3">
<label for="certname" class="form-label">Certificate Name <span
class="text-danger">*</span></label>
<input type="text" class="form-control" id="certname" name="certname" required>
</div>
<div class="col-md-6 mb-3">
<label for="certtype" class="form-label">Type Sertifikat</label>
<input type="text" class="form-control" id="certtype" name="certtype">
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Certificate ID</label>
<p id="modalCertId" class="form-control-plaintext">-</p>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="issuedate" class="form-label">Start Date <span
class="text-danger">*</span></label>
<input type="date" class="form-control" id="issuedate" name="issuedate">
</div>
<div class="col-md-6 mb-3">
<label for="expirydate" class="form-label">Expiry Date <span
class="text-danger">*</span></label>
<input type="date" class="form-control" id="expirydate" name="expirydate">
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Certificate Name</label>
<p id="modalCertName" class="form-control-plaintext">-</p>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="type" class="form-label">Site <span class="text-danger">*</span></label>
<select class="form-select select2" id="type" name="type" required>
<option value=''>-- Choose one --</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label for="productid" class="form-label">Product/Equipment <span
class="text-danger">*</span></label>
<select class="form-select select2" id="productid" name="productid">
<option value=''>-- Choose one --</option>
</select>
</div>
</div>
<!-- <div class="mb-3">
<label for="description" class="form-label">Description</label>
<textarea class="form-control" id="description" name="description" rows="2"></textarea>
</div> -->
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
<i class="fas fa-times"></i> Cancel
</button>
<button type="submit" class="btn btn-info text-white">
<i class="fas fa-save"></i> Save
</button>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Product/Equipment</label>
<p id="modalProductName" class="form-control-plaintext">-</p>
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Serial Number</label>
<p id="modalProductNumber" class="form-control-plaintext">-</p>
</div>
</div>
</form>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Issue Date</label>
<p id="modalIssueDate" class="form-control-plaintext">-</p>
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-bold">Expiry Date</label>
<p id="modalExpiryDate" class="form-control-plaintext">-</p>
</div>
</div>
<div class="row">
<div class="col-md-12 mb-3">
<label class="form-label fw-bold">Vendor</label>
<p id="modalVendor" class="form-control-plaintext">-</p>
</div>
</div>
<div class="alert alert-warning">
<i class="fa-solid fa-info-circle"></i>
<strong>Validation Information:</strong> Please review the certificate details above before validating. Once validated, the certificate status will change from "Need Validation" to "Active".
</div>
<div class="row">
<div class="col-md-12 mb-3">
<label class="form-label fw-bold">Certificate Preview</label>
<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') ?>
<style>
#certificatesTable {
width: 100% !important;
}
#certificatesTable_wrapper {
width: 100% !important;
}
</style>
<?= $this->endSection() ?>
<?= $this->section('script') ?>
<script>
$(function () {
// Store DataTable instance
let table = null;
$(function () {
// Initialize DataTable
table = $('#certificatesTable').DataTable({
"order": [
[0, "asc"]
],
"pageLength": 25,
"lengthMenu": [
[10, 25, 50, 100, -1],
[10, 25, 50, 100, "All"]
],
"language": {
"emptyTable": "No certificates available",
"info": "Showing _START_ to _END_ of _TOTAL_ certificates",
"infoEmpty": "No certificates found",
"infoFiltered": "(filtered from _MAX_ total certificates)"
},
"dom": '<"row"<"col-md-6"l>>rtip',
"columnDefs": [{
"orderable": false,
"targets": [7]
}, // Disable sorting on Action column
{
"searchable": true,
"targets": [0, 1, 2, 3, 4, 5, 6]
} // Enable search on all columns except Action
]
});
let table = $('#certificatesTable').DataTable({
order: [[5, '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 => {
let certid = cert.certid || '';
let certname = cert.certname || '-';
let productname = cert.productname || '-';
let productnumber = cert.productnumber || '';
let issuedateRaw = cert.issuedate || '';
let expirydateRaw = cert.expirydate || '';
let vendor = cert.vendor || '-';
let isval = cert.isval || null;
// Hide default DataTables search
$('#certificatesTable_filter').hide();
let issuedate = '-';
let expirydate = '-';
let statusBadge = '<span class="badge bg-warning text-dark">Need Validation</span>';
// Initialize Select2
$('.select2').select2({
theme: 'bootstrap-5',
width: '100%',
dropdownParent: $('.modal')
});
if (isval != null) {
if (issuedateRaw && issuedateRaw !== '0000-00-00') {
issuedate = new Date(issuedateRaw).toLocaleDateString('en-US', { month: 'short', day: '2-digit', year: 'numeric' });
}
// Custom search functionality
$('#searchInput').on('keyup', function () {
let searchValue = $(this).val().toLowerCase();
table.search(searchValue).draw();
});
if (expirydateRaw && expirydateRaw !== '0000-00-00') {
expirydate = new Date(expirydateRaw).toLocaleDateString('en-US', { month: 'short', day: '2-digit', year: 'numeric' });
let today = new Date();
let expiryDate = new Date(expirydateRaw);
let days = Math.ceil((expiryDate - today) / (1000 * 60 * 60 * 24));
// Status filter using DataTables column filter
$('#statusFilter').on('change', function () {
let status = $(this).val();
if (days < 0) {
statusBadge = '<span class="badge bg-danger">Expired</span>';
} else if (days <= 30) {
statusBadge = '<span class="badge bg-info">Expiring Soon</span>';
} else {
statusBadge = '<span class="badge bg-success">Active</span>';
}
} else {
statusBadge = '<span class="badge bg-secondary">N/A</span>';
}
}
// Filter by status column (index 6)
if (status === '') {
table.column(6).search('').draw();
} else {
// Search for the badge text in status column
let statusText = '';
switch (status) {
case 'active':
statusText = 'Active';
break;
case 'expired':
statusText = 'Expired';
break;
case 'expiring':
statusText = 'Expiring Soon';
break;
}
table.column(6).search(statusText).draw();
}
});
return [
`<strong>${certname}</strong><br><small class="text-muted">ID: ${certid}</small>`,
`${productname}${productnumber ? '<br><small class="text-muted">SN: ' + productnumber + '</small>' : ''}`,
`<a href="javascript:void(0)" class="activity-report-link text-decoration-none" data-certid="${certid}" style="color:#d43215b0;">Act ID - Nama AR - Nama User <i class="fa-solid fa-up-right-from-square"></i></a>`,
issuedate,
expirydate,
statusBadge,
isval == null
? `<button type="button" class="btn btn-warning text-dark btn-validate" data-certid="${certid}" data-certname="${certname}" data-productname="${productname}" data-productnumber="${productnumber}" data-issuedate="${issuedate}" data-expirydate="${expirydate}" data-vendor="${vendor}"><i class="fa-solid fa-check-double"></i></button>`
: `<button type="button" class="btn btn-success btn-view" data-certid="${certid}"><i class="fa-regular fa-file-pdf"></i></button>`
];
})
});
})
.catch(error => {
console.error('Error fetching data:', error);
callback({ data: [] });
});
},
columnDefs: [{
targets: 5,
render: function (data, type) {
if (type === 'sort') {
if (data.includes('Need Validation')) return 1;
if (data.includes('Expired')) return 2;
if (data.includes('Expiring Soon')) return 3;
if (data.includes('Active')) return 4;
return 5;
}
return data;
}
}]
});
// Type filter using DataTables column filter
$('#typeFilter').on('change', function () {
let type = $(this).val();
$('#certificatesTable_filter').hide();
// Filter by type column (index 3)
if (type === '') {
table.column(2).search('').draw();
} else {
// Capitalize first letter for search
let typeText = type.charAt(0).toUpperCase() + type.slice(1);
table.column(2).search(typeText).draw();
}
});
// Search
$('#searchInput').on('keyup', function () {
table.search(this.value).draw();
});
// Reset filters
window.resetFilters = function () {
$('#searchInput').val('');
$('#statusFilter').val('');
$('#typeFilter').val('');
// Status filter
$('#statusFilter').on('change', function () {
let map = {
active: 'Active',
expired: 'Expired',
expiring: 'Expiring Soon',
isval: 'Need Validation'
};
table.column(5).search(map[this.value] || '').draw();
});
// Reset DataTables search and filters
table.search('').columns().search('').draw();
// Reset
window.resetFilters = function () {
$('#searchInput, #statusFilter').val('');
table.search('').columns().search('').order([5,'asc']).draw();
};
// Re-apply default ordering
table.order([5, 'asc']).draw();
};
// View PDF
$(document).on('click', '.btn-view', function () {
let certid = $(this).data('certid');
window.open('<?= base_url('certificates/maintenance/show/') ?>' + certid, '_blank');
});
// View button click - Open PDF in new tab based on certificate type
$(document).on('click', '.btn-view', function () {
let certid = $(this).data('certid');
let certType = $(this).data('certtype');
let url = '<?= base_url('certificates/maintenance/show/') ?>' + certid;
window.open(url, '_blank');
});
// Activity report
$(document).on('click', '.activity-report-link', function () {
let certid = $(this).data('certid');
window.open(
'<?= base_url('certificates/maintenance/activity/') ?>' + certid,
'_blank',
'width=1200,height=800,scrollbars=yes,resizable=yes'
);
});
// Print button
$('#btnPrint').on('click', function () {
window.print();
});
// Open modal
$(document).on('click', '.btn-validate', function () {
// Date picker enhancement
$('input[type="date"]').on('focus', function () {
this.showPicker();
});
let btn = $(this);
// Re-attach event handlers after DataTables pagination/draw
table.on('draw.dt', function () {
// Event handlers are already attached using $(document).on(), so no need to re-attach
});
});
$('#modalCertId').text(btn.data('certid'));
$('#modalCertName').text(btn.data('certname'));
$('#modalProductName').text(btn.data('productname'));
$('#modalProductNumber').text(btn.data('productnumber'));
$('#modalIssueDate').text(btn.data('issuedate'));
$('#modalExpiryDate').text(btn.data('expirydate'));
$('#modalVendor').text(btn.data('vendor'));
$('#confirmValidateBtn').data('certid', btn.data('certid'));
$('#certificatePreview').attr(
'src',
'<?= base_url('certificates/maintenance/show/') ?>' + btn.data('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/validate') ?>',
{ certid, certificateType},
function (response) {
if (response.success) {
$('#validateModal').modal('hide');
alert('Certificate maintenance validated successfully');
location.reload();
} else {
alert(response.message || 'Validation failed');
}
}, 'json'
).fail(function () {
$('#validateModal').modal('hide');
alert('Server error.');
});
});
});
</script>
<?= $this->endSection() ?>

View File

@ -1,3 +1,4 @@
<?= $this->extend('layouts/main.php') ?>
<?= $this->section('content') ?>
@ -5,9 +6,16 @@
<div class="page-wrapper">
<div class="container-fluid">
<div class="row page-titles">
<div class="col-md-5 align-self-center">
<div class="col-md-6 align-self-center">
<h4 class="text-themecolor">Certificates Training Management</h4>
</div>
<div class="col-md-6 align-self-center text-end">
<button type="button" class="btn btn-info text-white btn-sm" data-bs-toggle="modal"
data-bs-target="#createModal">
<i class="fas fa-plus-circle"></i> &nbsp;Create
</button>
</div>
</div>
<div class="row">
@ -22,15 +30,7 @@
placeholder="Search certificates by name, product, type, vendor, or dates...">
</div>
</div>
<div class="col-md-4 mb-2">
<select id="statusFilter" class="form-select form-select-sm">
<option value="">All Status</option>
<option value="active">Active</option>
<option value="expired">Expired</option>
<option value="expiring">Expiring Soon</option>
</select>
</div>
<div class="col-md-4 mb-2">
<div class="col-md-6 mb-2">
<select id="typeFilter" class="form-select form-select-sm">
<option value="">All Types</option>
<option value="tms">TMS</option>
@ -38,110 +38,27 @@
<option value="boeki">Tokyo Boeki</option>
</select>
</div>
<div class="col-md-4 mb-2">
<div class="col-md-6 mb-2">
<button onclick="resetFilters()" class="btn btn-secondary btn-sm w-100">
<i class="fas fa-redo"></i> Reset Filters
</button>
</div>
</div>
<div class="">
<button type="button" class="btn btn-info text-white btn-sm" data-bs-toggle="modal"
data-bs-target="#createModal">
<i class="fas fa-plus-circle"></i> Create
</button>
</div>
<div class="table-responsive">
<table id="certificatesTable" class="table table-striped table-hover border">
<thead class="table-primary">
<tr>
<th class="text-center" style="width: 5%;">No</th>
<th style="width: 15%;">Certificate Name</th>
<th style="width: 37%;">Certificate Name</th>
<th style="width: 15%;">Product/Equipment</th>
<th style="width: 12%;">Issue Date</th>
<th style="width: 12%;">Expiry Date</th>
<th style="width: 10%;">Status</th>
<th style="width: 10%;">Vendor</th>
<th class="text-center" style="width: 9%;">Action</th>
</tr>
</thead>
<tbody>
<?php
if(isset($certificates) && !empty($certificates)) {
$no = 1;
foreach($certificates as $cert) {
$certid = $cert['certid'] ?? '';
$certname = $cert['certname'] ?? '-';
$productname = $cert['productname'] ?? '-';
$productnumber = $cert['productnumber'] ?? '';
$type = $cert['type'] ?? '-';
$issuedate = $cert['issuedate'] ?? '';
$expirydate = $cert['expirydate'] ?? '';
$vendor = $cert['vendor'] ?? '-';
// Format dates
if($issuedate && $issuedate != '0000-00-00') {
$issuedate = date('M d, Y', strtotime($issuedate));
} else {
$issuedate = '-';
}
if($expirydate && $expirydate != '0000-00-00') {
$expirydate = date('M d, Y', strtotime($expirydate));
// Check expiry status
$today = date('Y-m-d');
$expiryCheck = date('Y-m-d', strtotime($cert['expirydate']));
$daysUntilExpiry = (strtotime($expiryCheck) - strtotime($today)) / (60 * 60 * 24);
if($daysUntilExpiry < 0) {
$statusBadge = '<span class="badge bg-danger">Expired</span>';
$statusClass = 'expired';
} elseif($daysUntilExpiry <= 30) {
$statusBadge = '<span class="badge bg-warning text-dark">Expiring Soon</span>';
$statusClass = 'expiring';
} else {
$statusBadge = '<span class="badge bg-success">Active</span>';
$statusClass = 'active';
}
} else {
$expirydate = '-';
$statusBadge = '<span class="badge bg-secondary">N/A</span>';
$statusClass = '';
}
?>
<tr>
<td class="text-center"><?= $no++; ?></td>
<td>
<strong><?= htmlspecialchars($certname) ?></strong>
<br>
<small class="text-muted">ID: <?= $certid ?></small>
</td>
<td>
<?= htmlspecialchars($productname) ?>
<?php if($productnumber): ?>
<br><small class="text-muted">SN:
<?= htmlspecialchars($productnumber) ?></small>
<?php endif; ?>
</td>
<td><?= $issuedate ?></td>
<td><?= $expirydate ?></td>
<td><?= $statusBadge ?></td>
<td><?= htmlspecialchars($vendor) ?></td>
<td class="text-center">
<div class="btn-group btn-group-sm">
<button type="button" class="btn btn-success btn-view"
data-certid="<?= $certid ?>" data-certtype="<?= $type ?>"
title="View PDF">
<i class="fas fa-file-pdf"></i>
</button>
</div>
</td>
</tr>
<?php
}
}
?>
<!-- Saya Ingin Ini Pakai JSON -->
</tbody>
</table>
</div>
@ -166,6 +83,27 @@
<form id="createForm" action="<?= base_url('certificates/create') ?>" method="post">
<div class="modal-body">
<div class="row">
<div class="col-md-12 mb-3">
<label for="type" class="form-label">Activity Report <span class="text-danger">*</span></label>
<select class="form-select select2" id="type" name="type" required>
<option value=''>-- Choose one --</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label for="site" class="form-label">Site<span
class="text-danger">*</span></label>
<input type="text" class="form-control" id="site" name="site" required readonly>
</div>
<div class="col-md-6 mb-3">
<label for="productid" class="form-label">Product/Equipment<span
class="text-danger">*</span></label>
<input type="text" class="form-control" id="productid" name="productid" required readonly>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="certname" class="form-label">Certificate Name <span
@ -173,41 +111,22 @@
<input type="text" class="form-control" id="certname" name="certname" required>
</div>
<div class="col-md-6 mb-3">
<label for="certtype" class="form-label">Type Sertifikat</label>
<input type="text" class="form-control" id="certtype" name="certtype">
<label for="mainTrainingDate" class="form-label">Training Date<span
class="text-danger">*</span></label>
<input type="date" class="form-control" id="mainTrainingDate" name="trainingdate">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="issuedate" class="form-label">Start Date <span
class="text-danger">*</span></label>
<input type="date" class="form-control" id="issuedate" name="issuedate">
</div>
<div class="col-md-6 mb-3">
<label for="expirydate" class="form-label">Expiry Date <span
class="text-danger">*</span></label>
<input type="date" class="form-control" id="expirydate" name="expirydate">
<hr>
<div class="row mb-3">
<div class="col-12">
<button type="button" class="btn btn-sm btn-success" onclick="addAnalystRow()">
<i class="fas fa-plus-circle"></i> Add Analyst
</button>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="type" class="form-label">Site <span class="text-danger">*</span></label>
<select class="form-select select2" id="type" name="type" required>
<option value=''>-- Choose one --</option>
</select>
</div>
<div class="col-md-6 mb-3">
<label for="productid" class="form-label">Product/Equipment <span
class="text-danger">*</span></label>
<select class="form-select select2" id="productid" name="productid">
<option value=''>-- Choose one --</option>
</select>
</div>
<div id="analystRowsContainer">
<!-- Dynamic analyst rows will be added here -->
</div>
<!-- <div class="mb-3">
@ -255,11 +174,11 @@
"dom": '<"row"<"col-md-6"l>>rtip',
"columnDefs": [{
"orderable": false,
"targets": [7]
"targets": [5]
}, // Disable sorting on Action column
{
"searchable": true,
"targets": [0, 1, 2, 3, 4, 5, 6]
"targets": [0, 1, 2, 3, 4]
} // Enable search on all columns except Action
]
});
@ -274,48 +193,152 @@
dropdownParent: $('.modal')
});
// Fetch data from API and populate table
async function fetchCertificateData() {
try {
const response = await fetch('<?= base_url('certificates/api/getindextraining') ?>');
const data = await response.json();
if (data && Array.isArray(data)) {
populateTable(data);
} else {
console.log('No data available or invalid response format');
table.clear().draw();
}
} catch (error) {
console.error('Error fetching certificate data:', error);
table.clear().draw();
}
}
// Populate DataTable with fetched data
function populateTable(certificates) {
table.clear();
certificates.forEach((cert, index) => {
const certid = cert.certid || '';
const certname = cert.certname || '-';
const productname = cert.productname || '-';
const productnumber = cert.productnumber || '';
const issuedate = cert.issuedate || '-';
const vendor = cert.vendor || '-';
const type = cert.type || '-';
// Format issue date
let formattedDate = '-';
if (issuedate && issuedate !== '0000-00-00') {
const dateObj = new Date(issuedate);
if (!isNaN(dateObj.getTime())) {
formattedDate = dateObj.toLocaleDateString('en-US', { month: 'short', day: '2-digit', year: 'numeric' });
}
}
const rowData = [
index + 1,
`<strong>${escapeHtml(certname)}</strong><br><small class="text-muted">ID: ${escapeHtml(certid)}</small>`,
`${escapeHtml(productname)}${productnumber ? `<br><small class="text-muted">SN: ${escapeHtml(productnumber)}</small>` : ''}`,
formattedDate,
escapeHtml(vendor),
`<div class="btn-group btn-group-sm"><button type="button" class="btn btn-success btn-view" data-certid="${escapeHtml(certid)}" data-certtype="${escapeHtml(type)}" title="View PDF"><i class="fas fa-file-pdf"></i></button></div>`
];
table.row.add(rowData);
});
table.draw();
}
// Helper function to escape HTML
function escapeHtml(text) {
if (!text) return '';
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Fetch data on page load
fetchCertificateData();
// Handle Activity Report dropdown change
$('#type').on('change', function() {
const certid = $(this).val();
if (!certid) {
$('#site').val('');
$('#productid').val('');
return;
}
// Fetch certificate data from API
$.ajax({
url: '<?= base_url('certificates/api/getindextraining') ?>',
method: 'GET',
data: { actid: certid },
dataType: 'json',
success: function(response) {
if (response) {
$('#site').val(response.sitename || '');
$('#productid').val(response.productname || '');
} else {
$('#site').val('');
$('#productid').val('');
}
},
error: function(xhr, status, error) {
console.error('Error fetching certificate data:', error);
$('#site').val('');
$('#productid').val('');
}
});
});
// Add one default analyst row when modal opens
$('#createModal').on('shown.bs.modal', function() {
if($('#analystRowsContainer .analyst-row').length === 0) {
addAnalystRow();
}
// Populate Activity Report dropdown
populateActivityDropdown();
});
// Populate Activity Report dropdown from API
async function populateActivityDropdown() {
try {
const response = await fetch('<?= base_url('certificates/api/getindextraining') ?>');
const data = await response.json();
const dropdown = $('#type');
dropdown.empty();
dropdown.append('<option value="">-- Choose one --</option>');
if (data && Array.isArray(data)) {
data.forEach(item => {
dropdown.append(`<option value="${escapeHtml(item.certid)}">${escapeHtml(item.certname)}</option>`);
});
}
} catch (error) {
console.error('Error fetching activity data:', error);
}
}
// Custom search functionality
$('#searchInput').on('keyup', function () {
let searchValue = $(this).val().toLowerCase();
table.search(searchValue).draw();
});
// Status filter using DataTables column filter
$('#statusFilter').on('change', function () {
let status = $(this).val();
// Filter by status column (index 6)
if (status === '') {
table.column(6).search('').draw();
} else {
// Search for the badge text in status column
let statusText = '';
switch (status) {
case 'active':
statusText = 'Active';
break;
case 'expired':
statusText = 'Expired';
break;
case 'expiring':
statusText = 'Expiring Soon';
break;
}
table.column(6).search(statusText).draw();
}
});
// Type filter using DataTables column filter
$('#typeFilter').on('change', function () {
let type = $(this).val();
// Filter by type column (index 3)
// Filter by type column (index 1 - Certificate Name contains type info)
if (type === '') {
table.column(2).search('').draw();
table.column(1).search('').draw();
} else {
// Capitalize first letter for search
let typeText = type.charAt(0).toUpperCase() + type.slice(1);
table.column(2).search(typeText).draw();
table.column(1).search(typeText).draw();
}
});
@ -323,20 +346,18 @@
// Reset filters
window.resetFilters = function () {
$('#searchInput').val('');
$('#statusFilter').val('');
$('#typeFilter').val('');
// Reset DataTables search and filters
table.search('').columns().search('').draw();
// Re-apply default ordering
table.order([5, 'asc']).draw();
table.order([0, 'asc']).draw();
};
// View button click - Open PDF in new tab based on certificate type
$(document).on('click', '.btn-view', function () {
let certid = $(this).data('certid');
let certType = $(this).data('certtype');
let url = '<?= base_url('certificates/training/show/') ?>' + certid;
window.open(url, '_blank');
});
@ -351,6 +372,58 @@
this.showPicker();
});
// Re-attach date picker focus for dynamically added elements
$(document).on('focus', '.analyst-training-date', function() {
this.showPicker();
});
// Add analyst row
window.addAnalystRow = function() {
let rowCount = $('#analystRowsContainer .analyst-row').length;
let defaultTrainingDate = $('#mainTrainingDate').val();
let newRow = `
<div class="row analyst-row mb-2" data-row-index="${rowCount}">
<div class="col-md-6 mb-2">
<label class="form-label">Analyst Name <span class="text-danger">*</span></label>
<input type="text" class="form-control analyst-name" name="analyst_name[]" required>
</div>
<div class="col-md-5 mb-2">
<label class="form-label">Training Date <span class="text-danger">*</span></label>
<input type="date" class="form-control analyst-training-date" name="analyst_training_date[]" value="${defaultTrainingDate}" required>
</div>
<div class="col-md-1 mb-2">
<label class="form-label">&nbsp;</label>
<button type="button" class="btn btn-danger btn-sm w-100" onclick="removeAnalystRow(this)">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
`;
$('#analystRowsContainer').append(newRow);
};
// Remove analyst row
window.removeAnalystRow = function(btn) {
let totalRows = $('#analystRowsContainer .analyst-row').length;
if(totalRows <= 1) {
alert('At least one analyst is required. Cannot delete the last row.');
return;
}
if(confirm('Are you sure you want to remove this analyst?')) {
$(btn).closest('.analyst-row').remove();
}
};
// When main training date changes, update all analyst training dates
$('#mainTrainingDate').on('change', function() {
let newDate = $(this).val();
$('.analyst-training-date').val(newDate);
});
// Re-attach event handlers after DataTables pagination/draw
table.on('draw.dt', function () {
// Event handlers are already attached using $(document).on(), so no need to re-attach