diff --git a/plugins/hr-resources/src/components/Schedule.svelte b/plugins/hr-resources/src/components/Schedule.svelte index da7681fc110..0fc1cbac130 100644 --- a/plugins/hr-resources/src/components/Schedule.svelte +++ b/plugins/hr-resources/src/components/Schedule.svelte @@ -74,6 +74,7 @@ const query = createQuery() + let ancestors: Map, Ref[]> = new Map, Ref[]>() let descendants: Map, Department[]> = new Map, Department[]>() let departments: Map, Department> = new Map, Department>() @@ -83,6 +84,9 @@ query.query(hr.class.Department, {}, (res) => { departments.clear() descendants.clear() + ancestors.clear() + + // build descendants and departments for (const doc of res) { if (doc.parent !== undefined && doc._id !== hr.ids.Head) { const current = descendants.get(doc.parent) ?? [] @@ -91,8 +95,28 @@ } departments.set(doc._id, doc) } + + // build ancestors: for each department, walk up to root + const byId = new Map, Ref>() + for (const doc of res) { + byId.set(doc._id, doc.parent ?? hr.ids.Head) + } + + for (const doc of res) { + const list: Ref[] = [] + let parent: Ref | undefined = doc._id + while (parent !== undefined && parent !== hr.ids.Head) { + parent = byId.get(parent) + if (parent !== undefined) { + list.push(parent) + } + } + ancestors.set(doc._id, list) + } + departments = departments descendants = descendants + ancestors = ancestors }) function inc (val: number): void { @@ -299,6 +323,7 @@ export let descendants: Map, Department[]> + export let ancestors: Map, Ref[]> export let departmentById: Map, Department> export let currentDate: Date = new Date() export let mode: CalendarMode @@ -232,7 +233,7 @@ } } ) - let holidays = new Map, Date[]>() + let holidaysMap = new Map, Date[]>() const holidaysQuery = createQuery() $: holidaysQuery.query( hr.class.PublicHoliday, @@ -241,31 +242,33 @@ 'date.year': currentDate.getFullYear() }, (res) => { - const group = groupBy(res, 'department') - holidays = new Map() - for (const groupKey in group) { - holidays.set( - groupKey as Ref, - group[groupKey].map((holiday) => new Date(fromTzDate(holiday.date))) - ) - } + holidaysMap = toHolidaysMap(res) } ) + function toHolidaysMap (holidays: PublicHoliday[]): Map, Date[]> { + const group = groupBy(holidays, 'department') + const result = new Map() + for (const groupKey in group) { + // ensure unique holiday dates + const dates = new Set() + for (const holiday of group[groupKey]) { + dates.add(fromTzDate(holiday.date)) + } + result.set( + groupKey as Ref, + Array.from(dates).map((date) => new Date(date)) + ) + } + return result + } + async function getHolidays (month: Date): Promise, Date[]>> { const result = await client.findAll(hr.class.PublicHoliday, { 'date.month': month.getMonth(), 'date.year': month.getFullYear() }) - const group = groupBy(result, 'department') - const rMap = new Map() - for (const groupKey in group) { - rMap.set( - groupKey, - group[groupKey].map((holiday) => new Date(fromTzDate(holiday.date))) - ) - } - return rMap + return toHolidaysMap(result) } const client = getClient() @@ -285,15 +288,34 @@ return map } let staffDepartmentMap = new Map() - $: getDepartmentsForEmployee(departmentStaff).then((res) => { + $: void getDepartmentsForEmployee(departmentStaff).then((res) => { staffDepartmentMap = res }) + + function getDepartmentHolidays (department: Ref): Date[] { + const parents = ancestors.get(department) ?? [] + + const result = [] + + // get own holidays + const holidays = holidaysMap.get(department) ?? [] + result.push(...holidays) + + // get ancestor holidays + for (const parent of parents) { + const parentHolidays = holidaysMap.get(parent) ?? [] + result.push(...parentHolidays) + } + return result + } {#if staffDepartmentMap.size > 0} {#if mode === CalendarMode.Year} - + {:else if mode === CalendarMode.Month} + {@const holidays = getDepartmentHolidays(department)} + {#if display === 'chart'} + const client = getClient() - let existingHoliday: PublicHoliday | undefined = undefined const dispatch = createEventDispatcher() - async function findHoliday () { - existingHoliday = await client.findOne(hr.class.PublicHoliday, { date: timeToTzDate(date) }) + let description: string + let title: string + let existingHoliday: PublicHoliday | undefined = undefined + + async function getAncestors (department: Ref): Promise[]> { + const departments = await client.findAll(hr.class.Department, {}) + const byId = new Map, Ref>() + for (const doc of departments) { + byId.set(doc._id, doc.parent ?? hr.ids.Head) + } + + const ancestors: Ref[] = [] + let parent: Ref | undefined = department + while (parent !== undefined && parent !== hr.ids.Head) { + parent = byId.get(parent) + if (parent !== undefined) { + ancestors.push(parent) + } + } + return ancestors + } + + async function findHoliday (): Promise { + const holidays = await client.findAll(hr.class.PublicHoliday, { date: timeToTzDate(date) }) + + // look into current department first + let holiday = holidays.find((p) => p.department === department) + if (holiday === undefined) { + // if not found look at parent departments + const ancestors = await getAncestors(department) + holiday = holidays.find((p) => ancestors.includes(p.department)) + } + + existingHoliday = holiday if (existingHoliday !== undefined) { title = existingHoliday.title description = existingHoliday.description @@ -38,7 +68,7 @@ } } - async function saveHoliday () { + async function saveHoliday (): Promise { if (existingHoliday !== undefined) { await client.updateDoc(hr.class.PublicHoliday, core.space.Workspace, existingHoliday._id, { title, @@ -54,10 +84,16 @@ await client.createDoc(hr.class.PublicHoliday, core.space.Workspace, holiday) } } - findHoliday() - function deleteHoliday () { - existingHoliday && client.remove(existingHoliday) + let loading = true + void findHoliday().then(() => { + loading = false + }) + + function deleteHoliday (): void { + if (existingHoliday !== undefined) { + void client.remove(existingHoliday) + } dispatch('close') } @@ -67,9 +103,9 @@ on:close okLabel={existingHoliday ? presentation.string.Save : presentation.string.Ok} okAction={() => { - saveHoliday() + void saveHoliday() }} - canSave={true} + canSave={!loading} on:changeContent >
diff --git a/plugins/hr-resources/src/components/schedule/MonthTableView.svelte b/plugins/hr-resources/src/components/schedule/MonthTableView.svelte index 40983c84221..2e109976cf3 100644 --- a/plugins/hr-resources/src/components/schedule/MonthTableView.svelte +++ b/plugins/hr-resources/src/components/schedule/MonthTableView.svelte @@ -17,10 +17,9 @@ import { Doc, Ref } from '@hcengineering/core' import type { Request, RequestType, Staff } from '@hcengineering/hr' import { Department } from '@hcengineering/hr' - import { getEmbeddedLabel } from '@hcengineering/platform' - import { Button, DropdownIntlItem, Label, Loading, showPopup, tableToCSV } from '@hcengineering/ui' + import { Label, Loading } from '@hcengineering/ui' import { BuildModelKey, Viewlet, ViewletPreference } from '@hcengineering/view' - import { TableBrowser, ViewletSelector, ViewletSettingButton } from '@hcengineering/view-resources' + import { TableBrowser } from '@hcengineering/view-resources' import hr from '../../plugin' import { EmployeeReports, @@ -47,7 +46,7 @@ export let employeeRequests: Map, Request[]> export let timeReports: Map, EmployeeReports> - export let holidays: Map, Date[]> = new Map, Date[]>() + export let holidaysMap: Map, Date[]> = new Map, Date[]>() export let getHolidays: (month: Date) => Promise, Date[]>> $: month = getStartDate(currentDate.getFullYear(), currentDate.getMonth()) // getMonth(currentDate, currentDate.getMonth()) $: wDays = weekDays(month.getFullYear(), month.getMonth()) @@ -60,7 +59,7 @@ types, month.getFullYear(), month.getMonth(), - getHolidayDatesForEmployee(staffDepartmentMap, staff._id, holidays) + getHolidayDatesForEmployee(staffDepartmentMap, staff._id, holidaysMap) ) return ds.join(' ') } diff --git a/plugins/hr-resources/src/components/schedule/MonthView.svelte b/plugins/hr-resources/src/components/schedule/MonthView.svelte index 71ea3b27538..315d79c048f 100644 --- a/plugins/hr-resources/src/components/schedule/MonthView.svelte +++ b/plugins/hr-resources/src/components/schedule/MonthView.svelte @@ -76,7 +76,9 @@ export let editableList: Ref[] export let staffDepartmentMap: Map, Department[]> - export let holidays: Map, Date[]> + + export let holidays: Date[] + export let holidaysMap: Map, Date[]> const todayDate = new Date() @@ -191,7 +193,7 @@ if (requests.length === 0) return const weekend = isWeekend(day) const holiday = - holidays?.size > 0 && isHoliday(getHolidayDatesForEmployee(staffDepartmentMap, staff._id, holidays), day) + holidaysMap?.size > 0 && isHoliday(getHolidayDatesForEmployee(staffDepartmentMap, staff._id, holidaysMap), day) if (day && (weekend || holiday) && requests.some((req) => noWeekendHolidayType.includes(req.type))) { return } @@ -307,6 +309,7 @@ {#each values as value} {@const day = getDay(startDate, value)} {@const today = areDatesEqual(todayDate, day)} + {@const holiday = isHoliday(holidays, day)}
{day.getDate()}
@@ -361,7 +365,7 @@ {@const today = areDatesEqual(todayDate, day)} {@const weekend = isWeekend(day)} {@const holiday = isHoliday( - getHolidayDatesForEmployee(staffDepartmentMap, employee._id, holidays), + getHolidayDatesForEmployee(staffDepartmentMap, employee._id, holidaysMap), day )} {@const requests = getRequests(employeeRequests, day, day, employee._id)} @@ -474,6 +478,12 @@ background-color: #3871e0; border-radius: 0.375rem; } + + &.timeline-day-header__day--holiday { + color: white; + background-color: #d32f2f; + border-radius: 0.375rem; + } } .timeline-day-header__weekday { diff --git a/plugins/hr-resources/src/components/schedule/YearView.svelte b/plugins/hr-resources/src/components/schedule/YearView.svelte index fa319fa993e..cf9eff2e39a 100644 --- a/plugins/hr-resources/src/components/schedule/YearView.svelte +++ b/plugins/hr-resources/src/components/schedule/YearView.svelte @@ -38,7 +38,7 @@ export let employeeRequests: Map, Request[]> - export let holidays: Map, Date[]> + export let holidaysMap: Map, Date[]> export let staffDepartmentMap: Map, Department[]> function getTooltip (requests: Request[]): LabelAndProps | undefined { @@ -117,7 +117,7 @@ startDate, endDate, types, - getHolidayDatesForEmployee(staffDepartmentMap, employee._id, holidays) + getHolidayDatesForEmployee(staffDepartmentMap, employee._id, holidaysMap) )}
@@ -139,7 +139,7 @@ startDate, endDate, types, - [...holidays.values()].flat() + [...holidaysMap.values()].flat() )} diff --git a/plugins/hr-resources/src/utils.ts b/plugins/hr-resources/src/utils.ts index 2c27f723aa9..6a3ed41953e 100644 --- a/plugins/hr-resources/src/utils.ts +++ b/plugins/hr-resources/src/utils.ts @@ -218,14 +218,16 @@ export function getHolidayDatesForEmployee ( const deps = departmentMap.get(employee) if (deps === undefined) return [] if (holidays.size === 0) return [] - const dates = [] + const dates = new Map() for (const dep of deps) { const depDates = holidays?.get(dep._id) if (depDates !== undefined) { - dates.push(...depDates) + for (const date of depDates) { + dates.set(`${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`, date) + } } } - return dates + return [...dates.values()] } export interface EmployeeReports {