Schedule year view (#2145)

Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
Denis Bykhov 2022-06-25 23:33:12 +06:00 committed by GitHub
parent 4f8b4706e9
commit a74ae0ee89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 160 additions and 49 deletions

View File

@ -33,6 +33,7 @@
"@anticrm/platform": "~0.6.6", "@anticrm/platform": "~0.6.6",
"svelte": "^3.47", "svelte": "^3.47",
"@anticrm/calendar": "~0.6.0", "@anticrm/calendar": "~0.6.0",
"@anticrm/calendar-resources": "~0.6.0",
"@anticrm/hr": "~0.6.0", "@anticrm/hr": "~0.6.0",
"@anticrm/ui": "~0.6.0", "@anticrm/ui": "~0.6.0",
"@anticrm/presentation": "~0.6.2", "@anticrm/presentation": "~0.6.2",

View File

@ -46,7 +46,7 @@
} }
}) })
$: value = new Date(date).getTime() let value = new Date(date).getTime()
$: dueDate = new Date(value).setDate(new Date(value).getDate() + 1) $: dueDate = new Date(value).setDate(new Date(value).getDate() + 1)
export function canClose (): boolean { export function canClose (): boolean {
@ -72,6 +72,10 @@
function typeSelected (_id: Ref<RequestType>): void { function typeSelected (_id: Ref<RequestType>): void {
type = types.find((p) => p._id === _id) type = types.find((p) => p._id === _id)
dueDate =
Math.abs(type?.value ?? 0 % 1) === 0.5
? new Date(value).setHours(12)
: new Date(value).setDate(new Date(value).getDate() + 1)
} }
</script> </script>

View File

@ -18,16 +18,15 @@
import { Table } from '@anticrm/view-resources' import { Table } from '@anticrm/view-resources'
export let date: Date export let date: Date
export let endDate: number
export let employee: Ref<Staff> export let employee: Ref<Staff>
$: endDate = new Date(date).setDate(date.getDate() + 1)
</script> </script>
<Table <Table
_class={hr.class.Request} _class={hr.class.Request}
query={{ query={{
attachedTo: employee, attachedTo: employee,
dueDate: { $gte: date.getTime() }, dueDate: { $gt: date.getTime() },
date: { $lt: endDate } date: { $lt: endDate }
}} }}
config={['$lookup.type.label', 'date', 'dueDate']} config={['$lookup.type.label', 'date', 'dueDate']}

View File

@ -13,13 +13,14 @@
// limitations under the License. // limitations under the License.
--> -->
<script lang="ts"> <script lang="ts">
import calendar from '@anticrm/calendar' import calendar from '@anticrm/calendar-resources/src/plugin'
import { CalendarMode } from '@anticrm/calendar-resources'
import { Ref } from '@anticrm/core' import { Ref } from '@anticrm/core'
import { Department } from '@anticrm/hr' import { Department } from '@anticrm/hr'
import { createQuery, SpaceSelector } from '@anticrm/presentation' import { createQuery, SpaceSelector } from '@anticrm/presentation'
import { Button, Icon, IconBack, IconForward, Label } from '@anticrm/ui' import { Button, Icon, IconBack, IconForward, Label } from '@anticrm/ui'
import hr from '../plugin' import hr from '../plugin'
import ScheduleView from './ScheduleView.svelte' import ScheduleMonthView from './ScheduleView.svelte'
let department = hr.ids.Head let department = hr.ids.Head
let currentDate: Date = new Date() let currentDate: Date = new Date()
@ -29,6 +30,8 @@
let descendants: Map<Ref<Department>, Department[]> = new Map<Ref<Department>, Department[]>() let descendants: Map<Ref<Department>, Department[]> = new Map<Ref<Department>, Department[]>()
let departments: Map<Ref<Department>, Department> = new Map<Ref<Department>, Department>() let departments: Map<Ref<Department>, Department> = new Map<Ref<Department>, Department>()
let mode: CalendarMode = CalendarMode.Month
query.query(hr.class.Department, {}, (res) => { query.query(hr.class.Department, {}, (res) => {
departments.clear() departments.clear()
descendants.clear() descendants.clear()
@ -43,7 +46,16 @@
}) })
function inc (val: number): void { function inc (val: number): void {
switch (mode) {
case CalendarMode.Month: {
currentDate.setMonth(currentDate.getMonth() + val) currentDate.setMonth(currentDate.getMonth() + val)
break
}
case CalendarMode.Year: {
currentDate.setFullYear(currentDate.getFullYear() + val)
break
}
}
currentDate = currentDate currentDate = currentDate
} }
@ -58,6 +70,21 @@
<div class="ac-header__wrap-title"> <div class="ac-header__wrap-title">
<div class="ac-header__icon"><Icon icon={calendar.icon.Calendar} size={'small'} /></div> <div class="ac-header__icon"><Icon icon={calendar.icon.Calendar} size={'small'} /></div>
<span class="ac-header__title"><Label label={hr.string.Schedule} /></span> <span class="ac-header__title"><Label label={hr.string.Schedule} /></span>
<div class="flex gap-2 ml-6">
<Button
size={'small'}
label={calendar.string.ModeMonth}
on:click={() => {
mode = CalendarMode.Month
}}
/>
<Button
size={'small'}
label={calendar.string.ModeYear}
on:click={() => {
mode = CalendarMode.Year
}}
/>
<div class="flex ml-4 gap-2"> <div class="flex ml-4 gap-2">
<Button <Button
icon={IconBack} icon={IconBack}
@ -68,7 +95,7 @@
/> />
<Button <Button
size={'small'} size={'small'}
label={hr.string.Today} label={calendar.string.Today}
on:click={() => { on:click={() => {
currentDate = new Date() currentDate = new Date()
}} }}
@ -81,8 +108,11 @@
}} }}
/> />
</div> </div>
</div>
<div class="fs-title ml-4 flex-row-center"> <div class="fs-title ml-4 flex-row-center">
{#if mode === CalendarMode.Month}
{getMonthName(currentDate)} {getMonthName(currentDate)}
{/if}
{currentDate.getFullYear()} {currentDate.getFullYear()}
</div> </div>
</div> </div>
@ -90,5 +120,5 @@
</div> </div>
<div class="mr-6 h-full"> <div class="mr-6 h-full">
<ScheduleView {department} {descendants} departmentById={departments} {currentDate} /> <ScheduleMonthView {department} {descendants} departmentById={departments} {currentDate} {mode} />
</div> </div>

View File

@ -14,7 +14,7 @@
--> -->
<script lang="ts"> <script lang="ts">
import { getCurrentAccount, Ref, Timestamp } from '@anticrm/core' import { getCurrentAccount, Ref, Timestamp } from '@anticrm/core'
import type { Department, Request, Staff } from '@anticrm/hr' import type { Department, Request, RequestType, Staff } from '@anticrm/hr'
import { createQuery } from '@anticrm/presentation' import { createQuery } from '@anticrm/presentation'
import { import {
Label, Label,
@ -30,6 +30,7 @@
LabelAndProps LabelAndProps
} from '@anticrm/ui' } from '@anticrm/ui'
import { EmployeePresenter } from '@anticrm/contact-resources' import { EmployeePresenter } from '@anticrm/contact-resources'
import { CalendarMode } from '@anticrm/calendar-resources'
import contact from '@anticrm/contact-resources/src/plugin' import contact from '@anticrm/contact-resources/src/plugin'
import hr from '../plugin' import hr from '../plugin'
import { Employee, EmployeeAccount } from '@anticrm/contact' import { Employee, EmployeeAccount } from '@anticrm/contact'
@ -41,14 +42,30 @@
export let descendants: Map<Ref<Department>, Department[]> export let descendants: Map<Ref<Department>, Department[]>
export let departmentById: Map<Ref<Department>, Department> export let departmentById: Map<Ref<Department>, Department>
export let currentDate: Date = new Date() export let currentDate: Date = new Date()
export let mode: CalendarMode
$: startDate = new Date(new Date(currentDate).setDate(1)).setHours(0, 0, 0, 0) $: startDate = new Date(
$: endDate = new Date(startDate).setMonth(new Date(startDate).getMonth() + 1) new Date(mode === CalendarMode.Year ? new Date(currentDate).setMonth(1) : currentDate).setDate(1)
).setHours(0, 0, 0, 0)
$: endDate =
mode === CalendarMode.Year
? new Date(startDate).setFullYear(new Date(startDate).getFullYear() + 1)
: new Date(startDate).setMonth(new Date(startDate).getMonth() + 1)
$: departments = [department, ...getDescendants(department, descendants)] $: departments = [department, ...getDescendants(department, descendants)]
const lq = createQuery() const lq = createQuery()
const typeQuery = createQuery()
const staffQuery = createQuery() const staffQuery = createQuery()
let staff: Staff[] = [] let staff: Staff[] = []
let types: Map<Ref<RequestType>, RequestType> = new Map<Ref<RequestType>, RequestType>()
typeQuery.query(hr.class.RequestType, {}, (res) => {
types = new Map(
res.map((type) => {
return [type._id, type]
})
)
})
staffQuery.query(hr.mixin.Staff, {}, (res) => { staffQuery.query(hr.mixin.Staff, {}, (res) => {
staff = res staff = res
@ -96,7 +113,7 @@
if (requests === undefined) return [] if (requests === undefined) return []
const res: Request[] = [] const res: Request[] = []
const time = date.getTime() const time = date.getTime()
const endTime = new Date(date).setDate(date.getDate() + 1) const endTime = getEndDate(date)
for (const request of requests) { for (const request of requests) {
if (request.date < endTime && request.dueDate > time) { if (request.date < endTime && request.dueDate > time) {
res.push(request) res.push(request)
@ -134,16 +151,45 @@
return lead === currentEmployee return lead === currentEmployee
} }
function getEndDate (date: Date): number {
return mode === CalendarMode.Year
? new Date(date).setMonth(date.getMonth() + 1)
: new Date(date).setDate(date.getDate() + 1)
}
function getTooltip (employee: Staff, date: Date): LabelAndProps | undefined { function getTooltip (employee: Staff, date: Date): LabelAndProps | undefined {
const requests = getRequests(employee._id, date) const requests = getRequests(employee._id, date)
if (requests.length === 0) return if (requests.length === 0) return
const endDate = getEndDate(date)
return { return {
component: RequestsPopup, component: RequestsPopup,
props: { date, employee: employee._id } props: { date, endDate, employee: employee._id }
} }
} }
$: departmentStaff = staff.filter((p) => departments.includes(p.department) || employeeRequests.has(p._id)) $: departmentStaff = staff.filter((p) => departments.includes(p.department) || employeeRequests.has(p._id))
$: values = [...Array(mode === CalendarMode.Year ? 12 : daysInMonth(currentDate)).keys()]
function getMonthName (date: Date): string {
return new Intl.DateTimeFormat('default', { month: 'long' }).format(date)
}
function getMonth (date: Date, m: number): Date {
date = new Date(date)
date.setDate(1)
date.setMonth(m)
return date
}
function getTotal (requests: Request[]): number {
let total = 0
for (const request of requests) {
const type = types.get(request.type)
const days = (request.dueDate - request.date) / 1000 / 60 / 60 / 24
total += Math.ceil(days) * (type?.value ?? 0)
}
return total
}
</script> </script>
{#if departmentStaff.length} {#if departmentStaff.length}
@ -154,14 +200,27 @@
<th> <th>
<Label label={contact.string.Employee} /> <Label label={contact.string.Employee} />
</th> </th>
{#each [...Array(daysInMonth(currentDate)).keys()] as dayOfMonth} {#each values as value}
{@const day = getDay(new Date(startDate), dayOfMonth)} {#if mode === CalendarMode.Year}
{@const month = getMonth(currentDate, value)}
<th
class="fixed"
class:today={month.getFullYear() === todayDate.getFullYear() &&
month.getMonth() === todayDate.getMonth()}
>
<div class="cursor-pointer uppercase flex-col-center">
<div class="flex-center">{getMonthName(month)}</div>
</div>
</th>
{:else}
{@const day = getDay(new Date(startDate), value)}
<th class:today={areDatesEqual(todayDate, day)} class:weekend={isWeekend(day)}> <th class:today={areDatesEqual(todayDate, day)} class:weekend={isWeekend(day)}>
<div class="cursor-pointer uppercase flex-col-center"> <div class="cursor-pointer uppercase flex-col-center">
<div class="flex-center">{getWeekDayName(day, 'short')}</div> <div class="flex-center">{getWeekDayName(day, 'short')}</div>
<div class="flex-center">{day.getDate()}</div> <div class="flex-center">{day.getDate()}</div>
</div> </div>
</th> </th>
{/if}
{/each} {/each}
</tr> </tr>
</thead> </thead>
@ -171,8 +230,22 @@
<td> <td>
<EmployeePresenter value={employee} /> <EmployeePresenter value={employee} />
</td> </td>
{#each [...Array(daysInMonth(currentDate)).keys()] as dayOfMonth} {#each values as value}
{@const date = getDay(new Date(startDate), dayOfMonth)} {#if mode === CalendarMode.Year}
{@const month = getMonth(currentDate, value)}
{@const requests = getRequests(employee._id, month)}
<td
class:today={month.getFullYear() === todayDate.getFullYear() &&
month.getMonth() === todayDate.getMonth()}
class="fixed"
use:tooltip={getTooltip(employee, month)}
>
<div class="flex-center">
{getTotal(requests)}
</div>
</td>
{:else}
{@const date = getDay(new Date(startDate), value)}
{@const requests = getRequests(employee._id, date)} {@const requests = getRequests(employee._id, date)}
{@const editable = isEditable(employee)} {@const editable = isEditable(employee)}
<td <td
@ -186,6 +259,7 @@
<ScheduleRequests {requests} {date} {editable} /> <ScheduleRequests {requests} {date} {editable} />
{/if} {/if}
</td> </td>
{/if}
{/each} {/each}
</tr> </tr>
{/each} {/each}
@ -209,6 +283,9 @@
th { th {
width: auto; width: auto;
min-width: 1rem; min-width: 1rem;
&.fixed {
width: 5rem;
}
&:first-child { &:first-child {
width: 15rem; width: 15rem;
padding: 0.5rem; padding: 0.5rem;