feat : ajout de l'impression des tableaux d'absence
This commit is contained in:
31
frontend/composables/usePdfPrinter.ts
Normal file
31
frontend/composables/usePdfPrinter.ts
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user