Files
huly-platform/plugins/time-resources/src/components/PlanningCalendar.svelte
T
Artyom SavchenkoandGitHub 7cbdb612ed Fix missing events in planner (#10395)
* Fix missing events in planner

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* Clean up

Signed-off-by: Artem Savchenko <armisav@gmail.com>

---------

Signed-off-by: Artem Savchenko <armisav@gmail.com>
2026-01-12 20:39:58 +07:00

255 lines
7.6 KiB
Svelte

<script lang="ts">
import calendar, { AccessLevel, Calendar, Event, generateEventId, getAllEvents } from '@hcengineering/calendar'
import { DayCalendar, calendarByIdStore, hidePrivateEvents } from '@hcengineering/calendar-resources'
import { getCurrentEmployee } from '@hcengineering/contact'
import { Ref, SortingOrder, Timestamp, getCurrentAccount } from '@hcengineering/core'
import { IntlString, getEmbeddedLabel } from '@hcengineering/platform'
import { createQuery } from '@hcengineering/presentation'
import {
AnyComponent,
ButtonBase,
ButtonIcon,
IconChevronLeft,
IconChevronRight,
Label,
areDatesEqual,
showPopup,
ticker,
Header,
getFormattedDate,
resizeObserver,
deviceOptionsStore as deviceInfo
} from '@hcengineering/ui'
import { ToDo, WorkSlot } from '@hcengineering/time'
import time from '../plugin'
import IconSun from './icons/Sun.svelte'
export let dragItem: ToDo | null = null
export let currentDate: Date = new Date()
export let displayedDaysCount = 1
export let element: HTMLElement | undefined = undefined
export let createComponent: AnyComponent | undefined = calendar.component.CreateEvent
const q = createQuery()
function getFrom (date: Date): Timestamp {
return new Date(date).setHours(0, 0, 0, 0)
}
function getTo (date: Date, days: number = 3): Timestamp {
return new Date(date).setDate(date.getDate() + days)
}
let dayCalendar: DayCalendar
let raw: Event[] = []
let objects: Event[] = []
let showLabel: boolean = true
const rem = (n: number): number => n * $deviceInfo.fontSize
const myAcc = getCurrentAccount()
const socialStrings = myAcc.socialIds
const personalCalendar = `${myAcc.uuid}_calendar` as Ref<Calendar>
const calendarsQ = createQuery()
let calendars: Calendar[] = []
let todayDate = new Date()
$: calendarsQ.query(calendar.class.Calendar, { createdBy: { $in: socialStrings }, hidden: false }, (res) => {
calendars = res
})
$: from = getFrom(currentDate)
$: to = getTo(currentDate, displayedDaysCount)
function update (calendars: Calendar[]): void {
q.query<Event>(
calendar.class.Event,
{ calendar: { $in: [personalCalendar, ...calendars.map((p) => p._id)] } },
(result) => {
raw = result
},
{ sort: { date: SortingOrder.Ascending } }
)
}
$: update(calendars)
$: all = getAllEvents(raw, from, to)
$: objects = hidePrivateEvents(all, $calendarByIdStore)
function inc (val: number): void {
if (val === 0) {
currentDate = new Date()
dayCalendar.scrollToTime(currentDate)
return
}
currentDate.setDate(currentDate.getDate() + val)
currentDate = currentDate
}
function getTitle (day: Date, now: Timestamp): IntlString {
const today = new Date(now)
const tomorrow = new Date(new Date(now).setDate(new Date(now).getDate() + 1))
const yesterday = new Date(new Date(now).setDate(new Date(now).getDate() - 1))
if (areDatesEqual(day, today)) return time.string.Today
if (areDatesEqual(day, yesterday)) return time.string.Yesterday
if (areDatesEqual(day, tomorrow)) return time.string.Tomorrow
const isCurrentYear = day.getFullYear() === new Date().getFullYear()
return getEmbeddedLabel(
day.toLocaleDateString('default', {
month: 'long',
day: 'numeric',
year: isCurrentYear ? undefined : 'numeric'
})
)
}
const dragItemId = 'drag_item' as Ref<WorkSlot>
function dragEnter (e: CustomEvent<any>) {
if (dragItem != null) {
const current = raw.find((p) => p._id === dragItemId)
if (current !== undefined) {
current.attachedTo = dragItem._id
current.attachedToClass = dragItem._class
current.date = e.detail.date.getTime()
current.dueDate = new Date(e.detail.date).setMinutes(new Date(e.detail.date).getMinutes() + 30)
} else {
const ev: WorkSlot = {
_id: dragItemId,
allDay: false,
eventId: generateEventId(),
title: '',
description: '',
access: AccessLevel.Owner,
attachedTo: dragItem._id,
attachedToClass: dragItem._class,
_class: time.class.WorkSlot,
collection: 'events',
visibility: 'public',
blockTime: true,
calendar: personalCalendar,
space: calendar.space.Calendar,
modifiedBy: myAcc.primarySocialId,
participants: [getCurrentEmployee()],
modifiedOn: Date.now(),
date: e.detail.date.getTime(),
dueDate: new Date(e.detail.date).setMinutes(new Date(e.detail.date).getMinutes() + 30),
user: myAcc.primarySocialId
}
raw.push(ev)
}
raw = raw
all = getAllEvents(raw, from, to)
objects = hidePrivateEvents(all, $calendarByIdStore)
}
}
function dragLeave (event: DragEvent) {
const rect = dayCalendar.getCalendarRect()
if (!rect) return
if (event.x < rect.left || event.x > rect.right || event.y < rect.top || event.y > rect.bottom) {
raw = raw.filter((r) => r._id !== dragItemId)
}
}
function dragOut () {
if (dragItemId != null) {
raw = raw.filter((r) => r._id !== dragItemId)
}
}
function clear (dragItem: ToDo | null) {
if (dragItem === null) {
raw = raw.filter((p) => p._id !== dragItemId)
all = getAllEvents(raw, from, to)
objects = hidePrivateEvents(all, $calendarByIdStore)
}
}
$: clear(dragItem)
function showCreateDialog (date: Date, withTime: boolean) {
if (createComponent === undefined) {
return
}
showPopup(createComponent, { date, withTime }, 'top')
}
$: isToday = areDatesEqual(currentDate, new Date($ticker))
</script>
<div
class="hulyComponent modal"
bind:this={element}
use:resizeObserver={(element) => {
showLabel = showLabel ? element.clientWidth > rem(3.5) + 399 : element.clientWidth > rem(3.5) + 400
}}
>
<Header adaptive={'disabled'}>
<div class="heading-medium-20 line-height-auto overflow-label">
<Label label={time.string.Schedule} />: <Label label={getTitle(currentDate, $ticker)} />
</div>
<svelte:fragment slot="actions">
<ButtonIcon
icon={IconChevronLeft}
kind={'secondary'}
size={'small'}
on:click={() => {
inc(-1)
}}
/>
<ButtonBase
icon={IconSun}
label={showLabel ? time.string.TodayColon : undefined}
title={showLabel ? getFormattedDate(todayDate.getTime(), { weekday: 'short', day: 'numeric' }) : undefined}
type={showLabel ? 'type-button' : 'type-button-icon'}
kind={'secondary'}
size={'small'}
inheritFont
hasMenu
disabled={isToday}
on:click={() => {
inc(0)
}}
/>
<ButtonIcon
icon={IconChevronRight}
kind={'secondary'}
size={'small'}
on:click={() => {
inc(1)
}}
/>
</svelte:fragment>
</Header>
<div class="hulyComponent-content__container">
<DayCalendar
bind:this={dayCalendar}
events={objects}
bind:displayedDaysCount
startFromWeekStart={false}
clearCells={dragItem !== null}
{dragItemId}
on:dragEnter={dragEnter}
on:dragOut={dragOut}
on:dragleave={dragLeave}
on:create={(e) => {
showCreateDialog(e.detail.date, e.detail.withTime)
}}
on:dragDrop
bind:currentDate
bind:todayDate
/>
</div>
</div>
<style lang="scss">
.title {
padding: 1.75rem 2rem;
font-size: 1.25rem;
color: var(--theme-caption-color);
}
.tools {
padding: 0 2rem 0.75rem;
}
</style>