Files
SIRH/frontend/components/employees/RttTab.vue
tristan f493ea237b
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled
Ajout des notification + page employé (#6)
| Numéro du ticket | Titre du ticket |
|------------------|-----------------|
|                  |                 |

## Description de la PR

## Modification du .env

## Check list

- [ ] Pas de régression
- [ ] TU/TI/TF rédigée
- [ ] TU/TI/TF OK
- [ ] CHANGELOG modifié

Reviewed-on: #6
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-03-10 12:35:17 +00:00

221 lines
8.6 KiB
Vue

<template>
<section class="flex h-full min-h-0 flex-col overflow-hidden pt-8">
<div class="flex gap-10 justify-center items-center bg-primary-500 rounded-md text-white py-5">
<p class="text-[20px]"><span class="font-semibold">RTT à la date du jour :</span> {{ formatMinutes(summary?.availableMinutes ?? 0) }}</p>
<button class="flex justify-center items-center gap-2 bg-white text-primary-500 font-bold w-[150px] rounded-md py-[1px] text-md" @click="openNewPayment">
+ Payer les RTT
</button>
</div>
<div class="mt-8 min-h-0 flex-1 overflow-y-auto pr-2">
<div class="grid grid-cols-4 gap-10 pb-4">
<div
v-for="month in months"
:key="month.month"
class="rounded-md bg-tertiary-500 text-primary-500"
>
<div class="flex justify-center rounded-t-md bg-primary-500 py-3 font-bold text-white text-[18px]">
{{ month.label }}
</div>
<div class="grid grid-cols-[70%_30%] text-[18px] border border-primary-500">
<template v-for="week in month.weeks" :key="week.key">
<div class="py-[6px] pl-3 border-r border-b border-primary-500">
<span v-if="week.isEmpty">&nbsp;</span>
<span v-else>Semaine {{ week.weekNumber }}</span>
</div>
<div class="py-[6px] pl-3 border-b border-primary-500">
<span v-if="week.isEmpty">&nbsp;</span>
<span v-else>{{ formatMinutes(week.recoveryMinutes) }}</span>
</div>
</template>
<div class="py-[6px] pl-3 border-r border-b border-primary-500 font-semibold">Total</div>
<div class="py-[6px] pl-3 border-b border-primary-500 font-semibold">{{ formatMinutes(month.totalMinutes) }}</div>
<div class="py-[6px] pl-3 border-r border-b border-primary-500">Heure payée 25%</div>
<div class="py-[6px] pl-3 border-b border-primary-500 flex gap-3 items-center cursor-pointer hover:bg-primary-500/10"
@click="openEditPayment(month.month, '25')"
title="Modifier les heures payées"
>
<p>{{ formatMinutes(getMonthPaid25(month.month)) }}</p>
<div class="flex justify-center items-center bg-primary-500 rounded-md p-1">
<Icon name="mdi:pencil" size="16" class="self-center text-white" />
</div>
</div>
<div class="py-[6px] pl-3 border-r border-primary-500">Heure payée 50%</div>
<div class="py-[6px] px-3 flex gap-3 items-center cursor-pointer hover:bg-primary-500/10"
@click="openEditPayment(month.month, '50')"
title="Modifier les heures payées"
>
<p>{{ formatMinutes(getMonthPaid50(month.month)) }}</p>
<div class="flex justify-center items-center bg-primary-500 rounded-md p-1">
<Icon name="mdi:pencil" size="16" class="self-center text-white" />
</div>
</div>
</div>
</div>
</div>
</div>
<AppDrawer v-model="isPaymentDrawerOpen" :title="isEditMode ? 'Modifier le paiement RTT' : 'Payer des RTT'">
<form @submit.prevent="onSubmitPayment">
<div class="mb-4">
<label class="block text-sm font-medium text-neutral-700">Mois</label>
<select v-model.number="paymentForm.month" :disabled="isEditMode" class="mt-2 w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-md text-neutral-900 disabled:opacity-50 disabled:cursor-not-allowed">
<option v-for="m in orderedMonthOptions" :key="m.value" :value="m.value">{{ m.label }}</option>
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-neutral-700">Nombre d'heures</label>
<input v-model.number="paymentForm.hours" type="number" step="0.5" min="0" class="mt-2 w-full rounded-md border border-neutral-300 px-3 py-2 text-base text-neutral-900 focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-secondary-500/20" />
</div>
<div class="mb-6">
<label class="block text-sm font-medium text-neutral-700">Taux</label>
<select v-model="paymentForm.rate" :disabled="isEditMode" class="mt-2 w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-md text-neutral-900 disabled:opacity-50 disabled:cursor-not-allowed">
<option value="25">25%</option>
<option value="50">50%</option>
</select>
</div>
<div class="flex justify-end gap-3">
<button type="button" class="rounded-md border border-neutral-300 px-4 py-2 text-sm font-medium text-neutral-700 hover:bg-neutral-50" @click="isPaymentDrawerOpen = false">Annuler</button>
<button type="submit" class="rounded-md bg-primary-500 px-4 py-2 text-sm font-medium text-white hover:bg-primary-600">Enregistrer</button>
</div>
</form>
</AppDrawer>
</section>
</template>
<script setup lang="ts">
import type { EmployeeRttSummary } from '~/services/dto/employee-rtt-summary'
import AppDrawer from '~/components/AppDrawer.vue'
const props = defineProps<{
summary: EmployeeRttSummary | null
}>()
const emit = defineEmits<{
(event: 'submit-rtt-payment', month: number, minutes: number, rate: '25' | '50'): void
}>()
const isPaymentDrawerOpen = ref(false)
const isEditMode = ref(false)
const paymentForm = reactive({ month: 1, hours: 0, rate: '25' as '25' | '50' })
const monthLabels = [
'Janvier',
'Fevrier',
'Mars',
'Avril',
'Mai',
'Juin',
'Juillet',
'Aout',
'Septembre',
'Octobre',
'Novembre',
'Decembre'
] as const
const orderedMonthOptions = [
{ value: 6, label: 'Juin' },
{ value: 7, label: 'Juillet' },
{ value: 8, label: 'Aout' },
{ value: 9, label: 'Septembre' },
{ value: 10, label: 'Octobre' },
{ value: 11, label: 'Novembre' },
{ value: 12, label: 'Decembre' },
{ value: 1, label: 'Janvier' },
{ value: 2, label: 'Fevrier' },
{ value: 3, label: 'Mars' },
{ value: 4, label: 'Avril' },
{ value: 5, label: 'Mai' }
]
const paymentsByMonth = computed(() => {
const map = new Map<number, { paid25: number; paid50: number }>()
for (const mp of props.summary?.monthPayments ?? []) {
map.set(mp.month, { paid25: mp.paidMinutes25, paid50: mp.paidMinutes50 })
}
return map
})
const getMonthPaid25 = (month: number) => paymentsByMonth.value.get(month)?.paid25 ?? 0
const getMonthPaid50 = (month: number) => paymentsByMonth.value.get(month)?.paid50 ?? 0
const months = computed(() => {
type DisplayWeek = {
key: string
weekNumber: number
recoveryMinutes: number
isEmpty?: boolean
}
const byMonth = new Map<number, { month: number; label: string; weeks: DisplayWeek[]; totalMinutes: number }>()
const orderedMonths = [6, 7, 8, 9, 10, 11, 12, 1, 2, 3, 4, 5]
for (const month of orderedMonths) {
byMonth.set(month, {
month,
label: monthLabels[month - 1],
weeks: [],
totalMinutes: 0
})
}
for (const week of props.summary?.weeks ?? []) {
const month = byMonth.get(week.month)
if (!month) continue
month.weeks.push({
key: week.weekStart,
weekNumber: week.weekNumber,
recoveryMinutes: week.recoveryMinutes
})
month.totalMinutes += week.recoveryMinutes
}
return orderedMonths
.map((monthNumber) => byMonth.get(monthNumber)!)
.filter(Boolean)
.map((month) => {
const minRows = 5
const missing = Math.max(0, minRows - month.weeks.length)
for (let i = 0; i < missing; i += 1) {
month.weeks.push({
key: `empty-${month.month}-${i}`,
weekNumber: 0,
recoveryMinutes: 0,
isEmpty: true
})
}
return month
})
})
const formatMinutes = (minutes: number) => {
const abs = Math.abs(minutes)
const hours = Math.floor(abs / 60)
const rest = abs % 60
const sign = minutes < 0 ? '-' : ''
return `${sign}${hours.toString().padStart(2, '0')}h${rest.toString().padStart(2, '0')}`
}
const openNewPayment = () => {
isEditMode.value = false
paymentForm.month = 6
paymentForm.hours = 0
paymentForm.rate = '25'
isPaymentDrawerOpen.value = true
}
const openEditPayment = (month: number, rate: '25' | '50') => {
isEditMode.value = true
paymentForm.month = month
paymentForm.rate = rate
const currentMinutes = rate === '25' ? getMonthPaid25(month) : getMonthPaid50(month)
paymentForm.hours = currentMinutes / 60
isPaymentDrawerOpen.value = true
}
const onSubmitPayment = () => {
const minutes = Math.round(paymentForm.hours * 60)
emit('submit-rtt-payment', paymentForm.month, minutes, paymentForm.rate)
isPaymentDrawerOpen.value = false
}
</script>