| Numéro du ticket | Titre du ticket | |------------------|-----------------| | #322 | Page horaire | ## Description de la PR [#322] Page horaire ## Modification du .env ## Check list - [ ] Pas de régression - [ ] TU/TI/TF rédigée - [ ] TU/TI/TF OK - [ ] CHANGELOG modifié Reviewed-on: #4 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #4.
This commit is contained in:
@@ -12,7 +12,7 @@ export const listAbsenceTypes = async () => {
|
||||
}
|
||||
|
||||
export const createAbsenceType = async (
|
||||
payload: Pick<AbsenceType, 'code' | 'label' | 'color'>
|
||||
payload: Pick<AbsenceType, 'code' | 'label' | 'color' | 'countAsWorkedHours'>
|
||||
) => {
|
||||
const api = useApi()
|
||||
return api.post<AbsenceType>('/absence_types', payload, {
|
||||
@@ -23,7 +23,7 @@ export const createAbsenceType = async (
|
||||
|
||||
export const updateAbsenceType = async (
|
||||
id: number,
|
||||
payload: Pick<AbsenceType, 'code' | 'label' | 'color'>
|
||||
payload: Pick<AbsenceType, 'code' | 'label' | 'color' | 'countAsWorkedHours'>
|
||||
) => {
|
||||
const api = useApi()
|
||||
return api.patch<AbsenceType>(`/absence_types/${id}`, payload, {
|
||||
|
||||
@@ -8,6 +8,7 @@ export const getCurrentUser = () => {
|
||||
export const login = (username: string, password: string) => {
|
||||
const api = useApi()
|
||||
return api.post('/login_check', { username, password }, {
|
||||
toastOn401: true,
|
||||
toastErrorKey: 'errors.auth.login'
|
||||
})
|
||||
}
|
||||
|
||||
13
frontend/services/contracts.ts
Normal file
13
frontend/services/contracts.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { Contract } from './dto/contract'
|
||||
import { extractItems } from '~/utils/api'
|
||||
|
||||
export const listContracts = async () => {
|
||||
const api = useApi()
|
||||
const data = await api.get<Contract[] | { 'hydra:member'?: Contract[] }>(
|
||||
'/contracts',
|
||||
{},
|
||||
{ toast: false }
|
||||
)
|
||||
|
||||
return extractItems<Contract>(data)
|
||||
}
|
||||
@@ -3,4 +3,5 @@ export type AbsenceType = {
|
||||
code: string
|
||||
label: string
|
||||
color: string
|
||||
countAsWorkedHours: boolean
|
||||
}
|
||||
|
||||
25
frontend/services/dto/contract.ts
Normal file
25
frontend/services/dto/contract.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export const TRACKING_MODES = {
|
||||
TIME: 'TIME',
|
||||
PRESENCE: 'PRESENCE'
|
||||
} as const
|
||||
|
||||
export type TrackingMode = (typeof TRACKING_MODES)[keyof typeof TRACKING_MODES]
|
||||
|
||||
export const CONTRACT_TYPES = {
|
||||
FORFAIT: 'FORFAIT',
|
||||
H35: '35H',
|
||||
H39: '39H',
|
||||
INTERIM: 'INTERIM',
|
||||
CUSTOM: 'CUSTOM'
|
||||
} as const
|
||||
|
||||
export type ContractType = (typeof CONTRACT_TYPES)[keyof typeof CONTRACT_TYPES]
|
||||
|
||||
export type Contract = {
|
||||
id: number
|
||||
name: string
|
||||
trackingMode: TrackingMode
|
||||
type: ContractType
|
||||
weeklyHours?: number | null
|
||||
isActive?: boolean
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { Site } from './site'
|
||||
import type { Contract } from './contract'
|
||||
|
||||
export type Employee = {
|
||||
id: number
|
||||
firstName: string
|
||||
lastName: string
|
||||
site: Site
|
||||
contract?: Contract | null
|
||||
displayOrder?: number
|
||||
}
|
||||
|
||||
81
frontend/services/dto/work-hour.ts
Normal file
81
frontend/services/dto/work-hour.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import type { Employee } from './employee'
|
||||
import type { ContractType, TrackingMode } from './contract'
|
||||
|
||||
export type WorkHour = {
|
||||
id: number
|
||||
employee: Employee
|
||||
workDate: string
|
||||
morningFrom?: string | null
|
||||
morningTo?: string | null
|
||||
afternoonFrom?: string | null
|
||||
afternoonTo?: string | null
|
||||
eveningFrom?: string | null
|
||||
eveningTo?: string | null
|
||||
isPresentMorning?: boolean
|
||||
isPresentAfternoon?: boolean
|
||||
isValid?: boolean
|
||||
}
|
||||
|
||||
export type WorkHourEntryPayload = {
|
||||
employeeId: number
|
||||
morningFrom?: string | null
|
||||
morningTo?: string | null
|
||||
afternoonFrom?: string | null
|
||||
afternoonTo?: string | null
|
||||
eveningFrom?: string | null
|
||||
eveningTo?: string | null
|
||||
isPresentMorning?: boolean
|
||||
isPresentAfternoon?: boolean
|
||||
}
|
||||
|
||||
export type WeeklyWorkHourDailySummary = {
|
||||
date: string
|
||||
dayMinutes: number
|
||||
nightMinutes: number
|
||||
totalMinutes: number
|
||||
present?: number | null
|
||||
hasAbsence?: boolean
|
||||
absenceLabel?: string | null
|
||||
absenceColor?: string | null
|
||||
}
|
||||
|
||||
export type WeeklyWorkHourRowSummary = {
|
||||
employeeId: number
|
||||
firstName: string
|
||||
lastName: string
|
||||
siteName?: string | null
|
||||
contractName?: string | null
|
||||
contractType?: ContractType | null
|
||||
trackingMode?: TrackingMode | null
|
||||
daily: WeeklyWorkHourDailySummary[]
|
||||
weeklyDayMinutes: number
|
||||
weeklyNightMinutes: number
|
||||
weeklyTotalMinutes: number
|
||||
weeklyPresenceCount?: number
|
||||
weeklyOvertimeTotalMinutes?: number
|
||||
weeklyOvertime25Minutes?: number
|
||||
weeklyOvertime50Minutes?: number
|
||||
weeklyRecoveryMinutes?: number
|
||||
}
|
||||
|
||||
export type WeeklyWorkHourSummary = {
|
||||
weekStart: string
|
||||
weekEnd: string
|
||||
days: string[]
|
||||
rows: WeeklyWorkHourRowSummary[]
|
||||
}
|
||||
|
||||
export type WorkHourDayContextRow = {
|
||||
employeeId: number
|
||||
absenceLabel?: string | null
|
||||
absenceHalf?: 'AM' | 'PM' | null
|
||||
absentMorning: boolean
|
||||
absentAfternoon: boolean
|
||||
creditedMinutes: number
|
||||
creditedPresenceUnits: number
|
||||
}
|
||||
|
||||
export type WorkHourDayContext = {
|
||||
workDate: string
|
||||
rows: WorkHourDayContextRow[]
|
||||
}
|
||||
@@ -11,16 +11,28 @@ export const listEmployees = async () => {
|
||||
return extractItems<Employee>(data)
|
||||
}
|
||||
|
||||
export const listScopedEmployees = async () => {
|
||||
const api = useApi()
|
||||
const data = await api.get<Employee[] | { 'hydra:member'?: Employee[] }>(
|
||||
'/employees/scoped',
|
||||
{},
|
||||
{ toast: false }
|
||||
)
|
||||
return extractItems<Employee>(data)
|
||||
}
|
||||
|
||||
export const createEmployee = async (payload: {
|
||||
firstName: string
|
||||
lastName: string
|
||||
siteId?: number | null
|
||||
contractId: number
|
||||
}) => {
|
||||
const api = useApi()
|
||||
return api.post<Employee>('/employees', {
|
||||
firstName: payload.firstName,
|
||||
lastName: payload.lastName,
|
||||
site: payload.siteId ? `/api/sites/${payload.siteId}` : null
|
||||
site: payload.siteId ? `/api/sites/${payload.siteId}` : null,
|
||||
contract: `/api/contracts/${payload.contractId}`
|
||||
}, {
|
||||
toastSuccessKey: 'success.employee.create',
|
||||
toastErrorKey: 'errors.employee.create'
|
||||
@@ -33,6 +45,7 @@ export const updateEmployee = async (
|
||||
firstName: string
|
||||
lastName: string
|
||||
siteId?: number | null
|
||||
contractId: number
|
||||
displayOrder?: number
|
||||
}
|
||||
) => {
|
||||
@@ -41,6 +54,7 @@ export const updateEmployee = async (
|
||||
firstName: payload.firstName,
|
||||
lastName: payload.lastName,
|
||||
site: payload.siteId ? `/api/sites/${payload.siteId}` : null,
|
||||
contract: `/api/contracts/${payload.contractId}`,
|
||||
displayOrder: payload.displayOrder
|
||||
}, {
|
||||
toastSuccessKey: 'success.employee.update',
|
||||
|
||||
76
frontend/services/work-hours.ts
Normal file
76
frontend/services/work-hours.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import type {
|
||||
WorkHourDayContext,
|
||||
WorkHour,
|
||||
WorkHourEntryPayload,
|
||||
WeeklyWorkHourSummary
|
||||
} from './dto/work-hour'
|
||||
import { extractItems } from '~/utils/api'
|
||||
|
||||
export const listWorkHoursByDate = async (workDate: string) => {
|
||||
const api = useApi()
|
||||
const data = await api.get<WorkHour[] | { 'hydra:member'?: WorkHour[] }>(
|
||||
'/work_hours',
|
||||
{
|
||||
'workDate[after]': workDate,
|
||||
'workDate[before]': workDate
|
||||
},
|
||||
{ toast: false }
|
||||
)
|
||||
|
||||
return extractItems<WorkHour>(data)
|
||||
}
|
||||
|
||||
export const bulkUpsertWorkHours = async (payload: {
|
||||
workDate: string
|
||||
entries: WorkHourEntryPayload[]
|
||||
}) => {
|
||||
const api = useApi()
|
||||
return api.post<{
|
||||
processed: number
|
||||
created: number
|
||||
updated: number
|
||||
deleted: number
|
||||
}>(
|
||||
'/work-hours/bulk-upsert',
|
||||
payload,
|
||||
{
|
||||
toastSuccessMessage: 'Horaires enregistrés.',
|
||||
toastErrorMessage: "Impossible d'enregistrer les horaires."
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export const updateWorkHourValidation = async (
|
||||
id: number,
|
||||
isValid: boolean,
|
||||
options?: { toast?: boolean }
|
||||
) => {
|
||||
const api = useApi()
|
||||
return api.patch<WorkHour>(
|
||||
`/work_hours/${id}`,
|
||||
{ isValid },
|
||||
{
|
||||
toast: options?.toast ?? true,
|
||||
toastSuccessMessage: isValid ? 'Ligne validée.' : 'Validation retirée.',
|
||||
toastErrorMessage: 'Impossible de mettre à jour la validation.'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
export const getWeeklyWorkHourSummary = async (weekStart: string) => {
|
||||
const api = useApi()
|
||||
return api.get<WeeklyWorkHourSummary>(
|
||||
'/work-hours/weekly-summary',
|
||||
{ weekStart },
|
||||
{ toast: false }
|
||||
)
|
||||
}
|
||||
|
||||
export const getWorkHourDayContext = async (workDate: string) => {
|
||||
const api = useApi()
|
||||
return api.get<WorkHourDayContext>(
|
||||
'/work-hours/day-context',
|
||||
{ workDate },
|
||||
{ toast: false }
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user