Show raw Jira status everywhere instead of mapping to Open/In Progress/Closed
- Drop CHECK constraint on jira_tickets.status to allow any status string - Store raw Jira status directly in status column during sync (remove mapJiraStatusToLocal) - Remove VALID_TICKET_STATUSES validation on create/update endpoints - Remove separate Jira Status column from table (status IS the Jira status now) - Update frontend status badges to color-code dynamically based on status category - Update Open Tickets widget and CVE detail view to use isClosedStatus() helper - Make filter dropdown dynamic based on actual ticket statuses - Add migration script for dropping the constraint on other deployments
This commit is contained in:
@@ -25,6 +25,22 @@ import './App.css';
|
||||
|
||||
const API_BASE = process.env.REACT_APP_API_BASE || 'http://localhost:3001/api';
|
||||
|
||||
// Determine if a Jira status represents a "closed/done" state
|
||||
function isClosedStatus(status) {
|
||||
if (!status) return false;
|
||||
const lower = status.toLowerCase();
|
||||
return ['closed', 'done', 'resolved', 'complete', 'completed', 'cancelled', 'canceled', "won't do", 'declined'].some(s => lower.includes(s));
|
||||
}
|
||||
|
||||
function getTicketStatusColor(status) {
|
||||
if (!status) return '#F59E0B';
|
||||
if (isClosedStatus(status)) return '#10B981';
|
||||
const lower = status.toLowerCase();
|
||||
if (['open', 'to do', 'backlog', 'new'].some(s => lower === s)) return '#F59E0B';
|
||||
// Everything else (in progress, approval, prioritizing, etc.) gets blue/purple
|
||||
return '#0EA5E9';
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// INLINE STYLES - NUCLEAR OPTION FOR VISIBILITY
|
||||
// ============================================
|
||||
@@ -1072,7 +1088,7 @@ export default function App() {
|
||||
<div style={{...STYLES.statCard, border: '2px solid #F59E0B', boxShadow: '0 4px 16px rgba(0, 0, 0, 0.5), 0 0 20px rgba(245, 158, 11, 0.15), inset 0 1px 0 rgba(245, 158, 11, 0.15)'}}>
|
||||
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: '2px', background: 'linear-gradient(90deg, transparent, #F59E0B, transparent)', boxShadow: '0 0 8px rgba(245, 158, 11, 0.5)' }}></div>
|
||||
<div style={{ fontSize: '0.75rem', textTransform: 'uppercase', letterSpacing: '0.1em', color: '#CBD5E1', marginBottom: '0.25rem' }}>Open Tickets</div>
|
||||
<div style={{ fontSize: '1.5rem', fontWeight: '700', fontFamily: 'monospace', color: '#F59E0B', textShadow: '0 0 16px rgba(245, 158, 11, 0.4)' }}>{jiraTickets.filter(t => t.status !== 'Closed').length}</div>
|
||||
<div style={{ fontSize: '1.5rem', fontWeight: '700', fontFamily: 'monospace', color: '#F59E0B', textShadow: '0 0 16px rgba(245, 158, 11, 0.4)' }}>{jiraTickets.filter(t => !isClosedStatus(t.status)).length}</div>
|
||||
</div>
|
||||
<div style={{...STYLES.statCard, border: '2px solid #EF4444', boxShadow: '0 4px 16px rgba(0, 0, 0, 0.5), 0 0 20px rgba(239, 68, 68, 0.15), inset 0 1px 0 rgba(239, 68, 68, 0.15)'}}>
|
||||
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: '2px', background: 'linear-gradient(90deg, transparent, #EF4444, transparent)', boxShadow: '0 0 8px rgba(239, 68, 68, 0.5)' }}></div>
|
||||
@@ -1475,6 +1491,9 @@ export default function App() {
|
||||
<option value="Open">Open</option>
|
||||
<option value="In Progress">In Progress</option>
|
||||
<option value="Closed">Closed</option>
|
||||
{ticketForm.status && !['Open', 'In Progress', 'Closed'].includes(ticketForm.status) && (
|
||||
<option value={ticketForm.status}>{ticketForm.status}</option>
|
||||
)}
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex gap-3 pt-4">
|
||||
@@ -1544,6 +1563,9 @@ export default function App() {
|
||||
<option value="Open">Open</option>
|
||||
<option value="In Progress">In Progress</option>
|
||||
<option value="Closed">Closed</option>
|
||||
{ticketForm.status && !['Open', 'In Progress', 'Closed'].includes(ticketForm.status) && (
|
||||
<option value={ticketForm.status}>{ticketForm.status}</option>
|
||||
)}
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex gap-3 pt-4">
|
||||
@@ -2096,15 +2118,11 @@ export default function App() {
|
||||
</a>
|
||||
{ticket.summary && <span style={{ fontSize: '0.875rem', color: '#E4E8F1', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: '20rem' }}>{ticket.summary}</span>}
|
||||
<span style={
|
||||
ticket.status === 'Open' ? STYLES.badgeCritical :
|
||||
ticket.status === 'In Progress' ? STYLES.badgeHigh :
|
||||
STYLES.badgeLow
|
||||
isClosedStatus(ticket.status) ? STYLES.badgeLow :
|
||||
getTicketStatusColor(ticket.status) === '#0EA5E9' ? STYLES.badgeHigh :
|
||||
STYLES.badgeCritical
|
||||
}>
|
||||
<span style={STYLES.glowDot(
|
||||
ticket.status === 'Open' ? '#FF3366' :
|
||||
ticket.status === 'In Progress' ? '#FFB800' :
|
||||
'#00FF88'
|
||||
)}></span>
|
||||
<span style={STYLES.glowDot(getTicketStatusColor(ticket.status))}></span>
|
||||
{ticket.status}
|
||||
</span>
|
||||
</div>
|
||||
@@ -2220,12 +2238,12 @@ export default function App() {
|
||||
</div>
|
||||
<div className="text-center mb-3">
|
||||
<div style={{ fontSize: '2rem', fontWeight: '700', fontFamily: 'monospace', color: '#F59E0B', textShadow: '0 0 16px rgba(245, 158, 11, 0.4)' }}>
|
||||
{jiraTickets.filter(t => t.status !== 'Closed').length}
|
||||
{jiraTickets.filter(t => !isClosedStatus(t.status)).length}
|
||||
</div>
|
||||
<div className="text-xs text-gray-400 uppercase tracking-wider">Active</div>
|
||||
</div>
|
||||
<div className="space-y-2 max-h-96 overflow-y-auto">
|
||||
{jiraTickets.filter(t => t.status !== 'Closed').slice(0, 10).map(ticket => (
|
||||
{jiraTickets.filter(t => !isClosedStatus(t.status)).slice(0, 10).map(ticket => (
|
||||
<div key={ticket.id} style={{ background: 'linear-gradient(135deg, rgba(30, 41, 59, 0.85) 0%, rgba(51, 65, 85, 0.75) 100%)', border: '1px solid rgba(245, 158, 11, 0.25)', borderRadius: '0.375rem', padding: '0.5rem', boxShadow: '0 2px 6px rgba(0, 0, 0, 0.25), inset 0 1px 0 rgba(255, 255, 255, 0.03)' }}>
|
||||
<div className="flex items-start justify-between gap-2 mb-1">
|
||||
<a
|
||||
@@ -2254,13 +2272,13 @@ export default function App() {
|
||||
{ticket.summary && <div className="text-xs text-gray-300 mt-1 truncate">{ticket.summary}</div>}
|
||||
<div className="mt-2">
|
||||
<span style={{ ...STYLES.badgeHigh, fontSize: '0.65rem', padding: '0.25rem 0.5rem' }}>
|
||||
<span style={{...STYLES.glowDot('#F59E0B'), width: '6px', height: '6px'}}></span>
|
||||
<span style={{...STYLES.glowDot(getTicketStatusColor(ticket.status)), width: '6px', height: '6px'}}></span>
|
||||
{ticket.status}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{jiraTickets.filter(t => t.status !== 'Closed').length === 0 && (
|
||||
{jiraTickets.filter(t => !isClosedStatus(t.status)).length === 0 && (
|
||||
<div className="text-center py-8">
|
||||
<CheckCircle className="w-8 h-8 text-intel-success mx-auto mb-2" />
|
||||
<p className="text-sm text-gray-400 italic font-mono">No open tickets</p>
|
||||
|
||||
Reference in New Issue
Block a user