UBER-615 HR Schedule UI fixes (#3565)

Signed-off-by: Alexander Onnikov <alexander.onnikov@xored.com>
This commit is contained in:
Alexander Onnikov 2023-08-07 18:14:32 +07:00 committed by GitHub
parent eaaef54fa6
commit 32fd80c971
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 104 additions and 29 deletions

View File

@ -308,6 +308,27 @@ export function createModel (builder: Builder): void {
hr.action.EditDepartment
)
createAction(
builder,
{
action: view.actionImpl.ShowPopup,
actionProps: {
component: hr.component.CreateDepartment,
element: 'top',
fillProps: {
_id: 'space'
}
},
label: hr.string.CreateDepartment,
icon: hr.icon.Department,
input: 'focus',
category: hr.category.HR,
target: hr.class.Department,
context: { mode: 'context', application: hr.app.HR, group: 'create' }
},
hr.action.CreateDepartment
)
createAction(
builder,
{
@ -339,7 +360,8 @@ export function createModel (builder: Builder): void {
input: 'any',
category: hr.category.HR,
target: hr.class.Request,
context: { mode: 'context', application: hr.app.HR, group: 'create' }
context: { mode: 'context', application: hr.app.HR, group: 'create' },
override: [view.action.Open]
},
hr.action.EditRequest
)
@ -350,7 +372,7 @@ export function createModel (builder: Builder): void {
action: hr.actionImpl.EditRequestType,
actionProps: {},
label: hr.string.EditRequestType,
icon: view.icon.Open,
icon: view.icon.Edit,
input: 'any',
category: hr.category.HR,
target: hr.class.Request,

View File

@ -41,6 +41,7 @@ export default mergeIds(hrId, hr, {
},
component: {
Structure: '' as AnyComponent,
CreateDepartment: '' as AnyComponent,
EditDepartment: '' as AnyComponent,
DepartmentStaff: '' as AnyComponent,
DepartmentEditor: '' as AnyComponent,
@ -54,6 +55,7 @@ export default mergeIds(hrId, hr, {
HR: '' as Ref<ActionCategory>
},
action: {
CreateDepartment: '' as Ref<Action>,
EditDepartment: '' as Ref<Action>,
ArchiveDepartment: '' as Ref<Action>,
EditRequest: '' as Ref<Action>,

View File

@ -235,7 +235,7 @@
)`
divScroll.style.webkitMaskImage = gradient
}
if (divHScroll && horizontal) {
if (divHScroll && horizontal && !noFade) {
const gradientH = `linear-gradient(
90deg,
rgba(0, 0, 0, 1) ${shiftLeft}px,

View File

@ -250,7 +250,7 @@
{#if rows.length}
{@const dep = departmentById.get(department)}
<Scroller fade={{ multipler: { top: headerHeightRem, bottom: 0, left: headerWidthRem } }} horizontal>
<Scroller horizontal fade={{ multipler: { top: headerHeightRem, left: headerWidthRem } }} noFade>
<div bind:clientWidth={containerWidth} class="timeline">
{#key [containerWidthRem, columnWidthRem, headerWidthRem]}
<!-- Resource Header -->
@ -314,7 +314,14 @@
{#each tracks as track, trackIndex}
{#each track.elements as element}
{@const request = element.request}
<div class="timeline-event-wrapper" style={getElementStyle(element, trackIndex)}>
<div
class="timeline-event-wrapper"
style={getElementStyle(element, trackIndex)}
use:tooltip={{
component: RequestsPopup,
props: { requests: [request._id] }
}}
>
<ScheduleRequest {request} {editable} shouldShowDescription={element.length > 1} />
</div>
{/each}

View File

@ -16,7 +16,7 @@
import hr, { Request, RequestType } from '@hcengineering/hr'
import { getClient } from '@hcengineering/presentation'
import { closeTooltip, Icon, Label, showPopup } from '@hcengineering/ui'
import { ContextMenu, HTMLPresenter } from '@hcengineering/view-resources'
import { ContextMenu } from '@hcengineering/view-resources'
export let request: Request
export let editable: boolean = false
@ -30,10 +30,8 @@
})
}
function isAvailable (type: RequestType, request: Request): boolean {
// TODO Move availability to the Request model
const available = type.value >= 0
return available
function isAvailable (type: RequestType): boolean {
return type.value >= 0
}
function click (e: MouseEvent, request: Request) {
@ -43,11 +41,13 @@
closeTooltip()
showPopup(ContextMenu, { object: request }, e.target as HTMLElement)
}
$: description = shouldShowDescription ? request.description.replace(/<[^>]*>/g, '').trim() : ''
</script>
{#await getType(request) then type}
{#if type}
{@const available = isAvailable(type, request)}
{@const available = isAvailable(type)}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
@ -66,8 +66,8 @@
{#if shouldShowDescription}
<span class="overflow-label">
{#if request.description !== ''}
<HTMLPresenter value={request.description} />
{#if description !== ''}
{description}
{:else if type}
<Label label={type.label} />
{/if}

View File

@ -16,6 +16,10 @@
import { createEventDispatcher } from 'svelte'
import { Ref } from '@hcengineering/core'
import { Department } from '@hcengineering/hr'
import { getResource } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
import { Action, IconEdit } from '@hcengineering/ui'
import { getActions as getContributedActions } from '@hcengineering/view-resources'
import hr from '../../plugin'
@ -27,27 +31,48 @@
export let selected: Ref<Department> | undefined
export let level = 0
const client = getClient()
const dispatch = createEventDispatcher()
function getDescendants (department: Ref<Department>): Ref<Department>[] {
return (descendants.get(department) ?? []).map((p) => p._id)
return (descendants.get(department) ?? []).sort((a, b) => a.name.localeCompare(b.name)).map((p) => p._id)
}
async function getActions (obj: Department): Promise<Action[]> {
const result: Action[] = []
const extraActions = await getContributedActions(client, obj, obj._class)
for (const act of extraActions) {
result.push({
icon: act.icon ?? IconEdit,
label: act.label,
action: async (ctx: any, evt: Event) => {
const impl = await getResource(act.action)
await impl(obj, evt, act.actionProps)
}
})
}
return result
}
function handleDepartmentSelected (department: Ref<Department>): void {
dispatch('selected', department)
}
$: _departments = departments.map((it) => departmentById.get(it)).filter((it) => it !== undefined) as Department[]
$: _descendants = new Map(_departments.map((it) => [it._id, getDescendants(it._id)]))
</script>
{#each departments as dep}
{@const department = departmentById.get(dep)}
{@const desc = getDescendants(dep)}
{#each _departments as department}
{@const desc = _descendants.get(department._id) ?? []}
{#if department}
<TreeElement
_id={department._id}
icon={hr.icon.Department}
title={department.name}
selected={selected === department._id}
node={desc.length > 0}
actions={() => getActions(department)}
{level}
on:click={() => handleDepartmentSelected(department._id)}
>

View File

@ -34,19 +34,11 @@
<NavHeader label={hr.string.HRApplication} />
<Scroller shrink>
<!-- TODO Specials -->
<div class="antiNav-divider short line" />
<TreeNode label={hr.string.Departments} parent>
<DepartmentsHierarchy {departments} {descendants} {departmentById} selected={department} on:selected />
</TreeNode>
<!-- TODO Add Positions -->
<!-- <div class="antiNav-divider short line" /> -->
<!-- <TreeNode label={hr.string.Positions} parent> -->
<!-- </TreeNode> -->
</Scroller>
<NavFooter />

View File

@ -13,11 +13,14 @@
// limitations under the License.
-->
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { Doc, Ref } from '@hcengineering/core'
import type { Asset, IntlString } from '@hcengineering/platform'
import type { AnySvelteComponent } from '@hcengineering/ui'
import { Icon, IconChevronDown, Label } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import { Icon, IconChevronDown, IconMoreH, Label, Menu, showPopup } from '@hcengineering/ui'
import { Action } from '@hcengineering/view'
export let _id: Ref<Doc> | undefined = undefined
export let icon: Asset | AnySvelteComponent | undefined = undefined
export let iconProps: Record<string, any> | undefined = undefined
export let label: IntlString | undefined = undefined
@ -27,6 +30,15 @@
export let collapsed = false
export let selected = false
export let level = 0
export let actions: (originalEvent?: MouseEvent) => Promise<Action[]> = async () => []
let hovered = false
async function onMenuClick (ev: MouseEvent) {
showPopup(Menu, { actions: await actions(ev), ctx: _id }, ev.target as HTMLElement, () => {
hovered = false
})
hovered = true
}
$: style = `padding-left: calc(${level} * 1.25rem);`
@ -36,10 +48,10 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="antiNav-element"
class:hovered
class:selected
class:parent
class:collapsed
class:child={!node}
{style}
on:click={() => {
if (selected) {
@ -59,12 +71,25 @@
</span>
{#if node}
<div class="an-element__icon-arrow" class:collapsed>
<div
class="an-element__icon-arrow"
class:collapsed
on:click={(e) => {
e.stopPropagation()
e.preventDefault()
collapsed = !collapsed
}}
>
<IconChevronDown size={'small'} />
</div>
{/if}
</span>
<div class="an-element__tool" on:click|preventDefault|stopPropagation={onMenuClick}>
<IconMoreH size={'small'} />
</div>
</div>
{#if node && !collapsed}
<div class="antiNav-element__dropbox"><slot /></div>
{/if}

View File

@ -14,6 +14,7 @@
//
import { Resources } from '@hcengineering/platform'
import CreateDepartment from './components/CreateDepartment.svelte'
import DepartmentEditor from './components/DepartmentEditor.svelte'
import DepartmentStaff from './components/DepartmentStaff.svelte'
import EditDepartment from './components/EditDepartment.svelte'
@ -34,6 +35,7 @@ async function editRequestType (object: Request): Promise<void> {
export default async (): Promise<Resources> => ({
component: {
Structure,
CreateDepartment,
EditDepartment,
DepartmentStaff,
DepartmentEditor,