/* ════════════════════════════════════════════════════════════════════════ * FlowDeck — « Convertir en base de tâches » * * Modale de linkage : pour les trois rôles requis (Assigné à / Statut / * Échéance) on choisit soit une colonne existante compatible, soit on en * crée une nouvelle avec le bon type. Le serveur fait foi * (`POST /db/{id}/task-db/api`) : il valide les types, refuse le double * emploi d'une colonne et applique la limite de 10 bases connectées. * ══════════════════════════════════════════════════════════════════════ */ (function () { 'use strict'; if (window.__fdTaskDbLinkLoaded) return; window.__fdTaskDbLinkLoaded = true; var ROLE_LABELS = { assignee: 'Assigné à', status: 'Statut', due: "Date d'échéance" }; var ROLE_HINTS = { assignee: 'Colonne « personne » utilisée pour filtrer vos tâches.', status: 'Colonne statut ou sélection (À faire / En cours / Terminée).', due: 'Colonne de type date, utilisée par les vues et le calendrier.' }; var DEFAULT_NAMES = { assignee: 'Assigné à', status: 'Statut', due: "Échéance" }; var TYPE_LABELS = { person: 'personne', status: 'statut', select: 'sélection', date: 'date', text: 'texte', number: 'nombre', checkbox: 'case à cocher' }; function esc(s) { return String(s == null ? '' : s) .replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"').replace(/'/g, '''); } function csrf() { return (typeof window.getCsrf === 'function') ? window.getCsrf() : ''; } function api(path, opts) { opts = opts || {}; var headers = { 'X-CSRF-Token': csrf() }; if (opts.body) headers['Content-Type'] = 'application/json'; return fetch(path, { method: opts.method || 'GET', credentials: 'same-origin', headers: headers, body: opts.body ? JSON.stringify(opts.body) : undefined }).then(function (r) { return r.json().catch(function () { return {}; }).then(function (d) { if (!r.ok) throw new Error(d.detail || ('Erreur ' + r.status)); return d; }); }); } /* ── Modale ─────────────────────────────────────────────────────── */ function roleRow(state, role) { var selected = state.mapping[role]; var current = state.candidates[role] || []; // La colonne déjà mappée doit rester sélectionnable même si elle est // de type compatible mais exclue des candidats (déjà affectée). var all = state.all_properties || []; if (selected && !current.some(function (p) { return p.id === selected; })) { var kept = all.filter(function (p) { return p.id === selected; })[0]; if (kept) current = [kept].concat(current); } var h = '
'; h += '
'; h += '' + esc(ROLE_HINTS[role]) + '
'; h += '
'; h += ''; h += ''; h += '
'; return h; } function openModal(collectionId) { closeModal(); api('/db/' + collectionId + '/task-db/api').then(function (state) { if (!state.exists) return; var roles = ['assignee', 'status', 'due']; var h = '
'; h += '
' + (state.is_task ? 'Base de tâches' : 'Convertir en base de tâches') + '
'; h += '
Pour alimenter My Tasks, ' + 'reliez les trois colonnes ci-dessous. Une colonne absente sera créée ' + 'avec le bon type.
'; h += '
'; roles.forEach(function (r) { h += roleRow(state, r); }); h += '
'; if (state.sources_count >= state.max_sources) { h += '
⚠ Limite de ' + state.max_sources + ' bases connectées atteinte : déconnectez-en une avant.
'; } h += '
'; if (state.is_task) { h += ''; } h += ''; h += ''; h += ''; h += '
'; document.body.insertAdjacentHTML('beforeend', h); wire(state, collectionId); }).catch(function (e) { alert(e.message); }); } function closeModal() { var ov = document.getElementById('tdb-overlay'); if (ov) ov.remove(); } function wire(state, collectionId) { var overlay = document.getElementById('tdb-overlay'); overlay.addEventListener('click', function (e) { if (e.target === overlay) closeModal(); }); document.getElementById('tdb-close').onclick = closeModal; document.getElementById('tdb-cancel').onclick = closeModal; // Choisir une colonne masque le champ « nouvelle colonne ». overlay.addEventListener('change', function (e) { var role = e.target.dataset.tdbRole; if (!role) return; var input = overlay.querySelector('[data-tdb-new="' + role + '"]'); if (input) input.style.display = e.target.value ? 'none' : ''; }); var disable = document.getElementById('tdb-disable'); if (disable) { disable.onclick = function () { if (!confirm('Disconnecter cette base de My Tasks ?\n\n' + 'Les tâches restent dans la base.')) return; api('/db/' + collectionId + '/task-db/api', { method: 'DELETE' }) .then(function () { closeModal(); toast('Base déconnectée de My Tasks'); }) .catch(function (e) { alert(e.message); }); }; } document.getElementById('tdb-save').onclick = function () { var mapping = {}, create_missing = {}; ['assignee', 'status', 'due'].forEach(function (role) { var sel = overlay.querySelector('[data-tdb-role="' + role + '"]'); var val = sel ? sel.value : ''; if (val) { mapping[role] = Number(val); } else { var input = overlay.querySelector('[data-tdb-new="' + role + '"]'); create_missing[role] = { name: (input && input.value.trim()) || DEFAULT_NAMES[role] }; } }); api('/db/' + collectionId + '/task-db/api', { method: 'POST', body: { mapping: mapping, create_missing: create_missing } }) .then(function (d) { closeModal(); toast(d.configured ? '✅ Base connectée à My Tasks' : '⚠ Mapping incomplet — des colonnes manquent'); }) .catch(function (e) { alert(e.message); }); }; } function toast(msg) { var t = document.createElement('div'); t.textContent = msg; t.style.cssText = 'position:fixed;bottom:24px;left:50%;transform:translateX(-50%);' + 'z-index:1200;background:var(--bg-modal,#fff);color:var(--text-primary,#333);' + 'border:1px solid var(--border,#e3e3e0);border-radius:8px;padding:9px 16px;' + 'font-size:13px;box-shadow:0 8px 24px rgba(0,0,0,.16)'; document.body.appendChild(t); setTimeout(function () { t.remove(); }, 3200); } /* ── Point d'entrée depuis la barre de la base ──────────────────── */ window.FlowDeckTaskDb = { open: openModal, close: closeModal }; document.addEventListener('click', function (e) { var trigger = e.target.closest ? e.target.closest('[data-task-db]') : null; if (!trigger) return; e.preventDefault(); e.stopPropagation(); var id = trigger.dataset.taskDb; if (id) openModal(Number(id)); }); })();