Add issue type dropdown and Save to Dashboard from lookup
- Replace issue type text input with dropdown of STEAM project types (Story default) - Add Save to Dashboard button on lookup results to link existing Jira tickets locally - Make cve_id and vendor optional on local POST /api/jira-tickets endpoint - Fix: use normalized values in local ticket INSERT query
This commit is contained in:
@@ -182,6 +182,9 @@ export default function JiraPage() {
|
||||
const [lookupResult, setLookupResult] = useState(null);
|
||||
const [lookupLoading, setLookupLoading] = useState(false);
|
||||
const [lookupError, setLookupError] = useState(null);
|
||||
const [linkingSaving, setLinkingSaving] = useState(false);
|
||||
const [linkingError, setLinkingError] = useState(null);
|
||||
const [linkingSuccess, setLinkingSuccess] = useState(null);
|
||||
|
||||
// Add/Edit modal
|
||||
const [showForm, setShowForm] = useState(false);
|
||||
@@ -287,6 +290,37 @@ export default function JiraPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Link existing Jira ticket — save to local DB without recreating in Jira
|
||||
// ---------------------------------------------------------------------------
|
||||
const linkExistingTicket = async (issue) => {
|
||||
setLinkingError(null);
|
||||
setLinkingSuccess(null);
|
||||
setLinkingSaving(true);
|
||||
try {
|
||||
const jiraUrl = `https://jira.charter.com/browse/${issue.key}`;
|
||||
const res = await fetch(`${API_BASE}/jira-tickets`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({
|
||||
ticket_key: issue.key,
|
||||
url: jiraUrl,
|
||||
summary: issue.summary || '',
|
||||
status: issue.status === 'Open' || issue.status === 'In Progress' || issue.status === 'Closed' ? issue.status : 'Open',
|
||||
}),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
|
||||
setLinkingSuccess(`${issue.key} saved to dashboard.`);
|
||||
fetchTickets();
|
||||
} catch (err) {
|
||||
setLinkingError(err.message);
|
||||
} finally {
|
||||
setLinkingSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CRUD — save (create or update)
|
||||
@@ -715,6 +749,18 @@ export default function JiraPage() {
|
||||
<div><strong>Priority:</strong> {lookupResult.priority}</div>
|
||||
<div><strong>Assignee:</strong> {lookupResult.assignee || 'Unassigned'}</div>
|
||||
<div><strong>Updated:</strong> {lookupResult.updated ? new Date(lookupResult.updated).toLocaleString() : '-'}</div>
|
||||
{canWrite() && (
|
||||
<button
|
||||
style={{ ...STYLES.btn, ...STYLES.btnSuccess, marginTop: '0.75rem' }}
|
||||
onClick={() => linkExistingTicket(lookupResult)}
|
||||
disabled={linkingSaving}
|
||||
>
|
||||
{linkingSaving ? <Loader size={14} className="animate-spin" /> : <Plus size={14} />}
|
||||
Save to Dashboard
|
||||
</button>
|
||||
)}
|
||||
{linkingError && <div style={{ color: '#FCA5A5', fontSize: '0.75rem', marginTop: '0.5rem' }}>{linkingError}</div>}
|
||||
{linkingSuccess && <div style={{ color: '#6EE7B7', fontSize: '0.75rem', marginTop: '0.5rem' }}>{linkingSuccess}</div>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -828,8 +874,16 @@ export default function JiraPage() {
|
||||
<input style={STYLES.input} placeholder="Uses .env default" value={createJiraForm.project_key} onChange={e => setCreateJiraForm(f => ({ ...f, project_key: e.target.value.toUpperCase() }))} />
|
||||
</div>
|
||||
<div>
|
||||
<label style={{ fontSize: '0.75rem', color: '#94A3B8', display: 'block', marginBottom: '0.25rem' }}>Issue Type (optional)</label>
|
||||
<input style={STYLES.input} placeholder="Task" value={createJiraForm.issue_type} onChange={e => setCreateJiraForm(f => ({ ...f, issue_type: e.target.value }))} />
|
||||
<label style={{ fontSize: '0.75rem', color: '#94A3B8', display: 'block', marginBottom: '0.25rem' }}>Issue Type</label>
|
||||
<select style={{ ...STYLES.input, cursor: 'pointer' }} value={createJiraForm.issue_type} onChange={e => setCreateJiraForm(f => ({ ...f, issue_type: e.target.value }))}>
|
||||
<option value="">Story (default)</option>
|
||||
<option value="Story">Story</option>
|
||||
<option value="Epic">Epic</option>
|
||||
<option value="Program">Program</option>
|
||||
<option value="Project">Project</option>
|
||||
<option value="Reservation">Reservation</option>
|
||||
<option value="Automation Maintenance">Automation Maintenance</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<button style={{ ...STYLES.btn, ...STYLES.btnSuccess, justifyContent: 'center', marginTop: '0.5rem' }} onClick={createInJira} disabled={createJiraSaving}>
|
||||
|
||||
Reference in New Issue
Block a user