recreate project
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
<?= $this->extend('layout/management_layout') ?>
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
<?php if (session()->getFlashdata('success')): ?>
|
||||
<div class="alert alert-success"><?= session()->getFlashdata('success') ?></div>
|
||||
<?php elseif (session()->getFlashdata('error')): ?>
|
||||
<div class="alert alert-danger"><?= session()->getFlashdata('error') ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="container-fluid"
|
||||
data-config-endpoint="<?= esc($configEndpoint ?? site_url('api/configuration')) ?>"
|
||||
data-csrf-name="<?= esc(csrf_token()) ?>"
|
||||
data-csrf-value="<?= esc(csrf_hash()) ?>">
|
||||
<div class="wrapper">
|
||||
<!-- Centered Title with proper spacing -->
|
||||
<div class="text-center mb-4">
|
||||
<h2 class="text-center mt-4 mb-3">Configuration Management</h2>
|
||||
</div>
|
||||
|
||||
<!-- Spacer after the button -->
|
||||
<div class="mb-4"></div>
|
||||
|
||||
<!-- Add New Configuration Form -->
|
||||
<div id="config-form" style="display: none;" class="mb-4">
|
||||
<form action="/configuration/addConfig" method="post" class="mt-3">
|
||||
<?= csrf_field() ?>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="config_key">Config Key:</label>
|
||||
<input type="text" name="config_key" class="form-control" required>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="config_value">Config Value:</label>
|
||||
<input type="text" name="config_value" class="form-control" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
<a href="/configuration/configuration_view" class="btn btn-secondary">Cancel</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Display existing configurations -->
|
||||
<div class="table-responsive">
|
||||
<table id="configTable" class="display table table-striped align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Config Key</th>
|
||||
<th>Config Value</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="4" class="text-center text-muted">Loading configurations...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="mb-4">
|
||||
<button id="addNewBtn" class="btn btn-success">Add New Config</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Modal -->
|
||||
<div class="modal fade" id="editConfigModal" tabindex="-1" aria-labelledby="editConfigModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<form id="editConfigForm" method="post" class="modal-content">
|
||||
<?= csrf_field() ?>
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="editConfigModalLabel">Edit Configuration</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" name="config_id" id="config_id">
|
||||
<div class="mb-3">
|
||||
<label for="modal_config_key" class="form-label">Config Key:</label>
|
||||
<input type="text" name="config_key" id="modal_config_key" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="modal_config_value" class="form-label">Config Value:</label>
|
||||
<input type="text" name="config_value" id="modal_config_value" class="form-control" required>
|
||||
</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-config-endpoint]');
|
||||
if (!container) return;
|
||||
|
||||
const endpoint = container.dataset.configEndpoint;
|
||||
const csrfName = container.dataset.csrfName;
|
||||
const csrfValue = container.dataset.csrfValue;
|
||||
const CSRF_COOKIE_NAME = <?= json_encode(config('Security')->csrfCookieName ?? (config('Security')->cookieName ?? 'csrf_cookie_name')) ?>;
|
||||
function getCookie(n){
|
||||
return document.cookie.split(';').map(v=>v.trim()).find(v=>v.startsWith(n+'='))?.split('=')[1] || '';
|
||||
}
|
||||
|
||||
const addNewBtn = document.getElementById('addNewBtn');
|
||||
const configForm = document.getElementById('config-form');
|
||||
const tableBody = document.querySelector('#configTable tbody');
|
||||
const editModalElement = document.getElementById('editConfigModal');
|
||||
const editForm = document.getElementById('editConfigForm');
|
||||
const editModal = (editModalElement && window.bootstrap && window.bootstrap.Modal)
|
||||
? new window.bootstrap.Modal(editModalElement)
|
||||
: null;
|
||||
|
||||
let configs = [];
|
||||
|
||||
const safe = (value, fallback = '') =>
|
||||
value === undefined || value === null ? fallback : value;
|
||||
|
||||
if (addNewBtn && configForm) {
|
||||
addNewBtn.addEventListener('click', () => {
|
||||
const isVisible = configForm.style.display === 'block';
|
||||
configForm.style.display = isVisible ? 'none' : 'block';
|
||||
});
|
||||
}
|
||||
|
||||
const hasDataTables = () =>
|
||||
window.jQuery && window.jQuery.fn && window.jQuery.fn.DataTable;
|
||||
|
||||
const destroyDataTable = () => {
|
||||
if (!hasDataTables()) return;
|
||||
if (window.jQuery.fn.DataTable.isDataTable('#configTable')) {
|
||||
window.jQuery('#configTable').DataTable().clear().destroy();
|
||||
}
|
||||
};
|
||||
|
||||
const initDataTable = () => {
|
||||
if (!hasDataTables()) return;
|
||||
window.jQuery('#configTable').DataTable({
|
||||
pageLength: 25,
|
||||
lengthMenu: [10, 25, 50, 100],
|
||||
});
|
||||
};
|
||||
|
||||
const buildDeleteForm = (id) => {
|
||||
const form = document.createElement('form');
|
||||
form.method = 'post';
|
||||
form.action = '/configuration/deleteConfig/' + safe(id);
|
||||
form.className = 'd-inline';
|
||||
form.addEventListener('submit', (event) => {
|
||||
if (!confirm('Are you sure you want to delete this config?')) {
|
||||
event.preventDefault();
|
||||
}
|
||||
// Refresh CSRF token just before submit
|
||||
try {
|
||||
const fresh = decodeURIComponent(getCookie(CSRF_COOKIE_NAME) || '');
|
||||
if (fresh) {
|
||||
const hidden = form.querySelector('input[name="' + csrfName + '"]');
|
||||
if (hidden) hidden.value = fresh;
|
||||
}
|
||||
} catch(_) {}
|
||||
});
|
||||
|
||||
const csrfInput = document.createElement('input');
|
||||
csrfInput.type = 'hidden';
|
||||
csrfInput.name = csrfName;
|
||||
csrfInput.value = csrfValue;
|
||||
form.appendChild(csrfInput);
|
||||
|
||||
const deleteButton = document.createElement('button');
|
||||
deleteButton.type = 'submit';
|
||||
deleteButton.className = 'btn btn-sm btn-danger';
|
||||
deleteButton.textContent = 'Delete';
|
||||
form.appendChild(deleteButton);
|
||||
|
||||
return form;
|
||||
};
|
||||
|
||||
const openEditModal = (config) => {
|
||||
if (!editModal || !editForm) return;
|
||||
editForm.action = '/configuration/editConfig/' + safe(config.id);
|
||||
editForm.querySelector('#config_id').value = safe(config.id);
|
||||
editForm.querySelector('#modal_config_key').value = safe(config.config_key);
|
||||
editForm.querySelector('#modal_config_value').value = safe(config.config_value);
|
||||
editModal.show();
|
||||
};
|
||||
|
||||
const renderConfigs = function() {
|
||||
destroyDataTable();
|
||||
tableBody.innerHTML = '';
|
||||
|
||||
if (!Array.isArray(configs) || configs.length === 0) {
|
||||
const emptyRow = document.createElement('tr');
|
||||
const cell = document.createElement('td');
|
||||
cell.colSpan = 4;
|
||||
cell.classList.add('text-center', 'text-muted');
|
||||
cell.textContent = 'No configurations found.';
|
||||
emptyRow.appendChild(cell);
|
||||
tableBody.appendChild(emptyRow);
|
||||
return;
|
||||
}
|
||||
|
||||
configs.forEach(function(config) {
|
||||
const row = document.createElement('tr');
|
||||
|
||||
const idCell = document.createElement('td');
|
||||
idCell.textContent = safe(config.id);
|
||||
row.appendChild(idCell);
|
||||
|
||||
const keyCell = document.createElement('td');
|
||||
keyCell.textContent = safe(config.config_key);
|
||||
row.appendChild(keyCell);
|
||||
|
||||
const valueCell = document.createElement('td');
|
||||
valueCell.textContent = safe(config.config_value);
|
||||
row.appendChild(valueCell);
|
||||
|
||||
const actionsCell = document.createElement('td');
|
||||
actionsCell.className = 'd-flex gap-1';
|
||||
|
||||
const editButton = document.createElement('button');
|
||||
editButton.type = 'button';
|
||||
editButton.className = 'btn btn-sm btn-warning';
|
||||
editButton.textContent = 'Edit';
|
||||
editButton.addEventListener('click', function() {
|
||||
openEditModal(config);
|
||||
});
|
||||
actionsCell.appendChild(editButton);
|
||||
|
||||
actionsCell.appendChild(buildDeleteForm(config.id));
|
||||
|
||||
row.appendChild(actionsCell);
|
||||
tableBody.appendChild(row);
|
||||
});
|
||||
|
||||
initDataTable();
|
||||
};
|
||||
|
||||
const fetchConfigs = function() {
|
||||
tableBody.innerHTML = '<tr><td colspan="4" class="text-center text-muted">Loading configurations...</td></tr>';
|
||||
|
||||
fetch(endpoint, {
|
||||
headers: { 'Accept': 'application/json' },
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(function(response) {
|
||||
if (!response.ok) {
|
||||
throw new Error('Request failed with status ' + response.status);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(function(payload) {
|
||||
const list = payload && Array.isArray(payload.configs) ? payload.configs : [];
|
||||
configs = list;
|
||||
renderConfigs();
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error(error);
|
||||
destroyDataTable();
|
||||
tableBody.innerHTML = '<tr><td colspan="4" class="text-center text-danger">Failed to load configurations.</td></tr>';
|
||||
});
|
||||
};
|
||||
|
||||
fetchConfigs();
|
||||
});
|
||||
</script>
|
||||
<?= $this->endSection() ?>
|
||||
Reference in New Issue
Block a user