252 lines
10 KiB
Vue
252 lines
10 KiB
Vue
<template>
|
|
<section class="mt-8">
|
|
<div class="overflow-hidden bg-white">
|
|
<div
|
|
class="grid grid-cols-4 border border-black bg-tertiary-500 px-6 py-3 text-[20px] font-semibold text-black rounded-t-md">
|
|
<p>Date de début</p>
|
|
<p>Date de fin</p>
|
|
<p>Justificatif</p>
|
|
<p>Commentaire</p>
|
|
</div>
|
|
<div v-if="formations.length === 0" class="px-6 py-4 text-[20px] font-bold text-primary-500 border-x border-b border-primary-500 rounded-b-md">
|
|
Aucune formation.
|
|
</div>
|
|
<div v-else class="border-x border-b border-primary-500 rounded-b-md">
|
|
<div
|
|
v-for="item in formations"
|
|
:key="item.id"
|
|
class="grid grid-cols-4 border-b border-primary-500 px-6 py-3 text-md font-bold text-primary-500 last:border-b-0 cursor-pointer hover:bg-tertiary-500"
|
|
@click="onOpenEditDrawer(item)"
|
|
>
|
|
<p>{{ formatDate(item.startDate) }}</p>
|
|
<p>{{ formatDate(item.endDate) }}</p>
|
|
<p class="min-w-0">
|
|
<a
|
|
v-if="item.justificatifPath"
|
|
:href="getFormationJustificatifUrl(props.apiBase, item.id)"
|
|
target="_blank"
|
|
class="text-primary-500 hover:text-secondary-500 flex gap-2 items-center"
|
|
@click.stop
|
|
>
|
|
<Icon name="mdi:file-download-outline" size="20" class="shrink-0"/>
|
|
<span class="truncate">{{ item.justificatifName ?? 'Télécharger' }}</span>
|
|
</a>
|
|
<span v-else>-</span>
|
|
</p>
|
|
<p class="truncate">{{ item.comment ?? '-' }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="flex justify-center mb-4 mt-8">
|
|
<button
|
|
type="button"
|
|
class="flex w-[200px] items-center justify-center gap-2 rounded-md bg-primary-500 px-4 py-2 text-md text-white disabled:cursor-not-allowed disabled:opacity-50"
|
|
@click="onOpenCreateDrawer"
|
|
>
|
|
+ Ajouter
|
|
</button>
|
|
</div>
|
|
|
|
<AppDrawer v-model="isDrawerOpen" title="Formation">
|
|
<form class="space-y-4" @submit.prevent="onSubmit">
|
|
<div>
|
|
<label class="text-md font-semibold text-neutral-700" for="formation-start-date">
|
|
Date de début <span class="text-red-600">*</span>
|
|
</label>
|
|
<input
|
|
id="formation-start-date"
|
|
v-model="form.startDate"
|
|
type="date"
|
|
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>
|
|
<label class="text-md font-semibold text-neutral-700" for="formation-end-date">
|
|
Date de fin <span class="text-red-600">*</span>
|
|
</label>
|
|
<input
|
|
id="formation-end-date"
|
|
v-model="form.endDate"
|
|
type="date"
|
|
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"
|
|
/>
|
|
<p v-if="isDateRangeInvalid" class="mt-1 text-sm text-red-600">La date de fin doit être postérieure ou égale à la date de début.</p>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="text-md font-semibold text-neutral-700" for="formation-justificatif">
|
|
Justificatif
|
|
</label>
|
|
<div v-if="isEditing && editingItem?.justificatifName" class="mt-1 text-sm text-neutral-500">
|
|
Fichier actuel : {{ editingItem.justificatifName }}
|
|
</div>
|
|
<input
|
|
id="formation-justificatif"
|
|
ref="justificatifInput"
|
|
type="file"
|
|
accept="application/pdf"
|
|
class="mt-2 w-full rounded-md border border-neutral-300 px-3 py-2 text-base text-neutral-900 file:mr-3 file:rounded file:border-0 file:bg-primary-500 file:px-3 file:py-1 file:text-sm file:text-white"
|
|
@change="onJustificatifChange"
|
|
/>
|
|
<p v-if="justificatifError" class="mt-1 text-sm text-red-600">{{ justificatifError }}</p>
|
|
<p v-else class="mt-1 text-sm text-neutral-500">Fichier au format pdf</p>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="text-md font-semibold text-neutral-700" for="formation-comment">
|
|
Commentaire
|
|
</label>
|
|
<textarea
|
|
id="formation-comment"
|
|
v-model="form.comment"
|
|
rows="3"
|
|
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"
|
|
placeholder="Commentaire..."
|
|
/>
|
|
</div>
|
|
|
|
<div v-if="isEditing" class="grid grid-cols-2 gap-3 pt-2">
|
|
<button
|
|
type="button"
|
|
class="flex items-center justify-center rounded-md bg-red-500 px-4 py-2 text-md font-semibold text-white hover:bg-red-600"
|
|
@click="onDelete"
|
|
>
|
|
Supprimer
|
|
</button>
|
|
<button
|
|
type="submit"
|
|
class="flex items-center justify-center rounded-md bg-primary-500 px-4 py-2 text-md font-semibold text-white hover:bg-secondary-500 disabled:cursor-not-allowed disabled:opacity-50"
|
|
:disabled="!isFormValid"
|
|
>
|
|
Modifier
|
|
</button>
|
|
</div>
|
|
<div v-else 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:cursor-not-allowed disabled:opacity-50"
|
|
:disabled="!isFormValid"
|
|
>
|
|
+ Ajouter
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</AppDrawer>
|
|
</section>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type {Formation} from '~/services/dto/formation'
|
|
import {getFormationJustificatifUrl} from '~/services/formations'
|
|
import AppDrawer from '~/components/AppDrawer.vue'
|
|
|
|
const props = defineProps<{
|
|
formations: Formation[]
|
|
apiBase: string
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
(event: 'create', data: { startDate: string; endDate: string; comment?: string }, justificatifFile?: File): void
|
|
(event: 'update', id: number, data: { startDate: string; endDate: string; comment?: string }, justificatifFile?: File): void
|
|
(event: 'delete', id: number): void
|
|
}>()
|
|
|
|
const isDrawerOpen = ref(false)
|
|
const isEditing = ref(false)
|
|
const editingItem = ref<Formation | null>(null)
|
|
const selectedJustificatif = ref<File | undefined>(undefined)
|
|
const justificatifInput = ref<HTMLInputElement | null>(null)
|
|
const justificatifError = ref('')
|
|
|
|
const form = reactive({
|
|
startDate: '',
|
|
endDate: '',
|
|
comment: ''
|
|
})
|
|
|
|
const isDateRangeInvalid = computed(() => {
|
|
if (!form.startDate || !form.endDate) return false
|
|
return form.endDate < form.startDate
|
|
})
|
|
|
|
const isFormValid = computed(() => {
|
|
return Boolean(form.startDate) && Boolean(form.endDate) && !isDateRangeInvalid.value && !justificatifError.value
|
|
})
|
|
|
|
const formatDate = (dateStr: string): string => {
|
|
if (!dateStr) return '-'
|
|
const date = new Date(dateStr)
|
|
if (Number.isNaN(date.getTime())) return dateStr
|
|
return date.toLocaleDateString('fr-FR')
|
|
}
|
|
|
|
const resetForm = () => {
|
|
form.startDate = ''
|
|
form.endDate = ''
|
|
form.comment = ''
|
|
selectedJustificatif.value = undefined
|
|
justificatifError.value = ''
|
|
if (justificatifInput.value) {
|
|
justificatifInput.value.value = ''
|
|
}
|
|
}
|
|
|
|
const onOpenCreateDrawer = () => {
|
|
isEditing.value = false
|
|
editingItem.value = null
|
|
resetForm()
|
|
isDrawerOpen.value = true
|
|
}
|
|
|
|
const onOpenEditDrawer = (item: Formation) => {
|
|
isEditing.value = true
|
|
editingItem.value = item
|
|
form.startDate = item.startDate
|
|
form.endDate = item.endDate
|
|
form.comment = item.comment ?? ''
|
|
selectedJustificatif.value = undefined
|
|
justificatifError.value = ''
|
|
if (justificatifInput.value) {
|
|
justificatifInput.value.value = ''
|
|
}
|
|
isDrawerOpen.value = true
|
|
}
|
|
|
|
const onJustificatifChange = (event: Event) => {
|
|
const target = event.target as HTMLInputElement
|
|
const file = target.files?.[0]
|
|
if (file && file.type !== 'application/pdf') {
|
|
justificatifError.value = 'Seuls les fichiers PDF sont acceptés.'
|
|
selectedJustificatif.value = undefined
|
|
target.value = ''
|
|
return
|
|
}
|
|
justificatifError.value = ''
|
|
selectedJustificatif.value = file ?? undefined
|
|
}
|
|
|
|
const onSubmit = () => {
|
|
const data = {
|
|
startDate: form.startDate,
|
|
endDate: form.endDate,
|
|
comment: form.comment || undefined
|
|
}
|
|
|
|
if (isEditing.value && editingItem.value) {
|
|
emit('update', editingItem.value.id, data, selectedJustificatif.value)
|
|
} else {
|
|
emit('create', data, selectedJustificatif.value)
|
|
}
|
|
isDrawerOpen.value = false
|
|
}
|
|
|
|
const onDelete = () => {
|
|
if (!editingItem.value) return
|
|
const ok = window.confirm('Supprimer cette formation ?')
|
|
if (!ok) return
|
|
emit('delete', editingItem.value.id)
|
|
isDrawerOpen.value = false
|
|
}
|
|
</script>
|