Files
Lesstime/frontend/composables/useAbsenceHelpers.ts
Matthieu 2a0b202d32 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>
2026-05-22 11:31:31 +02:00

94 lines
2.8 KiB
TypeScript

import type { AbsenceRequest, AbsenceStatus, AbsenceType, HalfDay } from '~/services/dto/absence'
export type BadgeVariant = 'neutral' | 'info' | 'success' | 'warning' | 'danger'
const STATUS_VARIANTS: Record<AbsenceStatus, BadgeVariant> = {
pending: 'warning',
approved: 'success',
rejected: 'danger',
cancelled: 'neutral',
}
const STATUS_ICONS: Record<AbsenceStatus, string> = {
pending: 'mdi:clock-outline',
approved: 'mdi:check-circle-outline',
rejected: 'mdi:close-circle-outline',
cancelled: 'mdi:cancel',
}
// Colours used for the calendar bars, keyed by absence type.
const TYPE_COLORS: Record<AbsenceType, string> = {
cp: '#4A90D9',
mariage_pacs: '#E91E63',
conge_parental: '#9C27B0',
deces: '#607D8B',
maladie: '#C62828',
}
export function useAbsenceHelpers() {
const { t } = useI18n()
function statusLabel(status: AbsenceStatus): string {
return t(`absences.status.${status}`)
}
function statusVariant(status: AbsenceStatus): BadgeVariant {
return STATUS_VARIANTS[status] ?? 'neutral'
}
function statusIcon(status: AbsenceStatus): string {
return STATUS_ICONS[status] ?? 'mdi:help-circle-outline'
}
function typeLabel(type: AbsenceType): string {
return t(`absences.types.${type}`)
}
function typeColor(type: AbsenceType): string {
return TYPE_COLORS[type] ?? '#9CA3AF'
}
function halfDayLabel(half: HalfDay): string {
return t(`absences.halfDay.${half}`)
}
function formatDate(iso: string | null): string {
if (!iso) return ''
const d = new Date(iso)
if (isNaN(d.getTime())) return ''
const day = String(d.getDate()).padStart(2, '0')
const month = String(d.getMonth() + 1).padStart(2, '0')
return `${day}/${month}/${d.getFullYear()}`
}
/** Human-readable period with half-day annotations. */
function formatRange(req: Pick<AbsenceRequest, 'startDate' | 'endDate' | 'startHalfDay' | 'endHalfDay'>): string {
const start = formatDate(req.startDate)
const end = formatDate(req.endDate)
const startSuffix = req.startHalfDay ? ` (${halfDayLabel(req.startHalfDay)})` : ''
const endSuffix = req.endHalfDay ? ` (${halfDayLabel(req.endHalfDay)})` : ''
if (start === end) {
return `${start}${startSuffix}`
}
return `${start}${startSuffix}${end}${endSuffix}`
}
function formatDays(days: number): string {
const rounded = Math.round(days * 2) / 2
const unit = rounded > 1 ? t('absences.daysPlural') : t('absences.daySingular')
return `${rounded} ${unit}`
}
return {
statusLabel,
statusVariant,
statusIcon,
typeLabel,
typeColor,
halfDayLabel,
formatDate,
formatRange,
formatDays,
}
}