feat: implement member absence tracking and visual styling in calendar interventions
This commit is contained in:
parent
61a099eb4f
commit
9702a19db8
2 changed files with 22 additions and 4 deletions
|
|
@ -532,10 +532,17 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||||
const formattedMembers = dayData.members.map(m => {
|
const formattedMembers = dayData.members.map(m => {
|
||||||
const name = typeof m === 'object' && m ? m.name : m;
|
const name = typeof m === 'object' && m ? m.name : m;
|
||||||
const id = typeof m === 'object' && m ? m.id : null;
|
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 || '') : '';
|
||||||
|
|
||||||
|
let memberNameHtml = escapeHtml(name);
|
||||||
if (isMemberHistoryAllowed && id) {
|
if (isMemberHistoryAllowed && id) {
|
||||||
return `<a href="#" class="member-history-link text-decoration-none text-muted fw-semibold" data-member-id="${id}">${escapeHtml(name)}</a>`;
|
memberNameHtml = `<a href="#" class="member-history-link text-decoration-none ${isAbsent ? 'text-danger' : 'text-muted fw-semibold'}" data-member-id="${id}">${escapeHtml(name)}</a>`;
|
||||||
}
|
}
|
||||||
return escapeHtml(name);
|
if (isAbsent) {
|
||||||
|
return `<span class="text-decoration-line-through text-danger opacity-75" title="${escapeHtml(absenceReason || gettext('Absent'))}">${memberNameHtml}</span> <span class="badge text-bg-danger p-0 px-1 font-monospace" style="font-size:0.6rem;">${escapeHtml(absenceReason || gettext('Absent'))}</span>`;
|
||||||
|
}
|
||||||
|
return memberNameHtml;
|
||||||
}).join(', ');
|
}).join(', ');
|
||||||
membersHTML = `<div class="team-members-list text-muted small fst-italic mb-2" style="border-left: 3px solid ${teamColor};" title="${gettext('Composition de l’équipe')}">
|
membersHTML = `<div class="team-members-list text-muted small fst-italic mb-2" style="border-left: 3px solid ${teamColor};" title="${gettext('Composition de l’équipe')}">
|
||||||
<i class="bi bi-people-fill me-1" style="color: ${teamColor};"></i>${formattedMembers}
|
<i class="bi bi-people-fill me-1" style="color: ${teamColor};"></i>${formattedMembers}
|
||||||
|
|
|
||||||
|
|
@ -1506,6 +1506,11 @@ def member_history_ajax(request, member_id):
|
||||||
)
|
)
|
||||||
company_str = ", ".join(sorted(company_names)) if company_names else '—'
|
company_str = ", ".join(sorted(company_names)) if company_names else '—'
|
||||||
|
|
||||||
|
# 0. Récupérer les dates d'absences du membre
|
||||||
|
absent_dates = set(
|
||||||
|
CompanyMemberAbsence.objects.filter(member=member).values_list('date', flat=True)
|
||||||
|
)
|
||||||
|
|
||||||
# 1. Équipes et périodes valides du membre selon CompanyMemberHistory
|
# 1. Équipes et périodes valides du membre selon CompanyMemberHistory
|
||||||
team_periods_q = Q()
|
team_periods_q = Q()
|
||||||
for h in team_history_qs:
|
for h in team_history_qs:
|
||||||
|
|
@ -1527,7 +1532,7 @@ def member_history_ajax(request, member_id):
|
||||||
.order_by('-date', '-intervention__id')
|
.order_by('-date', '-intervention__id')
|
||||||
)
|
)
|
||||||
|
|
||||||
# 3. Dédoublonnage strict par ID d'intervention
|
# 3. Dédoublonnage strict par ID d'intervention et exclusion des jours d'absence
|
||||||
seen_itv_ids = set()
|
seen_itv_ids = set()
|
||||||
interventions_dict = {}
|
interventions_dict = {}
|
||||||
|
|
||||||
|
|
@ -1535,6 +1540,9 @@ def member_history_ajax(request, member_id):
|
||||||
itv = a.intervention
|
itv = a.intervention
|
||||||
if not itv or itv.id in seen_itv_ids:
|
if not itv or itv.id in seen_itv_ids:
|
||||||
continue
|
continue
|
||||||
|
# Si le membre était absent ce jour-là et n'était pas nommément assigné
|
||||||
|
if a.date and a.date in absent_dates and a.member_id != member.id:
|
||||||
|
continue
|
||||||
seen_itv_ids.add(itv.id)
|
seen_itv_ids.add(itv.id)
|
||||||
interventions_dict[itv.id] = {
|
interventions_dict[itv.id] = {
|
||||||
'id': itv.id,
|
'id': itv.id,
|
||||||
|
|
@ -1564,17 +1572,20 @@ def member_history_ajax(request, member_id):
|
||||||
for itv in extra_itvs:
|
for itv in extra_itvs:
|
||||||
if itv.id in seen_itv_ids:
|
if itv.id in seen_itv_ids:
|
||||||
continue
|
continue
|
||||||
seen_itv_ids.add(itv.id)
|
|
||||||
|
|
||||||
itv_date = (itv.end_time or itv.begin_time or itv.planned_begin_time or itv.creation_time)
|
itv_date = (itv.end_time or itv.begin_time or itv.planned_begin_time or itv.creation_time)
|
||||||
if itv_date:
|
if itv_date:
|
||||||
itv_date_local = timezone.localtime(itv_date) if timezone.is_aware(itv_date) else itv_date
|
itv_date_local = timezone.localtime(itv_date) if timezone.is_aware(itv_date) else itv_date
|
||||||
|
itv_date_only = itv_date_local.date()
|
||||||
|
if itv_date_only in absent_dates and itv.assigned_member_id != member.id and not itv.occupations.filter(member=member).exists():
|
||||||
|
continue
|
||||||
date_str = itv_date_local.strftime('%d/%m/%Y')
|
date_str = itv_date_local.strftime('%d/%m/%Y')
|
||||||
raw_date_str = itv_date_local.date().isoformat()
|
raw_date_str = itv_date_local.date().isoformat()
|
||||||
else:
|
else:
|
||||||
date_str = ''
|
date_str = ''
|
||||||
raw_date_str = ''
|
raw_date_str = ''
|
||||||
|
|
||||||
|
seen_itv_ids.add(itv.id)
|
||||||
interventions_dict[itv.id] = {
|
interventions_dict[itv.id] = {
|
||||||
'id': itv.id,
|
'id': itv.id,
|
||||||
'code': itv.code,
|
'code': itv.code,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue