fix : correction des Heures et ajout d'une validation pour les chefs de site
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled

This commit is contained in:
2026-02-26 14:49:28 +01:00
parent 5cced46254
commit b68fef61c4
20 changed files with 547 additions and 106 deletions

View File

@@ -6,21 +6,19 @@
:style="{ gridTemplateColumns: dayGridCols }"
>
<span>Nom</span>
<span class="pl-4">Début matin</span>
<span class="pr-2">Fin matin</span>
<span class="pl-2">Début après-midi</span>
<span class="pr-2">Fin après-midi</span>
<span class="pl-2">Début soir</span>
<span class="pr-2">Fin soir</span>
<span class="pl-2">Absence</span>
<span class="pl-2">Absence</span>
<span class="pl-4">Début matin</span>
<span class="pr-2">Fin matin</span>
<span class="pl-2">Début après-midi</span>
<span class="pr-2">Fin après-midi</span>
<span class="pl-2">Début soir</span>
<span class="pr-2">Fin soir</span>
<span class="pl-2">Jour</span>
<span>Nuit</span>
<span>Total</span>
<span class="inline-flex items-center gap-2">
<span v-if="isAdmin">Valider</span>
<span v-else>Validation RH</span>
<span v-if="isAdmin" class="inline-flex items-center gap-2">
<span>Valider</span>
<input
v-if="isAdmin"
ref="bulkValidationInput"
:checked="isBulkValidationChecked"
type="checkbox"
@@ -28,6 +26,8 @@
@change="onBulkValidationChange"
/>
</span>
<span v-else>Site <Icon name="mdi:check-bold" class="ml-1"/></span>
<span v-if="!isAdmin">RH <Icon name="mdi:check-bold" class="ml-1"/></span>
</div>
<div
@@ -41,82 +41,91 @@
{{ employee.firstName }} {{ employee.lastName }}
<span class="font-normal text-neutral-600">({{ contractLabel(employee) }})</span>
</p>
<p class="text-neutral-500 truncate">{{ employee.site?.name ?? 'Sans site' }}</p>
<p class="text-neutral-500 truncate inline-flex items-center gap-2">
<span>{{ employee.site?.name ?? 'Sans site' }}</span>
<span
v-if="isAdmin && (rows[employee.id]?.isSiteValid ?? false)"
class="rounded-full bg-green-500 flex justify-center item-center text-white p-0.5"
title="Validation site"
>
<Icon name="mdi:check"/>
</span>
</p>
</div>
<div class="pl-4">
<div class="pl-2 min-w-0 self-stretch flex flex-col justify-between py-0.5">
<p
class="w-full min-w-0 text-sm text-neutral-700 truncate"
:class="getRowAbsenceLabel(employee.id) ? '' : 'invisible'"
:title="getRowAbsenceLabel(employee.id) || ''"
>
{{ getRowAbsenceLabel(employee.id) || '—' }}
</p>
<button
type="button"
class="self-start text-left text-xs font-semibold underline"
:class="isHoliday || isRowLocked(employee.id) || !hasContractAtSelectedDate(employee.id) ? 'text-neutral-400 cursor-not-allowed' : 'text-primary-500 cursor-pointer'"
:disabled="isHoliday || isRowLocked(employee.id) || !hasContractAtSelectedDate(employee.id)"
@click="onAbsenceClick(employee.id)"
>
Modifier
</button>
</div>
<div class="pl-4">
<TimeSelect
v-if="isTimeTracking(employee)"
v-model="rows[employee.id].morningFrom"
:disabled="isRowLocked(employee.id) || (!isAdmin && isHalfLockedByAbsence(employee.id, 'AM'))"
:disabled="!hasContractAtSelectedDate(employee.id) || isRowLocked(employee.id) || (!isAdmin && isHalfLockedByAbsence(employee.id, 'AM'))"
/>
<input
v-else-if="isPresenceTracking(employee)"
v-model="rows[employee.id].isPresentMorning"
type="checkbox"
class="cursor-pointer h-4 w-4"
:disabled="isRowLocked(employee.id) || (!isAdmin && isHalfLockedByAbsence(employee.id, 'AM'))"
:disabled="!hasContractAtSelectedDate(employee.id) || isRowLocked(employee.id) || (!isAdmin && isHalfLockedByAbsence(employee.id, 'AM'))"
/>
</div>
<div class="pr-2">
<TimeSelect
v-if="isTimeTracking(employee)"
v-model="rows[employee.id].morningTo"
:disabled="isRowLocked(employee.id) || (!isAdmin && isHalfLockedByAbsence(employee.id, 'AM'))"
:disabled="!hasContractAtSelectedDate(employee.id) || isRowLocked(employee.id) || (!isAdmin && isHalfLockedByAbsence(employee.id, 'AM'))"
/>
</div>
<div class="pl-2">
<TimeSelect
v-if="isTimeTracking(employee)"
v-model="rows[employee.id].afternoonFrom"
:disabled="isRowLocked(employee.id) || (!isAdmin && isHalfLockedByAbsence(employee.id, 'PM'))"
:disabled="!hasContractAtSelectedDate(employee.id) || isRowLocked(employee.id) || (!isAdmin && isHalfLockedByAbsence(employee.id, 'PM'))"
/>
<input
v-else-if="isPresenceTracking(employee)"
v-model="rows[employee.id].isPresentAfternoon"
type="checkbox"
class="cursor-pointer h-4 w-4"
:disabled="isRowLocked(employee.id) || (!isAdmin && isHalfLockedByAbsence(employee.id, 'PM'))"
:disabled="!hasContractAtSelectedDate(employee.id) || isRowLocked(employee.id) || (!isAdmin && isHalfLockedByAbsence(employee.id, 'PM'))"
/>
</div>
<div class="pr-2">
<TimeSelect
v-if="isTimeTracking(employee)"
v-model="rows[employee.id].afternoonTo"
:disabled="isRowLocked(employee.id) || (!isAdmin && isHalfLockedByAbsence(employee.id, 'PM'))"
:disabled="!hasContractAtSelectedDate(employee.id) || isRowLocked(employee.id) || (!isAdmin && isHalfLockedByAbsence(employee.id, 'PM'))"
/>
</div>
<div class="pl-2">
<TimeSelect
v-if="isTimeTracking(employee)"
v-model="rows[employee.id].eveningFrom"
:disabled="isRowLocked(employee.id) || (!isAdmin && isEveningLockedByAbsence(employee.id))"
:disabled="!hasContractAtSelectedDate(employee.id) || isRowLocked(employee.id) || (!isAdmin && isEveningLockedByAbsence(employee.id))"
/>
</div>
<div class="pr-2">
<TimeSelect
v-if="isTimeTracking(employee)"
v-model="rows[employee.id].eveningTo"
:disabled="isRowLocked(employee.id) || (!isAdmin && isEveningLockedByAbsence(employee.id))"
:disabled="!hasContractAtSelectedDate(employee.id) || isRowLocked(employee.id) || (!isAdmin && isEveningLockedByAbsence(employee.id))"
/>
</div>
<div class="pl-2 min-w-0 self-stretch flex flex-col justify-between py-0.5">
<p
class="w-full min-w-0 text-sm text-neutral-700 truncate"
:class="getRowAbsenceLabel(employee.id) ? '' : 'invisible'"
:title="getRowAbsenceLabel(employee.id) || ''"
>
{{ getRowAbsenceLabel(employee.id) || '—' }}
</p>
<button
type="button"
class="self-start text-left text-xs font-semibold underline"
:class="isRowLocked(employee.id) ? 'text-neutral-400 cursor-not-allowed' : 'text-primary-500 cursor-pointer'"
:disabled="isRowLocked(employee.id)"
@click="onAbsenceClick(employee.id)"
>
Modifier
</button>
</div>
<div class="pl-2 text-sm font-semibold text-neutral-700">
<div v-if="isTimeTracking(employee)">{{ formatMinutes(getRowMetrics(employee.id).dayMinutes) }}</div>
</div>
@@ -127,15 +136,29 @@
<div v-if="isTimeTracking(employee)">{{ formatMinutes(getRowMetrics(employee.id).totalMinutes) }}</div>
<div v-else>{{ getPresenceDayValue(employee.id) }}</div>
</div>
<div>
<div v-if="isAdmin">
<input
v-if="isAdmin"
:checked="rows[employee.id]?.isValid ?? false"
type="checkbox"
class="h-4 w-4 cursor-pointer"
@change="onToggleValidation(employee.id, ($event.target as HTMLInputElement).checked)"
/>
<span v-else-if="rows[employee.id]?.isValid" class="text-xs font-semibold text-neutral-700">Validé</span>
</div>
<div v-else>
<input
v-if="isSiteManager"
:checked="rows[employee.id]?.isSiteValid ?? false"
type="checkbox"
class="h-4 w-4 cursor-pointer"
:disabled="!canToggleSiteValidation(employee.id) || isSiteValidationPending(employee.id)"
@change="onToggleSiteValidation(employee.id, ($event.target as HTMLInputElement).checked)"
/>
<span v-else-if="rows[employee.id]?.isSiteValid" class="text-xs font-semibold text-neutral-700">Validé</span>
<span v-else class="text-xs text-neutral-500">-</span>
</div>
<div v-if="!isAdmin">
<span v-if="rows[employee.id]?.isValid" class="text-xs font-semibold text-neutral-700">Validé</span>
<span v-else class="text-xs text-neutral-500">-</span>
</div>
</div>
</div>
@@ -153,18 +176,24 @@ const bulkValidationInput = ref<HTMLInputElement | null>(null)
const props = defineProps<{
employees: Employee[]
isAdmin: boolean
isSiteManager: boolean
dayGridCols: string
isHoliday: boolean
contractLabel: (employee: Employee) => string
isTimeTracking: (employee: Employee) => boolean
isPresenceTracking: (employee: Employee) => boolean
isRowLocked: (employeeId: number) => boolean
isHalfLockedByAbsence: (employeeId: number, half: 'AM' | 'PM') => boolean
isEveningLockedByAbsence: (employeeId: number) => boolean
hasContractAtSelectedDate: (employeeId: number) => boolean
isValidationPending: (employeeId: number) => boolean
isSiteValidationPending: (employeeId: number) => boolean
canToggleValidation: (employeeId: number) => boolean
canToggleSiteValidation: (employeeId: number) => boolean
isBulkValidationChecked: boolean
isBulkValidationIndeterminate: boolean
onToggleValidation: (employeeId: number, checked: boolean) => void
onToggleSiteValidation: (employeeId: number, checked: boolean) => void
onToggleValidationBulk: (checked: boolean) => void
getRowMetrics: (employeeId: number) => { dayMinutes: number; nightMinutes: number; totalMinutes: number }
getRowAbsenceLabel: (employeeId: number) => string
@@ -177,6 +206,10 @@ const onBulkValidationChange = (event: Event) => {
props.onToggleValidationBulk((event.target as HTMLInputElement).checked)
}
const onToggleSiteValidation = (employeeId: number, checked: boolean) => {
props.onToggleSiteValidation(employeeId, checked)
}
watch(
() => props.isBulkValidationIndeterminate,
(isIndeterminate) => {

View File

@@ -8,5 +8,6 @@ export type HourRow = {
eveningTo: string
isPresentMorning: boolean
isPresentAfternoon: boolean
isSiteValid: boolean
isValid: boolean
}