Files
SIRH/frontend/components/hours/HoursDayExportDrawer.vue
T
tristan 6f9d19bda3
Auto Tag Develop / tag (push) Successful in 7s
feat(heures) : export PDF des heures (vue jour) par sites (#24)
## Résumé
Ajoute un bouton **Exporter** (admin uniquement) à droite du titre « Heures » qui génère un **PDF d'une journée**, regroupé par site, reprenant les colonnes de la vue Jour **sans la colonne « Valider »**.

- Drawer : champ date (préremplit la date affichée) + cases à cocher des sites (préselectionnées sur le filtre courant).
- Portée identique à l'écran : non-conducteurs, sous contrat à la date, sites cochés (lignes vides incluses).
- Jour/Nuit/Total incluent le crédit d'absence et le crédit virtuel férié.

## Implémentation
- Back : `WorkHourDayExport` (ApiResource) + `WorkHourDayExportProvider`, endpoint `GET /work-hours/day-export?workDate=&siteIds=` (ROLE_ADMIN).
- Calcul des cellules mutualisé via `YearlyHoursExportBuilder::buildDayRowsForEmployees` (source unique de vérité).
- Gabarit `templates/work-hour-day-export/print.html.twig` (A4 portrait compact).
- Front : `HoursDayExportDrawer.vue` + câblage dans `pages/hours.vue`.
- Docs : `doc/hours-day-export.md`, `documentation-content.ts`, `CLAUDE.md`.

## Tests
- Test unitaire `YearlyHoursDayRowsTest` ajouté.
- Suite complète verte : 173 tests, 359 assertions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: #24
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
2026-06-09 12:59:04 +00:00

88 lines
2.9 KiB
Vue

<template>
<AppDrawer v-model="drawerOpen" title="Export des heures">
<form class="space-y-4" @submit.prevent="handleSubmit">
<div>
<label class="text-md font-semibold text-neutral-700" for="hours-export-date">
Date <span class="text-red-600">*</span>
</label>
<input
id="hours-export-date"
v-model="selectedDate"
type="date"
class="mt-2 w-full rounded-md border border-black px-3 py-2 text-md text-neutral-900"
>
</div>
<div>
<label class="text-md font-semibold text-neutral-700">
Sites <span class="text-red-600">*</span>
</label>
<MalioSelectCheckbox
v-model="selectedSites"
:options="siteOptions"
groupClass="w-full mt-2"
label="Sites"
display-select-all
/>
</div>
<div class="flex justify-center pt-2">
<button
type="submit"
class="flex w-[200px] items-center justify-center gap-2 rounded-md bg-primary-500 px-4 py-2 text-md font-semibold text-white hover:bg-secondary-500 disabled:opacity-50 disabled:cursor-not-allowed"
:disabled="isLoading || !selectedDate || selectedSites.length === 0"
>
<template v-if="isLoading">Génération en cours...</template>
<template v-else>Exporter</template>
</button>
</div>
</form>
</AppDrawer>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import AppDrawer from '~/components/AppDrawer.vue'
import type { Site } from '~/services/dto/site'
const props = defineProps<{
modelValue: boolean
sites: Site[]
initialDate: string
initialSiteIds: number[]
isLoading?: boolean
}>()
const emit = defineEmits<{
(event: 'update:modelValue', value: boolean): void
(event: 'submit', payload: { date: string; siteIds: number[] }): void
}>()
const drawerOpen = computed({
get: () => props.modelValue,
set: (value: boolean) => emit('update:modelValue', value)
})
const selectedDate = ref(props.initialDate)
const selectedSites = ref<number[]>([...props.initialSiteIds])
const siteOptions = computed(() =>
props.sites.map((site) => ({ label: site.name, value: site.id }))
)
const handleSubmit = () => {
if (!selectedDate.value || selectedSites.value.length === 0) return
emit('submit', { date: selectedDate.value, siteIds: [...selectedSites.value] })
}
watch(
() => props.modelValue,
(isOpen) => {
if (isOpen) {
selectedDate.value = props.initialDate
selectedSites.value = [...props.initialSiteIds]
}
}
)
</script>