feat: Add admin navigation, fix modals, clickable stats, update docs
- Add admin dropdown menu in dashboard header with links to all admin pages - Fix template modal: larger size (800px), responsive grid, type/priority dropdowns - Fix recurring tickets modal: add Type and Assign To fields, larger size - Make dashboard stat cards clickable for quick filtering - Fix user-activity query (remove is_active requirement) - Add table existence check in ticket_dependencies API - Fix table overflow on dashboard - Update Claude.md and README.md with current project status - Remove migrations directory (all migrations completed) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -79,7 +79,17 @@
|
||||
<?php if (isset($GLOBALS['currentUser'])): ?>
|
||||
<span class="user-name">👤 <?php echo htmlspecialchars($GLOBALS['currentUser']['display_name'] ?? $GLOBALS['currentUser']['username']); ?></span>
|
||||
<?php if ($GLOBALS['currentUser']['is_admin']): ?>
|
||||
<span class="admin-badge">Admin</span>
|
||||
<div class="admin-dropdown">
|
||||
<button class="admin-badge" onclick="toggleAdminMenu(event)">Admin ▼</button>
|
||||
<div class="admin-dropdown-content" id="adminDropdown">
|
||||
<a href="/admin/templates">📋 Templates</a>
|
||||
<a href="/admin/workflow">🔄 Workflow</a>
|
||||
<a href="/admin/recurring-tickets">🔁 Recurring Tickets</a>
|
||||
<a href="/admin/custom-fields">📝 Custom Fields</a>
|
||||
<a href="/admin/user-activity">👥 User Activity</a>
|
||||
<a href="/admin/audit-log">📜 Audit Log</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<button class="settings-icon" title="Settings (Alt+S)" onclick="openSettingsModal()">⚙</button>
|
||||
<?php endif; ?>
|
||||
@@ -667,5 +677,47 @@
|
||||
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/settings.js"></script>
|
||||
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/keyboard-shortcuts.js"></script>
|
||||
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/advanced-search.js"></script>
|
||||
<script>
|
||||
// Admin dropdown toggle
|
||||
function toggleAdminMenu(event) {
|
||||
event.stopPropagation();
|
||||
const dropdown = document.getElementById('adminDropdown');
|
||||
dropdown.classList.toggle('show');
|
||||
}
|
||||
|
||||
// Close admin dropdown when clicking outside
|
||||
document.addEventListener('click', function(event) {
|
||||
const dropdown = document.getElementById('adminDropdown');
|
||||
if (dropdown && !event.target.closest('.admin-dropdown')) {
|
||||
dropdown.classList.remove('show');
|
||||
}
|
||||
});
|
||||
|
||||
// Stat card click handlers for filtering
|
||||
document.querySelectorAll('.stat-card').forEach(card => {
|
||||
card.style.cursor = 'pointer';
|
||||
card.addEventListener('click', function() {
|
||||
const classList = this.classList;
|
||||
let url = '/?';
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
|
||||
if (classList.contains('stat-open')) {
|
||||
url += 'status=Open';
|
||||
} else if (classList.contains('stat-critical')) {
|
||||
url += 'status=Open,Pending,In+Progress&priority_max=1';
|
||||
} else if (classList.contains('stat-unassigned')) {
|
||||
url += 'status=Open,Pending,In+Progress&assigned_to=unassigned';
|
||||
} else if (classList.contains('stat-today')) {
|
||||
url += 'status=Open,Pending,In+Progress&created_from=' + today + '&created_to=' + today;
|
||||
} else if (classList.contains('stat-resolved')) {
|
||||
url += 'status=Closed&updated_from=' + today + '&updated_to=' + today;
|
||||
}
|
||||
|
||||
if (url !== '/?') {
|
||||
window.location.href = url;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -108,7 +108,7 @@
|
||||
|
||||
<!-- Create/Edit Modal -->
|
||||
<div class="settings-modal" id="recurringModal" style="display: none;">
|
||||
<div class="settings-content" style="max-width: 600px;">
|
||||
<div class="settings-content" style="max-width: 800px; width: 90%;">
|
||||
<div class="settings-header">
|
||||
<h3 id="modalTitle">Create Recurring Ticket</h3>
|
||||
<button class="close-settings" onclick="closeModal()">×</button>
|
||||
@@ -122,7 +122,7 @@
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<label for="description_template">Description Template</label>
|
||||
<textarea id="description_template" name="description_template" rows="4" style="width: 100%;"></textarea>
|
||||
<textarea id="description_template" name="description_template" rows="8" style="width: 100%; min-height: 150px;"></textarea>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<label for="schedule_type">Schedule Type *</label>
|
||||
@@ -140,25 +140,45 @@
|
||||
<label for="schedule_time">Schedule Time *</label>
|
||||
<input type="time" id="schedule_time" name="schedule_time" value="09:00" required>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<label for="category">Category</label>
|
||||
<select id="category" name="category">
|
||||
<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="priority">Priority</label>
|
||||
<select id="priority" name="priority">
|
||||
<option value="1">P1 - Critical</option>
|
||||
<option value="2">P2 - High</option>
|
||||
<option value="3">P3 - Medium</option>
|
||||
<option value="4" selected>P4 - Low</option>
|
||||
<option value="5">P5 - Lowest</option>
|
||||
</select>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1rem;">
|
||||
<div class="setting-row">
|
||||
<label for="category">Category</label>
|
||||
<select id="category" name="category" style="width: 100%;">
|
||||
<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="type">Type</label>
|
||||
<select id="type" name="type" style="width: 100%;">
|
||||
<option value="Issue">Issue</option>
|
||||
<option value="Maintenance">Maintenance</option>
|
||||
<option value="Install">Install</option>
|
||||
<option value="Task">Task</option>
|
||||
<option value="Upgrade">Upgrade</option>
|
||||
<option value="Problem">Problem</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<label for="priority">Priority</label>
|
||||
<select id="priority" name="priority" style="width: 100%;">
|
||||
<option value="1">P1 - Critical</option>
|
||||
<option value="2">P2 - High</option>
|
||||
<option value="3">P3 - Medium</option>
|
||||
<option value="4" selected>P4 - Low</option>
|
||||
<option value="5">P5 - Lowest</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<label for="assigned_to">Assign To</label>
|
||||
<select id="assigned_to" name="assigned_to" style="width: 100%;">
|
||||
<option value="">Unassigned</option>
|
||||
<!-- Populated by JavaScript -->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="settings-footer">
|
||||
@@ -269,15 +289,35 @@
|
||||
document.getElementById('schedule_day').value = rt.schedule_day || '';
|
||||
document.getElementById('schedule_time').value = rt.schedule_time ? rt.schedule_time.substring(0, 5) : '09:00';
|
||||
document.getElementById('category').value = rt.category || 'General';
|
||||
document.getElementById('type').value = rt.type || 'Issue';
|
||||
document.getElementById('priority').value = rt.priority || 4;
|
||||
document.getElementById('assigned_to').value = rt.assigned_to || '';
|
||||
document.getElementById('modalTitle').textContent = 'Edit Recurring Ticket';
|
||||
document.getElementById('recurringModal').style.display = 'flex';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Load users for assignee dropdown
|
||||
function loadUsers() {
|
||||
fetch('/api/get_users.php')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success && data.users) {
|
||||
const select = document.getElementById('assigned_to');
|
||||
data.users.forEach(user => {
|
||||
const option = document.createElement('option');
|
||||
option.value = user.user_id;
|
||||
option.textContent = user.display_name || user.username;
|
||||
select.appendChild(option);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize
|
||||
updateScheduleOptions();
|
||||
loadUsers();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
|
||||
<!-- Create/Edit Modal -->
|
||||
<div class="settings-modal" id="templateModal" style="display: none;">
|
||||
<div class="settings-content" style="max-width: 600px;">
|
||||
<div class="settings-content" style="max-width: 800px; width: 90%;">
|
||||
<div class="settings-header">
|
||||
<h3 id="modalTitle">Create Template</h3>
|
||||
<button class="close-settings" onclick="closeModal()">×</button>
|
||||
@@ -111,12 +111,12 @@
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<label for="description_template">Description Template</label>
|
||||
<textarea id="description_template" name="description_template" rows="6" style="width: 100%;" placeholder="Pre-filled description content"></textarea>
|
||||
<textarea id="description_template" name="description_template" rows="10" style="width: 100%; min-height: 200px;" placeholder="Pre-filled description content"></textarea>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem;">
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 1rem;">
|
||||
<div class="setting-row">
|
||||
<label for="category">Category</label>
|
||||
<select id="category" name="category">
|
||||
<select id="category" name="category" style="width: 100%;">
|
||||
<option value="">Any</option>
|
||||
<option value="General">General</option>
|
||||
<option value="Hardware">Hardware</option>
|
||||
@@ -127,18 +127,19 @@
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<label for="type">Type</label>
|
||||
<select id="type" name="type">
|
||||
<select id="type" name="type" style="width: 100%;">
|
||||
<option value="">Any</option>
|
||||
<option value="Maintenance">Maintenance</option>
|
||||
<option value="Install">Install</option>
|
||||
<option value="Task">Task</option>
|
||||
<option value="Upgrade">Upgrade</option>
|
||||
<option value="Issue">Issue</option>
|
||||
<option value="Problem">Problem</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<label for="priority">Priority</label>
|
||||
<select id="priority" name="priority">
|
||||
<select id="priority" name="priority" style="width: 100%;">
|
||||
<option value="1">P1</option>
|
||||
<option value="2">P2</option>
|
||||
<option value="3">P3</option>
|
||||
|
||||
Reference in New Issue
Block a user