feat : agence d'intérim sur les contrats INTERIM + renommage Types d'absence en Types de statut + colonne Absence en Statut
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled

- Nouvelle entité InterimAgency (table interim_agencies, API lecture seule)
- Sélecteur agence conditionnel dans les formulaires création employé et ajout contrat
- Affichage "Intérim (NomAgence)" sur la liste employés et l'historique contrat
- Date de fin obligatoire côté frontend pour CDD et INTERIM (aligné backend)
- Renommage "Types d'absence" → "Types de statut" (sidebar, page, titre)
- Renommage en-tête "Absence" → "Statut" sur les vues jour heures et conducteurs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-17 11:47:14 +02:00
parent 9e411be3c3
commit 1fe7f2cdde
27 changed files with 281 additions and 23 deletions

View File

@@ -1,7 +1,7 @@
<template>
<div class="h-full flex flex-col overflow-hidden">
<div class="flex items-center justify-between pb-6">
<h1 class="text-4xl font-bold text-primary-500">Types d'absence</h1>
<h1 class="text-4xl font-bold text-primary-500">Types de statut</h1>
<button
type="button"
class="rounded-lg bg-primary-500 px-4 py-2 text-md font-semibold text-white hover:bg-secondary-500"
@@ -164,7 +164,7 @@ import type { AbsenceType } from '~/services/dto/absence-type'
import { createAbsenceType, deleteAbsenceType, listAbsenceTypes, updateAbsenceType } from '~/services/absence-types'
useHead({
title: 'Types d\'absences'
title: 'Types de statut'
})
const isDrawerOpen = ref(false)

View File

@@ -148,6 +148,7 @@
:on-submit-suspension="submitSuspension"
:on-add-suspension-form="addSuspensionForm"
:current-contract-period-id="currentActiveContractPeriodId"
:interim-agencies="interimAgencies"
/>
<div v-else-if="showLeaveTab && activeTab === 'leave'" class="h-full">
<div v-if="isLeaveLoading" class="mt-6 rounded-lg border border-neutral-200 bg-white p-6 text-md text-neutral-600">
@@ -295,6 +296,7 @@ const {
submitSuspension,
addSuspensionForm,
currentActiveContractPeriodId,
interimAgencies,
isLeaveLoading,
isRttLoading,
mileageAllowances,

View File

@@ -72,7 +72,7 @@
class="absolute inset-0 flex items-center justify-center bg-primary-500 p-4 text-white opacity-0 transition-opacity duration-200 group-hover:opacity-100">
<div class="w-full rounded-md bg-white/15 p-4 text-sm">
<p class="text-base font-semibold">{{ employee.lastName }} {{ employee.firstName }}</p>
<p><strong>Type:</strong> {{ contractNatureLabel(employee.currentContractNature) }}</p>
<p><strong>Type:</strong> {{ employee.currentInterimAgencyName ? `${contractNatureLabel(employee.currentContractNature)} (${employee.currentInterimAgencyName})` : contractNatureLabel(employee.currentContractNature) }}</p>
<p><strong>Temps de travail:</strong> {{ employee.contract?.name ?? '-' }}</p>
<p><strong>Site:</strong> {{ employee.site?.name ?? '-' }}</p>
<p><strong>Date d'entrée :</strong> {{ employee.entryDate ? employee.entryDate.split('-').reverse().join('/') : '-' }}</p>
@@ -147,6 +147,21 @@
Le type de contrat est obligatoire.
</p>
</div>
<div v-if="form.contractNature === 'INTERIM'">
<label class="text-md font-semibold text-neutral-700" for="interim-agency">
Agence d'intérim
</label>
<select
id="interim-agency"
v-model="form.interimAgencyId"
class="mt-2 w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-md text-neutral-900"
>
<option value="">Aucune</option>
<option v-for="agency in interimAgencies" :key="agency.id" :value="agency.id">
{{ agency.name }}
</option>
</select>
</div>
<div>
<label class="text-md font-semibold text-neutral-700" for="contract">
Temps de travail <span class="text-red-600">*</span>
@@ -191,7 +206,7 @@
:class="contractEndDateFieldClass"
/>
<p v-if="showContractEndDateError" class="mt-1 text-sm text-red-600">
La date de fin est obligatoire pour un CDD.
La date de fin est obligatoire pour un CDD ou un Intérim.
</p>
</div>
<div class="rounded-md border border-neutral-200 bg-neutral-50 p-3">
@@ -246,6 +261,7 @@ import type {Site} from '~/services/dto/site'
import {listContracts} from '~/services/contracts'
import {createEmployee, deleteEmployee, listEmployees, updateEmployee} from '~/services/employees'
import {listSites} from '~/services/sites'
import {listInterimAgencies, type InterimAgency} from '~/services/interim-agencies'
import SiteFilterSelector from '~/components/SiteFilterSelector.vue'
import SalaryRecapDrawer from '~/components/SalaryRecapDrawer.vue'
import {contractNatureLabel, isContractNature, requiresContractEndDate, showsContractEndDate} from '~/utils/contract'
@@ -269,6 +285,7 @@ const drawerTitle = computed(() =>
const employees = ref<Employee[]>([])
const sites = ref<Site[]>([])
const contracts = ref<Contract[]>([])
const interimAgencies = ref<InterimAgency[]>([])
const employeeFilter = ref('')
const contractStatusFilter = ref<'active' | 'inactive' | 'all'>('active')
const selectedSiteIds = ref<number[]>([])
@@ -300,7 +317,8 @@ const form = reactive({
contractStartDate: '',
contractEndDate: '',
isDriver: false,
workDaysHours: null as Record<number, number> | null
workDaysHours: null as Record<number, number> | null,
interimAgencyId: '' as number | ''
})
const validationTouched = reactive({
@@ -451,8 +469,12 @@ const loadContracts = async () => {
contracts.value = await listContracts()
}
const loadInterimAgencies = async () => {
interimAgencies.value = await listInterimAgencies()
}
onMounted(async () => {
await Promise.all([loadEmployees(), loadSites(), loadContracts()])
await Promise.all([loadEmployees(), loadSites(), loadContracts(), loadInterimAgencies()])
if (form.contractStartDate === '') {
form.contractStartDate = new Date().toISOString().slice(0, 10)
}
@@ -503,7 +525,8 @@ const handleSubmit = async () => {
contractStartDate: form.contractStartDate,
contractEndDate: form.contractEndDate || null,
isDriverInput: form.isDriver,
workDaysHoursInput: requiresSchedule.value ? form.workDaysHours : null
workDaysHoursInput: requiresSchedule.value ? form.workDaysHours : null,
interimAgencyId: form.contractNature === 'INTERIM' && form.interimAgencyId !== '' ? Number(form.interimAgencyId) : null
})
}
@@ -516,6 +539,7 @@ const handleSubmit = async () => {
form.contractEndDate = ''
form.isDriver = false
form.workDaysHours = null
form.interimAgencyId = ''
editingEmployee.value = null
isDrawerOpen.value = false
await loadEmployees()
@@ -542,6 +566,12 @@ watch(showsContractEndDateComputed, (shows) => {
}
})
watch(() => form.contractNature, (nature) => {
if (nature !== 'INTERIM') {
form.interimAgencyId = ''
}
})
watch(requiresSchedule, (required) => {
if (!required) {
form.workDaysHours = null
@@ -567,6 +597,7 @@ const openCreate = () => {
form.contractEndDate = ''
form.isDriver = false
form.workDaysHours = null
form.interimAgencyId = ''
isDrawerOpen.value = true
}