diff --git a/loko/interventions/static/interventions/calendar/interventions_calendar.js b/loko/interventions/static/interventions/calendar/interventions_calendar.js index c398dba..a9c3315 100644 --- a/loko/interventions/static/interventions/calendar/interventions_calendar.js +++ b/loko/interventions/static/interventions/calendar/interventions_calendar.js @@ -294,10 +294,6 @@ document.addEventListener('DOMContentLoaded', function () { let currentModalMemberId = null; let draggedBacklogItem = null; let draggedBacklogGroup = null; - let _draggedTeamMember = null; - let _memberDragJustEnded = false; - let _pendingMemberDailyAssign = null; - let _lastTeamsDailyData = null; const csrfToken = getCookie('csrftoken'); let suppressDurationInputEvent = false; let suppressEndInputEvent = false; @@ -447,148 +443,7 @@ document.addEventListener('DOMContentLoaded', function () { } } - const TEAM_PALETTE = [ - '#0d6efd', '#6610f2', '#6f42c1', '#d63384', '#fd7e14', - '#198754', '#20c997', '#0dcaf0', '#495057', '#e83e8c' - ]; - function getTeamColor(teamId) { - return TEAM_PALETTE[Math.abs(teamId || 0) % TEAM_PALETTE.length]; - } - function buildTeamDailyMembersHtml(members, teamId, dateStr, teamName, canEditThisTeam, isPastDay) { - const teamColor = typeof getTeamColor === 'function' ? getTeamColor(teamId) : '#0d6efd'; - const isMemberHistoryAllowed = typeof canViewMemberHistory !== 'undefined' ? Boolean(canViewMemberHistory) : false; - const titleTooltip = canEditThisTeam && !isPastDay ? gettext('Modifier la composition de l’équipe pour ce jour') : gettext('Composition de l’équipe'); - const membersList = (members && Array.isArray(members)) ? members : []; - - if (membersList.length > 0) { - const chipsHtml = membersList.map(m => { - const name = typeof m === 'object' && m ? m.name : m; - const id = typeof m === 'object' && m ? m.id : null; - const isAbsent = typeof m === 'object' && m ? Boolean(m.is_absent) : false; - const absenceReason = typeof m === 'object' && m ? (m.absence_reason || '') : ''; - const isMultiTeam = typeof m === 'object' && m ? Boolean(m.is_multi_team) : false; - const otherTeams = typeof m === 'object' && m && m.other_teams ? m.other_teams : []; - - const canDrag = Boolean(canEditThisTeam && !isPastDay && id); - let chipTitle = escapeHtml(name); - if (canDrag) { - chipTitle += ` — ${gettext('Glisser pour réassigner. Cliquer pour gérer l’équipe.')}`; - } - - let multiTeamHtml = ''; - if (isMultiTeam) { - const otherNames = otherTeams.map(t => t.name).join(', '); - const tooltip = otherNames ? `${gettext('Planifié aussi dans')} : ${otherNames}` : gettext('Planifié dans plusieurs équipes ce jour'); - multiTeamHtml = ` `; - } - - let absentBadgeHtml = ''; - if (isAbsent) { - absentBadgeHtml = ` ${escapeHtml(absenceReason || gettext('Absent'))}`; - } - - let historyLinkHtml = ''; - if (isMemberHistoryAllowed && id) { - historyLinkHtml = ` `; - } - - return `
- ${escapeHtml(name)}${multiTeamHtml}${absentBadgeHtml}${historyLinkHtml} -
`; - }).join(''); - - return `
-
- - - ${gettext('Équipe')} (${membersList.length}) - - ${canEditThisTeam && !isPastDay ? ` - - ` : ''} -
-
- ${chipsHtml} -
-
`; - } else if (teamId !== null && teamId !== '' && typeof teamId !== 'undefined') { - return `
- (${gettext('Aucun membre')}) - ${canEditThisTeam && !isPastDay ? `` : ''} -
`; - } - return ''; - } - - function initTeamDailyMemberChips(container) { - const root = container || document.getElementById('teams-daily-table-body'); - if (!root) return; - - root.querySelectorAll('.team-daily-member-chip[draggable="true"]').forEach(chip => { - chip.addEventListener('dragstart', (e) => { - const memberId = parseInt(chip.getAttribute('data-member-id')); - const memberName = chip.getAttribute('data-member-name') || ''; - const sourceTeamId = chip.getAttribute('data-team-id') ? parseInt(chip.getAttribute('data-team-id')) : null; - const sourceDate = chip.getAttribute('data-date'); - - _draggedTeamMember = { - id: memberId, - name: memberName, - sourceTeamId: sourceTeamId, - sourceDate: sourceDate - }; - - chip.classList.add('is-dragging'); - e.dataTransfer.effectAllowed = 'move'; - e.dataTransfer.setData('text/plain', String(memberId)); - }); - - chip.addEventListener('dragend', () => { - chip.classList.remove('is-dragging'); - _draggedTeamMember = null; - _memberDragJustEnded = true; - setTimeout(() => { - _memberDragJustEnded = false; - }, 200); - }); - }); - } - - function showTeamDailyMultiTeamConfirmModal(member, targetTeamId, targetDate, otherAssignedTeams) { - const modalEl = document.getElementById('teamDailyMultiTeamConfirmModal'); - if (!modalEl) { - if (confirm(gettext("Ce membre est déjà affecté à une autre équipe ce jour-là. Voulez-vous le déplacer vers cette équipe ?"))) { - assignMemberToDailyTeam(member.id, targetTeamId, targetDate, 'move'); - } - return; - } - - const otherNames = otherAssignedTeams.map(t => t.name).join(', '); - const targetTeamObj = (_lastTeamsDailyData?.teams || []).find(t => t.id === targetTeamId); - const targetName = targetTeamObj ? targetTeamObj.name : `Équipe #${targetTeamId}`; - - const memberNameEl = document.getElementById('team-daily-multi-member-name'); - const dateEl = document.getElementById('team-daily-multi-date'); - const existingTeamsEl = document.getElementById('team-daily-multi-existing-teams'); - const targetTeamEl = document.getElementById('team-daily-multi-target-team'); - - if (memberNameEl) memberNameEl.textContent = member.name; - if (dateEl) dateEl.textContent = targetDate; - if (existingTeamsEl) existingTeamsEl.textContent = otherNames; - if (targetTeamEl) targetTeamEl.textContent = targetName; - - _pendingMemberDailyAssign = { - memberId: member.id, - targetTeamId: targetTeamId, - targetDate: targetDate - }; - - const modal = bootstrap.Modal.getOrCreateInstance(modalEl); - modal.show(); - } function renderTeamsDailyGrid(data) { const todayStr = formatDateISO(new Date()); @@ -5537,6 +5392,156 @@ document.addEventListener('click', function(e) { } }); +// State variables for teams daily view and member drag-and-drop +let _draggedTeamMember = null; +let _memberDragJustEnded = false; +let _pendingMemberDailyAssign = null; +let _lastTeamsDailyData = null; + +const TEAM_PALETTE = [ + '#0d6efd', '#6610f2', '#6f42c1', '#d63384', '#fd7e14', + '#198754', '#20c997', '#0dcaf0', '#495057', '#e83e8c' +]; + +function getTeamColor(teamId) { + return TEAM_PALETTE[Math.abs(teamId || 0) % TEAM_PALETTE.length]; +} + +function buildTeamDailyMembersHtml(members, teamId, dateStr, teamName, canEditThisTeam, isPastDay) { + const teamColor = typeof getTeamColor === 'function' ? getTeamColor(teamId) : '#0d6efd'; + const isMemberHistoryAllowed = typeof canViewMemberHistory !== 'undefined' ? Boolean(canViewMemberHistory) : false; + const titleTooltip = canEditThisTeam && !isPastDay ? gettext('Modifier la composition de l’équipe pour ce jour') : gettext('Composition de l’équipe'); + const membersList = (members && Array.isArray(members)) ? members : []; + + if (membersList.length > 0) { + const chipsHtml = membersList.map(m => { + const name = typeof m === 'object' && m ? m.name : m; + const id = typeof m === 'object' && m ? m.id : null; + const isAbsent = typeof m === 'object' && m ? Boolean(m.is_absent) : false; + const absenceReason = typeof m === 'object' && m ? (m.absence_reason || '') : ''; + const isMultiTeam = typeof m === 'object' && m ? Boolean(m.is_multi_team) : false; + const otherTeams = typeof m === 'object' && m && m.other_teams ? m.other_teams : []; + + const canDrag = Boolean(canEditThisTeam && !isPastDay && id); + let chipTitle = escapeHtml(name); + if (canDrag) { + chipTitle += ` — ${gettext('Glisser pour réassigner. Cliquer pour gérer l’équipe.')}`; + } + + let multiTeamHtml = ''; + if (isMultiTeam) { + const otherNames = otherTeams.map(t => t.name).join(', '); + const tooltip = otherNames ? `${gettext('Planifié aussi dans')} : ${otherNames}` : gettext('Planifié dans plusieurs équipes ce jour'); + multiTeamHtml = ` `; + } + + let absentBadgeHtml = ''; + if (isAbsent) { + absentBadgeHtml = ` ${escapeHtml(absenceReason || gettext('Absent'))}`; + } + + let historyLinkHtml = ''; + if (isMemberHistoryAllowed && id) { + historyLinkHtml = ` `; + } + + return `
+ ${escapeHtml(name)}${multiTeamHtml}${absentBadgeHtml}${historyLinkHtml} +
`; + }).join(''); + + return `
+
+ + + ${gettext('Équipe')} (${membersList.length}) + + ${canEditThisTeam && !isPastDay ? ` + + ` : ''} +
+
+ ${chipsHtml} +
+
`; + } else if (teamId !== null && teamId !== '' && typeof teamId !== 'undefined') { + return `
+ (${gettext('Aucun membre')}) + ${canEditThisTeam && !isPastDay ? `` : ''} +
`; + } + return ''; +} + +function initTeamDailyMemberChips(container) { + const root = container || document.getElementById('teams-daily-table-body'); + if (!root) return; + + root.querySelectorAll('.team-daily-member-chip[draggable="true"]').forEach(chip => { + chip.addEventListener('dragstart', (e) => { + const memberId = parseInt(chip.getAttribute('data-member-id')); + const memberName = chip.getAttribute('data-member-name') || ''; + const sourceTeamId = chip.getAttribute('data-team-id') ? parseInt(chip.getAttribute('data-team-id')) : null; + const sourceDate = chip.getAttribute('data-date'); + + _draggedTeamMember = { + id: memberId, + name: memberName, + sourceTeamId: sourceTeamId, + sourceDate: sourceDate + }; + + chip.classList.add('is-dragging'); + e.dataTransfer.effectAllowed = 'move'; + e.dataTransfer.setData('text/plain', String(memberId)); + }); + + chip.addEventListener('dragend', () => { + chip.classList.remove('is-dragging'); + _draggedTeamMember = null; + _memberDragJustEnded = true; + setTimeout(() => { + _memberDragJustEnded = false; + }, 200); + }); + }); +} + +function showTeamDailyMultiTeamConfirmModal(member, targetTeamId, targetDate, otherAssignedTeams) { + const modalEl = document.getElementById('teamDailyMultiTeamConfirmModal'); + if (!modalEl) { + if (confirm(gettext("Ce membre est déjà affecté à une autre équipe ce jour-là. Voulez-vous le déplacer vers cette équipe ?"))) { + assignMemberToDailyTeam(member.id, targetTeamId, targetDate, 'move'); + } + return; + } + + const otherNames = otherAssignedTeams.map(t => t.name).join(', '); + const targetTeamObj = (_lastTeamsDailyData?.teams || []).find(t => t.id === targetTeamId); + const targetName = targetTeamObj ? targetTeamObj.name : `Équipe #${targetTeamId}`; + + const memberNameEl = document.getElementById('team-daily-multi-member-name'); + const dateEl = document.getElementById('team-daily-multi-date'); + const existingTeamsEl = document.getElementById('team-daily-multi-existing-teams'); + const targetTeamEl = document.getElementById('team-daily-multi-target-team'); + + if (memberNameEl) memberNameEl.textContent = member.name; + if (dateEl) dateEl.textContent = targetDate; + if (existingTeamsEl) existingTeamsEl.textContent = otherNames; + if (targetTeamEl) targetTeamEl.textContent = targetName; + + _pendingMemberDailyAssign = { + memberId: member.id, + targetTeamId: targetTeamId, + targetDate: targetDate + }; + + const modal = bootstrap.Modal.getOrCreateInstance(modalEl); + modal.show(); +} + let _currentTeamDailyModalState = { teamId: null, dateStr: null,