add calendar icon next to the tasks to edit dates within the gant

This commit is contained in:
2026-09-25 08:59:13 +02:00
parent dece7b5ab8
commit d0d45de518
3 changed files with 234 additions and 13 deletions
+128
View File
@@ -1249,6 +1249,39 @@ GANTT
font-size: 11px;
color: var(--fg-muted);
padding-left: 20px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
}
.gantt-ticket-id-label {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.gantt-date-edit-btn {
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
width: 22px;
height: 22px;
border-radius: 6px;
border: 1px solid var(--border);
background: var(--bg-input);
color: var(--accent);
font-size: 11px;
cursor: pointer;
margin-right: 8px;
transition: all 0.2s ease;
}
.gantt-date-edit-btn:hover {
background: var(--accent-glow);
border-color: var(--accent);
transform: scale(1.08);
}
/* Grille de fond (colonnes verticales sous les barres) */
@@ -1353,4 +1386,99 @@ GANTT
.mr-4 { margin-right: 4px; }
/* --- Date Edit Modal --- */
.modal-overlay {
display: none;
position: fixed;
inset: 0;
z-index: 9998;
background: rgba(0, 0, 0, 0.55);
align-items: center;
justify-content: center;
animation: fadeIn 0.2s ease;
}
.modal-overlay.visible {
display: flex;
}
.modal-box {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow);
width: 100%;
max-width: 420px;
margin: 20px;
overflow: hidden;
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 18px 20px;
border-bottom: 1px solid var(--border);
}
.modal-title {
display: flex;
align-items: center;
gap: 10px;
font-size: 15px;
font-weight: 700;
color: var(--fg);
}
.modal-title i { color: var(--accent); }
.modal-close {
background: transparent;
border: none;
color: var(--fg-muted);
font-size: 16px;
cursor: pointer;
padding: 4px;
line-height: 1;
border-radius: 6px;
transition: all 0.2s ease;
}
.modal-close:hover {
color: var(--danger);
background: var(--accent-glow);
}
.modal-body {
padding: 20px;
display: flex;
flex-direction: column;
gap: 16px;
}
.modal-ticket-key {
font-family: 'JetBrains Mono', monospace;
font-weight: 700;
color: var(--accent);
font-size: 13px;
}
.modal-ticket-summary {
font-size: 12px;
color: var(--fg-muted);
margin-top: -10px;
}
.modal-date-input {
width: 100%;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 16px 20px;
border-top: 1px solid var(--border);
}
+78 -13
View File
@@ -368,9 +368,8 @@ async function updateTicket(ticketKey, dueDate, goLiveDate) {
}
const btn = document.querySelector(`[data-ticket="${ticketKey}"]`);
const orig = btn.innerHTML;
btn.innerHTML = '<span class="spinner"></span>';
btn.disabled = true;
const orig = btn ? btn.innerHTML : null;
if (btn) { btn.innerHTML = '<span class="spinner"></span>'; btn.disabled = true; }
const goLiveField = customFieldsMap.find(cf => cf.label.toLowerCase().includes('go live'));
const updates = {};
@@ -400,8 +399,7 @@ async function updateTicket(ticketKey, dueDate, goLiveDate) {
} catch (err) {
showToast(err.message, 'error');
} finally {
btn.innerHTML = orig;
btn.disabled = false;
if (btn) { btn.innerHTML = orig; btn.disabled = false; }
}
}
@@ -422,6 +420,66 @@ function handleUpdateClick(ticketKey, goLiveColName) {
updateTicket(ticketKey, dueDate, goLiveDate);
}
// --- GANTT DATE EDIT MODAL ---
let dateEditContext = { ticketKey: null, goLiveColName: null };
// Detects the real go-live custom field name, ignoring known built-in ticket properties
function getGoLiveColumnName() {
if (!allTickets.length) return null;
return Object.keys(allTickets[0]).find(key =>
!['key','url','summary','description','status','statusCategory','assignee','reporter','priority','issueType','created','updated','dueDate','resolution','resolutionDate','labels','components','fixVersions','projectName'].includes(key)
) || null;
}
function openDateEditModal(ticketKey) {
const ticket = allTickets.find(t => t.key === ticketKey);
if (!ticket) return;
const goLiveColName = getGoLiveColumnName();
dateEditContext = { ticketKey, goLiveColName };
document.getElementById('dateEditTicketKey').textContent = ticket.key;
document.getElementById('dateEditTicketSummary').textContent = ticket.summary || '';
document.getElementById('modalDueDate').value = ticket.dueDate ? ticket.dueDate.substring(0, 10) : '';
const goLiveGroup = document.getElementById('modalGoLiveGroup');
if (goLiveColName) {
goLiveGroup.style.display = '';
document.getElementById('modalGoLiveLabel').textContent = goLiveColName;
document.getElementById('modalGoLiveDate').value = ticket[goLiveColName] ? ticket[goLiveColName].substring(0, 10) : '';
} else {
goLiveGroup.style.display = 'none';
}
document.getElementById('dateEditModal').classList.add('visible');
}
function closeDateEditModal() {
document.getElementById('dateEditModal').classList.remove('visible');
dateEditContext = { ticketKey: null, goLiveColName: null };
}
async function saveDateEditModal() {
const { ticketKey, goLiveColName } = dateEditContext;
if (!ticketKey) return;
const dueDate = document.getElementById('modalDueDate').value || null;
const goLiveDate = goLiveColName ? (document.getElementById('modalGoLiveDate').value || null) : undefined;
const btn = document.getElementById('modalSaveBtn');
const orig = btn.innerHTML;
btn.innerHTML = '<span class="spinner"></span>';
btn.disabled = true;
try {
await updateTicket(ticketKey, dueDate, goLiveDate);
closeDateEditModal();
} finally {
btn.innerHTML = orig;
btn.disabled = false;
}
}
// --- RENDERING ---
function renderResults() {
document.getElementById('results-section').classList.add('visible');
@@ -444,12 +502,7 @@ function escHtml(s) { if(!s) return ''; const d=document.createElement('div'); d
function fmtDate(d) { if(!d) return '<span class="empty-cell">-</span>'; try { return new Date(d).toLocaleDateString('en-US',{day:'2-digit',month:'short',year:'numeric'}); } catch { return escHtml(d); } }
function renderTable() {
let goLiveColName = null;
if (allTickets.length > 0) {
goLiveColName = Object.keys(allTickets[0]).find(key =>
!['key','url','summary','description','status','statusCategory','assignee','reporter','priority','issueType','created','updated','dueDate','resolution','resolutionDate','labels','components','fixVersions','projectName'].includes(key)
);
}
const goLiveColName = getGoLiveColumnName();
const colMapping = {
'Ticket': 'key',
@@ -704,7 +757,10 @@ function renderRoadmap() {
html += `<div class="gantt-row">
<div class="gantt-sticky-col gantt-ticket-id" style="width:${leftWidth}px;" title="${escHtml(t.key)}: ${escHtml(t.summary)}">
${escHtml(shortTitle)}
<button class="gantt-date-edit-btn" onclick="event.stopPropagation(); openDateEditModal('${t.key}')" title="Edit dates">
<i class="fa-solid fa-calendar-days"></i>
</button>
<span class="gantt-ticket-id-label">${escHtml(shortTitle)}</span>
</div>
<div class="gantt-bar"
@@ -906,9 +962,18 @@ function copyJSON() { navigator.clipboard.writeText(JSON.stringify(allTickets, n
function clearResults() { allTickets=[]; filteredTickets=[]; document.getElementById('results-section').classList.remove('visible'); }
document.addEventListener('keydown', e => { if((e.ctrlKey||e.metaKey)&&e.key==='Enter'){e.preventDefault();startExtraction()} });
document.addEventListener('keydown', e => {
if((e.ctrlKey||e.metaKey)&&e.key==='Enter'){e.preventDefault();startExtraction()}
if(e.key === 'Escape') closeDateEditModal();
});
document.addEventListener('DOMContentLoaded', () => {
initTheme();
loadSettings();
const modalOverlay = document.getElementById('dateEditModal');
if (modalOverlay) {
modalOverlay.addEventListener('click', e => { if (e.target === modalOverlay) closeDateEditModal(); });
}
console.log("Settings loaded from local storage.");
});