mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-25 01:39:53 +00:00
Hr ui fix (#2112)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
parent
09130c5b69
commit
1091ced274
@ -34,6 +34,8 @@
|
||||
"@anticrm/model-view": "~0.6.0",
|
||||
"@anticrm/model-workbench": "~0.6.1",
|
||||
"@anticrm/model-contact": "~0.6.1",
|
||||
"@anticrm/model-chunter": "~0.6.0",
|
||||
"@anticrm/model-attachment": "~0.6.0",
|
||||
"@anticrm/hr": "~0.6.0",
|
||||
"@anticrm/hr-resources": "~0.6.0",
|
||||
"@anticrm/view": "~0.6.0"
|
||||
|
@ -14,14 +14,16 @@
|
||||
//
|
||||
|
||||
import { Employee } from '@anticrm/contact'
|
||||
import contact, { TEmployee } from '@anticrm/model-contact'
|
||||
import { IndexKind, Ref } from '@anticrm/core'
|
||||
import type { Department, Staff } from '@anticrm/hr'
|
||||
import { Builder, Index, Mixin, Model, Prop, TypeRef, TypeString, UX } from '@anticrm/model'
|
||||
import contact, { TEmployee, TEmployeeAccount } from '@anticrm/model-contact'
|
||||
import { Arr, IndexKind, Ref } from '@anticrm/core'
|
||||
import type { Department, DepartmentMember, Staff } from '@anticrm/hr'
|
||||
import { Builder, Index, Mixin, Model, Prop, TypeRef, Collection, TypeString, UX, ArrOf } from '@anticrm/model'
|
||||
import core, { TSpace } from '@anticrm/model-core'
|
||||
import workbench from '@anticrm/model-workbench'
|
||||
import hr from './plugin'
|
||||
import view, { createAction } from '@anticrm/model-view'
|
||||
import attachment from '@anticrm/model-attachment'
|
||||
import chunter from '@anticrm/model-chunter'
|
||||
|
||||
@Model(hr.class.Department, core.class.Space)
|
||||
@UX(hr.string.Department, hr.icon.Department)
|
||||
@ -33,12 +35,28 @@ export class TDepartment extends TSpace implements Department {
|
||||
@Index(IndexKind.FullText)
|
||||
name!: string
|
||||
|
||||
@Prop(Collection(contact.class.Channel), contact.string.ContactInfo)
|
||||
channels?: number
|
||||
|
||||
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
|
||||
attachments?: number
|
||||
|
||||
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
|
||||
comments?: number
|
||||
|
||||
avatar?: string | null
|
||||
|
||||
@Prop(TypeRef(contact.class.Employee), hr.string.TeamLead)
|
||||
teamLead!: Ref<Employee> | null
|
||||
|
||||
@Prop(ArrOf(TypeRef(hr.class.DepartmentMember)), contact.string.Members)
|
||||
declare members: Arr<Ref<DepartmentMember>>
|
||||
}
|
||||
|
||||
@Model(hr.class.DepartmentMember, contact.class.EmployeeAccount)
|
||||
@UX(contact.string.Employee, hr.icon.HR)
|
||||
export class TDepartmentMember extends TEmployeeAccount implements DepartmentMember {}
|
||||
|
||||
@Mixin(hr.mixin.Staff, contact.class.Employee)
|
||||
@UX(contact.string.Employee, hr.icon.HR)
|
||||
export class TStaff extends TEmployee implements Staff {
|
||||
@ -47,7 +65,7 @@ export class TStaff extends TEmployee implements Staff {
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TDepartment, TStaff)
|
||||
builder.createModel(TDepartment, TDepartmentMember, TStaff)
|
||||
|
||||
builder.createDoc(
|
||||
workbench.class.Application,
|
||||
@ -76,6 +94,14 @@ export function createModel (builder: Builder): void {
|
||||
inlineEditor: hr.component.DepartmentEditor
|
||||
})
|
||||
|
||||
builder.mixin(hr.class.Department, core.class.Class, view.mixin.ObjectEditor, {
|
||||
editor: hr.component.EditDepartment
|
||||
})
|
||||
|
||||
builder.mixin(hr.class.DepartmentMember, core.class.Class, view.mixin.ArrayEditor, {
|
||||
editor: hr.component.DepartmentStaff
|
||||
})
|
||||
|
||||
createAction(
|
||||
builder,
|
||||
{
|
||||
|
@ -118,7 +118,8 @@ export class TCollectionEditor extends TClass implements CollectionEditor {
|
||||
|
||||
@Mixin(view.mixin.ArrayEditor, core.class.Class)
|
||||
export class TArrayEditor extends TClass implements ArrayEditor {
|
||||
inlineEditor!: AnyComponent
|
||||
inlineEditor?: AnyComponent
|
||||
editor?: AnyComponent
|
||||
}
|
||||
|
||||
@Mixin(view.mixin.AttributePresenter, core.class.Class)
|
||||
|
@ -55,6 +55,9 @@
|
||||
|
||||
const typeClass = hierarchy.getClass(presenterClass.attrClass)
|
||||
const editorMixin = hierarchy.as(typeClass, mixinRef)
|
||||
if (category === 'array' && editorMixin.inlineEditor === undefined) {
|
||||
return
|
||||
}
|
||||
editor = getResource(editorMixin.inlineEditor).catch((cause) => {
|
||||
console.error(`failed to find editor for ${_class} ${attribute} ${presenterClass.attrClass} cause: ${cause}`)
|
||||
})
|
||||
|
@ -38,6 +38,7 @@
|
||||
"@anticrm/core": "~0.6.16",
|
||||
"@anticrm/panel": "~0.6.0",
|
||||
"@anticrm/contact": "~0.6.5",
|
||||
"@anticrm/view": "~0.6.0",
|
||||
"@anticrm/view-resources": "~0.6.0",
|
||||
"@anticrm/contact-resources": "~0.6.0",
|
||||
"@anticrm/setting": "~0.6.1",
|
||||
|
@ -19,25 +19,19 @@
|
||||
import CreateDepartment from './CreateDepartment.svelte'
|
||||
import DepartmentCard from './DepartmentCard.svelte'
|
||||
import hr from '../plugin'
|
||||
import { IconAdd, IconMoreV, Button, eventToHTMLElement, Label, showPopup, ActionIcon } from '@anticrm/ui'
|
||||
import { IconAdd, IconMoreV, Button, eventToHTMLElement, Label, showPopup, ActionIcon, showPanel } from '@anticrm/ui'
|
||||
import contact, { Employee } from '@anticrm/contact'
|
||||
import { EmployeePresenter } from '@anticrm/contact-resources'
|
||||
import DepartmentStaff from './DepartmentStaff.svelte'
|
||||
import { Menu } from '@anticrm/view-resources'
|
||||
import view from '@anticrm/view'
|
||||
|
||||
export let value: WithLookup<Department>
|
||||
export let descendants: Map<Ref<Department>, WithLookup<Department>[]>
|
||||
|
||||
$: currentDescendants = descendants.get(value._id) ?? []
|
||||
let expand = false
|
||||
|
||||
const client = getClient()
|
||||
|
||||
function toggle () {
|
||||
if (currentDescendants.length === 0) return
|
||||
expand = !expand
|
||||
}
|
||||
|
||||
async function changeLead (result: Employee | null | undefined): Promise<void> {
|
||||
if (result === undefined) {
|
||||
return
|
||||
@ -50,6 +44,8 @@
|
||||
}
|
||||
|
||||
function openLeadEditor (event: MouseEvent) {
|
||||
event?.preventDefault()
|
||||
event?.stopPropagation()
|
||||
showPopup(
|
||||
UsersPopup,
|
||||
{
|
||||
@ -64,13 +60,7 @@
|
||||
}
|
||||
|
||||
function createChild (e: MouseEvent) {
|
||||
showPopup(CreateDepartment, { space: value._id }, eventToHTMLElement(e), (res) => {
|
||||
if (res && !expand) expand = true
|
||||
})
|
||||
}
|
||||
|
||||
function editMembers (e: MouseEvent) {
|
||||
showPopup(DepartmentStaff, { _id: value._id }, 'float')
|
||||
showPopup(CreateDepartment, { space: value._id }, eventToHTMLElement(e))
|
||||
}
|
||||
|
||||
function showMenu (e: MouseEvent) {
|
||||
@ -82,29 +72,30 @@
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function edit (e: MouseEvent): void {
|
||||
showPanel(view.component.EditDoc, value._id, value._class, 'content')
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex-center w-full px-4">
|
||||
<div
|
||||
class="w-full mt-2 mb-2 container flex"
|
||||
class:cursor-pointer={currentDescendants.length}
|
||||
on:click|stopPropagation={toggle}
|
||||
on:click|stopPropagation={edit}
|
||||
on:contextmenu|preventDefault={showMenu}
|
||||
>
|
||||
{#if currentDescendants.length}
|
||||
<div class="verticalDivider" />
|
||||
<div class="verticalDivider" />
|
||||
{/if}
|
||||
<div class="flex-between pt-4 pb-4 pr-4 pl-2 w-full">
|
||||
<div class="flex-center">
|
||||
<div class="mr-2">
|
||||
<Button icon={IconAdd} on:click={createChild} />
|
||||
</div>
|
||||
<Avatar size={'medium'} avatar={value.avatar} icon={hr.icon.Department} />
|
||||
<div class="flex-row ml-2">
|
||||
<div class="fs-title">
|
||||
{value.name}
|
||||
</div>
|
||||
<div class="cursor-pointer" on:click|stopPropagation={editMembers}>
|
||||
<Label label={hr.string.MemberCount} params={{ count: value.members.length }} />
|
||||
</div>
|
||||
<Label label={hr.string.MemberCount} params={{ count: value.members.length }} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-center mr-2">
|
||||
@ -122,19 +113,16 @@
|
||||
onEmployeeEdit={openLeadEditor}
|
||||
/>
|
||||
</div>
|
||||
<Button icon={IconAdd} on:click={createChild} />
|
||||
<ActionIcon icon={IconMoreV} size={'medium'} action={showMenu} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if expand && currentDescendants.length}
|
||||
<div class="ml-8">
|
||||
{#each descendants.get(value._id) ?? [] as nested}
|
||||
<DepartmentCard value={nested} {descendants} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
<div class="ml-8">
|
||||
{#each currentDescendants as nested}
|
||||
<DepartmentCard value={nested} {descendants} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
|
@ -21,7 +21,7 @@
|
||||
import hr from '../plugin'
|
||||
|
||||
export let value: Ref<Department> | undefined
|
||||
export let label: IntlString = hr.string.Department
|
||||
export let label: IntlString = hr.string.ParentDepartmentLabel
|
||||
export let onChange: (value: any) => void
|
||||
export let kind: ButtonKind = 'no-border'
|
||||
export let size: ButtonSize = 'small'
|
||||
@ -31,7 +31,7 @@
|
||||
|
||||
<SpaceSelector
|
||||
_class={hr.class.Department}
|
||||
label={hr.string.ParentDepartmentLabel}
|
||||
{label}
|
||||
{size}
|
||||
{kind}
|
||||
{justify}
|
||||
|
@ -13,33 +13,31 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { Employee, EmployeeAccount } from '@anticrm/contact'
|
||||
import { Employee } from '@anticrm/contact'
|
||||
import { EmployeePresenter } from '@anticrm/contact-resources'
|
||||
import contact from '@anticrm/contact-resources/src/plugin'
|
||||
import { Ref, SortingOrder, WithLookup } from '@anticrm/core'
|
||||
import { Department, Staff } from '@anticrm/hr'
|
||||
import { Avatar, createQuery, MessageBox, getClient, UsersPopup } from '@anticrm/presentation'
|
||||
import { Scroller, Panel, Button, showPopup, eventToHTMLElement } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import StaffPresenter from './StaffPresenter.svelte'
|
||||
import { Department, DepartmentMember, Staff } from '@anticrm/hr'
|
||||
import { createQuery, getClient, MessageBox, UsersPopup } from '@anticrm/presentation'
|
||||
import { CircleButton, eventToHTMLElement, IconAdd, Label, Scroller, showPopup } from '@anticrm/ui'
|
||||
import hr from '../plugin'
|
||||
|
||||
export let _id: Ref<Department> | undefined
|
||||
export let objectId: Ref<Department> | undefined
|
||||
let value: Department | undefined
|
||||
let employees: WithLookup<Staff>[] = []
|
||||
let accounts: EmployeeAccount[] = []
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
let accounts: DepartmentMember[] = []
|
||||
|
||||
const departmentQuery = createQuery()
|
||||
const query = createQuery()
|
||||
const accountsQuery = createQuery()
|
||||
const client = getClient()
|
||||
|
||||
$: _id &&
|
||||
$: objectId &&
|
||||
value === undefined &&
|
||||
departmentQuery.query(
|
||||
hr.class.Department,
|
||||
{
|
||||
_id
|
||||
_id: objectId
|
||||
},
|
||||
(res) => ([value] = res)
|
||||
)
|
||||
@ -48,7 +46,7 @@
|
||||
accountsQuery.query(
|
||||
contact.class.EmployeeAccount,
|
||||
{
|
||||
_id: { $in: value.members as Ref<EmployeeAccount>[] }
|
||||
_id: { $in: value.members }
|
||||
},
|
||||
(res) => {
|
||||
accounts = res
|
||||
@ -79,7 +77,7 @@
|
||||
UsersPopup,
|
||||
{
|
||||
_class: contact.class.Employee,
|
||||
ignoreUsers: employees.filter((p) => p.department === _id).map((p) => p._id)
|
||||
ignoreUsers: employees.filter((p) => p.department === objectId).map((p) => p._id)
|
||||
},
|
||||
eventToHTMLElement(e),
|
||||
addMember
|
||||
@ -87,7 +85,7 @@
|
||||
}
|
||||
|
||||
async function addMember (employee: Employee | undefined): Promise<void> {
|
||||
if (employee === undefined || value === undefined) {
|
||||
if (employee === null || employee === undefined || value === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -131,32 +129,64 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Panel
|
||||
isHeader={true}
|
||||
isAside={false}
|
||||
isFullSize
|
||||
on:fullsize
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<svelte:fragment slot="title">
|
||||
<div class="antiTitle icon-wrapper">
|
||||
{#if value}
|
||||
<div class="wrapped-icon"><Avatar size={'medium'} avatar={value.avatar} icon={hr.icon.Department} /></div>
|
||||
<div class="title-wrapper">
|
||||
<span class="wrapped-title">{value.name}</span>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="container">
|
||||
<div class="flex flex-between">
|
||||
<div class="title"><Label label={contact.string.Members} /></div>
|
||||
<CircleButton id={hr.string.AddEmployee} icon={IconAdd} size={'small'} selected on:click={add} />
|
||||
</div>
|
||||
{#if employees.length > 0}
|
||||
<Scroller>
|
||||
<table class="antiTable">
|
||||
<thead class="scroller-thead">
|
||||
<tr class="scroller-thead__tr">
|
||||
<th><Label label={contact.string.Member} /></th>
|
||||
<th><Label label={hr.string.Department} /></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each employees as value}
|
||||
<tr class="antiTable-body__row">
|
||||
<td><EmployeePresenter {value} /></td>
|
||||
<td>
|
||||
{#if value.$lookup?.department}
|
||||
{value.$lookup.department.name}
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</Scroller>
|
||||
{:else}
|
||||
<div class="flex-col-center mt-5 create-container">
|
||||
<div class="text-sm content-dark-color mt-2">
|
||||
<Label label={contact.string.NoMembers} />
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
<div class="over-underline" on:click={add}><Label label={contact.string.AddMember} /></div>
|
||||
</div>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="utils">
|
||||
<Button label={hr.string.AddEmployee} kind={'primary'} on:click={add} />
|
||||
</svelte:fragment>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<Scroller>
|
||||
{#each employees as value}
|
||||
<StaffPresenter {value} />
|
||||
{/each}
|
||||
</Scroller>
|
||||
</Panel>
|
||||
<style lang="scss">
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
margin-right: 0.75rem;
|
||||
font-weight: 500;
|
||||
font-size: 1.25rem;
|
||||
color: var(--theme-caption-color);
|
||||
}
|
||||
}
|
||||
|
||||
.create-container {
|
||||
padding: 1rem;
|
||||
color: var(--theme-caption-color);
|
||||
background: var(--theme-bg-accent-color);
|
||||
border: 1px solid var(--theme-bg-accent-color);
|
||||
border-radius: 0.75rem;
|
||||
}
|
||||
</style>
|
||||
|
@ -14,11 +14,9 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createQuery, EditableAvatar, getClient } from '@anticrm/presentation'
|
||||
import { Panel } from '@anticrm/panel'
|
||||
import { createFocusManager, EditBox, FocusHandler } from '@anticrm/ui'
|
||||
|
||||
import { ActionContext } from '@anticrm/view-resources'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import { Department } from '@anticrm/hr'
|
||||
import core, { getCurrentAccount, Ref, Space } from '@anticrm/core'
|
||||
import hr from '../plugin'
|
||||
@ -27,20 +25,10 @@
|
||||
import { ChannelsEditor } from '@anticrm/contact-resources'
|
||||
import setting, { IntegrationType } from '@anticrm/setting'
|
||||
|
||||
export let _id: Ref<Department>
|
||||
let object: Department | undefined
|
||||
export let object: Department
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
const query = createQuery()
|
||||
query.query(
|
||||
hr.class.Department,
|
||||
{ _id },
|
||||
(res) => {
|
||||
object = res[0]
|
||||
},
|
||||
{ limit: 1 }
|
||||
)
|
||||
|
||||
async function onAvatarDone (e: any) {
|
||||
if (object === undefined) return
|
||||
@ -77,10 +65,6 @@
|
||||
|
||||
const manager = createFocusManager()
|
||||
|
||||
const _update = (result: any): void => {
|
||||
dispatch('update', result)
|
||||
}
|
||||
|
||||
let integrations: Set<Ref<IntegrationType>> = new Set<Ref<IntegrationType>>()
|
||||
const accountId = getCurrentAccount()._id
|
||||
const settingsQuery = createQuery()
|
||||
@ -91,63 +75,46 @@
|
||||
integrations = new Set(res.map((p) => p.type))
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<ActionContext
|
||||
context={{
|
||||
mode: 'editor'
|
||||
}}
|
||||
/>
|
||||
onMount(() => {
|
||||
dispatch('open', {
|
||||
ignoreKeys: ['comments', 'name', 'channels', 'private', 'archived'],
|
||||
collectionArrays: ['members']
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<FocusHandler {manager} />
|
||||
|
||||
{#if object !== undefined}
|
||||
<Panel
|
||||
icon={hr.icon.Department}
|
||||
title={object.name}
|
||||
{object}
|
||||
isHeader={false}
|
||||
isAside={true}
|
||||
on:update={(ev) => _update(ev.detail)}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
>
|
||||
<div class="flex-row-stretch flex-grow">
|
||||
<div class="mr-8">
|
||||
{#key object}
|
||||
<EditableAvatar
|
||||
avatar={object.avatar}
|
||||
size={'x-large'}
|
||||
icon={hr.icon.Department}
|
||||
on:done={onAvatarDone}
|
||||
on:remove={removeAvatar}
|
||||
/>
|
||||
{/key}
|
||||
<div class="flex-row-stretch flex-grow">
|
||||
<div class="mr-8">
|
||||
{#key object}
|
||||
<EditableAvatar
|
||||
avatar={object.avatar}
|
||||
size={'x-large'}
|
||||
icon={hr.icon.Department}
|
||||
on:done={onAvatarDone}
|
||||
on:remove={removeAvatar}
|
||||
/>
|
||||
{/key}
|
||||
</div>
|
||||
<div class="flex-grow flex-col">
|
||||
<div class="name">
|
||||
<EditBox
|
||||
placeholder={core.string.Name}
|
||||
maxWidth="20rem"
|
||||
bind:value={object.name}
|
||||
on:change={nameChange}
|
||||
focusIndex={1}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-grow flex-col">
|
||||
<div class="name">
|
||||
<EditBox
|
||||
placeholder={core.string.Name}
|
||||
maxWidth="20rem"
|
||||
bind:value={object.name}
|
||||
on:change={nameChange}
|
||||
focusIndex={1}
|
||||
/>
|
||||
</div>
|
||||
<div class="separator" />
|
||||
<div class="flex-row-center">
|
||||
<ChannelsEditor
|
||||
attachedTo={object._id}
|
||||
attachedClass={object._class}
|
||||
{integrations}
|
||||
focusIndex={10}
|
||||
on:click
|
||||
/>
|
||||
</div>
|
||||
<div class="separator" />
|
||||
<div class="flex-row-center">
|
||||
<ChannelsEditor attachedTo={object._id} attachedClass={object._class} {integrations} focusIndex={10} on:click />
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
|
@ -1,36 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { formatName } from '@anticrm/contact'
|
||||
import { WithLookup } from '@anticrm/core'
|
||||
import { Staff } from '@anticrm/hr'
|
||||
import { Avatar } from '@anticrm/presentation'
|
||||
|
||||
export let value: WithLookup<Staff>
|
||||
</script>
|
||||
|
||||
<div class="flex-between w-full p-4">
|
||||
<div class="flex-row-center">
|
||||
<Avatar avatar={value.avatar} size={'medium'} />
|
||||
<div class="fs-title ml-2">
|
||||
{formatName(value.name)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{#if value.$lookup?.department}
|
||||
{value.$lookup.department.name}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
@ -13,8 +13,8 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import type { Employee } from '@anticrm/contact'
|
||||
import type { Class, Doc, Mixin, Ref, Space } from '@anticrm/core'
|
||||
import type { Employee, EmployeeAccount } from '@anticrm/contact'
|
||||
import type { Arr, Class, Doc, Mixin, Ref, Space } from '@anticrm/core'
|
||||
import type { Asset, Plugin } from '@anticrm/platform'
|
||||
import { plugin } from '@anticrm/platform'
|
||||
|
||||
@ -25,8 +25,17 @@ export interface Department extends Space {
|
||||
space: Ref<Department>
|
||||
avatar?: string | null
|
||||
teamLead: Ref<Employee> | null
|
||||
attachments?: number
|
||||
comments?: number
|
||||
channels?: number
|
||||
members: Arr<Ref<DepartmentMember>>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface DepartmentMember extends EmployeeAccount {}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
@ -47,7 +56,8 @@ const hr = plugin(hrId, {
|
||||
HR: '' as Ref<Doc>
|
||||
},
|
||||
class: {
|
||||
Department: '' as Ref<Class<Department>>
|
||||
Department: '' as Ref<Class<Department>>,
|
||||
DepartmentMember: '' as Ref<Class<DepartmentMember>>
|
||||
},
|
||||
mixin: {
|
||||
Staff: '' as Ref<Mixin<Staff>>
|
||||
|
@ -22,23 +22,7 @@
|
||||
export let allowedCollections: string[] = []
|
||||
</script>
|
||||
|
||||
<ClassAttributeBar
|
||||
_class={object._class}
|
||||
{object}
|
||||
{ignoreKeys}
|
||||
to={undefined}
|
||||
{allowedCollections}
|
||||
vertical
|
||||
on:update
|
||||
/>
|
||||
<ClassAttributeBar _class={object._class} {object} {ignoreKeys} to={undefined} {allowedCollections} on:update />
|
||||
{#each mixins as mixin}
|
||||
<ClassAttributeBar
|
||||
_class={mixin._id}
|
||||
{object}
|
||||
{ignoreKeys}
|
||||
to={object._class}
|
||||
{allowedCollections}
|
||||
vertical
|
||||
on:update
|
||||
/>
|
||||
<ClassAttributeBar _class={mixin._id} {object} {ignoreKeys} to={object._class} {allowedCollections} on:update />
|
||||
{/each}
|
||||
|
@ -91,6 +91,7 @@
|
||||
|
||||
let ignoreKeys: string[] = []
|
||||
let allowedCollections: string[] = []
|
||||
let collectionArrays: string[] = []
|
||||
let ignoreMixins: Set<Ref<Mixin<Doc>>> = new Set<Ref<Mixin<Doc>>>()
|
||||
|
||||
async function updateKeys (): Promise<void> {
|
||||
@ -102,12 +103,14 @@
|
||||
}
|
||||
}
|
||||
const filtredKeys = Array.from(keysMap.values())
|
||||
keys = collectionsFilter(hierarchy, filtredKeys, false)
|
||||
keys = collectionsFilter(hierarchy, filtredKeys, false, allowedCollections)
|
||||
|
||||
const collectionKeys = collectionsFilter(hierarchy, filtredKeys, true)
|
||||
const collectionKeys = collectionsFilter(hierarchy, filtredKeys, true, collectionArrays)
|
||||
const editors: { key: KeyedAttribute; editor: AnyComponent }[] = []
|
||||
for (const k of collectionKeys) {
|
||||
if (allowedCollections.includes(k.key)) continue
|
||||
const editor = await getCollectionEditor(k)
|
||||
if (editor === undefined) continue
|
||||
editors.push({ key: k, editor })
|
||||
}
|
||||
collectionEditors = editors
|
||||
@ -129,10 +132,11 @@
|
||||
updateKeys()
|
||||
}
|
||||
|
||||
async function getCollectionEditor (key: KeyedAttribute): Promise<AnyComponent> {
|
||||
async function getCollectionEditor (key: KeyedAttribute): Promise<AnyComponent | undefined> {
|
||||
const attrClass = getAttributePresenterClass(hierarchy, key.attr)
|
||||
const clazz = hierarchy.getClass(attrClass.attrClass)
|
||||
const editorMixin = hierarchy.as(clazz, view.mixin.CollectionEditor)
|
||||
const mixinRef = attrClass.category === 'array' ? view.mixin.ArrayEditor : view.mixin.CollectionEditor
|
||||
const editorMixin = hierarchy.as(clazz, mixinRef)
|
||||
return editorMixin.editor
|
||||
}
|
||||
|
||||
@ -243,7 +247,13 @@
|
||||
on:update={updateKeys}
|
||||
/>
|
||||
{:else if dir === 'column'}
|
||||
<DocAttributeBar {object} {mixins} {ignoreKeys} {allowedCollections} on:update={updateKeys} />
|
||||
<DocAttributeBar
|
||||
{object}
|
||||
{mixins}
|
||||
ignoreKeys={[...ignoreKeys, ...collectionArrays]}
|
||||
{allowedCollections}
|
||||
on:update={updateKeys}
|
||||
/>
|
||||
{:else}
|
||||
<AttributesBar {object} _class={realObjectClass} {keys} />
|
||||
{/if}
|
||||
@ -258,12 +268,13 @@
|
||||
ignoreKeys = ev.detail.ignoreKeys
|
||||
ignoreMixins = new Set(ev.detail.ignoreMixins)
|
||||
allowedCollections = ev.detail.allowedCollections ?? []
|
||||
collectionArrays = ev.detail.collectionArrays ?? []
|
||||
getMixins(parentClass, object)
|
||||
updateKeys()
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{#each collectionEditors.filter((it) => !allowedCollections.includes(it.key.key)) as collection}
|
||||
{#each collectionEditors as collection}
|
||||
{#if collection.editor}
|
||||
<div class="mt-6">
|
||||
<Component
|
||||
|
@ -381,10 +381,19 @@ export function getFiltredKeys (
|
||||
return filterKeys(hierarchy, keys, ignoreKeys)
|
||||
}
|
||||
|
||||
export function collectionsFilter (hierarchy: Hierarchy, keys: KeyedAttribute[], get: boolean): KeyedAttribute[] {
|
||||
export function collectionsFilter (
|
||||
hierarchy: Hierarchy,
|
||||
keys: KeyedAttribute[],
|
||||
get: boolean,
|
||||
include: string[]
|
||||
): KeyedAttribute[] {
|
||||
const result: KeyedAttribute[] = []
|
||||
for (const key of keys) {
|
||||
if (isCollectionAttr(hierarchy, key) === get) result.push(key)
|
||||
if (include.includes(key.key)) {
|
||||
result.push(key)
|
||||
} else if (isCollectionAttr(hierarchy, key) === get) {
|
||||
result.push(key)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@ -99,7 +99,8 @@ export interface CollectionEditor extends Class<Doc> {
|
||||
* @public
|
||||
*/
|
||||
export interface ArrayEditor extends Class<Doc> {
|
||||
inlineEditor: AnyComponent
|
||||
editor?: AnyComponent
|
||||
inlineEditor?: AnyComponent
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -14,8 +14,8 @@
|
||||
//
|
||||
|
||||
import contact, { Employee } from '@anticrm/contact'
|
||||
import core, { Account, Ref, SortingOrder, Tx, TxFactory, TxMixin } from '@anticrm/core'
|
||||
import hr, { Department, Staff } from '@anticrm/hr'
|
||||
import core, { Ref, SortingOrder, Tx, TxFactory, TxMixin } from '@anticrm/core'
|
||||
import hr, { Department, DepartmentMember, Staff } from '@anticrm/hr'
|
||||
import { extractTx, TriggerControl } from '@anticrm/server-core'
|
||||
|
||||
async function getOldDepartment (
|
||||
@ -67,7 +67,7 @@ function exlude (first: Ref<Department>[], second: Ref<Department>[]): Ref<Depar
|
||||
|
||||
function getTxes (
|
||||
factory: TxFactory,
|
||||
account: Ref<Account>,
|
||||
account: Ref<DepartmentMember>,
|
||||
added: Ref<Department>[],
|
||||
removed?: Ref<Department>[]
|
||||
): Tx[] {
|
||||
|
Loading…
Reference in New Issue
Block a user