feat : ajout du nouveau système de contrat et ajout de filtre d'impression
All checks were successful
Auto Tag Develop / tag (push) Successful in 5s
All checks were successful
Auto Tag Develop / tag (push) Successful in 5s
This commit is contained in:
@@ -86,6 +86,8 @@
|
||||
<AbsencePrintDrawer
|
||||
v-model="isPrintOpen"
|
||||
:sites="sites"
|
||||
:contract-natures="contractNatureOptions"
|
||||
:work-contracts="workContractOptions"
|
||||
:print-form="printForm"
|
||||
@submit="handlePrint"
|
||||
@cancel="closePrint"
|
||||
@@ -221,7 +223,25 @@ const form = reactive({
|
||||
const printForm = reactive({
|
||||
from: '',
|
||||
to: '',
|
||||
siteIds: [] as number[]
|
||||
siteIds: [] as number[],
|
||||
contractNatures: [] as Array<'CDI' | 'CDD' | 'INTERIM'>,
|
||||
workContractIds: [] as number[]
|
||||
})
|
||||
|
||||
const contractNatureOptions = [
|
||||
{ value: 'CDI' as const, label: 'CDI' },
|
||||
{ value: 'CDD' as const, label: 'CDD' },
|
||||
{ value: 'INTERIM' as const, label: 'Intérim' }
|
||||
]
|
||||
|
||||
const workContractOptions = computed(() => {
|
||||
const byId = new Map<number, { id: number; name: string }>()
|
||||
for (const employee of employees.value) {
|
||||
const contract = employee.contract
|
||||
if (!contract?.id) continue
|
||||
byId.set(contract.id, { id: contract.id, name: contract.name })
|
||||
}
|
||||
return Array.from(byId.values()).sort((a, b) => a.name.localeCompare(b.name, 'fr'))
|
||||
})
|
||||
|
||||
// Remet le formulaire à zéro.
|
||||
@@ -249,6 +269,8 @@ const openPrint = () => {
|
||||
printForm.from = monthStart
|
||||
printForm.to = monthEnd
|
||||
printForm.siteIds = [...selectedSiteIds.value]
|
||||
printForm.contractNatures = contractNatureOptions.map((item) => item.value)
|
||||
printForm.workContractIds = workContractOptions.value.map((item) => item.id)
|
||||
isPrintOpen.value = true
|
||||
}
|
||||
|
||||
@@ -657,6 +679,12 @@ const handlePrint = async () => {
|
||||
if (printForm.siteIds.length > 0) {
|
||||
params.set('sites', printForm.siteIds.join(','))
|
||||
}
|
||||
if (printForm.contractNatures.length > 0) {
|
||||
params.set('contractNatures', printForm.contractNatures.join(','))
|
||||
}
|
||||
if (printForm.workContractIds.length > 0) {
|
||||
params.set('workContracts', printForm.workContractIds.join(','))
|
||||
}
|
||||
await printPdf(`/absences/print?${params.toString()}`)
|
||||
isPrintOpen.value = false
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-lg bg-primary-500 px-4 py-2 text-md font-semibold text-white hover:bg-secondary-500"
|
||||
@click="isDrawerOpen = true"
|
||||
@click="openCreate"
|
||||
>
|
||||
Ajouter un employé
|
||||
</button>
|
||||
@@ -32,10 +32,11 @@
|
||||
<div v-else class="flex-1 min-h-0 rounded-lg border border-neutral-200 bg-white overflow-hidden">
|
||||
<div class="h-full overflow-auto">
|
||||
<div class="min-w-[900px]">
|
||||
<div class="grid grid-cols-[120px_1fr_1fr_220px_200px] gap-4 border-b border-neutral-200 bg-tertiary-500 px-6 py-3 text-md font-semibold text-neutral-700 sticky top-0 z-10">
|
||||
<div class="grid grid-cols-[120px_1fr_1fr_180px_180px_200px] gap-4 border-b border-neutral-200 bg-tertiary-500 px-6 py-3 text-md font-semibold text-neutral-700 sticky top-0 z-10">
|
||||
<span class="text-left">Prénom</span>
|
||||
<span class="text-left">Nom</span>
|
||||
<span class="text-left">Site</span>
|
||||
<span class="text-left">Nature</span>
|
||||
<span class="text-left">Contrat</span>
|
||||
<span class="text-right">Actions</span>
|
||||
</div>
|
||||
@@ -46,7 +47,7 @@
|
||||
<div
|
||||
v-for="employee in filteredEmployees"
|
||||
:key="employee.id"
|
||||
class="grid grid-cols-[120px_1fr_1fr_220px_200px] items-center gap-4 border-b border-neutral-100 px-6 py-3 text-md text-neutral-800 last:border-b-0"
|
||||
class="grid grid-cols-[120px_1fr_1fr_180px_180px_200px] items-center gap-4 border-b border-neutral-100 px-6 py-3 text-md text-neutral-800 last:border-b-0"
|
||||
>
|
||||
<span>{{ employee.firstName }}</span>
|
||||
<span>{{ employee.lastName }}</span>
|
||||
@@ -57,6 +58,7 @@
|
||||
>
|
||||
{{ employee.site?.name ?? '-' }}
|
||||
</span>
|
||||
<span>{{ contractNatureLabel(employee.currentContractNature) }}</span>
|
||||
<span>{{ employee.contract?.name ?? '-' }}</span>
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<button
|
||||
@@ -128,24 +130,72 @@
|
||||
Le site est obligatoire.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-md font-semibold text-neutral-700" for="contract">
|
||||
Contrat <span class="text-red-600">*</span>
|
||||
</label>
|
||||
<select
|
||||
id="contract"
|
||||
v-model="form.contractId"
|
||||
:class="contractFieldClass"
|
||||
>
|
||||
<option value="">Sélectionner un contrat</option>
|
||||
<option v-for="contract in contracts" :key="contract.id" :value="contract.id">
|
||||
{{ contract.name }}
|
||||
</option>
|
||||
</select>
|
||||
<p v-if="showContractError" class="mt-1 text-sm text-red-600">
|
||||
Le contrat est obligatoire.
|
||||
</p>
|
||||
</div>
|
||||
<template v-if="!editingEmployee">
|
||||
<div>
|
||||
<label class="text-md font-semibold text-neutral-700" for="contract-nature">
|
||||
Type de contrat <span class="text-red-600">*</span>
|
||||
</label>
|
||||
<select
|
||||
id="contract-nature"
|
||||
v-model="form.contractNature"
|
||||
:class="contractNatureFieldClass"
|
||||
>
|
||||
<option value="CDI">CDI</option>
|
||||
<option value="CDD">CDD</option>
|
||||
<option value="INTERIM">Intérim</option>
|
||||
</select>
|
||||
<p v-if="showContractNatureError" class="mt-1 text-sm text-red-600">
|
||||
Le type de contrat est obligatoire.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-md font-semibold text-neutral-700" for="contract">
|
||||
Temps de travail <span class="text-red-600">*</span>
|
||||
</label>
|
||||
<select
|
||||
id="contract"
|
||||
v-model="form.contractId"
|
||||
:class="contractFieldClass"
|
||||
>
|
||||
<option value="">Sélectionner un contrat</option>
|
||||
<option v-for="contract in contracts" :key="contract.id" :value="contract.id">
|
||||
{{ contract.name }}
|
||||
</option>
|
||||
</select>
|
||||
<p v-if="showContractError" class="mt-1 text-sm text-red-600">
|
||||
Le temps de travail est obligatoire.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-md font-semibold text-neutral-700" for="contract-start-date">
|
||||
Début contrat <span class="text-red-600">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="contract-start-date"
|
||||
v-model="form.contractStartDate"
|
||||
type="date"
|
||||
:class="contractStartDateFieldClass"
|
||||
/>
|
||||
<p v-if="showContractStartDateError" class="mt-1 text-sm text-red-600">
|
||||
La date de début est obligatoire.
|
||||
</p>
|
||||
</div>
|
||||
<div v-if="requiresContractEndDate">
|
||||
<label class="text-md font-semibold text-neutral-700" for="contract-end-date">
|
||||
Fin contrat
|
||||
<span v-if="requiresContractEndDate" class="text-red-600">*</span>
|
||||
</label>
|
||||
<input
|
||||
id="contract-end-date"
|
||||
v-model="form.contractEndDate"
|
||||
type="date"
|
||||
:class="contractEndDateFieldClass"
|
||||
/>
|
||||
<p v-if="showContractEndDateError" class="mt-1 text-sm text-red-600">
|
||||
La date de fin est obligatoire pour un CDD ou un intérim.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
<div class="flex justify-end gap-3 pt-2">
|
||||
<button
|
||||
type="button"
|
||||
@@ -212,26 +262,54 @@ const filteredEmployees = computed(() => {
|
||||
})
|
||||
})
|
||||
|
||||
const contractNatureLabel = (value?: 'CDI' | 'CDD' | 'INTERIM') => {
|
||||
if (value === 'CDD') return 'CDD'
|
||||
if (value === 'INTERIM') return 'Intérim'
|
||||
return 'CDI'
|
||||
}
|
||||
|
||||
const form = reactive({
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
siteId: '' as number | '',
|
||||
contractId: '' as number | ''
|
||||
contractId: '' as number | '',
|
||||
contractNature: 'CDI' as 'CDI' | 'CDD' | 'INTERIM',
|
||||
contractStartDate: '',
|
||||
contractEndDate: ''
|
||||
})
|
||||
|
||||
const validationTouched = reactive({
|
||||
firstName: false,
|
||||
lastName: false,
|
||||
siteId: false,
|
||||
contractId: false
|
||||
contractId: false,
|
||||
contractNature: false,
|
||||
contractStartDate: false,
|
||||
contractEndDate: false
|
||||
})
|
||||
|
||||
const isFirstNameValid = computed(() => form.firstName.trim() !== '')
|
||||
const isLastNameValid = computed(() => form.lastName.trim() !== '')
|
||||
const isSiteValid = computed(() => form.siteId !== '')
|
||||
const isContractValid = computed(() => form.contractId !== '')
|
||||
const isContractNatureValid = computed(() => ['CDI', 'CDD', 'INTERIM'].includes(form.contractNature))
|
||||
const isContractStartDateValid = computed(() => form.contractStartDate !== '')
|
||||
const requiresContractEndDate = computed(() => form.contractNature === 'CDD' || form.contractNature === 'INTERIM')
|
||||
const isContractEndDateValid = computed(() => {
|
||||
if (!requiresContractEndDate.value) return true
|
||||
return form.contractEndDate !== ''
|
||||
})
|
||||
const isFormValid = computed(
|
||||
() => isFirstNameValid.value && isLastNameValid.value && isSiteValid.value && isContractValid.value
|
||||
() =>
|
||||
isFirstNameValid.value &&
|
||||
isLastNameValid.value &&
|
||||
isSiteValid.value &&
|
||||
(editingEmployee.value
|
||||
? true
|
||||
: (isContractValid.value &&
|
||||
isContractNatureValid.value &&
|
||||
isContractStartDateValid.value &&
|
||||
isContractEndDateValid.value))
|
||||
)
|
||||
|
||||
const showFirstNameError = computed(
|
||||
@@ -246,6 +324,15 @@ const showSiteError = computed(
|
||||
const showContractError = computed(
|
||||
() => validationTouched.contractId && !isContractValid.value
|
||||
)
|
||||
const showContractNatureError = computed(
|
||||
() => !editingEmployee.value && validationTouched.contractNature && !isContractNatureValid.value
|
||||
)
|
||||
const showContractStartDateError = computed(
|
||||
() => !editingEmployee.value && validationTouched.contractStartDate && !isContractStartDateValid.value
|
||||
)
|
||||
const showContractEndDateError = computed(
|
||||
() => !editingEmployee.value && validationTouched.contractEndDate && !isContractEndDateValid.value
|
||||
)
|
||||
|
||||
const baseInputClass =
|
||||
'mt-2 w-full rounded-md border px-3 py-2 text-base text-neutral-900 focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-secondary-500/20'
|
||||
@@ -277,6 +364,26 @@ const contractFieldClass = computed(() => {
|
||||
}
|
||||
return `${baseClass} border-neutral-300`
|
||||
})
|
||||
const contractNatureFieldClass = computed(() => {
|
||||
const baseClass =
|
||||
'mt-2 w-full rounded-md border bg-white px-3 py-2 text-md text-neutral-900'
|
||||
if (showContractNatureError.value) {
|
||||
return `${baseClass} border-red-500`
|
||||
}
|
||||
return `${baseClass} border-neutral-300`
|
||||
})
|
||||
const contractStartDateFieldClass = computed(() => {
|
||||
if (showContractStartDateError.value) {
|
||||
return `${baseInputClass} border-red-500`
|
||||
}
|
||||
return `${baseInputClass} border-neutral-300`
|
||||
})
|
||||
const contractEndDateFieldClass = computed(() => {
|
||||
if (showContractEndDateError.value) {
|
||||
return `${baseInputClass} border-red-500`
|
||||
}
|
||||
return `${baseInputClass} border-neutral-300`
|
||||
})
|
||||
|
||||
const submitButtonClass = computed(() => {
|
||||
if (isSubmitting.value || !isFormValid.value) {
|
||||
@@ -304,6 +411,9 @@ const loadContracts = async () => {
|
||||
|
||||
onMounted(async () => {
|
||||
await Promise.all([loadEmployees(), loadSites(), loadContracts()])
|
||||
if (form.contractStartDate === '') {
|
||||
form.contractStartDate = new Date().toISOString().slice(0, 10)
|
||||
}
|
||||
})
|
||||
|
||||
watch(sites, (nextSites) => {
|
||||
@@ -324,7 +434,12 @@ const handleSubmit = async () => {
|
||||
validationTouched.firstName = true
|
||||
validationTouched.lastName = true
|
||||
validationTouched.siteId = true
|
||||
validationTouched.contractId = true
|
||||
if (!editingEmployee.value) {
|
||||
validationTouched.contractId = true
|
||||
validationTouched.contractNature = true
|
||||
validationTouched.contractStartDate = true
|
||||
validationTouched.contractEndDate = true
|
||||
}
|
||||
if (!isFormValid.value) return
|
||||
|
||||
isSubmitting.value = true
|
||||
@@ -334,14 +449,17 @@ const handleSubmit = async () => {
|
||||
firstName: form.firstName,
|
||||
lastName: form.lastName,
|
||||
siteId: form.siteId === '' ? null : Number(form.siteId),
|
||||
contractId: Number(form.contractId)
|
||||
contractId: editingEmployee.value.contract?.id ?? Number(form.contractId)
|
||||
})
|
||||
} else {
|
||||
await createEmployee({
|
||||
firstName: form.firstName,
|
||||
lastName: form.lastName,
|
||||
siteId: form.siteId === '' ? null : Number(form.siteId),
|
||||
contractId: Number(form.contractId)
|
||||
contractId: Number(form.contractId),
|
||||
contractNature: form.contractNature,
|
||||
contractStartDate: form.contractStartDate,
|
||||
contractEndDate: requiresContractEndDate.value ? form.contractEndDate : null
|
||||
})
|
||||
}
|
||||
|
||||
@@ -349,6 +467,9 @@ const handleSubmit = async () => {
|
||||
form.lastName = ''
|
||||
form.siteId = ''
|
||||
form.contractId = ''
|
||||
form.contractNature = 'CDI'
|
||||
form.contractStartDate = new Date().toISOString().slice(0, 10)
|
||||
form.contractEndDate = ''
|
||||
editingEmployee.value = null
|
||||
isDrawerOpen.value = false
|
||||
await loadEmployees()
|
||||
@@ -363,6 +484,15 @@ watch(isDrawerOpen, (isOpen) => {
|
||||
validationTouched.lastName = false
|
||||
validationTouched.siteId = false
|
||||
validationTouched.contractId = false
|
||||
validationTouched.contractNature = false
|
||||
validationTouched.contractStartDate = false
|
||||
validationTouched.contractEndDate = false
|
||||
}
|
||||
})
|
||||
|
||||
watch(requiresContractEndDate, (required) => {
|
||||
if (!required) {
|
||||
form.contractEndDate = ''
|
||||
}
|
||||
})
|
||||
|
||||
@@ -371,7 +501,18 @@ const openEdit = (employee: Employee) => {
|
||||
form.firstName = employee.firstName
|
||||
form.lastName = employee.lastName
|
||||
form.siteId = employee.site?.id ?? ''
|
||||
form.contractId = employee.contract?.id ?? ''
|
||||
isDrawerOpen.value = true
|
||||
}
|
||||
|
||||
const openCreate = () => {
|
||||
editingEmployee.value = null
|
||||
form.firstName = ''
|
||||
form.lastName = ''
|
||||
form.siteId = ''
|
||||
form.contractId = ''
|
||||
form.contractNature = 'CDI'
|
||||
form.contractStartDate = new Date().toISOString().slice(0, 10)
|
||||
form.contractEndDate = ''
|
||||
isDrawerOpen.value = true
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
:on-toggle-validation-bulk="toggleValidationBulk"
|
||||
:get-row-metrics="getRowMetrics"
|
||||
:get-row-absence-label="getRowAbsenceLabel"
|
||||
:get-row-absence-style="getRowAbsenceStyle"
|
||||
:get-presence-day-value="getPresenceDayValue"
|
||||
:on-absence-click="openAbsenceDrawer"
|
||||
:format-minutes="formatMinutes"
|
||||
@@ -163,6 +164,7 @@ const {
|
||||
toggleValidationBulk,
|
||||
getRowMetrics,
|
||||
getRowAbsenceLabel,
|
||||
getRowAbsenceStyle,
|
||||
getPresenceDayValue,
|
||||
openAbsenceDrawer,
|
||||
submitAbsence,
|
||||
|
||||
Reference in New Issue
Block a user