import type { Contract } from '~/services/dto/contract' import type { Absence } from '~/services/dto/absence' import type { EmployeeLeaveSummary } from '~/services/dto/employee-leave-summary' import type { EmployeeRttSummary } from '~/services/dto/employee-rtt-summary' import type { ContractHistoryItem, Employee } from '~/services/dto/employee' import { CONTRACT_TYPES } from '~/services/dto/contract' import { listAbsences } from '~/services/absences' import { listContracts } from '~/services/contracts' import { getEmployeeLeaveSummary, updateFractionedDays } from '~/services/employee-leave-summary' import { getEmployeeRttSummary, createRttPayment } from '~/services/employee-rtt-summary' import { getEmployee, updateEmployee } from '~/services/employees' import { listPublicHolidays } from '~/services/public-holidays' import { formatNullableYmdToFr, getTodayYmd, shiftYmd } from '~/utils/date' import { contractNatureLabel, isContractNature, requiresContractEndDate, showsContractEndDate } from '~/utils/contract' export const useEmployeeDetailPage = () => { const route = useRoute() const toast = useToast() const employee = ref(null) const isLoading = ref(false) const activeTab = ref<'contract' | 'leave' | 'rtt'>('contract') const contracts = ref([]) const employeeAbsences = ref([]) const leaveSummary = ref(null) const rttSummary = ref(null) const publicHolidays = ref>({}) const isContractDrawerOpen = ref(false) const isContractSubmitting = ref(false) const isCreateContractDrawerOpen = ref(false) const isCreateContractSubmitting = ref(false) const contractForm = reactive({ contractId: '' as number | '', contractName: '', weeklyHours: null as number | null, contractNature: 'CDI' as 'CDI' | 'CDD' | 'INTERIM', startDate: '', endDate: '', paidLeaveSettled: false, comment: '' }) const validationTouched = reactive({ endDate: false }) const createContractForm = reactive({ contractId: '' as number | '', contractNature: 'CDI' as 'CDI' | 'CDD' | 'INTERIM', startDate: '', endDate: '' }) const createValidationTouched = reactive({ contractId: false, contractNature: false, startDate: false, endDate: false }) const contractHistory = computed(() => employee.value?.contractHistory ?? []) const showLeaveTab = computed(() => employee.value?.currentContractNature !== 'INTERIM') const employeeContractWorkLabel = computed(() => { const contract = employee.value?.contract if (!contract) return '-' if (contract.type === CONTRACT_TYPES.FORFAIT) return 'Forfait' if (contract.weeklyHours !== null && contract.weeklyHours !== undefined) return `${contract.weeklyHours} heures` return contract.name || '-' }) const formatDate = (value?: string | null) => formatNullableYmdToFr(value) const contractHistoryLabel = (item: ContractHistoryItem) => { if (item.weeklyHours !== null && item.weeklyHours !== undefined) { return `${item.weeklyHours} heures` } return item.contractName ?? '-' } const currentActiveContractPeriod = computed(() => { const today = getTodayYmd() const history = employee.value?.contractHistory ?? [] return history.find((item) => item.startDate <= today && (!item.endDate || item.endDate >= today)) ?? null }) const canCloseCurrentContract = computed(() => { const active = currentActiveContractPeriod.value if (!active) return false if (!active.endDate) return true return active.endDate > getTodayYmd() }) const canCreateContract = computed(() => { const active = currentActiveContractPeriod.value if (!active) return true return !!active.endDate }) const isContractEndDateValid = computed(() => contractForm.endDate !== '') const showContractEndDateError = computed(() => validationTouched.endDate && !isContractEndDateValid.value) const showsCreateContractEndDate = computed(() => showsContractEndDate(createContractForm.contractNature)) const requiresCreateContractEndDate = computed(() => requiresContractEndDate(createContractForm.contractNature)) const isCreateContractValid = computed(() => createContractForm.contractId !== '') const isCreateContractNatureValid = computed(() => isContractNature(createContractForm.contractNature)) const isCreateContractStartDateValid = computed(() => createContractForm.startDate !== '') const isCreateContractEndDateValid = computed(() => !requiresCreateContractEndDate.value || createContractForm.endDate !== '') const isCreateContractFormValid = computed(() => isCreateContractValid.value && isCreateContractNatureValid.value && isCreateContractStartDateValid.value && isCreateContractEndDateValid.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' const readonlyFieldClass = `${baseInputClass} border-neutral-300 bg-neutral-100 text-neutral-700` const contractEndDateFieldClass = computed(() => showContractEndDateError.value ? `${baseInputClass} border-red-500` : `${baseInputClass} border-neutral-300`) const baseSelectClass = 'mt-2 w-full rounded-md border bg-white px-3 py-2 text-md text-neutral-900' const createContractFieldClass = computed(() => createValidationTouched.contractId && !isCreateContractValid.value ? `${baseSelectClass} border-red-500` : `${baseSelectClass} border-neutral-300`) const createContractNatureFieldClass = computed(() => createValidationTouched.contractNature && !isCreateContractNatureValid.value ? `${baseSelectClass} border-red-500` : `${baseSelectClass} border-neutral-300`) const createContractStartDateFieldClass = computed(() => createValidationTouched.startDate && !isCreateContractStartDateValid.value ? `${baseInputClass} border-red-500` : `${baseInputClass} border-neutral-300`) const createContractEndDateFieldClass = computed(() => createValidationTouched.endDate && !isCreateContractEndDateValid.value ? `${baseInputClass} border-red-500` : `${baseInputClass} border-neutral-300`) const closeContractWorkedHoursLabel = computed(() => { if (contractForm.weeklyHours !== null && contractForm.weeklyHours !== undefined) return `${contractForm.weeklyHours} heures` return contractForm.contractName || '-' }) const resetContractValidation = () => { validationTouched.endDate = false } const hydrateContractFormFromCurrent = () => { const current = employee.value const active = currentActiveContractPeriod.value if (!current || !active) return contractForm.contractId = active.contractId ?? current.contract?.id ?? '' contractForm.contractName = active.contractName ?? current.contract?.name ?? '' contractForm.weeklyHours = active.weeklyHours ?? current.contract?.weeklyHours ?? null contractForm.contractNature = active.contractNature contractForm.startDate = active.startDate contractForm.endDate = getTodayYmd() contractForm.paidLeaveSettled = false contractForm.comment = '' } const openCloseContractDrawer = () => { if (!employee.value || !canCloseCurrentContract.value) return hydrateContractFormFromCurrent() resetContractValidation() isContractDrawerOpen.value = true } const setContractDrawerOpen = (open: boolean) => { isContractDrawerOpen.value = open } const resetCreateValidation = () => { createValidationTouched.contractId = false createValidationTouched.contractNature = false createValidationTouched.startDate = false createValidationTouched.endDate = false } const openCreateContractDrawer = () => { if (!employee.value || !canCreateContract.value) return createContractForm.contractId = '' createContractForm.contractNature = 'CDI' createContractForm.endDate = '' createContractForm.startDate = currentActiveContractPeriod.value?.endDate ? (shiftYmd(currentActiveContractPeriod.value.endDate, 1) ?? currentActiveContractPeriod.value.endDate) : getTodayYmd() resetCreateValidation() isCreateContractDrawerOpen.value = true } const setCreateContractDrawerOpen = (open: boolean) => { isCreateContractDrawerOpen.value = open } const loadEmployee = async () => { const idParam = Array.isArray(route.params.id) ? route.params.id[0] : route.params.id const employeeId = Number(idParam) if (!Number.isInteger(employeeId) || employeeId <= 0) { return } isLoading.value = true try { const loadedEmployee = await getEmployee(employeeId) employee.value = loadedEmployee const now = new Date() const isForfait = loadedEmployee.contract?.type === CONTRACT_TYPES.FORFAIT const leaveYear = isForfait ? now.getFullYear() : (now.getMonth() >= 5 ? now.getFullYear() + 1 : now.getFullYear()) const rttYear = now.getMonth() >= 5 ? now.getFullYear() + 1 : now.getFullYear() const from = isForfait ? `${leaveYear}-01-01` : `${leaveYear - 1}-06-01` const to = isForfait ? `${leaveYear}-12-31` : `${leaveYear}-05-31` const holidayYears = isForfait ? [leaveYear] : [leaveYear - 1, leaveYear] const [absences, summary, rtt, ...holidayResults] = await Promise.all([ listAbsences({ from, to, employeeId: loadedEmployee.id }), showLeaveTab.value ? getEmployeeLeaveSummary(loadedEmployee.id, leaveYear) : Promise.resolve(null), getEmployeeRttSummary(loadedEmployee.id, rttYear), ...holidayYears.map((y) => listPublicHolidays('metropole', y)) ]) employeeAbsences.value = absences leaveSummary.value = summary rttSummary.value = rtt publicHolidays.value = Object.assign({}, ...holidayResults) if (!showLeaveTab.value && activeTab.value === 'leave') { activeTab.value = 'contract' } } finally { isLoading.value = false } } const submitContractUpdate = async () => { if (!employee.value || isContractSubmitting.value || !currentActiveContractPeriod.value) return validationTouched.endDate = true if (!isContractEndDateValid.value) return if (contractForm.endDate < currentActiveContractPeriod.value.startDate) { toast.error({ title: 'Erreur', message: `La date de fin doit être postérieure au ${formatDate(currentActiveContractPeriod.value.startDate)}.` }) return } isContractSubmitting.value = true try { await updateEmployee(employee.value.id, { firstName: employee.value.firstName, lastName: employee.value.lastName, siteId: employee.value.site?.id ?? null, contractId: Number(contractForm.contractId), contractEndDate: contractForm.endDate || null, contractPaidLeaveSettled: contractForm.paidLeaveSettled, contractComment: contractForm.comment || null }) isContractDrawerOpen.value = false await loadEmployee() } finally { isContractSubmitting.value = false } } const submitCreateContract = async () => { if (!employee.value || isCreateContractSubmitting.value) return createValidationTouched.contractId = true createValidationTouched.contractNature = true createValidationTouched.startDate = true createValidationTouched.endDate = true if (!isCreateContractFormValid.value) return if (currentActiveContractPeriod.value?.endDate) { const minStartDate = shiftYmd(currentActiveContractPeriod.value.endDate, 1) ?? currentActiveContractPeriod.value.endDate if (createContractForm.startDate < minStartDate) { toast.error({ title: 'Erreur', message: `La date de début doit être au moins le ${formatDate(minStartDate)}.` }) return } } isCreateContractSubmitting.value = true try { await updateEmployee(employee.value.id, { firstName: employee.value.firstName, lastName: employee.value.lastName, siteId: employee.value.site?.id ?? null, contractId: Number(createContractForm.contractId), contractNature: createContractForm.contractNature, contractStartDate: createContractForm.startDate, contractEndDate: createContractForm.endDate || null }) isCreateContractDrawerOpen.value = false await loadEmployee() } finally { isCreateContractSubmitting.value = false } } const submitFractionedDays = async (days: number) => { if (!employee.value) return const year = leaveSummary.value?.year ?? undefined await updateFractionedDays(employee.value.id, days, year) await loadEmployee() } const submitRttPayment = async (month: number, minutes: number, rate: '25' | '50') => { if (!employee.value) return const year = rttSummary.value?.year ?? undefined await createRttPayment(employee.value.id, month, minutes, rate, year) await loadEmployee() } watch(showsCreateContractEndDate, (shows) => { if (!shows) { createContractForm.endDate = '' } }) onMounted(async () => { contracts.value = await listContracts() await loadEmployee() }) return { employee, isLoading, activeTab, contracts, employeeAbsences, leaveSummary, rttSummary, publicHolidays, showLeaveTab, contractHistory, employeeContractWorkLabel, contractForm, createContractForm, isContractDrawerOpen, isContractSubmitting, isCreateContractDrawerOpen, isCreateContractSubmitting, canCloseCurrentContract, canCreateContract, readonlyFieldClass, closeContractWorkedHoursLabel, contractEndDateFieldClass, showContractEndDateError, isContractEndDateValid, createContractNatureFieldClass, createContractFieldClass, createContractStartDateFieldClass, showsCreateContractEndDate, requiresCreateContractEndDate, createContractEndDateFieldClass, isCreateContractFormValid, contractNatureLabel, contractHistoryLabel, formatDate, openCloseContractDrawer, openCreateContractDrawer, setContractDrawerOpen, setCreateContractDrawerOpen, submitContractUpdate, submitCreateContract, submitFractionedDays, submitRttPayment } }