import type { Ref } from 'vue' import type { Contract } from '~/services/dto/contract' import type { ContractHistoryItem, Employee } from '~/services/dto/employee' import { listContracts } from '~/services/contracts' import { updateEmployee } from '~/services/employees' import { createSuspension, updateSuspension } from '~/services/contractSuspensions' import { formatNullableYmdToFr, getTodayYmd, shiftYmd } from '~/utils/date' import { contractNatureLabel, isContractNature, requiresContractEndDate, showsContractEndDate } from '~/utils/contract' type SuspensionForm = { id: number | null startDate: string endDate: string comment: string } export const useEmployeeContract = (employee: Ref, reloadEmployee: () => Promise) => { const toast = useToast() const contracts = ref([]) const isContractDrawerOpen = ref(false) const isContractSubmitting = ref(false) const isCreateContractDrawerOpen = ref(false) const isCreateContractSubmitting = ref(false) const suspensionForms = ref([]) const isSuspensionSubmitting = 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: '', isDriver: false }) const createValidationTouched = reactive({ contractId: false, contractNature: false, startDate: false, endDate: false }) const contractHistory = computed(() => employee.value?.contractHistory ?? []) 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 lastEndedContractPeriod = computed(() => { if (currentActiveContractPeriod.value) return null const today = getTodayYmd() const history = employee.value?.contractHistory ?? [] const ended = history.filter((item) => item.endDate && item.endDate < today) if (ended.length === 0) return null return ended.reduce((latest, item) => (item.endDate! > latest.endDate! ? item : latest)) }) const editableContractPeriod = computed(() => currentActiveContractPeriod.value ?? lastEndedContractPeriod.value) const currentActiveContractPeriodId = computed(() => { const period = currentActiveContractPeriod.value return period?.periodId ?? null }) const canCloseCurrentContract = computed(() => { const active = currentActiveContractPeriod.value if (active) { if (!active.endDate) return true return active.endDate > getTodayYmd() } return !!lastEndedContractPeriod.value }) const canCreateContract = computed(() => { const active = editableContractPeriod.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 hydrateSuspensionForms = () => { const current = employee.value?.currentSuspensions ?? [] suspensionForms.value = current.map(s => ({ id: s.id, startDate: s.startDate, endDate: s.endDate ?? '', comment: s.comment ?? '' })) } const hydrateContractFormFromCurrent = () => { const current = employee.value const period = editableContractPeriod.value if (!current || !period) return contractForm.contractId = period.contractId ?? current.contract?.id ?? '' contractForm.contractName = period.contractName ?? current.contract?.name ?? '' contractForm.weeklyHours = period.weeklyHours ?? current.contract?.weeklyHours ?? null contractForm.contractNature = period.contractNature contractForm.startDate = period.startDate contractForm.endDate = period.endDate ?? getTodayYmd() contractForm.paidLeaveSettled = false contractForm.comment = '' } const openCloseContractDrawer = () => { if (!employee.value || !canCloseCurrentContract.value) return hydrateContractFormFromCurrent() resetContractValidation() hydrateSuspensionForms() 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.isDriver = false createContractForm.startDate = editableContractPeriod.value?.endDate ? (shiftYmd(editableContractPeriod.value.endDate, 1) ?? editableContractPeriod.value.endDate) : getTodayYmd() resetCreateValidation() isCreateContractDrawerOpen.value = true } const setCreateContractDrawerOpen = (open: boolean) => { isCreateContractDrawerOpen.value = open } const submitContractUpdate = async () => { const period = editableContractPeriod.value if (!employee.value || isContractSubmitting.value || !period) return validationTouched.endDate = true if (!isContractEndDateValid.value) return if (contractForm.endDate < period.startDate) { toast.error({ title: 'Erreur', message: `La date de fin doit être postérieure au ${formatDate(period.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 reloadEmployee() } 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 (editableContractPeriod.value?.endDate) { const minStartDate = shiftYmd(editableContractPeriod.value.endDate, 1) ?? editableContractPeriod.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, isDriverInput: createContractForm.isDriver }) isCreateContractDrawerOpen.value = false await reloadEmployee() } finally { isCreateContractSubmitting.value = false } } const submitSuspension = async (index: number) => { const form = suspensionForms.value[index] if (!form || !form.startDate) return const periodId = currentActiveContractPeriodId.value if (!periodId) return isSuspensionSubmitting.value = true try { if (form.id) { await updateSuspension(form.id, { startDate: form.startDate, endDate: form.endDate || null, comment: form.comment || null }) } else { await createSuspension({ contractPeriodId: periodId, startDate: form.startDate, endDate: form.endDate || null, comment: form.comment || null }) } await reloadEmployee() hydrateSuspensionForms() } finally { isSuspensionSubmitting.value = false } } const addSuspensionForm = () => { suspensionForms.value.push({ id: null, startDate: '', endDate: '', comment: '' }) } const loadContracts = async () => { contracts.value = await listContracts() } watch(showsCreateContractEndDate, (shows) => { if (!shows) { createContractForm.endDate = '' } }) return { contracts, contractHistory, 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, suspensionForms, isSuspensionSubmitting, submitSuspension, addSuspensionForm, currentActiveContractPeriodId, loadContracts } }