233 lines
9.8 KiB
PHP
233 lines
9.8 KiB
PHP
<?= $this->extend('layout/management_layout') ?>
|
|
<?= $this->section('content') ?>
|
|
|
|
<div class="container-fluid"
|
|
data-template-endpoint="<?= esc($templateEndpoint ?? site_url('api/attendance-templates')) ?>"
|
|
data-csrf-name="<?= esc(csrf_token()) ?>"
|
|
data-csrf-value="<?= esc(csrf_hash()) ?>">
|
|
<div class="wrapper">
|
|
<div class="text-center mb-4">
|
|
<h2 class="text-center mt-4 mb-3">Attendance Comment Templates</h2>
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<button id="addNewBtn" class="btn btn-success">Add New Template</button>
|
|
</div>
|
|
|
|
<div class="table-responsive">
|
|
<table id="templatesTable" class="display table table-striped align-middle">
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>Min Score</th>
|
|
<th>Max Score</th>
|
|
<th>Template Text</th>
|
|
<th>Active</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td colspan="6" class="text-center text-muted">Loading templates...</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="modal fade" id="editTemplateModal" tabindex="-1" aria-labelledby="editTemplateModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<form id="editTemplateForm" class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="editTemplateModalLabel">Edit Template</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<input type="hidden" name="id" id="template_id">
|
|
<div class="mb-3">
|
|
<label for="min_score" class="form-label">Min Score:</label>
|
|
<input type="number" name="min_score" id="min_score" class="form-control" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="max_score" class="form-label">Max Score:</label>
|
|
<input type="number" name="max_score" id="max_score" class="form-control" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="template_text" class="form-label">Template Text:</label>
|
|
<textarea name="template_text" id="template_text" class="form-control" required></textarea>
|
|
</div>
|
|
<div class="form-check mb-3">
|
|
<input class="form-check-input" type="checkbox" name="is_active" id="is_active" value="on">
|
|
<label class="form-check-label" for="is_active">
|
|
Active
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">Save changes</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?= $this->endSection() ?>
|
|
|
|
<?= $this->section('scripts') ?>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const container = document.querySelector('[data-template-endpoint]');
|
|
if (!container) return;
|
|
|
|
const endpoint = container.dataset.templateEndpoint;
|
|
const csrfName = container.dataset.csrfName;
|
|
let csrfValue = container.dataset.csrfValue;
|
|
|
|
const addNewBtn = document.getElementById('addNewBtn');
|
|
const tableBody = document.querySelector('#templatesTable tbody');
|
|
const editModalElement = document.getElementById('editTemplateModal');
|
|
const editForm = document.getElementById('editTemplateForm');
|
|
const editModal = new window.bootstrap.Modal(editModalElement);
|
|
|
|
let templates = [];
|
|
|
|
const safe = (value, fallback = '') =>
|
|
value === undefined || value === null ? fallback : value;
|
|
|
|
const fetchTemplates = async () => {
|
|
tableBody.innerHTML = '<tr><td colspan="6" class="text-center text-muted">Loading templates...</td></tr>';
|
|
try {
|
|
const response = await fetch(endpoint, {
|
|
headers: { 'Accept': 'application/json' },
|
|
});
|
|
if (!response.ok) throw new Error('Failed to fetch');
|
|
const payload = await response.json();
|
|
templates = payload.templates || [];
|
|
renderTemplates();
|
|
} catch (error) {
|
|
console.error(error);
|
|
tableBody.innerHTML = '<tr><td colspan="6" class="text-center text-danger">Failed to load templates.</td></tr>';
|
|
}
|
|
};
|
|
|
|
const renderTemplates = () => {
|
|
if (window.jQuery.fn.DataTable.isDataTable('#templatesTable')) {
|
|
window.jQuery('#templatesTable').DataTable().destroy();
|
|
}
|
|
tableBody.innerHTML = '';
|
|
|
|
if (templates.length === 0) {
|
|
tableBody.innerHTML = '<tr><td colspan="6" class="text-center text-muted">No templates found.</td></tr>';
|
|
return;
|
|
}
|
|
|
|
templates.forEach(template => {
|
|
const row = document.createElement('tr');
|
|
row.innerHTML = `
|
|
<td>${safe(template.id)}</td>
|
|
<td>${safe(template.min_score)}</td>
|
|
<td>${safe(template.max_score)}</td>
|
|
<td>${safe(template.template_text)}</td>
|
|
<td>${template.is_active ? 'Yes' : 'No'}</td>
|
|
<td class="d-flex gap-1"></td>
|
|
`;
|
|
|
|
const actionsCell = row.querySelector('td:last-child');
|
|
|
|
const editButton = document.createElement('button');
|
|
editButton.className = 'btn btn-sm btn-warning';
|
|
editButton.textContent = 'Edit';
|
|
editButton.onclick = () => openEditModal(template);
|
|
actionsCell.appendChild(editButton);
|
|
|
|
const deleteButton = document.createElement('button');
|
|
deleteButton.className = 'btn btn-sm btn-danger';
|
|
deleteButton.textContent = 'Delete';
|
|
deleteButton.onclick = () => deleteTemplate(template.id);
|
|
actionsCell.appendChild(deleteButton);
|
|
|
|
tableBody.appendChild(row);
|
|
});
|
|
|
|
window.jQuery('#templatesTable').DataTable({
|
|
pageLength: 25,
|
|
lengthMenu: [10, 25, 50, 100],
|
|
});
|
|
};
|
|
|
|
const openEditModal = (template = {}) => {
|
|
editForm.reset();
|
|
editForm.querySelector('#template_id').value = template.id || '';
|
|
editForm.querySelector('#min_score').value = template.min_score || '';
|
|
editForm.querySelector('#max_score').value = template.max_score || '';
|
|
editForm.querySelector('#template_text').value = template.template_text || '';
|
|
editForm.querySelector('#is_active').checked = template.is_active || false;
|
|
editModal.show();
|
|
};
|
|
|
|
addNewBtn.addEventListener('click', () => openEditModal());
|
|
|
|
editForm.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
const formData = new FormData(editForm);
|
|
const data = Object.fromEntries(formData.entries());
|
|
data[csrfName] = csrfValue;
|
|
|
|
try {
|
|
const response = await fetch(`${endpoint}/save`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
},
|
|
body: new URLSearchParams(data).toString()
|
|
});
|
|
|
|
const result = await response.json();
|
|
if (response.ok && result.status === 'success') {
|
|
editModal.hide();
|
|
await fetchTemplates(); // Refresh table
|
|
} else {
|
|
alert('Failed to save template.');
|
|
}
|
|
} catch (error) {
|
|
console.error('Save error:', error);
|
|
alert('An error occurred while saving.');
|
|
}
|
|
});
|
|
|
|
const deleteTemplate = async (id) => {
|
|
if (!confirm('Are you sure you want to delete this template?')) return;
|
|
|
|
const data = new URLSearchParams();
|
|
data.append(csrfName, csrfValue);
|
|
data.append('id', id);
|
|
|
|
try {
|
|
const response = await fetch(`${endpoint}/delete`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
},
|
|
body: data
|
|
});
|
|
|
|
const result = await response.json();
|
|
if (response.ok && result.status === 'success') {
|
|
await fetchTemplates(); // Refresh table
|
|
} else {
|
|
alert('Failed to delete template.');
|
|
}
|
|
} catch (error) {
|
|
console.error('Delete error:', error);
|
|
alert('An error occurred while deleting.');
|
|
}
|
|
};
|
|
|
|
fetchTemplates();
|
|
});
|
|
</script>
|
|
<?= $this->endSection() ?>
|