Bug commentaires + statuts + ajout personnage depuis l'app

- Fix parseHTML commentaires (attributs perdus au rechargement)
- Statut: Posée → Validée, Germe supprimé (17 fiches migrées)
- Bouton "+" pour ajouter un personnage directement depuis le formulaire
- Zone "Après l'histoire" sur la frise (couleur prune)
- Icône frise harmonisée (symbole plat)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Etienne Delvarre
2026-05-29 17:32:00 +02:00
parent e5706aa50a
commit 526b598769
2 changed files with 67 additions and 9 deletions
+40 -9
View File
@@ -14,9 +14,9 @@ const Comment = Mark.create({
name: 'comment', name: 'comment',
addAttributes() { addAttributes() {
return { return {
text: { default: '' }, text: { default: '', parseHTML: el => el.getAttribute('data-comment') || '' },
author: { default: 'E' }, author: { default: 'E', parseHTML: el => el.getAttribute('data-author') || 'E' },
date: { default: '' }, date: { default: '', parseHTML: el => el.getAttribute('data-date') || '' },
}; };
}, },
parseHTML() { parseHTML() {
@@ -177,13 +177,12 @@ window.dispatchEvent(new Event('tiptap-loaded'));
.card[data-acte="En attente"] { border-top-color: var(--border); } .card[data-acte="En attente"] { border-top-color: var(--border); }
/* Status badge colors */ /* Status badge colors */
.badge-germe { background: var(--border); color: var(--text-muted); } .badge-validee { background: #eef3e8; color: #5a7a3a; }
.badge-etudier { background: #f5f0e4; color: #a07850; } .badge-etudier { background: #f5f0e4; color: #a07850; }
.badge-posee { background: #eef3e8; color: #5a7a3a; }
.badge-abandonnee { background: var(--border); color: var(--text-hint); text-decoration: line-through; } .badge-abandonnee { background: var(--border); color: var(--text-hint); text-decoration: line-through; }
.badge-conflit { background: #f5e4e4; color: #8b3030; } .badge-conflit { background: #f5e4e4; color: #8b3030; }
.dark .badge-etudier { background: #3a3020; color: #c49860; } .dark .badge-etudier { background: #3a3020; color: #c49860; }
.dark .badge-posee { background: #2a3520; color: #8aaa6a; } .dark .badge-validee { background: #2a3520; color: #8aaa6a; }
.dark .badge-conflit { background: #3a2020; color: #c06060; } .dark .badge-conflit { background: #3a2020; color: #c06060; }
.badge-perso { background: var(--bg); color: var(--text-muted); border: 1px solid var(--border); } .badge-perso { background: var(--bg); color: var(--text-muted); border: 1px solid var(--border); }
@@ -217,6 +216,8 @@ window.dispatchEvent(new Event('tiptap-loaded'));
.multi-select-container { display: flex; flex-wrap: wrap; gap: 5px; padding: 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-input); min-height: 38px; } .multi-select-container { display: flex; flex-wrap: wrap; gap: 5px; padding: 8px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-input); min-height: 38px; }
.multi-select-container .ms-tag { font-size: 12px; padding: 3px 9px; border-radius: 6px; background: var(--border); color: var(--text-muted); cursor: pointer; } .multi-select-container .ms-tag { font-size: 12px; padding: 3px 9px; border-radius: 6px; background: var(--border); color: var(--text-muted); cursor: pointer; }
.multi-select-container .ms-tag.selected { background: var(--accent); color: var(--bg); } .multi-select-container .ms-tag.selected { background: var(--accent); color: var(--bg); }
.multi-select-container .ms-add { font-size: 12px; padding: 3px 9px; border-radius: 6px; background: transparent; border: 1px dashed var(--border); color: var(--text-hint); cursor: pointer; }
.multi-select-container .ms-add:hover { border-color: var(--accent); color: var(--accent); }
.modal-actions { display: flex; gap: 10px; justify-content: space-between; margin-top: 8px; } .modal-actions { display: flex; gap: 10px; justify-content: space-between; margin-top: 8px; }
.btn-save { padding: 9px 22px; background: var(--accent); color: var(--bg); border: none; border-radius: 8px; font-size: 14px; font-weight: 600; font-family: 'Lora', serif; cursor: pointer; } .btn-save { padding: 9px 22px; background: var(--accent); color: var(--bg); border: none; border-radius: 8px; font-size: 14px; font-weight: 600; font-family: 'Lora', serif; cursor: pointer; }
.btn-save:hover { background: var(--accent-hover); } .btn-save:hover { background: var(--accent-hover); }
@@ -720,8 +721,8 @@ function clearFilters() {
const statusClass = s => { const statusClass = s => {
if (!s) return ''; if (!s) return '';
const map = { 'Germe': 'badge-germe', 'À étudier': 'badge-etudier', 'Posée': 'badge-posee', 'Abandonnée': 'badge-abandonnee', 'En conflit': 'badge-conflit' }; const map = { 'À étudier': 'badge-etudier', 'Validée': 'badge-validee', 'Abandonnée': 'badge-abandonnee', 'En conflit': 'badge-conflit' };
return map[s] || 'badge-germe'; return map[s] || 'badge-etudier';
}; };
function makeCard(r) { function makeCard(r) {
@@ -870,6 +871,36 @@ function populateSelects() {
container.appendChild(tag); container.appendChild(tag);
}); });
} }
const addBtn = document.createElement('span');
addBtn.className = 'ms-add';
addBtn.textContent = '+';
addBtn.title = 'Ajouter un personnage';
addBtn.onclick = async () => {
const name = prompt('Nom du personnage :');
if (!name || !name.trim()) return;
const trimmed = name.trim();
if (columns.Personnage && columns.Personnage.options.includes(trimmed)) {
alert('Ce personnage existe déjà.');
return;
}
const res = await fetch('/api/columns/personnage', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: trimmed }),
});
if (res.ok) {
if (!columns.Personnage) columns.Personnage = { type: 'MultiSelect', options: [] };
columns.Personnage.options.push(trimmed);
const tag = document.createElement('span');
tag.className = 'ms-tag selected';
tag.textContent = trimmed;
tag.onclick = () => tag.classList.toggle('selected');
container.insertBefore(tag, addBtn);
} else {
alert('Erreur lors de l\'ajout.');
}
};
container.appendChild(addBtn);
} }
function openNew() { function openNew() {
@@ -877,7 +908,7 @@ function openNew() {
document.getElementById('modal-label').textContent = 'Nouvelle idee'; document.getElementById('modal-label').textContent = 'Nouvelle idee';
document.getElementById('f-title').value = ''; document.getElementById('f-title').value = '';
document.getElementById('f-type').value = ''; document.getElementById('f-type').value = '';
document.getElementById('f-statut').value = 'Germe'; document.getElementById('f-statut').value = 'À étudier';
document.getElementById('f-acte').value = 'En attente'; document.getElementById('f-acte').value = 'En attente';
document.getElementById('f-resume').value = ''; document.getElementById('f-resume').value = '';
document.getElementById('f-interet').value = ''; document.getElementById('f-interet').value = '';
+27
View File
@@ -126,6 +126,33 @@ app.get('/api/columns', requireAuth, async (req, res) => {
} }
}); });
// Add a new Personnage option
app.post('/api/columns/personnage', requireAuth, async (req, res) => {
try {
const { name } = req.body;
if (!name) return res.status(400).json({ error: 'Nom requis' });
// Get current column meta
const metaUrl = `${NOCODB_URL}/api/v2/meta/tables/${NOCODB_TABLE_ID}`;
const metaRes = await fetch(metaUrl, { headers: { 'xc-token': NOCODB_TOKEN } });
const table = await metaRes.json();
const col = table.columns.find(c => c.title === 'Personnage');
if (!col) return res.status(404).json({ error: 'Colonne Personnage introuvable' });
const options = col.colOptions?.options || [];
if (options.some(o => o.title === name)) return res.status(409).json({ error: 'Existe déjà' });
options.push({ title: name });
const patchUrl = `${NOCODB_URL}/api/v2/meta/columns/${col.id}`;
const patchRes = await fetch(patchUrl, {
method: 'PATCH',
headers: { 'xc-token': NOCODB_TOKEN, 'Content-Type': 'application/json' },
body: JSON.stringify({ colOptions: { options } }),
});
if (!patchRes.ok) return res.status(500).json({ error: 'Erreur NocoDB' });
res.json({ ok: true });
} catch (e) {
res.status(500).json({ error: e.message });
}
});
// Bulk update order (for drag-and-drop) // Bulk update order (for drag-and-drop)
app.post('/api/reorder', requireAuth, async (req, res) => { app.post('/api/reorder', requireAuth, async (req, res) => {
try { try {