diff --git a/public/index.html b/public/index.html index 59c5af6..7f18121 100644 --- a/public/index.html +++ b/public/index.html @@ -14,9 +14,9 @@ const Comment = Mark.create({ name: 'comment', addAttributes() { return { - text: { default: '' }, - author: { default: 'E' }, - date: { default: '' }, + text: { default: '', parseHTML: el => el.getAttribute('data-comment') || '' }, + author: { default: 'E', parseHTML: el => el.getAttribute('data-author') || 'E' }, + date: { default: '', parseHTML: el => el.getAttribute('data-date') || '' }, }; }, parseHTML() { @@ -177,13 +177,12 @@ window.dispatchEvent(new Event('tiptap-loaded')); .card[data-acte="En attente"] { border-top-color: var(--border); } /* 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-posee { background: #eef3e8; color: #5a7a3a; } .badge-abandonnee { background: var(--border); color: var(--text-hint); text-decoration: line-through; } .badge-conflit { background: #f5e4e4; color: #8b3030; } .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; } .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 .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-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; } .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); } @@ -720,8 +721,8 @@ function clearFilters() { const statusClass = s => { if (!s) return ''; - const map = { 'Germe': 'badge-germe', 'À étudier': 'badge-etudier', 'Posée': 'badge-posee', 'Abandonnée': 'badge-abandonnee', 'En conflit': 'badge-conflit' }; - return map[s] || 'badge-germe'; + const map = { 'À étudier': 'badge-etudier', 'Validée': 'badge-validee', 'Abandonnée': 'badge-abandonnee', 'En conflit': 'badge-conflit' }; + return map[s] || 'badge-etudier'; }; function makeCard(r) { @@ -870,6 +871,36 @@ function populateSelects() { 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() { @@ -877,7 +908,7 @@ function openNew() { document.getElementById('modal-label').textContent = 'Nouvelle idee'; document.getElementById('f-title').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-resume').value = ''; document.getElementById('f-interet').value = ''; diff --git a/server.js b/server.js index 8245e5f..fa94ed8 100644 --- a/server.js +++ b/server.js @@ -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) app.post('/api/reorder', requireAuth, async (req, res) => { try {