434 lines
13 KiB
PHP
434 lines
13 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-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> Create
|
|
</button>
|
|
</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-6 mb-2">
|
|
<select id="typeFilter" class="form-select form-select-sm">
|
|
<option value="">All Types</option>
|
|
<option value="tms">TMS</option>
|
|
<option value="joko">Jokoh</option>
|
|
<option value="boeki">Tokyo Boeki</option>
|
|
</select>
|
|
</div>
|
|
<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="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: 37%;">Certificate Name</th>
|
|
<th style="width: 15%;">Product/Equipment</th>
|
|
<th style="width: 12%;">Issue Date</th>
|
|
<th style="width: 10%;">Vendor</th>
|
|
<th class="text-center" style="width: 9%;">Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<!-- Saya Ingin Ini Pakai JSON -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Create Modal -->
|
|
<div class="modal fade" id="createModal" tabindex="-1" aria-labelledby="createModalLabel" 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> Create Certificate Training
|
|
</h5>
|
|
<button type="button" class="btn-close btn-close-white" 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-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
|
|
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="mainTrainingDate" class="form-label">Training Date<span
|
|
class="text-danger">*</span></label>
|
|
<input type="date" class="form-control" id="mainTrainingDate" name="trainingdate">
|
|
</div>
|
|
</div>
|
|
|
|
<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 id="analystRowsContainer">
|
|
<!-- Dynamic analyst rows will be added here -->
|
|
</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>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?= $this->endSection() ?>
|
|
|
|
<?= $this->section('script') ?>
|
|
<script>
|
|
$(function () {
|
|
// Store DataTable instance
|
|
let table = null;
|
|
|
|
// 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": [5]
|
|
}, // Disable sorting on Action column
|
|
{
|
|
"searchable": true,
|
|
"targets": [0, 1, 2, 3, 4]
|
|
} // Enable search on all columns except Action
|
|
]
|
|
});
|
|
|
|
// Hide default DataTables search
|
|
$('#certificatesTable_filter').hide();
|
|
|
|
// Initialize Select2
|
|
$('.select2').select2({
|
|
theme: 'bootstrap-5',
|
|
width: '100%',
|
|
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();
|
|
});
|
|
|
|
// Type filter using DataTables column filter
|
|
$('#typeFilter').on('change', function () {
|
|
let type = $(this).val();
|
|
|
|
// Filter by type column (index 1 - Certificate Name contains type info)
|
|
if (type === '') {
|
|
table.column(1).search('').draw();
|
|
} else {
|
|
// Capitalize first letter for search
|
|
let typeText = type.charAt(0).toUpperCase() + type.slice(1);
|
|
table.column(1).search(typeText).draw();
|
|
}
|
|
});
|
|
|
|
|
|
// Reset filters
|
|
window.resetFilters = function () {
|
|
$('#searchInput').val('');
|
|
$('#typeFilter').val('');
|
|
|
|
// Reset DataTables search and filters
|
|
table.search('').columns().search('').draw();
|
|
|
|
// Re-apply default ordering
|
|
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 url = '<?= base_url('certificates/training/show/') ?>' + certid;
|
|
window.open(url, '_blank');
|
|
});
|
|
|
|
// Print button
|
|
$('#btnPrint').on('click', function () {
|
|
window.print();
|
|
});
|
|
|
|
// Date picker enhancement
|
|
$('input[type="date"]').on('focus', function () {
|
|
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"> </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
|
|
});
|
|
});
|
|
|
|
</script>
|
|
<?= $this->endSection() ?>
|