feat : migrate filter/form UI to @malio/layer-ui + fix hours/calendar contract scoping

- Add @malio/layer-ui as Nuxt layer (extends in nuxt.config.ts)
- Migrate site/employee/contract filters to MalioSelectCheckbox / MalioInputText / MalioSelect on employees, calendar and hours screens
- Migrate absence drawer selects + submit button to Malio (MalioSelect + MalioButton)
- Migrate calendar "Ajouter une absence" / "Imprimer" actions to MalioButton
- Drop now-unused EmployeeNameFilterInput and SiteFilterSelector components
- Hours day view: resolve contract nature at selected date (WorkHourDayContext.contractNature) instead of employee.currentContractNature (today-based); fixes interim contracts showing as CDI when mission ended
- Calendar: hide employees whose contract periods do not intersect the displayed month
- Layout: scrollbar-gutter:stable on <main> to avoid horizontal shift when dropdowns open
- Update functional-rules.md, in-app documentation, CLAUDE.md to match

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 16:54:38 +02:00
parent 90843dd997
commit bd93c52197
25 changed files with 309 additions and 368 deletions

View File

@@ -34,19 +34,29 @@
</button>
</div>
</div>
<div class="flex gap-3 py-7">
<div class="flex items-center gap-3 py-7">
<div class="w-80">
<EmployeeNameFilterInput v-model="employeeFilter"/>
<MalioInputText
v-model="employeeFilter"
label="Recherche d'un employé"
icon-name="mdi:magnify"
/>
</div>
<SiteFilterSelector v-if="sites.length > 0" v-model="selectedSiteIds" :sites="sites"/>
<select
<div v-if="sites.length > 0" class="relative z-50 w-80">
<MalioSelectCheckbox
v-model="selectedSiteIds"
:options="siteOptions"
label="Sites"
display-select-all
/>
</div>
<MalioSelect
v-model="contractStatusFilter"
class="rounded-md border border-primary-500 bg-white px-3 py-2 text-md font-semibold text-primary-500 cursor-pointer"
>
<option value="active">Avec contrat</option>
<option value="inactive">Sans contrat</option>
<option value="all">Tous</option>
</select>
label="Statut contrat"
:options="contractStatusOptions"
group-class="w-40"
/>
</div>
</div>
@@ -275,7 +285,6 @@ 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 BulkYearlyHoursDrawer from '~/components/BulkYearlyHoursDrawer.vue'
import {contractNatureLabel, isContractNature, requiresContractEndDate, showsContractEndDate} from '~/utils/contract'
@@ -304,7 +313,13 @@ const contracts = ref<Contract[]>([])
const interimAgencies = ref<InterimAgency[]>([])
const employeeFilter = ref('')
const contractStatusFilter = ref<'active' | 'inactive' | 'all'>('active')
const contractStatusOptions = [
{ label: 'Avec contrat', value: 'active' },
{ label: 'Sans contrat', value: 'inactive' },
{ label: 'Tous', value: 'all' }
]
const selectedSiteIds = ref<number[]>([])
const siteOptions = computed(() => sites.value.map((site) => ({ label: site.name, value: site.id })))
const filteredEmployees = computed<Employee[]>(() => {
if (selectedSiteIds.value.length === 0) return []