feat : ajout de filtres sur la page employé

This commit is contained in:
2026-02-20 09:37:37 +01:00
parent a3bc7db073
commit d9fa301159

View File

@@ -1,59 +1,80 @@
<template> <template>
<div> <div class="h-full overflow-hidden flex flex-col">
<div class="flex items-center justify-between pb-12"> <div class="shrink-0">
<h1 class="text-4xl font-bold text-primary-500">Employés</h1> <div class="flex items-center justify-between">
<button <h1 class="text-4xl font-bold text-primary-500">Employés</h1>
type="button" </div>
class="rounded-lg bg-primary-500 px-4 py-2 text-md font-semibold text-white hover:bg-secondary-500"
@click="isDrawerOpen = true" <div class="flex flex-col gap-3 py-6">
> <div class="flex justify-between">
Ajouter un employé <SiteFilterSelector v-if="sites.length > 0" v-model="selectedSiteIds" :sites="sites" />
</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="isDrawerOpen = true"
>
Ajouter un employé
</button>
</div>
<div class="w-80">
<EmployeeNameFilterInput v-model="employeeFilter" />
</div>
</div>
</div> </div>
<div <div
v-if="!isLoading && employees.length === 0" v-if="!isLoading && filteredEmployees.length === 0"
class="rounded-lg border border-neutral-200 bg-white p-6 text-md text-neutral-600" class="rounded-lg border border-neutral-200 bg-white p-6 text-md text-neutral-600"
> >
Aucun employé pour le moment. Aucun employé pour le moment.
</div> </div>
<div v-else class="max-h-[80vh] overflow-auto rounded-lg border border-neutral-200 bg-white"> <div v-else class="flex-1 min-h-0 rounded-lg border border-neutral-200 bg-white overflow-hidden">
<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"> <div class="h-full overflow-auto">
<span class="text-left">Prénom</span> <div class="min-w-[900px]">
<span class="text-left">Nom</span> <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">
<span class="text-left">Site</span> <span class="text-left">Prénom</span>
<span class="text-left">Contrat</span> <span class="text-left">Nom</span>
<span class="text-right">Actions</span> <span class="text-left">Site</span>
</div> <span class="text-left">Contrat</span>
<div v-if="isLoading" class="px-6 py-4 text-md text-neutral-500"> <span class="text-right">Actions</span>
Chargement... </div>
</div> <div v-if="isLoading" class="px-6 py-4 text-md text-neutral-500">
<div v-else> Chargement...
<div </div>
v-for="employee in employees" <div v-else>
:key="employee.id" <div
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" v-for="employee in filteredEmployees"
> :key="employee.id"
<span>{{ employee.firstName }}</span> 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"
<span>{{ employee.lastName }}</span>
<span>{{ employee.site?.name ?? '-' }}</span>
<span>{{ employee.contract?.name ?? '-' }}</span>
<div class="flex items-center justify-end gap-2">
<button
type="button"
class="rounded-md border border-neutral-200 px-2 py-1 text-md font-semibold text-neutral-700 hover:bg-neutral-100"
@click="openEdit(employee)"
> >
Modifier <span>{{ employee.firstName }}</span>
</button> <span>{{ employee.lastName }}</span>
<button <span
type="button" class="inline-flex w-fit max-w-full rounded-md px-2 py-1 text-sm font-semibold"
class="rounded-md border border-red-200 px-2 py-1 text-md font-semibold text-red-600 hover:bg-red-50" :style="employee.site ? { backgroundColor: employee.site.color, color: '#0f172a' } : {}"
@click="confirmDelete(employee)" :class="employee.site ? '' : 'bg-neutral-100 text-neutral-600'"
> >
Supprimer {{ employee.site?.name ?? '-' }}
</button> </span>
<span>{{ employee.contract?.name ?? '-' }}</span>
<div class="flex items-center justify-end gap-2">
<button
type="button"
class="rounded-md border border-neutral-200 px-2 py-1 text-md font-semibold text-neutral-700 hover:bg-neutral-100"
@click="openEdit(employee)"
>
Modifier
</button>
<button
type="button"
class="rounded-md border border-red-200 px-2 py-1 text-md font-semibold text-red-600 hover:bg-red-50"
@click="confirmDelete(employee)"
>
Supprimer
</button>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -153,10 +174,12 @@ import type { Site } from '~/services/dto/site'
import { listContracts } from '~/services/contracts' import { listContracts } from '~/services/contracts'
import { createEmployee, deleteEmployee, listEmployees, updateEmployee } from '~/services/employees' import { createEmployee, deleteEmployee, listEmployees, updateEmployee } from '~/services/employees'
import { listSites } from '~/services/sites' import { listSites } from '~/services/sites'
import SiteFilterSelector from '~/components/SiteFilterSelector.vue'
const isDrawerOpen = ref(false) const isDrawerOpen = ref(false)
const isSubmitting = ref(false) const isSubmitting = ref(false)
const isLoading = ref(false) const isLoading = ref(false)
const sitesInitialized = ref(false)
const editingEmployee = ref<Employee | null>(null) const editingEmployee = ref<Employee | null>(null)
const drawerTitle = computed(() => const drawerTitle = computed(() =>
editingEmployee.value ? 'Modifier un employé' : 'Ajouter un employé' editingEmployee.value ? 'Modifier un employé' : 'Ajouter un employé'
@@ -165,6 +188,26 @@ const drawerTitle = computed(() =>
const employees = ref<Employee[]>([]) const employees = ref<Employee[]>([])
const sites = ref<Site[]>([]) const sites = ref<Site[]>([])
const contracts = ref<Contract[]>([]) const contracts = ref<Contract[]>([])
const employeeFilter = ref('')
const selectedSiteIds = ref<number[]>([])
const filteredEmployees = computed(() => {
if (selectedSiteIds.value.length === 0) return []
const filter = employeeFilter.value.trim().toLowerCase()
const bySite = employees.value.filter((employee) => {
const siteId = employee.site?.id
return !!siteId && selectedSiteIds.value.includes(siteId)
})
if (!filter) return bySite
return bySite.filter((employee) => {
const firstName = employee.firstName?.toLowerCase() ?? ''
const lastName = employee.lastName?.toLowerCase() ?? ''
return firstName.includes(filter) || lastName.includes(filter)
})
})
const form = reactive({ const form = reactive({
firstName: '', firstName: '',
@@ -260,6 +303,19 @@ onMounted(async () => {
await Promise.all([loadEmployees(), loadSites(), loadContracts()]) await Promise.all([loadEmployees(), loadSites(), loadContracts()])
}) })
watch(sites, (nextSites) => {
const currentSiteIds = nextSites.map((site) => site.id)
if (!sitesInitialized.value) {
if (currentSiteIds.length === 0) return
selectedSiteIds.value = currentSiteIds
sitesInitialized.value = true
return
}
selectedSiteIds.value = selectedSiteIds.value.filter((siteId) => currentSiteIds.includes(siteId))
}, { immediate: true })
const handleSubmit = async () => { const handleSubmit = async () => {
if (isSubmitting.value) return if (isSubmitting.value) return
validationTouched.firstName = true validationTouched.firstName = true