refactor: move team daily view helper functions and state variables to file scope

This commit is contained in:
kdeterme 2026-10-05 09:56:25 +02:00
parent 63c4a8e539
commit 512f6786e3

View file

@ -294,10 +294,6 @@ document.addEventListener('DOMContentLoaded', function () {
let currentModalMemberId = null; let currentModalMemberId = null;
let draggedBacklogItem = null; let draggedBacklogItem = null;
let draggedBacklogGroup = null; let draggedBacklogGroup = null;
let _draggedTeamMember = null;
let _memberDragJustEnded = false;
let _pendingMemberDailyAssign = null;
let _lastTeamsDailyData = null;
const csrfToken = getCookie('csrftoken'); const csrfToken = getCookie('csrftoken');
let suppressDurationInputEvent = false; let suppressDurationInputEvent = false;
let suppressEndInputEvent = 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 = ` <i class="bi bi-diagram-3 text-warning ms-1" style="font-size:0.7rem;" title="${escapeHtml(tooltip)}"></i>`;
}
let absentBadgeHtml = '';
if (isAbsent) {
absentBadgeHtml = ` <span class="badge text-bg-danger p-0 px-1 ms-1 font-monospace" style="font-size:0.6rem;">${escapeHtml(absenceReason || gettext('Absent'))}</span>`;
}
let historyLinkHtml = '';
if (isMemberHistoryAllowed && id) {
historyLinkHtml = ` <i class="bi bi-clock-history text-muted member-history-icon ms-1" style="font-size:0.7rem; cursor:pointer;" data-member-id="${id}" title="${gettext('Historique du membre')}"></i>`;
}
return `<div class="team-daily-member-chip${canDrag ? ' draggable' : ''}${isAbsent ? ' is-absent' : ''}" ${canDrag ? 'draggable="true"' : ''} data-member-id="${id || ''}" data-member-name="${escapeHtml(name)}" data-team-id="${teamId || ''}" data-date="${dateStr}" title="${chipTitle}">
<span class="member-name-text">${escapeHtml(name)}</span>${multiTeamHtml}${absentBadgeHtml}${historyLinkHtml}
</div>`;
}).join('');
return `<div class="team-members-list small mb-2" style="border-left: 3px solid ${teamColor};" data-team-id="${teamId || ''}" data-date="${dateStr}" data-team-name="${escapeHtml(teamName || '')}">
<div class="d-flex align-items-center justify-content-between">
<span class="clickable-team-members d-inline-flex align-items-center" style="cursor: pointer;" title="${escapeHtml(titleTooltip)}" data-team-id="${teamId || ''}" data-date="${dateStr}" data-team-name="${escapeHtml(teamName || '')}">
<i class="bi bi-people-fill me-1" style="color: ${teamColor};"></i>
<span class="fw-semibold text-secondary" style="font-size: 0.72rem;">${gettext('Équipe')} (${membersList.length})</span>
</span>
${canEditThisTeam && !isPastDay ? `
<button type="button" class="btn btn-link btn-sm p-0 text-muted clickable-team-members" style="font-size: 0.72rem; text-decoration: none;" title="${escapeHtml(titleTooltip)}" data-team-id="${teamId || ''}" data-date="${dateStr}" data-team-name="${escapeHtml(teamName || '')}">
<i class="bi bi-pencil"></i>
</button>
` : ''}
</div>
<div class="team-daily-members-chips">
${chipsHtml}
</div>
</div>`;
} else if (teamId !== null && teamId !== '' && typeof teamId !== 'undefined') {
return `<div class="team-members-list clickable-team-members text-muted small fst-italic mb-2 text-opacity-50" style="border-left: 3px solid #cbd5e1; cursor: pointer;" title="${escapeHtml(titleTooltip)}" data-team-id="${teamId || ''}" data-date="${dateStr}" data-team-name="${escapeHtml(teamName || '')}">
<i class="bi bi-people me-1"></i>(${gettext('Aucun membre')})
${canEditThisTeam && !isPastDay ? `<i class="bi bi-plus-circle ms-1 text-primary"></i>` : ''}
</div>`;
}
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) { function renderTeamsDailyGrid(data) {
const todayStr = formatDateISO(new Date()); 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 = ` <i class="bi bi-diagram-3 text-warning ms-1" style="font-size:0.7rem;" title="${escapeHtml(tooltip)}"></i>`;
}
let absentBadgeHtml = '';
if (isAbsent) {
absentBadgeHtml = ` <span class="badge text-bg-danger p-0 px-1 ms-1 font-monospace" style="font-size:0.6rem;">${escapeHtml(absenceReason || gettext('Absent'))}</span>`;
}
let historyLinkHtml = '';
if (isMemberHistoryAllowed && id) {
historyLinkHtml = ` <i class="bi bi-clock-history text-muted member-history-icon ms-1" style="font-size:0.7rem; cursor:pointer;" data-member-id="${id}" title="${gettext('Historique du membre')}"></i>`;
}
return `<div class="team-daily-member-chip${canDrag ? ' draggable' : ''}${isAbsent ? ' is-absent' : ''}" ${canDrag ? 'draggable="true"' : ''} data-member-id="${id || ''}" data-member-name="${escapeHtml(name)}" data-team-id="${teamId || ''}" data-date="${dateStr}" title="${chipTitle}">
<span class="member-name-text">${escapeHtml(name)}</span>${multiTeamHtml}${absentBadgeHtml}${historyLinkHtml}
</div>`;
}).join('');
return `<div class="team-members-list small mb-2" style="border-left: 3px solid ${teamColor};" data-team-id="${teamId || ''}" data-date="${dateStr}" data-team-name="${escapeHtml(teamName || '')}">
<div class="d-flex align-items-center justify-content-between">
<span class="clickable-team-members d-inline-flex align-items-center" style="cursor: pointer;" title="${escapeHtml(titleTooltip)}" data-team-id="${teamId || ''}" data-date="${dateStr}" data-team-name="${escapeHtml(teamName || '')}">
<i class="bi bi-people-fill me-1" style="color: ${teamColor};"></i>
<span class="fw-semibold text-secondary" style="font-size: 0.72rem;">${gettext('Équipe')} (${membersList.length})</span>
</span>
${canEditThisTeam && !isPastDay ? `
<button type="button" class="btn btn-link btn-sm p-0 text-muted clickable-team-members" style="font-size: 0.72rem; text-decoration: none;" title="${escapeHtml(titleTooltip)}" data-team-id="${teamId || ''}" data-date="${dateStr}" data-team-name="${escapeHtml(teamName || '')}">
<i class="bi bi-pencil"></i>
</button>
` : ''}
</div>
<div class="team-daily-members-chips">
${chipsHtml}
</div>
</div>`;
} else if (teamId !== null && teamId !== '' && typeof teamId !== 'undefined') {
return `<div class="team-members-list clickable-team-members text-muted small fst-italic mb-2 text-opacity-50" style="border-left: 3px solid #cbd5e1; cursor: pointer;" title="${escapeHtml(titleTooltip)}" data-team-id="${teamId || ''}" data-date="${dateStr}" data-team-name="${escapeHtml(teamName || '')}">
<i class="bi bi-people me-1"></i>(${gettext('Aucun membre')})
${canEditThisTeam && !isPastDay ? `<i class="bi bi-plus-circle ms-1 text-primary"></i>` : ''}
</div>`;
}
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 = { let _currentTeamDailyModalState = {
teamId: null, teamId: null,
dateStr: null, dateStr: null,