if (!window.__fdBoardScriptsLoaded) { window.__fdBoardScriptsLoaded = true; /* exported setActiveTab, kanbanBoard, filterSystem, sortSystem, newIssueForm, showNewIssue -- appeles depuis les attributs HTML des templates */ const BD=(()=>{try{const el=document.getElementById('bd-config');return el?JSON.parse(el.textContent):{}}catch{return {}}})(); var owner = BD.owner; var repo = BD.repo; var initialView = BD.initial_view; // Auto-switch to view from URL param document.addEventListener('DOMContentLoaded', function() { if (initialView && initialView !== 'kanban') { const tab = document.querySelector(`.view-tab[data-view="${initialView}"]`); if (tab) tab.click(); } }); function setActiveTab(el) { document.querySelectorAll('.view-tab').forEach(t => t.classList.remove('active')); el.classList.add('active'); } // A20 phase 3 : registre Alpine.data (le build CSP ne résout pas les // globales window — probe « Undefined variable: kanbanBoard »). if (window.Alpine) { Alpine.data('kanbanBoard', kanbanBoard); fdRefreshXData('kanbanBoard'); } else document.addEventListener('alpine:init', function () { Alpine.data('kanbanBoard', kanbanBoard); }); function kanbanBoard() { return { collapsedGroups: [], toggleGroup(id) { const idx = this.collapsedGroups.indexOf(id); idx >= 0 ? this.collapsedGroups.splice(idx, 1) : this.collapsedGroups.push(id); }, newCard(groupId, status) { document.getElementById('new-issue-form').style.display = 'block'; document.querySelector('#new-issue-form').__x.$data.status = status; }, newGroup() { console.log('New group'); } }; } // A20 phase 3 : registre Alpine.data (le build CSP ne résout pas les // globales window — probe « Undefined variable: filterSystem »). if (window.Alpine) { Alpine.data('filterSystem', filterSystem); fdRefreshXData('filterSystem'); } else document.addEventListener('alpine:init', function () { Alpine.data('filterSystem', filterSystem); }); function filterSystem() { return { activeFilters: [], statusFilters: [], showStatusMenu: false, statusOptions: [ { value: 'todo', label: 'To-do', color: 'var(--gray)' }, { value: 'progress', label: 'In progress', color: 'var(--blue)' }, { value: 'done', label: 'Complete', color: 'var(--green)' }, ], get statusFilterLabel() { return this.statusFilters.length ? this.statusFilters.join(', ') : 'All'; }, toggleStatus(val) { const idx = this.statusFilters.indexOf(val); idx >= 0 ? this.statusFilters.splice(idx, 1) : this.statusFilters.push(val); }, addFilter() { const prop = prompt('Filter by property (status, assignee, label):'); if (!prop) return; const val = prompt('Value:'); if (!val) return; this.activeFilters.push({ property: prop, value: val }); this.refreshView(); }, removeFilter(i) { this.activeFilters.splice(i, 1); }, resetFilters() { this.activeFilters = []; this.statusFilters = []; }, removeFilterAndRefresh(i) { this.removeFilter(i); this.refreshView(); }, resetFiltersAndRefresh() { this.resetFilters(); this.refreshView(); }, pickStatus(v) { this.toggleStatus(v); this.showStatusMenu = false; this.refreshView(); }, refreshView() { const activeTab = document.querySelector('.view-tab.active'); if (activeTab) activeTab.click(); } }; } // A20 phase 3 : registre Alpine.data (le build CSP ne résout pas les // globales window — probe « Undefined variable: sortSystem »). if (window.Alpine) { Alpine.data('sortSystem', sortSystem); fdRefreshXData('sortSystem'); } else document.addEventListener('alpine:init', function () { Alpine.data('sortSystem', sortSystem); }); function sortSystem() { return { sorts: [], showSortPanel: false, addSort() { const field = prompt('Sort by (name, status, assignee, deadline):'); if (!field) return; this.sorts.push({ field, dir: 'asc' }); this.applySorts(); }, removeSort(i) { this.sorts.splice(i, 1); this.applySorts(); }, toggleDir(i) { this.sorts[i].dir = this.sorts[i].dir === 'asc' ? 'desc' : 'asc'; this.applySorts(); }, clearSorts() { this.sorts = []; this.applySorts(); }, applySorts() { const activeTab = document.querySelector('.view-tab.active'); const url = new URL(activeTab.getAttribute('hx-get'), window.location.origin); this.sorts.forEach(s => url.searchParams.append('sort', s.field + ':' + s.dir)); htmx.ajax('GET', url.pathname + url.search, { target: '#view-content', swap: 'innerHTML' }); } }; } // A20 phase 3 : registre Alpine.data (le build CSP ne résout pas les // globales window — probe « Undefined variable: newIssueForm »). if (window.Alpine) { Alpine.data('newIssueForm', newIssueForm); fdRefreshXData('newIssueForm'); } else document.addEventListener('alpine:init', function () { Alpine.data('newIssueForm', newIssueForm); }); function newIssueForm() { return { title: '', status: 'todo', create() { if (!this.title.trim()) return; const csrf = getCsrf(); fetch(`/api/issues/${owner}/${repo}?title=${encodeURIComponent(this.title)}&labels=${this.status}`, { method: 'POST', headers: { 'X-CSRF-Token': csrf } }) .then(r => r.json()) .then(data => { this.title = ''; this.hide(); // Refresh current view const activeTab = document.querySelector('.view-tab.active'); if (activeTab) activeTab.click(); }); }, hide() { document.getElementById('new-issue-form').style.display = 'none'; }, show() { document.getElementById('new-issue-form').style.display = 'block'; } }; } function showNewIssue() { const form = document.getElementById('new-issue-form'); form.style.display = form.style.display === 'none' ? 'block' : 'none'; } // Init SortableJS after HTMX swaps document.addEventListener('htmx:afterSwap', function(evt) { if (evt.target.id === 'view-content') { document.querySelectorAll('.kanban-cards').forEach(el => { if (el._sortable) el._sortable.destroy(); el._sortable = new Sortable(el, { group: 'kanban', animation: 200, ghostClass: 'sortable-ghost', dragClass: 'sortable-drag', onEnd: function(evt) { const cardId = evt.item.dataset.cardId; const toStatus = evt.to.dataset.status; const csrf = getCsrf(); fetch(`/api/move?owner=${owner}&repo=${repo}&issue_id=${cardId}&column=${toStatus}`, { method: 'POST', headers: { 'X-CSRF-Token': csrf } }) .then(() => { // ponytail: refresh current view after move const activeTab = document.querySelector('.view-tab.active'); if (activeTab) activeTab.click(); }); } }); }); } }); }