feat: full inline issue editing (title + body + assignee)
Replaced bare prompt() with proper edit form in card modal: - showEditForm: renders title input + body textarea + assignee field - saveIssueEdit: PATCH /api/issues/... with title, body, assignee - 'Annuler' button returns to card detail view - Old editIssueInline (prompt only) removed - Added .form-label CSS style
This commit is contained in:
@@ -211,6 +211,10 @@
|
||||
function renderCardDetail(owner, repo, data) {
|
||||
const issue = data.issue || {}, comments = data.comments || [], checklists = data.checklists || [], card = data.card || {};
|
||||
let h = '';
|
||||
|
||||
// Edit mode toggle: store data for later
|
||||
window._issueData = {owner, repo, issue, comments, checklists, card};
|
||||
|
||||
h += `<div class="detail-section"><span class="detail-state badge ${issue.state}">${issue.state}</span> <strong>${escHtml(issue.title||'')}</strong></div>`;
|
||||
h += `<div class="detail-meta">`;
|
||||
(issue.labels||[]).forEach(l => h += `<span class="card-label" style="background:#${l.color||'666'}33;color:#${l.color||'ccc'}">${escHtml(l.name)}</span>`);
|
||||
@@ -238,11 +242,45 @@
|
||||
comments.forEach(c => h += `<div class="comment"><img src="${c.user?.avatar_url||''}" width="24" height="24" class="avatar"><strong>${escHtml(c.user?.login||'')}</strong><span class="comment-date">${(c.created_at||'').substr(0,10)}</span><div class="comment-body">${escHtml(c.body||'')}</div></div>`);
|
||||
h += `</div>`;
|
||||
h += `<div class="detail-actions"><a href="${issue.html_url||''}" target="_blank" class="btn btn-sm">🔗 Ouvrir dans Gitea</a>`;
|
||||
h += `<button onclick="editIssueInline('${owner}','${repo}',${issue.number})" class="btn btn-sm">✏️ Éditer</button>`;
|
||||
h += `<button onclick="showEditForm('${owner}','${repo}',${issue.number})" class="btn btn-sm">✏️ Éditer</button>`;
|
||||
h += `<button onclick="closeModal()" class="btn btn-sm">Fermer</button></div>`;
|
||||
return h;
|
||||
}
|
||||
|
||||
function showEditForm(owner, repo, issueId) {
|
||||
const content = document.getElementById('card-modal-content');
|
||||
const data = window._issueData || {};
|
||||
const issue = data.issue || {};
|
||||
fetch(`/api/collaborators/${owner}/${repo}`).then(r=>r.json()).then(cdata => {
|
||||
let h = '<form onsubmit="saveIssueEdit(event,\''+owner+'\',\''+repo+'\','+issueId+')">';
|
||||
h += '<label class="form-label">Titre</label>';
|
||||
h += '<input type="text" id="edit-title" value="'+escHtml(issue.title||'')+'" class="form-input" required>';
|
||||
h += '<label class="form-label">Description</label>';
|
||||
h += '<textarea id="edit-body" rows="5" class="form-input">'+escHtml(issue.body||'')+'</textarea>';
|
||||
h += '<div class="form-row">';
|
||||
h += '<input type="text" id="edit-assignee" value="'+(issue.assignee?issue.assignee.login:'')+'" placeholder="Assigné à (login)" class="form-input" style="flex:1">';
|
||||
h += '</div>';
|
||||
h += '<div class="detail-actions" style="margin-top:8px">';
|
||||
h += '<button type="submit" class="btn btn-primary">💾 Sauvegarder</button>';
|
||||
h += '<button type="button" onclick="openCardDetail(\''+owner+'\',\''+repo+'\','+issueId+')" class="btn btn-sm">Annuler</button>';
|
||||
h += '</div></form>';
|
||||
content.innerHTML = h;
|
||||
});
|
||||
}
|
||||
|
||||
async function saveIssueEdit(evt, owner, repo, issueId) {
|
||||
evt.preventDefault();
|
||||
const title = document.getElementById('edit-title').value.trim();
|
||||
const body = document.getElementById('edit-body').value.trim();
|
||||
const assignee = document.getElementById('edit-assignee').value.trim();
|
||||
if (!title) return;
|
||||
const params = new URLSearchParams({title, body, assignee});
|
||||
await fetch(`/api/issues/${owner}/${repo}/${issueId}?${params}`, {
|
||||
method: 'PATCH', headers: {'X-CSRF-Token': getCSRFToken()}
|
||||
});
|
||||
openCardDetail(owner, repo, issueId);
|
||||
}
|
||||
|
||||
function escHtml(s) { return (s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
||||
async function updateCardMeta(o,r,i,priority,dueDate) { await fetch(`/api/card/${o}/${r}/${i}?priority=${priority}&due_date=${dueDate}`,{method:'POST',headers:{'X-CSRF-Token':getCSRFToken()}}); }
|
||||
async function toggleCheckItem(id,checked) { await fetch(`/api/checklist-items/${id}?checked=${checked}`,{method:'PATCH',headers:{'X-CSRF-Token':getCSRFToken()}}); }
|
||||
@@ -259,11 +297,6 @@
|
||||
openCardDetail(o,r,issueId);
|
||||
}
|
||||
async function createChecklist(o,r,issueId) { await fetch(`/api/checklists/${o}/${r}/${issueId}`,{method:'POST',headers:{'X-CSRF-Token':getCSRFToken()}}); openCardDetail(o,r,issueId); }
|
||||
async function editIssueInline(o,r,issueId) {
|
||||
const t=prompt('Nouveau titre:',''); if(!t)return;
|
||||
await fetch(`/api/issues/${o}/${r}/${issueId}?title=${encodeURIComponent(t)}`,{method:'PATCH',headers:{'X-CSRF-Token':getCSRFToken()}});
|
||||
closeModal(); window.location.reload();
|
||||
}
|
||||
async function registerWebhook(o,r) {
|
||||
try {
|
||||
const resp=await fetch(`/api/webhook/register/${o}/${r}`,{method:'POST',headers:{'X-CSRF-Token':getCSRFToken()}});
|
||||
|
||||
@@ -199,6 +199,7 @@ body { min-height: 100vh; }
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.form-input:focus, .form-select:focus { outline: none; border-color: var(--accent); }
|
||||
.form-label { font-size: 12px; color: var(--text-dim); display: block; margin: 8px 0 4px 0; }
|
||||
.form-input-sm { background: var(--bg); border: 1px solid var(--border); border-radius: 4px; padding: 4px 6px; color: var(--text); font-size: 12px; }
|
||||
.form-select-sm { background: var(--bg); border: 1px solid var(--border); border-radius: 4px; padding: 2px 4px; color: var(--text); font-size: 12px; }
|
||||
.form-row { display: flex; gap: 8px; }
|
||||
|
||||
Reference in New Issue
Block a user