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,7 +1,13 @@
<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">
<div class="flex items-center justify-between">
<h1 class="text-4xl font-bold text-primary-500">Employés</h1> <h1 class="text-4xl font-bold text-primary-500">Employés</h1>
</div>
<div class="flex flex-col gap-3 py-6">
<div class="flex justify-between">
<SiteFilterSelector v-if="sites.length > 0" v-model="selectedSiteIds" :sites="sites" />
<button <button
type="button" type="button"
class="rounded-lg bg-primary-500 px-4 py-2 text-md font-semibold text-white hover:bg-secondary-500" class="rounded-lg bg-primary-500 px-4 py-2 text-md font-semibold text-white hover:bg-secondary-500"
@@ -10,16 +16,23 @@
Ajouter un employé Ajouter un employé
</button> </button>
</div> </div>
<div class="w-80">
<EmployeeNameFilterInput v-model="employeeFilter" />
</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">
<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">
<span class="text-left">Prénom</span> <span class="text-left">Prénom</span>
<span class="text-left">Nom</span> <span class="text-left">Nom</span>
<span class="text-left">Site</span> <span class="text-left">Site</span>
@@ -31,13 +44,19 @@
</div> </div>
<div v-else> <div v-else>
<div <div
v-for="employee in employees" v-for="employee in filteredEmployees"
:key="employee.id" :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_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.firstName }}</span> <span>{{ employee.firstName }}</span>
<span>{{ employee.lastName }}</span> <span>{{ employee.lastName }}</span>
<span>{{ employee.site?.name ?? '-' }}</span> <span
class="inline-flex w-fit max-w-full rounded-md px-2 py-1 text-sm font-semibold"
:style="employee.site ? { backgroundColor: employee.site.color, color: '#0f172a' } : {}"
:class="employee.site ? '' : 'bg-neutral-100 text-neutral-600'"
>
{{ employee.site?.name ?? '-' }}
</span>
<span>{{ employee.contract?.name ?? '-' }}</span> <span>{{ employee.contract?.name ?? '-' }}</span>
<div class="flex items-center justify-end gap-2"> <div class="flex items-center justify-end gap-2">
<button <button
@@ -58,6 +77,8 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
<AppDrawer v-model="isDrawerOpen" :title="drawerTitle"> <AppDrawer v-model="isDrawerOpen" :title="drawerTitle">
<form class="space-y-4" @submit.prevent="handleSubmit"> <form class="space-y-4" @submit.prevent="handleSubmit">
@@ -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