feat : ajout de l'impression des tableaux d'absence

This commit is contained in:
2026-02-06 16:20:09 +01:00
parent ee26fdd045
commit 03f5552dd4
7 changed files with 834 additions and 3 deletions

View File

@@ -0,0 +1,31 @@
import {useApi} from '~/composables/useApi'
export const usePdfPrinter = () => {
const api = useApi()
const printPdf = async (url: string): Promise<void> => {
const blob = await api.getBlob(url);
const pdfBlob = blob.type === 'application/pdf'
? blob
: new Blob([blob], { type: 'application/pdf' });
const blobUrl = URL.createObjectURL(pdfBlob);
const filename = `test.pdf`;
const a = document.createElement('a');
a.href = blobUrl;
a.download = filename;
a.style.display = 'none';
document.body.appendChild(a);
a.click();
a.remove();
// L'ouverture dans un nouvel onglet déclenche un 2e PDF sans le nom personnalisé.
setTimeout(() => URL.revokeObjectURL(blobUrl), 60_000);
}
return {
printPdf
}
}

View File

@@ -39,6 +39,13 @@
>
Ajouter une absence
</button>
<button
type="button"
class="rounded-lg bg-primary-500 px-4 py-2 text-md font-semibold text-white hover:bg-secondary-500"
@click="openPrint"
>
Imprimer
</button>
</div>
</div>
@@ -174,6 +181,64 @@
</div>
</form>
</AppDrawer>
<AppDrawer v-model="isPrintOpen" title="Imprimer les absences">
<form class="space-y-4" @submit.prevent="handlePrint">
<div class="grid grid-cols-2 gap-4">
<div>
<label class="text-md font-semibold text-neutral-700" for="print-from">Date de début</label>
<input
id="print-from"
v-model="printForm.from"
type="date"
class="mt-2 w-full rounded-md border border-neutral-300 px-3 py-2 text-md text-neutral-900"
/>
</div>
<div>
<label class="text-md font-semibold text-neutral-700" for="print-to">Date de fin</label>
<input
id="print-to"
v-model="printForm.to"
type="date"
class="mt-2 w-full rounded-md border border-neutral-300 px-3 py-2 text-md text-neutral-900"
/>
</div>
</div>
<div class="space-y-2">
<p class="text-md font-semibold text-neutral-700">Sites</p>
<div class="flex flex-wrap gap-4 rounded-md border border-neutral-300 px-3 py-2">
<div v-for="site in sites" :key="site.id" class="flex items-center gap-2">
<div :style="{ backgroundColor: site.color }" class="h-4 w-4 rounded"></div>
<label class="text-md" :for="`print-site-${site.id}`">{{ site.name }}</label>
<input
:id="`print-site-${site.id}`"
v-model="printForm.siteIds"
:value="site.id"
type="checkbox"
class="h-4 w-4"
/>
</div>
</div>
</div>
<div class="flex justify-end gap-3 pt-2">
<button
type="button"
class="rounded-lg border border-neutral-200 px-4 py-2 text-md font-semibold text-neutral-700 hover:bg-neutral-100"
@click="closePrint"
>
Annuler
</button>
<button
type="submit"
class="rounded-lg bg-primary-500 px-4 py-2 text-md font-semibold text-white hover:bg-secondary-500"
>
Imprimer
</button>
</div>
</form>
</AppDrawer>
</div>
</template>
@@ -232,6 +297,7 @@ const absences = ref<Absence[]>([])
const isDrawerOpen = ref(false)
const isSubmitting = ref(false)
const editingAbsence = ref<Absence | null>(null)
const isPrintOpen = ref(false)
const now = new Date()
const selectedMonth = ref(now.getMonth())
@@ -269,6 +335,12 @@ const form = reactive({
comment: ''
})
const printForm = reactive({
from: '',
to: '',
siteIds: [] as number[]
})
const resetForm = () => {
form.employeeId = ''
@@ -284,6 +356,65 @@ const closeDrawer = () => {
resetForm()
}
const openPrint = () => {
const monthStart = toYmd(selectedYear.value, selectedMonth.value, 1)
const monthEnd = toYmd(selectedYear.value, selectedMonth.value + 1, 0)
printForm.from = monthStart
printForm.to = monthEnd
printForm.siteIds = [...selectedSiteIds.value]
isPrintOpen.value = true
}
const closePrint = () => {
isPrintOpen.value = false
}
const parseYmd = (value: string) => {
const [year, month, day] = value.split('-').map(Number)
if (!year || !month || !day) return null
return new Date(year, month - 1, day)
}
const addMonths = (date: Date, months: number) => {
const next = new Date(date.getFullYear(), date.getMonth() + months, date.getDate())
if (next.getMonth() !== (date.getMonth() + months) % 12) {
next.setDate(0)
}
return next
}
const enforcePrintRange = () => {
if (!printForm.from) return
const start = parseYmd(printForm.from)
if (!start) return
const maxEnd = addMonths(start, 2)
maxEnd.setDate(maxEnd.getDate() - 1)
const maxEndYmd = toYmd(maxEnd.getFullYear(), maxEnd.getMonth(), maxEnd.getDate())
if (!printForm.to) {
printForm.to = maxEndYmd
return
}
const end = parseYmd(printForm.to)
if (!end) {
printForm.to = maxEndYmd
return
}
if (end < start) {
printForm.to = printForm.from
return
}
if (end > maxEnd) {
printForm.to = maxEndYmd
}
}
watch(() => printForm.from, enforcePrintRange)
watch(() => printForm.to, enforcePrintRange)
const loadEmployees = async () => {
employees.value = await listEmployees()
}
@@ -434,4 +565,15 @@ const formatEmployeeName = (employee: Employee) => {
return `${employee.firstName} ${initial}`.trim()
}
const { printPdf } = usePdfPrinter()
const handlePrint = async () => {
const params = new URLSearchParams()
params.set('from', printForm.from)
params.set('to', printForm.to)
if (printForm.siteIds.length > 0) {
params.set('sites', printForm.siteIds.join(','))
}
await printPdf(`/absences/print?${params.toString()}`)
isPrintOpen.value = false
}
</script>