feat(absences) : avancement module absences + suppression du portail client

Deux lots regroupés sur la branche feat/absence-management.

Suppression complète du portail client :
- retire ROLE_CLIENT (security.yaml) ; User::getRoles() ajoute toujours ROLE_USER
- supprime l'entité ClientTicket (+ repo, states, relations), User.client et
  User.allowedProjects, NotificationService, ProjectAllowedExtension, le bloc
  ROLE_CLIENT de MailAccessChecker
- front : pages /portal, layout portal, composants client-ticket/,
  AdminClientTicketTab, services/dto/i18n/docs associés
- fixtures : retire les users client-liot / client-acme
- migration Version20260522110000 (drop client_ticket, user_allowed_projects,
  colonnes liées ; task_document.task_id -> NOT NULL)
- tests : retire les cas obsolètes testant le blocage des clients sur le mail

Module gestion des absences (WIP) :
- entités / migrations (Version20260521160000, Version20260522090000)
- pages absences.vue / team-absences.vue, composants frontend/components/absence/
- services front, AccrueLeaveCommand, PublicHolidayController

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matthieu
2026-05-22 11:31:31 +02:00
parent de98924fd3
commit 2a0b202d32
109 changed files with 3918 additions and 3656 deletions

View File

@@ -9,6 +9,7 @@
v-for="day in days"
:key="'header-' + day.dateStr"
class="flex-1 border-r border-neutral-100 py-2 text-center"
:class="{ 'bg-orange-50': day.holiday }"
>
<div class="text-lg font-bold" :class="isToday(day.date) ? 'text-orange-500' : 'text-neutral-900'">
{{ day.dayNum }}
@@ -16,6 +17,14 @@
<div class="text-xs" :class="isToday(day.date) ? 'text-orange-500' : 'text-neutral-500'">
{{ day.label }}
</div>
<div
v-if="day.holiday"
class="flex items-center justify-center gap-0.5 truncate px-1 text-[10px] font-medium text-amber-600"
:title="day.holiday"
>
<Icon name="mdi:star-four-points-outline" size="10" class="flex-shrink-0" />
<span class="truncate">{{ day.holiday }}</span>
</div>
<div class="text-[10px] text-neutral-400">{{ day.totalFormatted }}</div>
</div>
</div>
@@ -40,6 +49,7 @@
:key="day.dateStr"
:ref="(el) => { dayColumnEls[dayIndex] = el as HTMLElement }"
class="relative flex-1 border-r border-neutral-100"
:class="{ 'bg-orange-50': day.holiday }"
@click="onClickGrid($event, day)"
@contextmenu.prevent="onContextMenuGrid($event, day)"
>
@@ -141,8 +151,10 @@
<script setup lang="ts">
import type { TimeEntry } from '~/services/dto/time-entry'
import { useAbsenceService } from '~/services/absences'
const { t } = useI18n()
const absenceService = useAbsenceService()
const props = defineProps<{
entries: TimeEntry[]
@@ -209,6 +221,23 @@ onMounted(() => {
})
})
// --- Public holidays (computed server-side, shared with the absence calendar) ---
const holidays = ref<Record<string, string>>({})
async function loadHolidays() {
const count = props.viewMode === 'week' ? 7 : 1
const start = new Date(props.startDate)
const end = new Date(start)
end.setDate(end.getDate() + count - 1)
try {
holidays.value = await absenceService.getPublicHolidays(toDateStr(start), toDateStr(end))
} catch {
holidays.value = {}
}
}
watch(() => [props.startDate, props.viewMode], loadHolidays, { immediate: true })
// --- Days computation ---
const days = computed(() => {
const count = props.viewMode === 'week' ? 7 : 1
@@ -231,6 +260,7 @@ const days = computed(() => {
dateStr,
dayNum: d.getDate(),
label: dayLabels[d.getDay()],
holiday: holidays.value[dateStr] ?? null,
totalFormatted: `${String(totalH).padStart(2, '0')}:${String(totalM).padStart(2, '0')}:${String(totalS).padStart(2, '0')}`,
})
}