mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-23 19:44:59 +03:00
UBER-615 HR Schedule UI fixes (#3565)
Signed-off-by: Alexander Onnikov <alexander.onnikov@xored.com>
This commit is contained in:
parent
eaaef54fa6
commit
32fd80c971
@ -308,6 +308,27 @@ export function createModel (builder: Builder): void {
|
|||||||
hr.action.EditDepartment
|
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(
|
createAction(
|
||||||
builder,
|
builder,
|
||||||
{
|
{
|
||||||
@ -339,7 +360,8 @@ export function createModel (builder: Builder): void {
|
|||||||
input: 'any',
|
input: 'any',
|
||||||
category: hr.category.HR,
|
category: hr.category.HR,
|
||||||
target: hr.class.Request,
|
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
|
hr.action.EditRequest
|
||||||
)
|
)
|
||||||
@ -350,7 +372,7 @@ export function createModel (builder: Builder): void {
|
|||||||
action: hr.actionImpl.EditRequestType,
|
action: hr.actionImpl.EditRequestType,
|
||||||
actionProps: {},
|
actionProps: {},
|
||||||
label: hr.string.EditRequestType,
|
label: hr.string.EditRequestType,
|
||||||
icon: view.icon.Open,
|
icon: view.icon.Edit,
|
||||||
input: 'any',
|
input: 'any',
|
||||||
category: hr.category.HR,
|
category: hr.category.HR,
|
||||||
target: hr.class.Request,
|
target: hr.class.Request,
|
||||||
|
@ -41,6 +41,7 @@ export default mergeIds(hrId, hr, {
|
|||||||
},
|
},
|
||||||
component: {
|
component: {
|
||||||
Structure: '' as AnyComponent,
|
Structure: '' as AnyComponent,
|
||||||
|
CreateDepartment: '' as AnyComponent,
|
||||||
EditDepartment: '' as AnyComponent,
|
EditDepartment: '' as AnyComponent,
|
||||||
DepartmentStaff: '' as AnyComponent,
|
DepartmentStaff: '' as AnyComponent,
|
||||||
DepartmentEditor: '' as AnyComponent,
|
DepartmentEditor: '' as AnyComponent,
|
||||||
@ -54,6 +55,7 @@ export default mergeIds(hrId, hr, {
|
|||||||
HR: '' as Ref<ActionCategory>
|
HR: '' as Ref<ActionCategory>
|
||||||
},
|
},
|
||||||
action: {
|
action: {
|
||||||
|
CreateDepartment: '' as Ref<Action>,
|
||||||
EditDepartment: '' as Ref<Action>,
|
EditDepartment: '' as Ref<Action>,
|
||||||
ArchiveDepartment: '' as Ref<Action>,
|
ArchiveDepartment: '' as Ref<Action>,
|
||||||
EditRequest: '' as Ref<Action>,
|
EditRequest: '' as Ref<Action>,
|
||||||
|
@ -235,7 +235,7 @@
|
|||||||
)`
|
)`
|
||||||
divScroll.style.webkitMaskImage = gradient
|
divScroll.style.webkitMaskImage = gradient
|
||||||
}
|
}
|
||||||
if (divHScroll && horizontal) {
|
if (divHScroll && horizontal && !noFade) {
|
||||||
const gradientH = `linear-gradient(
|
const gradientH = `linear-gradient(
|
||||||
90deg,
|
90deg,
|
||||||
rgba(0, 0, 0, 1) ${shiftLeft}px,
|
rgba(0, 0, 0, 1) ${shiftLeft}px,
|
||||||
|
@ -250,7 +250,7 @@
|
|||||||
{#if rows.length}
|
{#if rows.length}
|
||||||
{@const dep = departmentById.get(department)}
|
{@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">
|
<div bind:clientWidth={containerWidth} class="timeline">
|
||||||
{#key [containerWidthRem, columnWidthRem, headerWidthRem]}
|
{#key [containerWidthRem, columnWidthRem, headerWidthRem]}
|
||||||
<!-- Resource Header -->
|
<!-- Resource Header -->
|
||||||
@ -314,7 +314,14 @@
|
|||||||
{#each tracks as track, trackIndex}
|
{#each tracks as track, trackIndex}
|
||||||
{#each track.elements as element}
|
{#each track.elements as element}
|
||||||
{@const request = element.request}
|
{@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} />
|
<ScheduleRequest {request} {editable} shouldShowDescription={element.length > 1} />
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
import hr, { Request, RequestType } from '@hcengineering/hr'
|
import hr, { Request, RequestType } from '@hcengineering/hr'
|
||||||
import { getClient } from '@hcengineering/presentation'
|
import { getClient } from '@hcengineering/presentation'
|
||||||
import { closeTooltip, Icon, Label, showPopup } from '@hcengineering/ui'
|
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 request: Request
|
||||||
export let editable: boolean = false
|
export let editable: boolean = false
|
||||||
@ -30,10 +30,8 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAvailable (type: RequestType, request: Request): boolean {
|
function isAvailable (type: RequestType): boolean {
|
||||||
// TODO Move availability to the Request model
|
return type.value >= 0
|
||||||
const available = type.value >= 0
|
|
||||||
return available
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function click (e: MouseEvent, request: Request) {
|
function click (e: MouseEvent, request: Request) {
|
||||||
@ -43,11 +41,13 @@
|
|||||||
closeTooltip()
|
closeTooltip()
|
||||||
showPopup(ContextMenu, { object: request }, e.target as HTMLElement)
|
showPopup(ContextMenu, { object: request }, e.target as HTMLElement)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: description = shouldShowDescription ? request.description.replace(/<[^>]*>/g, '').trim() : ''
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#await getType(request) then type}
|
{#await getType(request) then type}
|
||||||
{#if type}
|
{#if type}
|
||||||
{@const available = isAvailable(type, request)}
|
{@const available = isAvailable(type)}
|
||||||
|
|
||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div
|
<div
|
||||||
@ -66,8 +66,8 @@
|
|||||||
|
|
||||||
{#if shouldShowDescription}
|
{#if shouldShowDescription}
|
||||||
<span class="overflow-label">
|
<span class="overflow-label">
|
||||||
{#if request.description !== ''}
|
{#if description !== ''}
|
||||||
<HTMLPresenter value={request.description} />
|
{description}
|
||||||
{:else if type}
|
{:else if type}
|
||||||
<Label label={type.label} />
|
<Label label={type.label} />
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -16,6 +16,10 @@
|
|||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import { Ref } from '@hcengineering/core'
|
import { Ref } from '@hcengineering/core'
|
||||||
import { Department } from '@hcengineering/hr'
|
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'
|
import hr from '../../plugin'
|
||||||
|
|
||||||
@ -27,27 +31,48 @@
|
|||||||
export let selected: Ref<Department> | undefined
|
export let selected: Ref<Department> | undefined
|
||||||
export let level = 0
|
export let level = 0
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
function getDescendants (department: Ref<Department>): Ref<Department>[] {
|
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 {
|
function handleDepartmentSelected (department: Ref<Department>): void {
|
||||||
dispatch('selected', department)
|
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>
|
</script>
|
||||||
|
|
||||||
{#each departments as dep}
|
{#each _departments as department}
|
||||||
{@const department = departmentById.get(dep)}
|
{@const desc = _descendants.get(department._id) ?? []}
|
||||||
{@const desc = getDescendants(dep)}
|
|
||||||
|
|
||||||
{#if department}
|
{#if department}
|
||||||
<TreeElement
|
<TreeElement
|
||||||
|
_id={department._id}
|
||||||
icon={hr.icon.Department}
|
icon={hr.icon.Department}
|
||||||
title={department.name}
|
title={department.name}
|
||||||
selected={selected === department._id}
|
selected={selected === department._id}
|
||||||
node={desc.length > 0}
|
node={desc.length > 0}
|
||||||
|
actions={() => getActions(department)}
|
||||||
{level}
|
{level}
|
||||||
on:click={() => handleDepartmentSelected(department._id)}
|
on:click={() => handleDepartmentSelected(department._id)}
|
||||||
>
|
>
|
||||||
|
@ -34,19 +34,11 @@
|
|||||||
<NavHeader label={hr.string.HRApplication} />
|
<NavHeader label={hr.string.HRApplication} />
|
||||||
|
|
||||||
<Scroller shrink>
|
<Scroller shrink>
|
||||||
<!-- TODO Specials -->
|
|
||||||
|
|
||||||
<div class="antiNav-divider short line" />
|
<div class="antiNav-divider short line" />
|
||||||
|
|
||||||
<TreeNode label={hr.string.Departments} parent>
|
<TreeNode label={hr.string.Departments} parent>
|
||||||
<DepartmentsHierarchy {departments} {descendants} {departmentById} selected={department} on:selected />
|
<DepartmentsHierarchy {departments} {descendants} {departmentById} selected={department} on:selected />
|
||||||
</TreeNode>
|
</TreeNode>
|
||||||
|
|
||||||
<!-- TODO Add Positions -->
|
|
||||||
<!-- <div class="antiNav-divider short line" /> -->
|
|
||||||
|
|
||||||
<!-- <TreeNode label={hr.string.Positions} parent> -->
|
|
||||||
<!-- </TreeNode> -->
|
|
||||||
</Scroller>
|
</Scroller>
|
||||||
|
|
||||||
<NavFooter />
|
<NavFooter />
|
||||||
|
@ -13,11 +13,14 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
import { Doc, Ref } from '@hcengineering/core'
|
||||||
import type { Asset, IntlString } from '@hcengineering/platform'
|
import type { Asset, IntlString } from '@hcengineering/platform'
|
||||||
import type { AnySvelteComponent } from '@hcengineering/ui'
|
import type { AnySvelteComponent } from '@hcengineering/ui'
|
||||||
import { Icon, IconChevronDown, Label } from '@hcengineering/ui'
|
import { Icon, IconChevronDown, IconMoreH, Label, Menu, showPopup } from '@hcengineering/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { Action } from '@hcengineering/view'
|
||||||
|
|
||||||
|
export let _id: Ref<Doc> | undefined = undefined
|
||||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||||
export let iconProps: Record<string, any> | undefined = undefined
|
export let iconProps: Record<string, any> | undefined = undefined
|
||||||
export let label: IntlString | undefined = undefined
|
export let label: IntlString | undefined = undefined
|
||||||
@ -27,6 +30,15 @@
|
|||||||
export let collapsed = false
|
export let collapsed = false
|
||||||
export let selected = false
|
export let selected = false
|
||||||
export let level = 0
|
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);`
|
$: style = `padding-left: calc(${level} * 1.25rem);`
|
||||||
|
|
||||||
@ -36,10 +48,10 @@
|
|||||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div
|
<div
|
||||||
class="antiNav-element"
|
class="antiNav-element"
|
||||||
|
class:hovered
|
||||||
class:selected
|
class:selected
|
||||||
class:parent
|
class:parent
|
||||||
class:collapsed
|
class:collapsed
|
||||||
class:child={!node}
|
|
||||||
{style}
|
{style}
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
@ -59,12 +71,25 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
{#if node}
|
{#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'} />
|
<IconChevronDown size={'small'} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<div class="an-element__tool" on:click|preventDefault|stopPropagation={onMenuClick}>
|
||||||
|
<IconMoreH size={'small'} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if node && !collapsed}
|
{#if node && !collapsed}
|
||||||
<div class="antiNav-element__dropbox"><slot /></div>
|
<div class="antiNav-element__dropbox"><slot /></div>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import { Resources } from '@hcengineering/platform'
|
import { Resources } from '@hcengineering/platform'
|
||||||
|
import CreateDepartment from './components/CreateDepartment.svelte'
|
||||||
import DepartmentEditor from './components/DepartmentEditor.svelte'
|
import DepartmentEditor from './components/DepartmentEditor.svelte'
|
||||||
import DepartmentStaff from './components/DepartmentStaff.svelte'
|
import DepartmentStaff from './components/DepartmentStaff.svelte'
|
||||||
import EditDepartment from './components/EditDepartment.svelte'
|
import EditDepartment from './components/EditDepartment.svelte'
|
||||||
@ -34,6 +35,7 @@ async function editRequestType (object: Request): Promise<void> {
|
|||||||
export default async (): Promise<Resources> => ({
|
export default async (): Promise<Resources> => ({
|
||||||
component: {
|
component: {
|
||||||
Structure,
|
Structure,
|
||||||
|
CreateDepartment,
|
||||||
EditDepartment,
|
EditDepartment,
|
||||||
DepartmentStaff,
|
DepartmentStaff,
|
||||||
DepartmentEditor,
|
DepartmentEditor,
|
||||||
|
Loading…
Reference in New Issue
Block a user