mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-29 04:55:03 +02:00
fix: better public holidays display (#10901)
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
@@ -74,6 +74,7 @@
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
let ancestors: Map<Ref<Department>, Ref<Department>[]> = new Map<Ref<Department>, Ref<Department>[]>()
|
||||
let descendants: Map<Ref<Department>, Department[]> = new Map<Ref<Department>, Department[]>()
|
||||
let departments: Map<Ref<Department>, Department> = new Map<Ref<Department>, 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<Department>, Ref<Department>>()
|
||||
for (const doc of res) {
|
||||
byId.set(doc._id, doc.parent ?? hr.ids.Head)
|
||||
}
|
||||
|
||||
for (const doc of res) {
|
||||
const list: Ref<Department>[] = []
|
||||
let parent: Ref<Department> | 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 @@
|
||||
<ScheduleView
|
||||
{department}
|
||||
{descendants}
|
||||
{ancestors}
|
||||
departmentById={departments}
|
||||
staffQuery={resultQuery}
|
||||
{currentDate}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import { CalendarMode } from '@hcengineering/calendar-resources'
|
||||
import { Employee, getCurrentEmployee } from '@hcengineering/contact'
|
||||
import { DocumentQuery, Ref } from '@hcengineering/core'
|
||||
import { Department, Request, RequestType, Staff, fromTzDate } from '@hcengineering/hr'
|
||||
import { Department, PublicHoliday, Request, RequestType, Staff, fromTzDate } from '@hcengineering/hr'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import tracker, { Issue } from '@hcengineering/tracker'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
export let department: Ref<Department>
|
||||
export let descendants: Map<Ref<Department>, Department[]>
|
||||
export let ancestors: Map<Ref<Department>, Ref<Department>[]>
|
||||
export let departmentById: Map<Ref<Department>, Department>
|
||||
export let currentDate: Date = new Date()
|
||||
export let mode: CalendarMode
|
||||
@@ -232,7 +233,7 @@
|
||||
}
|
||||
}
|
||||
)
|
||||
let holidays = new Map<Ref<Department>, Date[]>()
|
||||
let holidaysMap = new Map<Ref<Department>, 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<Department>,
|
||||
group[groupKey].map((holiday) => new Date(fromTzDate(holiday.date)))
|
||||
)
|
||||
}
|
||||
holidaysMap = toHolidaysMap(res)
|
||||
}
|
||||
)
|
||||
|
||||
function toHolidaysMap (holidays: PublicHoliday[]): Map<Ref<Department>, Date[]> {
|
||||
const group = groupBy(holidays, 'department')
|
||||
const result = new Map()
|
||||
for (const groupKey in group) {
|
||||
// ensure unique holiday dates
|
||||
const dates = new Set<number>()
|
||||
for (const holiday of group[groupKey]) {
|
||||
dates.add(fromTzDate(holiday.date))
|
||||
}
|
||||
result.set(
|
||||
groupKey as Ref<Department>,
|
||||
Array.from(dates).map((date) => new Date(date))
|
||||
)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async function getHolidays (month: Date): Promise<Map<Ref<Department>, 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<Department>): 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
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if staffDepartmentMap.size > 0}
|
||||
{#if mode === CalendarMode.Year}
|
||||
<YearView {departmentStaff} {employeeRequests} {types} {currentDate} {holidays} {staffDepartmentMap} />
|
||||
<YearView {departmentStaff} {employeeRequests} {types} {currentDate} {holidaysMap} {staffDepartmentMap} />
|
||||
{:else if mode === CalendarMode.Month}
|
||||
{@const holidays = getDepartmentHolidays(department)}
|
||||
|
||||
{#if display === 'chart'}
|
||||
<MonthView
|
||||
{departmentStaff}
|
||||
@@ -303,6 +325,7 @@
|
||||
{editableList}
|
||||
{currentDate}
|
||||
{holidays}
|
||||
{holidaysMap}
|
||||
{department}
|
||||
{departmentById}
|
||||
{staffDepartmentMap}
|
||||
@@ -314,7 +337,7 @@
|
||||
{types}
|
||||
{currentDate}
|
||||
{timeReports}
|
||||
{holidays}
|
||||
{holidaysMap}
|
||||
{staffDepartmentMap}
|
||||
{getHolidays}
|
||||
{preference}
|
||||
|
||||
@@ -21,16 +21,46 @@
|
||||
import hr from '../../plugin'
|
||||
import DepartmentEditor from '../DepartmentEditor.svelte'
|
||||
|
||||
let description: string
|
||||
let title: string
|
||||
export let date: Timestamp
|
||||
export let department: Ref<Department>
|
||||
|
||||
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<Department>): Promise<Ref<Department>[]> {
|
||||
const departments = await client.findAll(hr.class.Department, {})
|
||||
const byId = new Map<Ref<Department>, Ref<Department>>()
|
||||
for (const doc of departments) {
|
||||
byId.set(doc._id, doc.parent ?? hr.ids.Head)
|
||||
}
|
||||
|
||||
const ancestors: Ref<Department>[] = []
|
||||
let parent: Ref<Department> | 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<void> {
|
||||
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<void> {
|
||||
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')
|
||||
}
|
||||
</script>
|
||||
@@ -67,9 +103,9 @@
|
||||
on:close
|
||||
okLabel={existingHoliday ? presentation.string.Save : presentation.string.Ok}
|
||||
okAction={() => {
|
||||
saveHoliday()
|
||||
void saveHoliday()
|
||||
}}
|
||||
canSave={true}
|
||||
canSave={!loading}
|
||||
on:changeContent
|
||||
>
|
||||
<div class="flex-grow mt-4">
|
||||
|
||||
@@ -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<Ref<Staff>, Request[]>
|
||||
export let timeReports: Map<Ref<Employee>, EmployeeReports>
|
||||
export let holidays: Map<Ref<Department>, Date[]> = new Map<Ref<Department>, Date[]>()
|
||||
export let holidaysMap: Map<Ref<Department>, Date[]> = new Map<Ref<Department>, Date[]>()
|
||||
export let getHolidays: (month: Date) => Promise<Map<Ref<Department>, 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(' ')
|
||||
}
|
||||
|
||||
@@ -76,7 +76,9 @@
|
||||
export let editableList: Ref<Employee>[]
|
||||
|
||||
export let staffDepartmentMap: Map<Ref<Staff>, Department[]>
|
||||
export let holidays: Map<Ref<Department>, Date[]>
|
||||
|
||||
export let holidays: Date[]
|
||||
export let holidaysMap: Map<Ref<Department>, 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)}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
@@ -321,6 +324,7 @@
|
||||
<div
|
||||
class="timeline-day-header__day flex-col-center justify-center"
|
||||
class:timeline-day-header__day--today={today}
|
||||
class:timeline-day-header__day--holiday={holiday && !today}
|
||||
>
|
||||
{day.getDate()}
|
||||
</div>
|
||||
@@ -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 {
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
export let employeeRequests: Map<Ref<Staff>, Request[]>
|
||||
|
||||
export let holidays: Map<Ref<Department>, Date[]>
|
||||
export let holidaysMap: Map<Ref<Department>, Date[]>
|
||||
export let staffDepartmentMap: Map<Ref<Staff>, Department[]>
|
||||
|
||||
function getTooltip (requests: Request[]): LabelAndProps | undefined {
|
||||
@@ -117,7 +117,7 @@
|
||||
startDate,
|
||||
endDate,
|
||||
types,
|
||||
getHolidayDatesForEmployee(staffDepartmentMap, employee._id, holidays)
|
||||
getHolidayDatesForEmployee(staffDepartmentMap, employee._id, holidaysMap)
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
@@ -139,7 +139,7 @@
|
||||
startDate,
|
||||
endDate,
|
||||
types,
|
||||
[...holidays.values()].flat()
|
||||
[...holidaysMap.values()].flat()
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -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<string, Date>()
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user