feat : ajout d'un champ commentaire sur les contrats + correction de plusieurs bugs

This commit is contained in:
2026-03-06 16:58:29 +01:00
parent 4cf2608cdd
commit e794ad2514
28 changed files with 235 additions and 59 deletions

View File

@@ -86,6 +86,19 @@
<p v-if="showContractEndDateError" class="mt-1 text-sm text-red-600">La date de fin est obligatoire.</p>
</div>
<div>
<label class="text-md font-semibold text-neutral-700" for="contract-comment">
Commentaire
</label>
<textarea
id="contract-comment"
v-model="contractForm.comment"
rows="3"
class="mt-2 w-full rounded-md border border-neutral-300 px-3 py-2 text-base text-neutral-900 focus:border-primary-500 focus:outline-none focus:ring-2 focus:ring-secondary-500/20"
placeholder="Motif de la clôture..."
/>
</div>
<div class="rounded-md border border-neutral-200 bg-neutral-50 p-3">
<label class="inline-flex items-center gap-2 text-md font-semibold text-neutral-700" for="contract-paid-leave-settled">
<input
@@ -191,6 +204,7 @@ type ContractForm = {
startDate: string
endDate: string
paidLeaveSettled: boolean
comment: string
}
type CreateContractForm = {

View File

@@ -41,9 +41,9 @@
>
<div
class="h-6 w-6"
:class="getDayClass(day.leave)"
:style="getDayStyle(day.leave)"
:title="getDayTitle(day.leave)"
:class="getDayClass(day)"
:style="getDayStyle(day)"
:title="getDayTitle(day)"
>
{{ getDayText(day) }}
</div>
@@ -65,13 +65,13 @@ type DayLeaveState = {
am: boolean
pm: boolean
labels: string[]
hasCongeTypeC: boolean
hasOtherTypes: boolean
colors: string[]
}
const props = defineProps<{
absences: Absence[]
summary: EmployeeLeaveSummary | null
publicHolidays: Record<string, string>
}>()
const monthLabels = [
@@ -124,8 +124,7 @@ const dayLeaveMap = computed(() => {
am: false,
pm: false,
labels: [] as string[],
hasCongeTypeC: false,
hasOtherTypes: false
colors: [] as string[]
}
const isStart = ymd === startYmd
@@ -150,18 +149,21 @@ const dayLeaveMap = computed(() => {
}
const typeLabel = absence.type?.label ?? absence.type?.code ?? 'Absence'
const typeCode = (absence.type?.code ?? '').toUpperCase()
const typeColor = absence.type?.color ?? '#222783'
const halfSuffix = am && !pm ? ' (Matin)' : (!am && pm ? ' (Apres-midi)' : '')
const hoverLabel = `${typeLabel}${halfSuffix}`
const colors = existing.colors.includes(typeColor)
? existing.colors
: [...existing.colors, typeColor]
map.set(ymd, {
am: existing.am || am,
pm: existing.pm || pm,
labels: existing.labels.includes(hoverLabel)
? existing.labels
: [...existing.labels, hoverLabel],
hasCongeTypeC: existing.hasCongeTypeC || typeCode === 'C',
hasOtherTypes: existing.hasOtherTypes || typeCode !== 'C'
colors
})
}
}
@@ -180,7 +182,7 @@ const months = computed(() => {
const daysInMonth = new Date(monthYear, monthIndex + 1, 0).getDate()
const mondayBasedFirstDay = (first.getDay() + 6) % 7
const cells: Array<{ ymd: string; label: string; leave: DayLeaveState | null } | null> = []
const cells: Array<{ ymd: string; label: string; leave: DayLeaveState | null; isHoliday: boolean } | null> = []
for (let i = 0; i < mondayBasedFirstDay; i += 1) {
cells.push(null)
@@ -191,7 +193,8 @@ const months = computed(() => {
cells.push({
ymd,
label: String(day),
leave: dayLeaveMap.value.get(ymd) ?? null
leave: dayLeaveMap.value.get(ymd) ?? null,
isHoliday: ymd in props.publicHolidays
})
}
@@ -206,37 +209,37 @@ const months = computed(() => {
})
})
const getDayClass = (leave: DayLeaveState | null) => {
if (!leave) return 'text-primary-500'
if (leave.am && leave.pm) {
return leave.hasOtherTypes
? 'bg-red-600 text-white rounded font-semibold'
: 'bg-primary-500 text-white rounded font-semibold'
const getDayClass = (day: { leave: DayLeaveState | null; isHoliday: boolean }) => {
if (day.leave) {
return 'rounded font-semibold text-white'
}
return 'rounded text-primary-700 font-semibold text-white'
if (day.isHoliday) return 'text-primary-500 rounded font-semibold'
return 'text-primary-500'
}
const getDayStyle = (leave: DayLeaveState | null) => {
if (!leave || (leave.am && leave.pm)) return undefined
const color = leave.hasOtherTypes ? '#dc2626' : '#222783'
const backgroundImage = leave.am
? `linear-gradient(135deg, ${color} 0 50%, transparent 50% 100%)`
: `linear-gradient(135deg, transparent 0 50%, ${color} 50% 100%)`
return {
backgroundImage,
backgroundColor: 'transparent'
const getDayStyle = (day: { leave: DayLeaveState | null; isHoliday: boolean }) => {
if (day.leave) {
const color = day.leave.colors[0] ?? '#222783'
if (day.leave.am && day.leave.pm) {
return { backgroundColor: color }
}
const backgroundImage = day.leave.am
? `linear-gradient(135deg, ${color} 0 50%, transparent 50% 100%)`
: `linear-gradient(135deg, transparent 0 50%, ${color} 50% 100%)`
return { backgroundImage, backgroundColor: 'transparent' }
}
if (day.isHoliday) return { backgroundColor: 'rgb(179, 229, 252)' }
return undefined
}
const getDayText = (day: { label: string; leave: DayLeaveState | null }) => {
return day.label
}
const getDayTitle = (leave: DayLeaveState | null) => {
if (!leave || leave.labels.length === 0) return ''
return leave.labels.join(' / ')
const getDayTitle = (day: { leave: DayLeaveState | null; isHoliday: boolean; ymd: string }) => {
if (day.leave && day.leave.labels.length > 0) return day.leave.labels.join(' / ')
if (day.isHoliday) return props.publicHolidays[day.ymd] ?? 'Jour férié'
return ''
}
const formatCount = (value: number | null | undefined) => {

View File

@@ -63,6 +63,9 @@
<Icon name="mdi:check"/>
</span>
</p>
<p v-if="isAdmin && getRowUpdatedAt(employee.id)" class="text-neutral-400 text-xs truncate">
Modifié le {{ getRowUpdatedAt(employee.id) }}
</p>
</div>
<div class="pl-2 min-w-0 self-stretch flex flex-col gap-1 justify-between py-0.5">
<p
@@ -216,6 +219,7 @@ const props = defineProps<{
getRowMetrics: (employeeId: number) => { dayMinutes: number; nightMinutes: number; totalMinutes: number }
getRowAbsenceLabel: (employeeId: number) => string
getRowAbsenceStyle: (employeeId: number) => { backgroundColor: string } | undefined
getRowUpdatedAt: (employeeId: number) => string
getPresenceDayValue: (employeeId: number) => string
onAbsenceClick: (employeeId: number) => void
formatMinutes: (minutes: number) => string

View File

@@ -10,4 +10,5 @@ export type HourRow = {
isPresentAfternoon: boolean
isSiteValid: boolean
isValid: boolean
updatedAt: string | null
}

View File

@@ -167,8 +167,9 @@ const closeMenu = () => {
const commitInput = () => {
const normalized = normalizeTypedTime(inputValue.value)
if (normalized === null) {
inputValue.value = props.modelValue
if (normalized === null || (normalized !== '' && !timeSlots.value.includes(normalized))) {
emit('update:modelValue', '')
inputValue.value = ''
closeMenu()
return
}