Feature 3: Implement Status Transitions with Workflow Validation
Add comprehensive workflow management system for ticket status transitions: - Created WorkflowModel.php for managing status transition rules - Updated TicketController.php to load allowed transitions for each ticket - Modified TicketView.php to display dynamic status dropdown with only allowed transitions - Enhanced api/update_ticket.php with server-side workflow validation - Added updateTicketStatus() JavaScript function for client-side status changes - Included CSS styling for status select dropdown with color-coded states - Transitions can require comments or admin privileges - Status changes are validated against status_transitions table This feature enforces proper ticket workflows and prevents invalid status changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -267,6 +267,98 @@ function handleAssignmentChange() {
|
||||
});
|
||||
}
|
||||
|
||||
function updateTicketStatus() {
|
||||
const statusSelect = document.getElementById('statusSelect');
|
||||
const selectedOption = statusSelect.options[statusSelect.selectedIndex];
|
||||
const newStatus = selectedOption.value;
|
||||
const requiresComment = selectedOption.dataset.requiresComment === '1';
|
||||
const requiresAdmin = selectedOption.dataset.requiresAdmin === '1';
|
||||
|
||||
// Check if transitioning to the same status (current)
|
||||
if (selectedOption.text.includes('(current)')) {
|
||||
return; // No change needed
|
||||
}
|
||||
|
||||
// Warn if comment is required
|
||||
if (requiresComment) {
|
||||
const proceed = confirm(`This status change requires a comment. Please add a comment explaining the reason for this transition.\n\nProceed with status change to "${newStatus}"?`);
|
||||
if (!proceed) {
|
||||
// Reset to current status
|
||||
statusSelect.selectedIndex = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract ticket ID
|
||||
let ticketId;
|
||||
if (window.location.href.includes('?id=')) {
|
||||
ticketId = window.location.href.split('id=')[1];
|
||||
} else {
|
||||
const matches = window.location.pathname.match(/\/ticket\/(\d+)/);
|
||||
ticketId = matches ? matches[1] : null;
|
||||
}
|
||||
|
||||
if (!ticketId) {
|
||||
console.error('Could not determine ticket ID');
|
||||
statusSelect.selectedIndex = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Update status via API
|
||||
fetch('/api/update_ticket.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
ticket_id: ticketId,
|
||||
status: newStatus
|
||||
})
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
return response.text().then(text => {
|
||||
console.error('Server response:', text);
|
||||
throw new Error('Network response was not ok');
|
||||
});
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// Update the dropdown to show new status as current
|
||||
const newClass = 'status-' + newStatus.toLowerCase().replace(/ /g, '-');
|
||||
statusSelect.className = 'editable status-select ' + newClass;
|
||||
|
||||
// Update the selected option text to show as current
|
||||
selectedOption.text = newStatus + ' (current)';
|
||||
|
||||
// Move the selected option to the top
|
||||
statusSelect.remove(statusSelect.selectedIndex);
|
||||
statusSelect.insertBefore(selectedOption, statusSelect.firstChild);
|
||||
statusSelect.selectedIndex = 0;
|
||||
|
||||
console.log('Status updated successfully to:', newStatus);
|
||||
|
||||
// Reload page to refresh activity timeline
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 500);
|
||||
} else {
|
||||
console.error('Error updating status:', data.error || 'Unknown error');
|
||||
alert('Error updating status: ' + (data.error || 'Unknown error'));
|
||||
// Reset to current status
|
||||
statusSelect.selectedIndex = 0;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error updating status:', error);
|
||||
alert('Error updating status: ' + error.message);
|
||||
// Reset to current status
|
||||
statusSelect.selectedIndex = 0;
|
||||
});
|
||||
}
|
||||
|
||||
function showTab(tabName) {
|
||||
// Hide all tab contents
|
||||
const descriptionTab = document.getElementById('description-tab');
|
||||
|
||||
Reference in New Issue
Block a user