392 lines
14 KiB
TypeScript
392 lines
14 KiB
TypeScript
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, formatWorkDaysHoursSummary, isContractNature, requiresContractEndDate, requiresWorkDaysHours, showsContractEndDate } from '~/utils/contract'
|
|
|
|
type SuspensionForm = {
|
|
id: number | null
|
|
startDate: string
|
|
endDate: string
|
|
comment: string
|
|
}
|
|
|
|
export const useEmployeeContract = (employee: Ref<Employee | null>, reloadEmployee: () => Promise<void>) => {
|
|
const toast = useToast()
|
|
const contracts = ref<Contract[]>([])
|
|
const isContractDrawerOpen = ref(false)
|
|
const isContractSubmitting = ref(false)
|
|
const isCreateContractDrawerOpen = ref(false)
|
|
const isCreateContractSubmitting = ref(false)
|
|
const suspensionForms = ref<SuspensionForm[]>([])
|
|
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: '',
|
|
workDaysHours: null as Record<number, number> | null
|
|
})
|
|
|
|
const validationTouched = reactive({
|
|
endDate: false
|
|
})
|
|
|
|
const createContractForm = reactive({
|
|
contractId: '' as number | '',
|
|
contractNature: 'CDI' as 'CDI' | 'CDD' | 'INTERIM',
|
|
startDate: '',
|
|
endDate: '',
|
|
isDriver: false,
|
|
workDaysHours: null as Record<number, number> | null
|
|
})
|
|
|
|
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) => {
|
|
const base = item.weeklyHours !== null && item.weeklyHours !== undefined
|
|
? `${item.weeklyHours} heures`
|
|
: (item.contractName ?? '-')
|
|
const scheduleSummary = formatWorkDaysHoursSummary(item.workDaysHours)
|
|
return scheduleSummary ? `${base} (${scheduleSummary})` : base
|
|
}
|
|
|
|
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<number | null>(() => {
|
|
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 selectedCreateContract = computed<Contract | null>(() =>
|
|
contracts.value.find((c) => c.id === Number(createContractForm.contractId)) ?? null
|
|
)
|
|
const requiresCreateWorkDaysHours = computed(() =>
|
|
requiresWorkDaysHours(selectedCreateContract.value, createContractForm.contractNature)
|
|
)
|
|
const createScheduleTotalMinutes = computed(() => {
|
|
const raw = createContractForm.workDaysHours ?? {}
|
|
return Object.values(raw).reduce((s, n) => s + (Number(n) || 0), 0)
|
|
})
|
|
const isCreateScheduleValid = computed(() => {
|
|
if (!requiresCreateWorkDaysHours.value) return true
|
|
const expected = (selectedCreateContract.value?.weeklyHours ?? 0) * 60
|
|
return expected > 0 && createScheduleTotalMinutes.value === expected
|
|
})
|
|
const isCreateContractFormValid = computed(() =>
|
|
isCreateContractValid.value &&
|
|
isCreateContractNatureValid.value &&
|
|
isCreateContractStartDateValid.value &&
|
|
isCreateContractEndDateValid.value &&
|
|
isCreateScheduleValid.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 = ''
|
|
contractForm.workDaysHours = period.workDaysHours ?? null
|
|
}
|
|
|
|
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.workDaysHours = null
|
|
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,
|
|
workDaysHoursInput: requiresCreateWorkDaysHours.value ? createContractForm.workDaysHours : null
|
|
})
|
|
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 = ''
|
|
}
|
|
})
|
|
|
|
watch(requiresCreateWorkDaysHours, (required) => {
|
|
if (!required) {
|
|
createContractForm.workDaysHours = null
|
|
}
|
|
})
|
|
|
|
return {
|
|
contracts,
|
|
contractHistory,
|
|
contractForm,
|
|
createContractForm,
|
|
isContractDrawerOpen,
|
|
isContractSubmitting,
|
|
isCreateContractDrawerOpen,
|
|
isCreateContractSubmitting,
|
|
canCloseCurrentContract,
|
|
canCreateContract,
|
|
readonlyFieldClass,
|
|
closeContractWorkedHoursLabel,
|
|
contractEndDateFieldClass,
|
|
showContractEndDateError,
|
|
isContractEndDateValid,
|
|
createContractNatureFieldClass,
|
|
createContractFieldClass,
|
|
createContractStartDateFieldClass,
|
|
showsCreateContractEndDate,
|
|
requiresCreateContractEndDate,
|
|
createContractEndDateFieldClass,
|
|
isCreateContractFormValid,
|
|
requiresCreateWorkDaysHours,
|
|
selectedCreateContract,
|
|
contractNatureLabel,
|
|
contractHistoryLabel,
|
|
formatDate,
|
|
openCloseContractDrawer,
|
|
openCreateContractDrawer,
|
|
setContractDrawerOpen,
|
|
setCreateContractDrawerOpen,
|
|
submitContractUpdate,
|
|
submitCreateContract,
|
|
suspensionForms,
|
|
isSuspensionSubmitting,
|
|
submitSuspension,
|
|
addSuspensionForm,
|
|
currentActiveContractPeriodId,
|
|
loadContracts
|
|
}
|
|
}
|