255 lines
13 KiB
PHP
255 lines
13 KiB
PHP
|
|
<?php
|
|||
|
|
// Admin view for managing custom fields
|
|||
|
|
// Receives $customFields from controller
|
|||
|
|
?>
|
|||
|
|
<!DOCTYPE html>
|
|||
|
|
<html lang="en">
|
|||
|
|
<head>
|
|||
|
|
<meta charset="UTF-8">
|
|||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
|
<title>Custom Fields - Admin</title>
|
|||
|
|
<link rel="icon" type="image/png" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/images/favicon.png">
|
|||
|
|
<link rel="stylesheet" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/css/dashboard.css">
|
|||
|
|
<link rel="stylesheet" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/css/ticket.css">
|
|||
|
|
<script>
|
|||
|
|
window.CSRF_TOKEN = '<?php
|
|||
|
|
require_once __DIR__ . '/../../middleware/CsrfMiddleware.php';
|
|||
|
|
echo CsrfMiddleware::getToken();
|
|||
|
|
?>';
|
|||
|
|
</script>
|
|||
|
|
</head>
|
|||
|
|
<body>
|
|||
|
|
<div class="user-header">
|
|||
|
|
<div class="user-header-left">
|
|||
|
|
<a href="/" class="back-link">← Dashboard</a>
|
|||
|
|
<span style="margin-left: 1rem; color: var(--terminal-amber);">Admin: Custom Fields</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="user-header-right">
|
|||
|
|
<?php if (isset($GLOBALS['currentUser'])): ?>
|
|||
|
|
<span class="user-name"><?php echo htmlspecialchars($GLOBALS['currentUser']['display_name'] ?? $GLOBALS['currentUser']['username']); ?></span>
|
|||
|
|
<span class="admin-badge">Admin</span>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="ascii-frame-outer" style="max-width: 1200px; margin: 2rem auto;">
|
|||
|
|
<span class="bottom-left-corner">╚</span>
|
|||
|
|
<span class="bottom-right-corner">╝</span>
|
|||
|
|
|
|||
|
|
<div class="ascii-section-header">Custom Fields Management</div>
|
|||
|
|
<div class="ascii-content">
|
|||
|
|
<div class="ascii-frame-inner">
|
|||
|
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
|
|||
|
|
<h2 style="margin: 0;">Custom Field Definitions</h2>
|
|||
|
|
<button onclick="showCreateModal()" class="btn">+ New Field</button>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<table style="width: 100%;">
|
|||
|
|
<thead>
|
|||
|
|
<tr>
|
|||
|
|
<th>Order</th>
|
|||
|
|
<th>Field Name</th>
|
|||
|
|
<th>Label</th>
|
|||
|
|
<th>Type</th>
|
|||
|
|
<th>Category</th>
|
|||
|
|
<th>Required</th>
|
|||
|
|
<th>Status</th>
|
|||
|
|
<th>Actions</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
<?php if (empty($customFields)): ?>
|
|||
|
|
<tr>
|
|||
|
|
<td colspan="8" style="text-align: center; padding: 2rem; color: var(--terminal-green-dim);">
|
|||
|
|
No custom fields defined.
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
<?php else: ?>
|
|||
|
|
<?php foreach ($customFields as $field): ?>
|
|||
|
|
<tr>
|
|||
|
|
<td><?php echo $field['display_order']; ?></td>
|
|||
|
|
<td><code><?php echo htmlspecialchars($field['field_name']); ?></code></td>
|
|||
|
|
<td><?php echo htmlspecialchars($field['field_label']); ?></td>
|
|||
|
|
<td><?php echo ucfirst($field['field_type']); ?></td>
|
|||
|
|
<td><?php echo htmlspecialchars($field['category'] ?? 'All'); ?></td>
|
|||
|
|
<td><?php echo $field['is_required'] ? 'Yes' : 'No'; ?></td>
|
|||
|
|
<td>
|
|||
|
|
<span style="color: <?php echo $field['is_active'] ? 'var(--status-open)' : 'var(--status-closed)'; ?>;">
|
|||
|
|
<?php echo $field['is_active'] ? 'Active' : 'Inactive'; ?>
|
|||
|
|
</span>
|
|||
|
|
</td>
|
|||
|
|
<td>
|
|||
|
|
<button onclick="editField(<?php echo $field['field_id']; ?>)" class="btn btn-small">Edit</button>
|
|||
|
|
<button onclick="deleteField(<?php echo $field['field_id']; ?>)" class="btn btn-small btn-danger">Delete</button>
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
<?php endforeach; ?>
|
|||
|
|
<?php endif; ?>
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- Create/Edit Modal -->
|
|||
|
|
<div class="settings-modal" id="fieldModal" style="display: none;">
|
|||
|
|
<div class="settings-content" style="max-width: 500px;">
|
|||
|
|
<div class="settings-header">
|
|||
|
|
<h3 id="modalTitle">Create Custom Field</h3>
|
|||
|
|
<button class="close-settings" onclick="closeModal()">×</button>
|
|||
|
|
</div>
|
|||
|
|
<form id="fieldForm" onsubmit="saveField(event)">
|
|||
|
|
<input type="hidden" id="field_id" name="field_id">
|
|||
|
|
<div class="settings-body">
|
|||
|
|
<div class="setting-row">
|
|||
|
|
<label for="field_name">Field Name * (internal)</label>
|
|||
|
|
<input type="text" id="field_name" name="field_name" required pattern="[a-z_]+" placeholder="e.g., server_name">
|
|||
|
|
</div>
|
|||
|
|
<div class="setting-row">
|
|||
|
|
<label for="field_label">Field Label * (display)</label>
|
|||
|
|
<input type="text" id="field_label" name="field_label" required placeholder="e.g., Server Name">
|
|||
|
|
</div>
|
|||
|
|
<div class="setting-row">
|
|||
|
|
<label for="field_type">Field Type *</label>
|
|||
|
|
<select id="field_type" name="field_type" required onchange="toggleOptionsField()">
|
|||
|
|
<option value="text">Text</option>
|
|||
|
|
<option value="textarea">Text Area</option>
|
|||
|
|
<option value="select">Dropdown (Select)</option>
|
|||
|
|
<option value="checkbox">Checkbox</option>
|
|||
|
|
<option value="date">Date</option>
|
|||
|
|
<option value="number">Number</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
<div class="setting-row" id="options_row" style="display: none;">
|
|||
|
|
<label for="field_options">Options (one per line)</label>
|
|||
|
|
<textarea id="field_options" name="field_options" rows="4" placeholder="Option 1 Option 2 Option 3"></textarea>
|
|||
|
|
</div>
|
|||
|
|
<div class="setting-row">
|
|||
|
|
<label for="category">Category (empty = all)</label>
|
|||
|
|
<select id="category" name="category">
|
|||
|
|
<option value="">All Categories</option>
|
|||
|
|
<option value="General">General</option>
|
|||
|
|
<option value="Hardware">Hardware</option>
|
|||
|
|
<option value="Software">Software</option>
|
|||
|
|
<option value="Network">Network</option>
|
|||
|
|
<option value="Security">Security</option>
|
|||
|
|
</select>
|
|||
|
|
</div>
|
|||
|
|
<div class="setting-row">
|
|||
|
|
<label for="display_order">Display Order</label>
|
|||
|
|
<input type="number" id="display_order" name="display_order" value="0" min="0">
|
|||
|
|
</div>
|
|||
|
|
<div class="setting-row">
|
|||
|
|
<label><input type="checkbox" id="is_required" name="is_required"> Required field</label>
|
|||
|
|
</div>
|
|||
|
|
<div class="setting-row">
|
|||
|
|
<label><input type="checkbox" id="is_active" name="is_active" checked> Active</label>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="settings-footer">
|
|||
|
|
<button type="submit" class="btn btn-primary">Save</button>
|
|||
|
|
<button type="button" class="btn btn-secondary" onclick="closeModal()">Cancel</button>
|
|||
|
|
</div>
|
|||
|
|
</form>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/toast.js"></script>
|
|||
|
|
<script>
|
|||
|
|
function showCreateModal() {
|
|||
|
|
document.getElementById('modalTitle').textContent = 'Create Custom Field';
|
|||
|
|
document.getElementById('fieldForm').reset();
|
|||
|
|
document.getElementById('field_id').value = '';
|
|||
|
|
document.getElementById('is_active').checked = true;
|
|||
|
|
toggleOptionsField();
|
|||
|
|
document.getElementById('fieldModal').style.display = 'flex';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function closeModal() {
|
|||
|
|
document.getElementById('fieldModal').style.display = 'none';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function toggleOptionsField() {
|
|||
|
|
const type = document.getElementById('field_type').value;
|
|||
|
|
document.getElementById('options_row').style.display = type === 'select' ? 'block' : 'none';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function saveField(e) {
|
|||
|
|
e.preventDefault();
|
|||
|
|
const form = document.getElementById('fieldForm');
|
|||
|
|
const data = {
|
|||
|
|
field_id: document.getElementById('field_id').value,
|
|||
|
|
field_name: document.getElementById('field_name').value,
|
|||
|
|
field_label: document.getElementById('field_label').value,
|
|||
|
|
field_type: document.getElementById('field_type').value,
|
|||
|
|
category: document.getElementById('category').value || null,
|
|||
|
|
display_order: parseInt(document.getElementById('display_order').value) || 0,
|
|||
|
|
is_required: document.getElementById('is_required').checked ? 1 : 0,
|
|||
|
|
is_active: document.getElementById('is_active').checked ? 1 : 0
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
if (data.field_type === 'select') {
|
|||
|
|
const options = document.getElementById('field_options').value.split('\n').filter(o => o.trim());
|
|||
|
|
data.field_options = { options: options };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const method = data.field_id ? 'PUT' : 'POST';
|
|||
|
|
const url = '/api/custom_fields.php' + (data.field_id ? '?id=' + data.field_id : '');
|
|||
|
|
|
|||
|
|
fetch(url, {
|
|||
|
|
method: method,
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
'X-CSRF-Token': window.CSRF_TOKEN
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify(data)
|
|||
|
|
})
|
|||
|
|
.then(r => r.json())
|
|||
|
|
.then(result => {
|
|||
|
|
if (result.success) {
|
|||
|
|
window.location.reload();
|
|||
|
|
} else {
|
|||
|
|
toast.error(result.error || 'Failed to save');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function editField(id) {
|
|||
|
|
fetch('/api/custom_fields.php?id=' + id)
|
|||
|
|
.then(r => r.json())
|
|||
|
|
.then(data => {
|
|||
|
|
if (data.success && data.field) {
|
|||
|
|
const f = data.field;
|
|||
|
|
document.getElementById('field_id').value = f.field_id;
|
|||
|
|
document.getElementById('field_name').value = f.field_name;
|
|||
|
|
document.getElementById('field_label').value = f.field_label;
|
|||
|
|
document.getElementById('field_type').value = f.field_type;
|
|||
|
|
document.getElementById('category').value = f.category || '';
|
|||
|
|
document.getElementById('display_order').value = f.display_order;
|
|||
|
|
document.getElementById('is_required').checked = f.is_required == 1;
|
|||
|
|
document.getElementById('is_active').checked = f.is_active == 1;
|
|||
|
|
toggleOptionsField();
|
|||
|
|
if (f.field_options && f.field_options.options) {
|
|||
|
|
document.getElementById('field_options').value = f.field_options.options.join('\n');
|
|||
|
|
}
|
|||
|
|
document.getElementById('modalTitle').textContent = 'Edit Custom Field';
|
|||
|
|
document.getElementById('fieldModal').style.display = 'flex';
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function deleteField(id) {
|
|||
|
|
if (!confirm('Delete this custom field? All values will be lost.')) return;
|
|||
|
|
fetch('/api/custom_fields.php?id=' + id, {
|
|||
|
|
method: 'DELETE',
|
|||
|
|
headers: { 'X-CSRF-Token': window.CSRF_TOKEN }
|
|||
|
|
})
|
|||
|
|
.then(r => r.json())
|
|||
|
|
.then(data => {
|
|||
|
|
if (data.success) window.location.reload();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
</body>
|
|||
|
|
</html>
|