mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 11:01:54 +03:00
Inactive employee (#2157)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
parent
61216392e9
commit
168ae1cd54
@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
## 0.6.30 (upcoming)
|
## 0.6.30 (upcoming)
|
||||||
|
|
||||||
|
Core:
|
||||||
|
|
||||||
|
- Allow to leave workspace
|
||||||
|
- Allow to kick employee
|
||||||
|
|
||||||
HR:
|
HR:
|
||||||
|
|
||||||
- Allow to change assignee in Kanban
|
- Allow to change assignee in Kanban
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
"@anticrm/ui": "~0.6.0",
|
"@anticrm/ui": "~0.6.0",
|
||||||
"@anticrm/platform": "~0.6.6",
|
"@anticrm/platform": "~0.6.6",
|
||||||
"@anticrm/contact": "~0.6.5",
|
"@anticrm/contact": "~0.6.5",
|
||||||
"@anticrm/contact-resources": "~0.6.0"
|
"@anticrm/contact-resources": "~0.6.0",
|
||||||
|
"@anticrm/view": "~0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,6 +121,8 @@ export class TStatus extends TAttachedDoc implements Status {
|
|||||||
@Model(contact.class.Employee, contact.class.Person)
|
@Model(contact.class.Employee, contact.class.Person)
|
||||||
@UX(contact.string.Employee, contact.icon.Person, undefined, 'name')
|
@UX(contact.string.Employee, contact.icon.Person, undefined, 'name')
|
||||||
export class TEmployee extends TPerson implements Employee {
|
export class TEmployee extends TPerson implements Employee {
|
||||||
|
active!: boolean
|
||||||
|
|
||||||
@Prop(Collection(contact.class.Status), contact.string.Status)
|
@Prop(Collection(contact.class.Status), contact.string.Status)
|
||||||
statuses?: number
|
statuses?: number
|
||||||
}
|
}
|
||||||
@ -243,7 +245,7 @@ export function createModel (builder: Builder): void {
|
|||||||
})
|
})
|
||||||
|
|
||||||
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.AttributeEditor, {
|
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.AttributeEditor, {
|
||||||
inlineEditor: contact.component.PersonEditor
|
inlineEditor: contact.component.EmployeeEditor
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.mixin(contact.class.Channel, core.class.Class, view.mixin.AttributePresenter, {
|
builder.mixin(contact.class.Channel, core.class.Class, view.mixin.AttributePresenter, {
|
||||||
@ -393,6 +395,22 @@ export function createModel (builder: Builder): void {
|
|||||||
group: 'create'
|
group: 'create'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
createAction(
|
||||||
|
builder,
|
||||||
|
{
|
||||||
|
action: contact.actionImpl.KickEmployee,
|
||||||
|
label: contact.string.KickEmployee,
|
||||||
|
category: contact.category.Contact,
|
||||||
|
target: contact.class.Employee,
|
||||||
|
input: 'focus',
|
||||||
|
context: {
|
||||||
|
mode: ['context'],
|
||||||
|
group: 'other'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
contact.action.KickEmployee
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export { contactOperation } from './migration'
|
export { contactOperation } from './migration'
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
import { TxOperations } from '@anticrm/core'
|
import { TxOperations } from '@anticrm/core'
|
||||||
import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model'
|
import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model'
|
||||||
import core from '@anticrm/model-core'
|
import core from '@anticrm/model-core'
|
||||||
import contact from './index'
|
import contact, { DOMAIN_CONTACT } from './index'
|
||||||
|
|
||||||
async function createSpace (tx: TxOperations): Promise<void> {
|
async function createSpace (tx: TxOperations): Promise<void> {
|
||||||
const current = await tx.findOne(core.class.Space, {
|
const current = await tx.findOne(core.class.Space, {
|
||||||
@ -56,8 +56,22 @@ async function createSpace (tx: TxOperations): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setActiveEmployee (client: MigrationClient): Promise<void> {
|
||||||
|
await client.update(
|
||||||
|
DOMAIN_CONTACT,
|
||||||
|
{
|
||||||
|
_class: contact.class.Employee
|
||||||
|
},
|
||||||
|
{
|
||||||
|
active: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export const contactOperation: MigrateOperation = {
|
export const contactOperation: MigrateOperation = {
|
||||||
async migrate (client: MigrationClient): Promise<void> {},
|
async migrate (client: MigrationClient): Promise<void> {
|
||||||
|
await setActiveEmployee(client)
|
||||||
|
},
|
||||||
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
async upgrade (client: MigrationUpgradeClient): Promise<void> {
|
||||||
const tx = new TxOperations(client, core.account.System)
|
const tx = new TxOperations(client, core.account.System)
|
||||||
await createSpace(tx)
|
await createSpace(tx)
|
||||||
|
@ -20,6 +20,7 @@ import {} from '@anticrm/core'
|
|||||||
import { ObjectSearchCategory, ObjectSearchFactory } from '@anticrm/model-presentation'
|
import { ObjectSearchCategory, ObjectSearchFactory } from '@anticrm/model-presentation'
|
||||||
import { IntlString, mergeIds, Resource } from '@anticrm/platform'
|
import { IntlString, mergeIds, Resource } from '@anticrm/platform'
|
||||||
import type { AnyComponent } from '@anticrm/ui'
|
import type { AnyComponent } from '@anticrm/ui'
|
||||||
|
import { Action, ActionCategory, ViewAction } from '@anticrm/view'
|
||||||
|
|
||||||
export default mergeIds(contactId, contact, {
|
export default mergeIds(contactId, contact, {
|
||||||
component: {
|
component: {
|
||||||
@ -38,7 +39,8 @@ export default mergeIds(contactId, contact, {
|
|||||||
Members: '' as AnyComponent,
|
Members: '' as AnyComponent,
|
||||||
MemberPresenter: '' as AnyComponent,
|
MemberPresenter: '' as AnyComponent,
|
||||||
EditMember: '' as AnyComponent,
|
EditMember: '' as AnyComponent,
|
||||||
EmployeeArrayEditor: '' as AnyComponent
|
EmployeeArrayEditor: '' as AnyComponent,
|
||||||
|
EmployeeEditor: '' as AnyComponent
|
||||||
},
|
},
|
||||||
string: {
|
string: {
|
||||||
Persons: '' as IntlString,
|
Persons: '' as IntlString,
|
||||||
@ -71,5 +73,14 @@ export default mergeIds(contactId, contact, {
|
|||||||
EmployeeCategory: '' as Ref<ObjectSearchCategory>,
|
EmployeeCategory: '' as Ref<ObjectSearchCategory>,
|
||||||
PersonCategory: '' as Ref<ObjectSearchCategory>,
|
PersonCategory: '' as Ref<ObjectSearchCategory>,
|
||||||
OrganizationCategory: '' as Ref<ObjectSearchCategory>
|
OrganizationCategory: '' as Ref<ObjectSearchCategory>
|
||||||
|
},
|
||||||
|
category: {
|
||||||
|
Contact: '' as Ref<ActionCategory>
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
KickEmployee: '' as Ref<Action>
|
||||||
|
},
|
||||||
|
actionImpl: {
|
||||||
|
KickEmployee: '' as ViewAction
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -54,7 +54,8 @@ export const demoOperation: MigrateOperation = {
|
|||||||
if (current === undefined) {
|
if (current === undefined) {
|
||||||
const employee = await tx.createDoc(contact.class.Employee, contact.space.Employee, {
|
const employee = await tx.createDoc(contact.class.Employee, contact.space.Employee, {
|
||||||
name: 'Chen,Rosamund',
|
name: 'Chen,Rosamund',
|
||||||
city: 'Mountain View'
|
city: 'Mountain View',
|
||||||
|
active: true
|
||||||
})
|
})
|
||||||
|
|
||||||
await tx.createDoc<EmployeeAccount>(contact.class.EmployeeAccount, core.space.Model, {
|
await tx.createDoc<EmployeeAccount>(contact.class.EmployeeAccount, core.space.Model, {
|
||||||
|
60
packages/presentation/src/components/EmployeeBox.svelte
Normal file
60
packages/presentation/src/components/EmployeeBox.svelte
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<!--
|
||||||
|
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||||
|
// Copyright © 2021 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 contact, { Employee } from '@anticrm/contact'
|
||||||
|
import type { Class, DocumentQuery, FindOptions, Ref } from '@anticrm/core'
|
||||||
|
import type { IntlString } from '@anticrm/platform'
|
||||||
|
import { ButtonKind, ButtonSize, LabelAndProps } from '@anticrm/ui'
|
||||||
|
import presentation from '..'
|
||||||
|
import IconPerson from './icons/Person.svelte'
|
||||||
|
import UserBox from './UserBox.svelte'
|
||||||
|
|
||||||
|
export let _class: Ref<Class<Employee>> = contact.class.Employee
|
||||||
|
export let options: FindOptions<Employee> | undefined = undefined
|
||||||
|
export let docQuery: DocumentQuery<Employee> | undefined = {
|
||||||
|
active: true
|
||||||
|
}
|
||||||
|
export let label: IntlString
|
||||||
|
export let placeholder: IntlString = presentation.string.Search
|
||||||
|
export let value: Ref<Employee> | null | undefined
|
||||||
|
export let allowDeselect = false
|
||||||
|
export let titleDeselect: IntlString | undefined = undefined
|
||||||
|
export let kind: ButtonKind = 'no-border'
|
||||||
|
export let size: ButtonSize = 'small'
|
||||||
|
export let justify: 'left' | 'center' = 'center'
|
||||||
|
export let width: string | undefined = undefined
|
||||||
|
export let focusIndex = -1
|
||||||
|
export let showTooltip: LabelAndProps | undefined = undefined
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<UserBox
|
||||||
|
{_class}
|
||||||
|
{options}
|
||||||
|
{docQuery}
|
||||||
|
{label}
|
||||||
|
icon={IconPerson}
|
||||||
|
{placeholder}
|
||||||
|
bind:value
|
||||||
|
{allowDeselect}
|
||||||
|
{titleDeselect}
|
||||||
|
{kind}
|
||||||
|
{size}
|
||||||
|
{justify}
|
||||||
|
{width}
|
||||||
|
{focusIndex}
|
||||||
|
{showTooltip}
|
||||||
|
on:change
|
||||||
|
/>
|
@ -16,7 +16,6 @@
|
|||||||
import type { Class, Doc, DocumentQuery, FindOptions, Ref } from '@anticrm/core'
|
import type { Class, Doc, DocumentQuery, FindOptions, Ref } from '@anticrm/core'
|
||||||
import type { IntlString } from '@anticrm/platform'
|
import type { IntlString } from '@anticrm/platform'
|
||||||
import {
|
import {
|
||||||
AnyComponent,
|
|
||||||
Button,
|
Button,
|
||||||
CheckBox,
|
CheckBox,
|
||||||
createFocusManager,
|
createFocusManager,
|
||||||
@ -32,6 +31,7 @@
|
|||||||
import { createEventDispatcher, afterUpdate } from 'svelte'
|
import { createEventDispatcher, afterUpdate } from 'svelte'
|
||||||
import presentation from '..'
|
import presentation from '..'
|
||||||
import { createQuery, getClient } from '../utils'
|
import { createQuery, getClient } from '../utils'
|
||||||
|
import { ObjectCreate } from '../types'
|
||||||
|
|
||||||
export let _class: Ref<Class<Doc>>
|
export let _class: Ref<Class<Doc>>
|
||||||
export let options: FindOptions<Doc> | undefined = undefined
|
export let options: FindOptions<Doc> | undefined = undefined
|
||||||
@ -53,13 +53,7 @@
|
|||||||
|
|
||||||
export let groupBy = '_class'
|
export let groupBy = '_class'
|
||||||
|
|
||||||
export let create:
|
export let create: ObjectCreate | undefined = undefined
|
||||||
| {
|
|
||||||
component: AnyComponent
|
|
||||||
label: IntlString
|
|
||||||
update: (doc: Doc) => string
|
|
||||||
}
|
|
||||||
| undefined = undefined
|
|
||||||
|
|
||||||
let search: string = ''
|
let search: string = ''
|
||||||
let objects: Doc[] = []
|
let objects: Doc[] = []
|
||||||
@ -151,7 +145,7 @@
|
|||||||
// We expect reference to new object.
|
// We expect reference to new object.
|
||||||
const newPerson = await client.findOne(_class, { _id: res })
|
const newPerson = await client.findOne(_class, { _id: res })
|
||||||
if (newPerson !== undefined) {
|
if (newPerson !== undefined) {
|
||||||
search = c.update(newPerson)
|
search = c.update?.(newPerson) ?? ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
Button,
|
Button,
|
||||||
eventToHTMLElement,
|
eventToHTMLElement,
|
||||||
getFocusManager,
|
getFocusManager,
|
||||||
AnyComponent,
|
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipAlignment,
|
TooltipAlignment,
|
||||||
ButtonKind,
|
ButtonKind,
|
||||||
@ -33,6 +32,7 @@
|
|||||||
|
|
||||||
import type { Ref, Class, Space, DocumentQuery } from '@anticrm/core'
|
import type { Ref, Class, Space, DocumentQuery } from '@anticrm/core'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
import { ObjectCreate } from '../types'
|
||||||
|
|
||||||
export let _class: Ref<Class<Space>>
|
export let _class: Ref<Class<Space>>
|
||||||
export let spaceQuery: DocumentQuery<Space> | undefined = { archived: false }
|
export let spaceQuery: DocumentQuery<Space> | undefined = { archived: false }
|
||||||
@ -40,17 +40,13 @@
|
|||||||
export let value: Ref<Space> | undefined
|
export let value: Ref<Space> | undefined
|
||||||
export let focusIndex = -1
|
export let focusIndex = -1
|
||||||
export let focus = false
|
export let focus = false
|
||||||
export let create:
|
export let create: ObjectCreate | undefined = undefined
|
||||||
| {
|
|
||||||
component: AnyComponent
|
|
||||||
label: IntlString
|
|
||||||
}
|
|
||||||
| undefined = undefined
|
|
||||||
export let labelDirection: TooltipAlignment | undefined = undefined
|
export let labelDirection: TooltipAlignment | undefined = undefined
|
||||||
export let kind: ButtonKind = 'no-border'
|
export let kind: ButtonKind = 'no-border'
|
||||||
export let size: ButtonSize = 'small'
|
export let size: ButtonSize = 'small'
|
||||||
export let justify: 'left' | 'center' = 'center'
|
export let justify: 'left' | 'center' = 'center'
|
||||||
export let width: string | undefined = undefined
|
export let width: string | undefined = undefined
|
||||||
|
export let allowDeselect = false
|
||||||
|
|
||||||
let selected: Space | undefined
|
let selected: Space | undefined
|
||||||
|
|
||||||
@ -70,6 +66,7 @@
|
|||||||
{
|
{
|
||||||
_class,
|
_class,
|
||||||
label,
|
label,
|
||||||
|
allowDeselect,
|
||||||
options: { sort: { modifiedOn: -1 } },
|
options: { sort: { modifiedOn: -1 } },
|
||||||
selected,
|
selected,
|
||||||
spaceQuery,
|
spaceQuery,
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Class, DocumentQuery, Ref, Space } from '@anticrm/core'
|
import type { Class, DocumentQuery, Ref, Space } from '@anticrm/core'
|
||||||
import type { IntlString } from '@anticrm/platform'
|
import type { IntlString } from '@anticrm/platform'
|
||||||
import { AnyComponent, ButtonKind, ButtonSize } from '@anticrm/ui'
|
import { ButtonKind, ButtonSize } from '@anticrm/ui'
|
||||||
|
import { ObjectCreate } from '../types'
|
||||||
import SpaceSelect from './SpaceSelect.svelte'
|
import SpaceSelect from './SpaceSelect.svelte'
|
||||||
|
|
||||||
export let space: Ref<Space> | undefined = undefined
|
export let space: Ref<Space> | undefined = undefined
|
||||||
@ -26,13 +27,9 @@
|
|||||||
export let size: ButtonSize = 'small'
|
export let size: ButtonSize = 'small'
|
||||||
export let justify: 'left' | 'center' = 'center'
|
export let justify: 'left' | 'center' = 'center'
|
||||||
export let width: string | undefined = undefined
|
export let width: string | undefined = undefined
|
||||||
|
export let allowDeselect = false
|
||||||
|
|
||||||
export let create:
|
export let create: ObjectCreate | undefined = undefined
|
||||||
| {
|
|
||||||
component: AnyComponent
|
|
||||||
label: IntlString
|
|
||||||
}
|
|
||||||
| undefined = undefined
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SpaceSelect
|
<SpaceSelect
|
||||||
@ -41,6 +38,7 @@
|
|||||||
focusIndex={-10}
|
focusIndex={-10}
|
||||||
{_class}
|
{_class}
|
||||||
spaceQuery={query}
|
spaceQuery={query}
|
||||||
|
{allowDeselect}
|
||||||
{label}
|
{label}
|
||||||
{size}
|
{size}
|
||||||
{kind}
|
{kind}
|
||||||
|
@ -14,20 +14,15 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Class, Doc, DocumentQuery, Ref, Space } from '@anticrm/core'
|
import type { Class, Doc, DocumentQuery, Ref, Space } from '@anticrm/core'
|
||||||
import { IntlString } from '@anticrm/platform'
|
import { ObjectCreate } from '../types'
|
||||||
import { AnyComponent } from '@anticrm/ui'
|
|
||||||
import ObjectPopup from './ObjectPopup.svelte'
|
import ObjectPopup from './ObjectPopup.svelte'
|
||||||
import SpaceInfo from './SpaceInfo.svelte'
|
import SpaceInfo from './SpaceInfo.svelte'
|
||||||
|
|
||||||
export let _class: Ref<Class<Space>>
|
export let _class: Ref<Class<Space>>
|
||||||
export let selected: Ref<Space> | undefined
|
export let selected: Ref<Space> | undefined
|
||||||
export let spaceQuery: DocumentQuery<Space> | undefined
|
export let spaceQuery: DocumentQuery<Space> | undefined
|
||||||
export let create:
|
export let create: ObjectCreate | undefined = undefined
|
||||||
| {
|
export let allowDeselect = false
|
||||||
component: AnyComponent
|
|
||||||
label: IntlString
|
|
||||||
}
|
|
||||||
| undefined = undefined
|
|
||||||
|
|
||||||
$: _create =
|
$: _create =
|
||||||
create !== undefined
|
create !== undefined
|
||||||
@ -43,7 +38,7 @@
|
|||||||
{selected}
|
{selected}
|
||||||
bind:docQuery={spaceQuery}
|
bind:docQuery={spaceQuery}
|
||||||
multiSelect={false}
|
multiSelect={false}
|
||||||
allowDeselect={false}
|
{allowDeselect}
|
||||||
shadows={true}
|
shadows={true}
|
||||||
create={_create}
|
create={_create}
|
||||||
on:update
|
on:update
|
||||||
|
@ -15,10 +15,9 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Contact, formatName } from '@anticrm/contact'
|
import contact, { Contact, formatName } from '@anticrm/contact'
|
||||||
import type { Class, FindOptions, Ref } from '@anticrm/core'
|
import type { Class, DocumentQuery, FindOptions, Ref } from '@anticrm/core'
|
||||||
import type { Asset, IntlString } from '@anticrm/platform'
|
import type { Asset, IntlString } from '@anticrm/platform'
|
||||||
import {
|
import {
|
||||||
AnyComponent,
|
|
||||||
AnySvelteComponent,
|
AnySvelteComponent,
|
||||||
Button,
|
Button,
|
||||||
ButtonKind,
|
ButtonKind,
|
||||||
@ -30,6 +29,7 @@
|
|||||||
} from '@anticrm/ui'
|
} from '@anticrm/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import presentation from '..'
|
import presentation from '..'
|
||||||
|
import { ObjectCreate } from '../types'
|
||||||
import { getClient } from '../utils'
|
import { getClient } from '../utils'
|
||||||
import IconPerson from './icons/Person.svelte'
|
import IconPerson from './icons/Person.svelte'
|
||||||
import UserInfo from './UserInfo.svelte'
|
import UserInfo from './UserInfo.svelte'
|
||||||
@ -38,6 +38,7 @@
|
|||||||
export let _class: Ref<Class<Contact>>
|
export let _class: Ref<Class<Contact>>
|
||||||
export let excluded: Ref<Contact>[] | undefined = undefined
|
export let excluded: Ref<Contact>[] | undefined = undefined
|
||||||
export let options: FindOptions<Contact> | undefined = undefined
|
export let options: FindOptions<Contact> | undefined = undefined
|
||||||
|
export let docQuery: DocumentQuery<Contact> | undefined = undefined
|
||||||
export let label: IntlString
|
export let label: IntlString
|
||||||
export let icon: Asset | AnySvelteComponent | undefined = IconPerson
|
export let icon: Asset | AnySvelteComponent | undefined = IconPerson
|
||||||
export let placeholder: IntlString = presentation.string.Search
|
export let placeholder: IntlString = presentation.string.Search
|
||||||
@ -52,12 +53,7 @@
|
|||||||
export let focusIndex = -1
|
export let focusIndex = -1
|
||||||
export let showTooltip: LabelAndProps | undefined = undefined
|
export let showTooltip: LabelAndProps | undefined = undefined
|
||||||
|
|
||||||
export let create:
|
export let create: ObjectCreate | undefined = undefined
|
||||||
| {
|
|
||||||
component: AnyComponent
|
|
||||||
label: IntlString
|
|
||||||
}
|
|
||||||
| undefined = undefined
|
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
@ -85,6 +81,7 @@
|
|||||||
{
|
{
|
||||||
_class,
|
_class,
|
||||||
options,
|
options,
|
||||||
|
docQuery,
|
||||||
ignoreUsers: excluded ?? [],
|
ignoreUsers: excluded ?? [],
|
||||||
icon,
|
icon,
|
||||||
allowDeselect,
|
allowDeselect,
|
||||||
@ -108,54 +105,31 @@
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: hideIcon = size === 'x-large' || (size === 'large' && kind !== 'link')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div bind:this={container} class="min-w-0" class:w-full={width === '100%'}>
|
<div bind:this={container} class="min-w-0" class:w-full={width === '100%'}>
|
||||||
{#if kind !== 'link'}
|
<Button
|
||||||
<Button
|
{focusIndex}
|
||||||
{focusIndex}
|
icon={hideIcon && selected ? undefined : icon}
|
||||||
icon={size === 'x-large' && selected ? undefined : icon}
|
width={width ?? 'min-content'}
|
||||||
width={width ?? 'min-content'}
|
{size}
|
||||||
{size}
|
{kind}
|
||||||
{kind}
|
{justify}
|
||||||
{justify}
|
{showTooltip}
|
||||||
{showTooltip}
|
on:click={_click}
|
||||||
on:click={_click}
|
>
|
||||||
>
|
<span slot="content" class="overflow-label disabled">
|
||||||
<span slot="content" class="overflow-label disabled">
|
{#if selected}
|
||||||
{#if selected}
|
{#if hideIcon}
|
||||||
{#if size === 'x-large'}
|
<UserInfo value={selected} size={kind === 'link' ? 'x-small' : 'medium'} {icon} />
|
||||||
<UserInfo value={selected} size={'medium'} {icon} />
|
|
||||||
{:else}
|
|
||||||
{getName(selected)}
|
|
||||||
{/if}
|
|
||||||
{:else}
|
{:else}
|
||||||
<Label {label} />
|
{getName(selected)}
|
||||||
{/if}
|
{/if}
|
||||||
</span>
|
{:else}
|
||||||
</Button>
|
<Label {label} />
|
||||||
{:else}
|
{/if}
|
||||||
<Button
|
</span>
|
||||||
{focusIndex}
|
</Button>
|
||||||
icon={(size === 'x-large' || size === 'large') && selected ? undefined : icon}
|
|
||||||
width={width ?? 'min-content'}
|
|
||||||
{size}
|
|
||||||
{kind}
|
|
||||||
{justify}
|
|
||||||
{showTooltip}
|
|
||||||
on:click={_click}
|
|
||||||
>
|
|
||||||
<span slot="content" class="overflow-label disabled">
|
|
||||||
{#if selected}
|
|
||||||
{#if size === 'x-large' || size === 'large'}
|
|
||||||
<UserInfo value={selected} size={'x-small'} {icon} />
|
|
||||||
{:else}
|
|
||||||
{getName(selected)}
|
|
||||||
{/if}
|
|
||||||
{:else}
|
|
||||||
<Label {label} />
|
|
||||||
{/if}
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,20 +13,22 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Person } from '@anticrm/contact'
|
import contact, { Employee } from '@anticrm/contact'
|
||||||
import type { Class, Doc, Ref } from '@anticrm/core'
|
import type { Class, DocumentQuery, Ref } from '@anticrm/core'
|
||||||
import type { IntlString } from '@anticrm/platform'
|
import type { IntlString } from '@anticrm/platform'
|
||||||
import { translate } from '@anticrm/platform'
|
import { ButtonKind, ButtonSize, Label, TooltipAlignment } from '@anticrm/ui'
|
||||||
import type { ButtonKind, ButtonSize, TooltipAlignment } from '@anticrm/ui'
|
|
||||||
import { Tooltip, showPopup, Button } from '@anticrm/ui'
|
import { Tooltip, showPopup, Button } from '@anticrm/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import presentation, { CombineAvatars, UsersPopup } from '..'
|
import presentation, { CombineAvatars, UsersPopup } from '..'
|
||||||
import { createQuery } from '../utils'
|
import { createQuery } from '../utils'
|
||||||
import Members from './icons/Members.svelte'
|
import Members from './icons/Members.svelte'
|
||||||
|
|
||||||
export let items: Ref<Person>[] = []
|
export let items: Ref<Employee>[] = []
|
||||||
export let _class: Ref<Class<Doc>>
|
export let _class: Ref<Class<Employee>> = contact.class.Employee
|
||||||
export let label: IntlString
|
export let label: IntlString
|
||||||
|
export let docQuery: DocumentQuery<Employee> | undefined = {
|
||||||
|
active: true
|
||||||
|
}
|
||||||
|
|
||||||
export let kind: ButtonKind = 'no-border'
|
export let kind: ButtonKind = 'no-border'
|
||||||
export let size: ButtonSize = 'small'
|
export let size: ButtonSize = 'small'
|
||||||
@ -34,11 +36,11 @@
|
|||||||
export let width: string | undefined = undefined
|
export let width: string | undefined = undefined
|
||||||
export let labelDirection: TooltipAlignment | undefined = undefined
|
export let labelDirection: TooltipAlignment | undefined = undefined
|
||||||
|
|
||||||
let persons: Person[] = []
|
let persons: Employee[] = []
|
||||||
|
|
||||||
const query = createQuery()
|
const query = createQuery()
|
||||||
|
|
||||||
$: query.query<Person>(_class, { _id: { $in: items } }, (result) => {
|
$: query.query<Employee>(_class, { _id: { $in: items } }, (result) => {
|
||||||
persons = result
|
persons = result
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -50,6 +52,7 @@
|
|||||||
{
|
{
|
||||||
_class,
|
_class,
|
||||||
label,
|
label,
|
||||||
|
docQuery,
|
||||||
multiSelect: true,
|
multiSelect: true,
|
||||||
allowDeselect: false,
|
allowDeselect: false,
|
||||||
selectedUsers: items
|
selectedUsers: items
|
||||||
@ -80,9 +83,9 @@
|
|||||||
{#if persons.length > 0}
|
{#if persons.length > 0}
|
||||||
<div class="flex-row-center flex-nowrap pointer-events-none">
|
<div class="flex-row-center flex-nowrap pointer-events-none">
|
||||||
<CombineAvatars {_class} bind:items size={'inline'} />
|
<CombineAvatars {_class} bind:items size={'inline'} />
|
||||||
{#await translate(presentation.string.NumberMembers, { count: persons.length }) then text}
|
<span class="overflow-label ml-1-5">
|
||||||
<span class="overflow-label ml-1-5">{text}</span>
|
<Label label={presentation.string.NumberMembers} params={{ count: persons.length }} />
|
||||||
{/await}
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
|
@ -15,17 +15,19 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher, afterUpdate } from 'svelte'
|
import { createEventDispatcher, afterUpdate } from 'svelte'
|
||||||
import { Contact, getFirstName, Person } from '@anticrm/contact'
|
import { Contact, getFirstName, Person } from '@anticrm/contact'
|
||||||
import type { Class, Doc, FindOptions, Ref } from '@anticrm/core'
|
import type { Class, Doc, DocumentQuery, FindOptions, Ref } from '@anticrm/core'
|
||||||
import type { Asset, IntlString } from '@anticrm/platform'
|
import type { Asset, IntlString } from '@anticrm/platform'
|
||||||
import { AnyComponent, AnySvelteComponent, Icon, Label } from '@anticrm/ui'
|
import { AnySvelteComponent, Icon, Label } from '@anticrm/ui'
|
||||||
import presentation from '..'
|
import presentation from '..'
|
||||||
import { getClient } from '../utils'
|
import { getClient } from '../utils'
|
||||||
import ObjectPopup from './ObjectPopup.svelte'
|
import ObjectPopup from './ObjectPopup.svelte'
|
||||||
import UserInfo from './UserInfo.svelte'
|
import UserInfo from './UserInfo.svelte'
|
||||||
|
import { ObjectCreate } from '../types'
|
||||||
|
|
||||||
export let _class: Ref<Class<Contact>>
|
export let _class: Ref<Class<Contact>>
|
||||||
export let options: FindOptions<Contact> | undefined = undefined
|
export let options: FindOptions<Contact> | undefined = undefined
|
||||||
export let selected: Ref<Person> | undefined
|
export let selected: Ref<Person> | undefined
|
||||||
|
export let docQuery: DocumentQuery<Contact> | undefined = undefined
|
||||||
|
|
||||||
export let multiSelect: boolean = false
|
export let multiSelect: boolean = false
|
||||||
export let allowDeselect: boolean = false
|
export let allowDeselect: boolean = false
|
||||||
@ -35,14 +37,9 @@
|
|||||||
export let ignoreUsers: Ref<Person>[] = []
|
export let ignoreUsers: Ref<Person>[] = []
|
||||||
export let shadows: boolean = true
|
export let shadows: boolean = true
|
||||||
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
export let icon: Asset | AnySvelteComponent | undefined = undefined
|
||||||
|
export let create: ObjectCreate | undefined = undefined
|
||||||
|
|
||||||
const hierarchy = getClient().getHierarchy()
|
const hierarchy = getClient().getHierarchy()
|
||||||
export let create:
|
|
||||||
| {
|
|
||||||
component: AnyComponent
|
|
||||||
label: IntlString
|
|
||||||
}
|
|
||||||
| undefined = undefined
|
|
||||||
|
|
||||||
$: _create =
|
$: _create =
|
||||||
create !== undefined
|
create !== undefined
|
||||||
@ -68,6 +65,7 @@
|
|||||||
{allowDeselect}
|
{allowDeselect}
|
||||||
{titleDeselect}
|
{titleDeselect}
|
||||||
{placeholder}
|
{placeholder}
|
||||||
|
{docQuery}
|
||||||
groupBy={'_class'}
|
groupBy={'_class'}
|
||||||
bind:selectedObjects={selectedUsers}
|
bind:selectedObjects={selectedUsers}
|
||||||
bind:ignoreObjects={ignoreUsers}
|
bind:ignoreObjects={ignoreUsers}
|
||||||
|
@ -37,6 +37,7 @@ export { default as SpacesMultiPopup } from './components/SpacesMultiPopup.svelt
|
|||||||
export { default as UserBox } from './components/UserBox.svelte'
|
export { default as UserBox } from './components/UserBox.svelte'
|
||||||
export { default as UserBoxList } from './components/UserBoxList.svelte'
|
export { default as UserBoxList } from './components/UserBoxList.svelte'
|
||||||
export { default as UserInfo } from './components/UserInfo.svelte'
|
export { default as UserInfo } from './components/UserInfo.svelte'
|
||||||
|
export { default as EmployeeBox } from './components/EmployeeBox.svelte'
|
||||||
export { default as UsersPopup } from './components/UsersPopup.svelte'
|
export { default as UsersPopup } from './components/UsersPopup.svelte'
|
||||||
export { default as MembersBox } from './components/MembersBox.svelte'
|
export { default as MembersBox } from './components/MembersBox.svelte'
|
||||||
export { default as IconMembers } from './components/icons/Members.svelte'
|
export { default as IconMembers } from './components/icons/Members.svelte'
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Client, Doc } from '@anticrm/core'
|
import { Client, Doc } from '@anticrm/core'
|
||||||
import { Asset, IntlString, Resource } from '@anticrm/platform'
|
import { Asset, IntlString, Resource } from '@anticrm/platform'
|
||||||
import { AnySvelteComponent } from '@anticrm/ui'
|
import { AnyComponent, AnySvelteComponent } from '@anticrm/ui'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -14,6 +14,15 @@ export interface ObjectSearchResult {
|
|||||||
iconProps?: any
|
iconProps?: any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface ObjectCreate {
|
||||||
|
component: AnyComponent
|
||||||
|
label: IntlString
|
||||||
|
update?: (doc: Doc) => string
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Employee } from '@anticrm/contact'
|
import { Employee } from '@anticrm/contact'
|
||||||
import { Class, Ref, Space } from '@anticrm/core'
|
import { Class, Ref, Space } from '@anticrm/core'
|
||||||
import { SpaceMultiBoxList, UserBoxList } from '@anticrm/presentation'
|
import { SpaceMultiBoxList, UserBoxList } from '@anticrm/presentation'
|
||||||
import { DropdownLabelsIntl } from '@anticrm/ui'
|
import { DropdownLabelsIntl } from '@anticrm/ui'
|
||||||
@ -31,7 +31,6 @@
|
|||||||
<div class="filterBlockContainer">
|
<div class="filterBlockContainer">
|
||||||
<div class="simpleFilterButton">
|
<div class="simpleFilterButton">
|
||||||
<UserBoxList
|
<UserBoxList
|
||||||
_class={contact.class.Employee}
|
|
||||||
items={selectedParticipants}
|
items={selectedParticipants}
|
||||||
label={attachment.string.FileBrowserFilterFrom}
|
label={attachment.string.FileBrowserFilterFrom}
|
||||||
on:update={(evt) => {
|
on:update={(evt) => {
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
import { AttachmentDroppable, AttachmentsPresenter } from '@anticrm/attachment-resources'
|
import { AttachmentDroppable, AttachmentsPresenter } from '@anticrm/attachment-resources'
|
||||||
import type { Card } from '@anticrm/board'
|
import type { Card } from '@anticrm/board'
|
||||||
import { CommentsPresenter } from '@anticrm/chunter-resources'
|
import { CommentsPresenter } from '@anticrm/chunter-resources'
|
||||||
import contact, { Employee } from '@anticrm/contact'
|
import { Employee } from '@anticrm/contact'
|
||||||
import type { Ref, WithLookup } from '@anticrm/core'
|
import type { Ref, WithLookup } from '@anticrm/core'
|
||||||
import notification from '@anticrm/notification'
|
import notification from '@anticrm/notification'
|
||||||
import view from '@anticrm/view'
|
import view from '@anticrm/view'
|
||||||
@ -217,12 +217,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{#if (object.members?.length ?? 0) > 0}
|
{#if (object.members?.length ?? 0) > 0}
|
||||||
<div class="flex justify-end mt-1 mb-2" style:pointer-events={dragoverAttachment ? 'none' : 'all'}>
|
<div class="flex justify-end mt-1 mb-2" style:pointer-events={dragoverAttachment ? 'none' : 'all'}>
|
||||||
<UserBoxList
|
<UserBoxList items={object.members} label={board.string.Members} on:update={updateMembers} />
|
||||||
_class={contact.class.Employee}
|
|
||||||
items={object.members}
|
|
||||||
label={board.string.Members}
|
|
||||||
on:update={updateMembers}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Employee } from '@anticrm/contact'
|
import { Employee } from '@anticrm/contact'
|
||||||
import { Ref } from '@anticrm/core'
|
import { Ref } from '@anticrm/core'
|
||||||
|
|
||||||
import { UserBoxList } from '@anticrm/presentation'
|
import { UserBoxList } from '@anticrm/presentation'
|
||||||
@ -8,4 +8,4 @@
|
|||||||
export let value: Ref<Employee>[]
|
export let value: Ref<Employee>[]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<UserBoxList items={value} _class={contact.class.Employee} label={board.string.Members} on:update />
|
<UserBoxList items={value} label={board.string.Members} on:update />
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Ref } from '@anticrm/core'
|
import { Ref } from '@anticrm/core'
|
||||||
import { createQuery, getClient, UserBox, MessageBox } from '@anticrm/presentation'
|
import { createQuery, getClient, EmployeeBox, MessageBox } from '@anticrm/presentation'
|
||||||
import type { TodoItem } from '@anticrm/task'
|
import type { TodoItem } from '@anticrm/task'
|
||||||
import task, { calcRank } from '@anticrm/task'
|
import task, { calcRank } from '@anticrm/task'
|
||||||
import {
|
import {
|
||||||
@ -30,7 +30,7 @@
|
|||||||
DateRangePresenter
|
DateRangePresenter
|
||||||
} from '@anticrm/ui'
|
} from '@anticrm/ui'
|
||||||
import { ContextMenu, HTMLPresenter } from '@anticrm/view-resources'
|
import { ContextMenu, HTMLPresenter } from '@anticrm/view-resources'
|
||||||
import contact, { Employee } from '@anticrm/contact'
|
import { Employee } from '@anticrm/contact'
|
||||||
|
|
||||||
import board from '../../plugin'
|
import board from '../../plugin'
|
||||||
import { getDateIcon } from '../../utils/BoardUtils'
|
import { getDateIcon } from '../../utils/BoardUtils'
|
||||||
@ -273,8 +273,7 @@
|
|||||||
on:change={(e) => updateDueDate(item, e.detail)}
|
on:change={(e) => updateDueDate(item, e.detail)}
|
||||||
noShift
|
noShift
|
||||||
/>
|
/>
|
||||||
<UserBox
|
<EmployeeBox
|
||||||
_class={contact.class.Employee}
|
|
||||||
label={board.string.Assignee}
|
label={board.string.Assignee}
|
||||||
bind:value={item.assignee}
|
bind:value={item.assignee}
|
||||||
allowDeselect={true}
|
allowDeselect={true}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Employee, EmployeeAccount } from '@anticrm/contact'
|
import { Employee, EmployeeAccount } from '@anticrm/contact'
|
||||||
import { Class, Doc, getCurrentAccount, Ref } from '@anticrm/core'
|
import { Class, Doc, getCurrentAccount, Ref } from '@anticrm/core'
|
||||||
import { Card, getClient, UserBoxList } from '@anticrm/presentation'
|
import { Card, getClient, UserBoxList } from '@anticrm/presentation'
|
||||||
import ui, { EditBox, DateRangePresenter } from '@anticrm/ui'
|
import ui, { EditBox, DateRangePresenter } from '@anticrm/ui'
|
||||||
@ -71,6 +71,6 @@
|
|||||||
<svelte:fragment slot="pool">
|
<svelte:fragment slot="pool">
|
||||||
<DateRangePresenter bind:value withTime editable labelNull={ui.string.SelectDate} />
|
<DateRangePresenter bind:value withTime editable labelNull={ui.string.SelectDate} />
|
||||||
<DateRangePresenter bind:value={dueDate} labelNull={calendar.string.DueTo} withTime editable />
|
<DateRangePresenter bind:value={dueDate} labelNull={calendar.string.DueTo} withTime editable />
|
||||||
<UserBoxList _class={contact.class.Employee} bind:items={participants} label={calendar.string.Participants} />
|
<UserBoxList bind:items={participants} label={calendar.string.Participants} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Employee, EmployeeAccount } from '@anticrm/contact'
|
import { Employee, EmployeeAccount } from '@anticrm/contact'
|
||||||
import { Class, Doc, getCurrentAccount, Ref } from '@anticrm/core'
|
import { Class, Doc, getCurrentAccount, Ref } from '@anticrm/core'
|
||||||
import { Card, getClient, UserBoxList } from '@anticrm/presentation'
|
import { Card, getClient, UserBoxList } from '@anticrm/presentation'
|
||||||
import ui, { EditBox, DateRangePresenter } from '@anticrm/ui'
|
import ui, { EditBox, DateRangePresenter } from '@anticrm/ui'
|
||||||
@ -74,6 +74,6 @@
|
|||||||
<svelte:fragment slot="pool">
|
<svelte:fragment slot="pool">
|
||||||
<!-- <TimeShiftPicker title={calendar.string.Date} bind:value direction="after" /> -->
|
<!-- <TimeShiftPicker title={calendar.string.Date} bind:value direction="after" /> -->
|
||||||
<DateRangePresenter bind:value withTime={true} editable={true} labelNull={ui.string.SelectDate} />
|
<DateRangePresenter bind:value withTime={true} editable={true} labelNull={ui.string.SelectDate} />
|
||||||
<UserBoxList _class={contact.class.Employee} bind:items={participants} label={calendar.string.Participants} />
|
<UserBoxList bind:items={participants} label={calendar.string.Participants} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -73,6 +73,9 @@
|
|||||||
<UsersPopup
|
<UsersPopup
|
||||||
selected={undefined}
|
selected={undefined}
|
||||||
_class={contact.class.Employee}
|
_class={contact.class.Employee}
|
||||||
|
docQuery={{
|
||||||
|
active: true
|
||||||
|
}}
|
||||||
multiSelect={true}
|
multiSelect={true}
|
||||||
allowDeselect={true}
|
allowDeselect={true}
|
||||||
selectedUsers={selectedEmployees}
|
selectedUsers={selectedEmployees}
|
||||||
|
@ -70,9 +70,5 @@
|
|||||||
dispatch('close')
|
dispatch('close')
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<UserBoxList
|
<UserBoxList label={chunter.string.Members} on:update={(evt) => (employeeIds = evt.detail)} />
|
||||||
_class={contact.class.Employee}
|
|
||||||
label={chunter.string.Members}
|
|
||||||
on:update={(evt) => (employeeIds = evt.detail)}
|
|
||||||
/>
|
|
||||||
</SpaceCreateCard>
|
</SpaceCreateCard>
|
||||||
|
@ -63,6 +63,8 @@
|
|||||||
"Member": "Member",
|
"Member": "Member",
|
||||||
"Members": "Members",
|
"Members": "Members",
|
||||||
"NoMembers": "No members added",
|
"NoMembers": "No members added",
|
||||||
"AddMember": "Add member"
|
"AddMember": "Add member",
|
||||||
|
"KickEmployee": "Kick an employee",
|
||||||
|
"KickEmployeeDescr": "Are you sure you want to kick the employee out of the workspace? This action cannot be undone"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -63,6 +63,8 @@
|
|||||||
"Member": "Сотрудник",
|
"Member": "Сотрудник",
|
||||||
"Members": "Сотрудники",
|
"Members": "Сотрудники",
|
||||||
"NoMembers": "Нет добавленных сотрудников",
|
"NoMembers": "Нет добавленных сотрудников",
|
||||||
"AddMember": "Добавить сотрудника"
|
"AddMember": "Добавить сотрудника",
|
||||||
|
"KickEmployee": "Исключить сотрудника",
|
||||||
|
"KickEmployeeDescr": "Вы действительно хотите выгнать сотрудника из рабочего пространства? Это действие нельзя отменить"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -43,6 +43,7 @@
|
|||||||
"@anticrm/notification-resources": "~0.6.0",
|
"@anticrm/notification-resources": "~0.6.0",
|
||||||
"@anticrm/panel": "~0.6.0",
|
"@anticrm/panel": "~0.6.0",
|
||||||
"@anticrm/view-resources": "~0.6.0",
|
"@anticrm/view-resources": "~0.6.0",
|
||||||
"@anticrm/attachment": "~0.6.1"
|
"@anticrm/attachment": "~0.6.1",
|
||||||
|
"@anticrm/login-resources": "~0.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Person } from '@anticrm/contact'
|
import { Employee } from '@anticrm/contact'
|
||||||
import { Ref } from '@anticrm/core'
|
import { Ref } from '@anticrm/core'
|
||||||
import { IntlString } from '@anticrm/platform'
|
import { IntlString } from '@anticrm/platform'
|
||||||
import { UserBoxList } from '@anticrm/presentation'
|
import { UserBoxList } from '@anticrm/presentation'
|
||||||
import contact from '../plugin'
|
|
||||||
|
|
||||||
export let label: IntlString
|
export let label: IntlString
|
||||||
export let value: Ref<Person>[]
|
export let value: Ref<Employee>[]
|
||||||
export let onChange: (refs: Ref<Person>[]) => void
|
export let onChange: (refs: Ref<Employee>[]) => void
|
||||||
|
|
||||||
let timer: any
|
let timer: any
|
||||||
|
|
||||||
function onUpdate (evt: CustomEvent<Ref<Person>[]>): void {
|
function onUpdate (evt: CustomEvent<Ref<Employee>[]>): void {
|
||||||
clearTimeout(timer)
|
clearTimeout(timer)
|
||||||
timer = setTimeout(() => {
|
timer = setTimeout(() => {
|
||||||
onChange(evt.detail)
|
onChange(evt.detail)
|
||||||
@ -19,13 +18,4 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<UserBoxList
|
<UserBoxList items={value} {label} on:update={onUpdate} kind={'link'} size={'medium'} justify={'left'} width={'100%'} />
|
||||||
_class={contact.class.Employee}
|
|
||||||
items={value}
|
|
||||||
{label}
|
|
||||||
on:update={onUpdate}
|
|
||||||
kind={'link'}
|
|
||||||
size={'medium'}
|
|
||||||
justify={'left'}
|
|
||||||
width={'100%'}
|
|
||||||
/>
|
|
||||||
|
@ -0,0 +1,49 @@
|
|||||||
|
<!--
|
||||||
|
// 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 { Employee } from '@anticrm/contact'
|
||||||
|
import { DocumentQuery, Ref, RefTo } from '@anticrm/core'
|
||||||
|
import { IntlString } from '@anticrm/platform'
|
||||||
|
import { EmployeeBox } from '@anticrm/presentation'
|
||||||
|
import contact from '../plugin'
|
||||||
|
import { ButtonKind, ButtonSize } from '@anticrm/ui'
|
||||||
|
|
||||||
|
export let value: Ref<Employee> | undefined
|
||||||
|
export let label: IntlString = contact.string.Employee
|
||||||
|
export let onChange: (value: any) => void
|
||||||
|
export let type: RefTo<Employee> | undefined
|
||||||
|
export let kind: ButtonKind = 'no-border'
|
||||||
|
export let size: ButtonSize = 'small'
|
||||||
|
export let justify: 'left' | 'center' = 'center'
|
||||||
|
export let width: string | undefined = undefined
|
||||||
|
|
||||||
|
$: _class = type?.to ?? contact.class.Employee
|
||||||
|
|
||||||
|
const query: DocumentQuery<Employee> = {
|
||||||
|
active: true
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<EmployeeBox
|
||||||
|
{_class}
|
||||||
|
docQuery={query}
|
||||||
|
{label}
|
||||||
|
{kind}
|
||||||
|
{size}
|
||||||
|
{justify}
|
||||||
|
{width}
|
||||||
|
bind:value
|
||||||
|
on:change={(e) => onChange(e.detail)}
|
||||||
|
/>
|
@ -14,10 +14,11 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { Contact, formatName } from '@anticrm/contact'
|
import { Contact, Employee, formatName } from '@anticrm/contact'
|
||||||
import { Class, Client, Ref } from '@anticrm/core'
|
import { Class, Client, Ref } from '@anticrm/core'
|
||||||
import { Resources } from '@anticrm/platform'
|
import { Resources } from '@anticrm/platform'
|
||||||
import { Avatar, ObjectSearchResult, UserInfo } from '@anticrm/presentation'
|
import { Avatar, getClient, MessageBox, ObjectSearchResult, UserInfo } from '@anticrm/presentation'
|
||||||
|
import { showPopup } from '@anticrm/ui'
|
||||||
import Channels from './components/Channels.svelte'
|
import Channels from './components/Channels.svelte'
|
||||||
import ChannelsEditor from './components/ChannelsEditor.svelte'
|
import ChannelsEditor from './components/ChannelsEditor.svelte'
|
||||||
import ChannelsPresenter from './components/ChannelsPresenter.svelte'
|
import ChannelsPresenter from './components/ChannelsPresenter.svelte'
|
||||||
@ -45,6 +46,8 @@ import Members from './components/Members.svelte'
|
|||||||
import MemberPresenter from './components/MemberPresenter.svelte'
|
import MemberPresenter from './components/MemberPresenter.svelte'
|
||||||
import EditMember from './components/EditMember.svelte'
|
import EditMember from './components/EditMember.svelte'
|
||||||
import EmployeeArrayEditor from './components/EmployeeArrayEditor.svelte'
|
import EmployeeArrayEditor from './components/EmployeeArrayEditor.svelte'
|
||||||
|
import EmployeeEditor from './components/EmployeeEditor.svelte'
|
||||||
|
import { leaveWorkspace } from '@anticrm/login-resources'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Channels,
|
Channels,
|
||||||
@ -55,7 +58,8 @@ export {
|
|||||||
ChannelsDropdown,
|
ChannelsDropdown,
|
||||||
EmployeePresenter,
|
EmployeePresenter,
|
||||||
EmployeeBrowser,
|
EmployeeBrowser,
|
||||||
MemberPresenter
|
MemberPresenter,
|
||||||
|
EmployeeEditor
|
||||||
}
|
}
|
||||||
|
|
||||||
async function queryContact (
|
async function queryContact (
|
||||||
@ -73,7 +77,30 @@ async function queryContact (
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function kickEmployee (doc: Employee): Promise<void> {
|
||||||
|
const client = getClient()
|
||||||
|
const email = await client.findOne(contact.class.EmployeeAccount, { employee: doc._id })
|
||||||
|
if (email === undefined) return
|
||||||
|
showPopup(
|
||||||
|
MessageBox,
|
||||||
|
{
|
||||||
|
label: contact.string.KickEmployee,
|
||||||
|
message: contact.string.KickEmployeeDescr
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
(res?: boolean) => {
|
||||||
|
if (res === true) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
|
leaveWorkspace(email.email)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export default async (): Promise<Resources> => ({
|
export default async (): Promise<Resources> => ({
|
||||||
|
actionImpl: {
|
||||||
|
KickEmployee: kickEmployee
|
||||||
|
},
|
||||||
component: {
|
component: {
|
||||||
PersonEditor,
|
PersonEditor,
|
||||||
OrganizationEditor,
|
OrganizationEditor,
|
||||||
@ -94,7 +121,8 @@ export default async (): Promise<Resources> => ({
|
|||||||
Members,
|
Members,
|
||||||
MemberPresenter,
|
MemberPresenter,
|
||||||
EditMember,
|
EditMember,
|
||||||
EmployeeArrayEditor
|
EmployeeArrayEditor,
|
||||||
|
EmployeeEditor
|
||||||
},
|
},
|
||||||
completion: {
|
completion: {
|
||||||
EmployeeQuery: async (client: Client, query: string) => await queryContact(contact.class.Employee, client, query),
|
EmployeeQuery: async (client: Client, query: string) => await queryContact(contact.class.Employee, client, query),
|
||||||
|
@ -56,6 +56,8 @@ export default mergeIds(contactId, contact, {
|
|||||||
Member: '' as IntlString,
|
Member: '' as IntlString,
|
||||||
Members: '' as IntlString,
|
Members: '' as IntlString,
|
||||||
NoMembers: '' as IntlString,
|
NoMembers: '' as IntlString,
|
||||||
AddMember: '' as IntlString
|
AddMember: '' as IntlString,
|
||||||
|
KickEmployee: '' as IntlString,
|
||||||
|
KickEmployeeDescr: '' as IntlString
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -105,6 +105,7 @@ export interface Status extends AttachedDoc {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export interface Employee extends Person {
|
export interface Employee extends Person {
|
||||||
|
active: boolean
|
||||||
statuses?: number
|
statuses?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,9 +13,9 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Employee } from '@anticrm/contact'
|
import { Employee } from '@anticrm/contact'
|
||||||
import { Ref } from '@anticrm/core'
|
import { Ref } from '@anticrm/core'
|
||||||
import { Card, getClient, SpaceSelector, UserBox } from '@anticrm/presentation'
|
import { Card, getClient, SpaceSelector, EmployeeBox } from '@anticrm/presentation'
|
||||||
import { Button, createFocusManager, EditBox, FocusHandler } from '@anticrm/ui'
|
import { Button, createFocusManager, EditBox, FocusHandler } from '@anticrm/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import hr from '../plugin'
|
import hr from '../plugin'
|
||||||
@ -74,16 +74,13 @@
|
|||||||
<SpaceSelector _class={hr.class.Department} label={hr.string.ParentDepartmentLabel} bind:space />
|
<SpaceSelector _class={hr.class.Department} label={hr.string.ParentDepartmentLabel} bind:space />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="pool">
|
<svelte:fragment slot="pool">
|
||||||
<UserBox
|
<EmployeeBox
|
||||||
focusIndex={3}
|
focusIndex={3}
|
||||||
_class={contact.class.Employee}
|
|
||||||
label={hr.string.TeamLead}
|
label={hr.string.TeamLead}
|
||||||
placeholder={hr.string.TeamLead}
|
placeholder={hr.string.TeamLead}
|
||||||
bind:value={lead}
|
bind:value={lead}
|
||||||
allowDeselect
|
allowDeselect
|
||||||
titleDeselect={hr.string.UnAssignLead}
|
titleDeselect={hr.string.UnAssignLead}
|
||||||
kind={'no-border'}
|
|
||||||
size={'small'}
|
|
||||||
/>
|
/>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -52,7 +52,10 @@
|
|||||||
_class: contact.class.Employee,
|
_class: contact.class.Employee,
|
||||||
selected: value.$lookup?.teamLead,
|
selected: value.$lookup?.teamLead,
|
||||||
allowDeselect: true,
|
allowDeselect: true,
|
||||||
placeholder: hr.string.TeamLead
|
placeholder: hr.string.TeamLead,
|
||||||
|
docQuery: {
|
||||||
|
active: true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
eventToHTMLElement(event),
|
eventToHTMLElement(event),
|
||||||
changeLead
|
changeLead
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
{size}
|
{size}
|
||||||
{kind}
|
{kind}
|
||||||
{justify}
|
{justify}
|
||||||
|
allowDeselect
|
||||||
{width}
|
{width}
|
||||||
bind:space={value}
|
bind:space={value}
|
||||||
on:change={(e) => onChange(e.detail)}
|
on:change={(e) => onChange(e.detail)}
|
||||||
|
@ -77,6 +77,9 @@
|
|||||||
UsersPopup,
|
UsersPopup,
|
||||||
{
|
{
|
||||||
_class: contact.class.Employee,
|
_class: contact.class.Employee,
|
||||||
|
docQuery: {
|
||||||
|
active: true
|
||||||
|
},
|
||||||
ignoreUsers: employees.filter((p) => p.department === objectId).map((p) => p._id)
|
ignoreUsers: employees.filter((p) => p.department === objectId).map((p) => p._id)
|
||||||
},
|
},
|
||||||
eventToHTMLElement(e),
|
eventToHTMLElement(e),
|
||||||
|
@ -434,3 +434,34 @@ export async function changePassword (oldPassword: string, password: string): Pr
|
|||||||
body: serialize(request)
|
body: serialize(request)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function leaveWorkspace (email: string): Promise<void> {
|
||||||
|
const accountsUrl = getMetadata(login.metadata.AccountsUrl)
|
||||||
|
|
||||||
|
if (accountsUrl === undefined) {
|
||||||
|
throw new Error('accounts url not specified')
|
||||||
|
}
|
||||||
|
|
||||||
|
const overrideToken = getMetadata(login.metadata.OverrideLoginToken)
|
||||||
|
if (overrideToken !== undefined) {
|
||||||
|
const endpoint = getMetadata(login.metadata.OverrideEndpoint)
|
||||||
|
if (endpoint !== undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const token = fetchMetadataLocalStorage(login.metadata.LoginToken) as string
|
||||||
|
|
||||||
|
const request: Request<[string]> = {
|
||||||
|
method: 'leaveWorkspace',
|
||||||
|
params: [email]
|
||||||
|
}
|
||||||
|
|
||||||
|
await fetch(accountsUrl, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Authorization: 'Bearer ' + token,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: serialize(request)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
import contact from '@anticrm/contact'
|
import contact from '@anticrm/contact'
|
||||||
import { Account, Class, Client, Doc, generateId, Ref, SortingOrder, Space } from '@anticrm/core'
|
import { Account, Class, Client, Doc, generateId, Ref, SortingOrder, Space } from '@anticrm/core'
|
||||||
import { getResource, OK, Resource, Severity, Status } from '@anticrm/platform'
|
import { getResource, OK, Resource, Severity, Status } from '@anticrm/platform'
|
||||||
import { Card, createQuery, getClient, SpaceSelector, UserBox } from '@anticrm/presentation'
|
import { Card, createQuery, EmployeeBox, getClient, SpaceSelector, UserBox } from '@anticrm/presentation'
|
||||||
import type { Applicant, Candidate } from '@anticrm/recruit'
|
import type { Applicant, Candidate } from '@anticrm/recruit'
|
||||||
import task, { calcRank, SpaceWithStates, State } from '@anticrm/task'
|
import task, { calcRank, SpaceWithStates, State } from '@anticrm/task'
|
||||||
import ui, {
|
import ui, {
|
||||||
@ -269,16 +269,13 @@
|
|||||||
create={{ component: recruit.component.CreateCandidate, label: recruit.string.CreateTalent }}
|
create={{ component: recruit.component.CreateCandidate, label: recruit.string.CreateTalent }}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
<UserBox
|
<EmployeeBox
|
||||||
focusIndex={2}
|
focusIndex={2}
|
||||||
_class={contact.class.Employee}
|
|
||||||
label={recruit.string.AssignRecruiter}
|
label={recruit.string.AssignRecruiter}
|
||||||
placeholder={recruit.string.Recruiters}
|
placeholder={recruit.string.Recruiters}
|
||||||
bind:value={doc.assignee}
|
bind:value={doc.assignee}
|
||||||
allowDeselect
|
allowDeselect
|
||||||
titleDeselect={recruit.string.UnAssignRecruiter}
|
titleDeselect={recruit.string.UnAssignRecruiter}
|
||||||
kind={'no-border'}
|
|
||||||
size={'small'}
|
|
||||||
/>
|
/>
|
||||||
{#if states.length > 0}
|
{#if states.length > 0}
|
||||||
<Button
|
<Button
|
||||||
|
@ -180,6 +180,6 @@
|
|||||||
on:change={updateStart}
|
on:change={updateStart}
|
||||||
/>
|
/>
|
||||||
<DateRangePresenter bind:value={dueDate} labelNull={recruit.string.DueDate} withTime editable />
|
<DateRangePresenter bind:value={dueDate} labelNull={recruit.string.DueDate} withTime editable />
|
||||||
<UserBoxList _class={contact.class.Employee} bind:items={doc.participants} label={calendar.string.Participants} />
|
<UserBoxList bind:items={doc.participants} label={calendar.string.Participants} />
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -43,6 +43,8 @@
|
|||||||
"EditAttribute": "Edit attribute",
|
"EditAttribute": "Edit attribute",
|
||||||
"CreateEnum": "Create enum",
|
"CreateEnum": "Create enum",
|
||||||
"Enums": "Enums",
|
"Enums": "Enums",
|
||||||
"NewValue": "New value"
|
"NewValue": "New value",
|
||||||
|
"Leave": "Leave workspace",
|
||||||
|
"LeaveDescr": "Are you sure you want to leave the workspace? This action cannot be undone."
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -43,6 +43,8 @@
|
|||||||
"EditAttribute": "Редактирование атрибута",
|
"EditAttribute": "Редактирование атрибута",
|
||||||
"CreateEnum": "Создать справочник",
|
"CreateEnum": "Создать справочник",
|
||||||
"Enums": "Справочники",
|
"Enums": "Справочники",
|
||||||
"NewValue": "Новое значение"
|
"NewValue": "Новое значение",
|
||||||
|
"Leave": "Покинуть рабочее пространство",
|
||||||
|
"LeaveDescr": "Вы действительно хотите покинуть рабочее пространство? Отменить это действие невозможно"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,15 +15,16 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { AttributeEditor, createQuery, EditableAvatar, getClient } from '@anticrm/presentation'
|
import { AttributeEditor, createQuery, EditableAvatar, getClient } from '@anticrm/presentation'
|
||||||
|
|
||||||
import setting from '@anticrm/setting'
|
import setting from '../plugin'
|
||||||
import { EditBox, Icon, Label, createFocusManager, FocusHandler } from '@anticrm/ui'
|
import { EditBox, Icon, Label, createFocusManager, FocusHandler, Button, showPopup } from '@anticrm/ui'
|
||||||
import contact, { Employee, EmployeeAccount, getFirstName, getLastName } from '@anticrm/contact'
|
import contact, { Employee, EmployeeAccount, getFirstName, getLastName } from '@anticrm/contact'
|
||||||
import contactRes from '@anticrm/contact-resources/src/plugin'
|
import contactRes from '@anticrm/contact-resources/src/plugin'
|
||||||
import { getCurrentAccount } from '@anticrm/core'
|
import { getCurrentAccount } from '@anticrm/core'
|
||||||
import { getResource } from '@anticrm/platform'
|
import { getResource } from '@anticrm/platform'
|
||||||
import attachment from '@anticrm/attachment'
|
import attachment from '@anticrm/attachment'
|
||||||
import { changeName } from '@anticrm/login-resources'
|
import { changeName, leaveWorkspace } from '@anticrm/login-resources'
|
||||||
import { ChannelsEditor } from '@anticrm/contact-resources'
|
import { ChannelsEditor } from '@anticrm/contact-resources'
|
||||||
|
import MessageBox from '@anticrm/presentation/src/components/MessageBox.svelte'
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
|
||||||
let employee: Employee | undefined
|
let employee: Employee | undefined
|
||||||
@ -71,6 +72,22 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const manager = createFocusManager()
|
const manager = createFocusManager()
|
||||||
|
|
||||||
|
async function leave (): Promise<void> {
|
||||||
|
showPopup(
|
||||||
|
MessageBox,
|
||||||
|
{
|
||||||
|
label: setting.string.Leave,
|
||||||
|
message: setting.string.LeaveDescr
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
async (res?: boolean) => {
|
||||||
|
if (res === true) {
|
||||||
|
await leaveWorkspace(getCurrentAccount().email)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FocusHandler {manager} />
|
<FocusHandler {manager} />
|
||||||
@ -80,51 +97,62 @@
|
|||||||
<div class="ac-header__icon"><Icon icon={setting.icon.EditProfile} size={'medium'} /></div>
|
<div class="ac-header__icon"><Icon icon={setting.icon.EditProfile} size={'medium'} /></div>
|
||||||
<div class="ac-header__title"><Label label={setting.string.EditProfile} /></div>
|
<div class="ac-header__title"><Label label={setting.string.EditProfile} /></div>
|
||||||
</div>
|
</div>
|
||||||
{#if employee}
|
<div class="ac-body p-10">
|
||||||
<div class="ac-body columns p-10">
|
{#if employee}
|
||||||
<div class="mr-8">
|
<div class="flex flex-grow">
|
||||||
<EditableAvatar avatar={employee.avatar} size={'x-large'} on:done={onAvatarDone} on:remove={removeAvatar} />
|
<div class="mr-8">
|
||||||
</div>
|
<EditableAvatar avatar={employee.avatar} size={'x-large'} on:done={onAvatarDone} on:remove={removeAvatar} />
|
||||||
<div class="flex-grow flex-col">
|
</div>
|
||||||
<div class="flex-col">
|
<div class="flex-grow flex-col">
|
||||||
<div class="name">
|
<div class="flex-col">
|
||||||
<EditBox
|
<div class="name">
|
||||||
placeholder={contactRes.string.PersonFirstNamePlaceholder}
|
<EditBox
|
||||||
maxWidth="20rem"
|
placeholder={contactRes.string.PersonFirstNamePlaceholder}
|
||||||
bind:value={firstName}
|
maxWidth="20rem"
|
||||||
focus
|
bind:value={firstName}
|
||||||
focusIndex={1}
|
focus
|
||||||
on:change={() => {
|
focusIndex={1}
|
||||||
changeName(firstName, lastName)
|
on:change={() => {
|
||||||
}}
|
changeName(firstName, lastName)
|
||||||
/>
|
}}
|
||||||
</div>
|
/>
|
||||||
<div class="name">
|
</div>
|
||||||
<EditBox
|
<div class="name">
|
||||||
placeholder={contactRes.string.PersonLastNamePlaceholder}
|
<EditBox
|
||||||
maxWidth="20rem"
|
placeholder={contactRes.string.PersonLastNamePlaceholder}
|
||||||
bind:value={lastName}
|
maxWidth="20rem"
|
||||||
focusIndex={2}
|
bind:value={lastName}
|
||||||
on:change={() => {
|
focusIndex={2}
|
||||||
changeName(firstName, lastName)
|
on:change={() => {
|
||||||
}}
|
changeName(firstName, lastName)
|
||||||
/>
|
}}
|
||||||
</div>
|
/>
|
||||||
<div class="location">
|
</div>
|
||||||
<AttributeEditor
|
<div class="location">
|
||||||
maxWidth="20rem"
|
<AttributeEditor
|
||||||
_class={contact.class.Person}
|
maxWidth="20rem"
|
||||||
object={employee}
|
_class={contact.class.Person}
|
||||||
focusIndex={3}
|
object={employee}
|
||||||
key="city"
|
focusIndex={3}
|
||||||
/>
|
key="city"
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="separator" />
|
||||||
|
<ChannelsEditor attachedTo={employee._id} attachedClass={employee._class} focusIndex={10} allowOpen={false} />
|
||||||
</div>
|
</div>
|
||||||
<div class="separator" />
|
|
||||||
<ChannelsEditor attachedTo={employee._id} attachedClass={employee._class} focusIndex={10} allowOpen={false} />
|
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
|
<div class="footer">
|
||||||
|
<Button
|
||||||
|
icon={setting.icon.Signout}
|
||||||
|
label={setting.string.Leave}
|
||||||
|
on:click={() => {
|
||||||
|
leave()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@ -144,4 +172,8 @@
|
|||||||
height: 1px;
|
height: 1px;
|
||||||
background-color: var(--theme-card-divider);
|
background-color: var(--theme-card-divider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -14,19 +14,12 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Class, Doc, DocumentQuery, Enum, Ref } from '@anticrm/core'
|
import type { Class, Doc, DocumentQuery, Enum, Ref } from '@anticrm/core'
|
||||||
import { IntlString } from '@anticrm/platform'
|
import { ObjectCreate, ObjectPopup } from '@anticrm/presentation'
|
||||||
import { ObjectPopup } from '@anticrm/presentation'
|
|
||||||
import { AnyComponent } from '@anticrm/ui'
|
|
||||||
|
|
||||||
export let _class: Ref<Class<Enum>>
|
export let _class: Ref<Class<Enum>>
|
||||||
export let selected: Ref<Enum> | undefined
|
export let selected: Ref<Enum> | undefined
|
||||||
export let query: DocumentQuery<Enum> | undefined
|
export let query: DocumentQuery<Enum> | undefined
|
||||||
export let create:
|
export let create: ObjectCreate | undefined = undefined
|
||||||
| {
|
|
||||||
component: AnyComponent
|
|
||||||
label: IntlString
|
|
||||||
}
|
|
||||||
| undefined = undefined
|
|
||||||
|
|
||||||
$: _create =
|
$: _create =
|
||||||
create !== undefined
|
create !== undefined
|
||||||
|
@ -21,24 +21,19 @@
|
|||||||
Button,
|
Button,
|
||||||
eventToHTMLElement,
|
eventToHTMLElement,
|
||||||
getFocusManager,
|
getFocusManager,
|
||||||
AnyComponent,
|
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipAlignment
|
TooltipAlignment
|
||||||
} from '@anticrm/ui'
|
} from '@anticrm/ui'
|
||||||
import EnumPopup from './EnumPopup.svelte'
|
import EnumPopup from './EnumPopup.svelte'
|
||||||
|
|
||||||
import core, { Ref, Class, DocumentQuery, Enum } from '@anticrm/core'
|
import core, { Ref, Class, DocumentQuery, Enum } from '@anticrm/core'
|
||||||
|
import { ObjectCreate } from '@anticrm/presentation'
|
||||||
|
|
||||||
export let label: IntlString
|
export let label: IntlString
|
||||||
export let value: Enum | undefined
|
export let value: Enum | undefined
|
||||||
export let focusIndex = -1
|
export let focusIndex = -1
|
||||||
export let focus = false
|
export let focus = false
|
||||||
export let create:
|
export let create: ObjectCreate | undefined = undefined
|
||||||
| {
|
|
||||||
component: AnyComponent
|
|
||||||
label: IntlString
|
|
||||||
}
|
|
||||||
| undefined = undefined
|
|
||||||
export let labelDirection: TooltipAlignment | undefined = undefined
|
export let labelDirection: TooltipAlignment | undefined = undefined
|
||||||
|
|
||||||
const _class: Ref<Class<Enum>> = core.class.Enum
|
const _class: Ref<Class<Enum>> = core.class.Enum
|
||||||
|
@ -39,6 +39,8 @@ export default mergeIds(settingId, setting, {
|
|||||||
EditAttribute: '' as IntlString,
|
EditAttribute: '' as IntlString,
|
||||||
CreateEnum: '' as IntlString,
|
CreateEnum: '' as IntlString,
|
||||||
Enums: '' as IntlString,
|
Enums: '' as IntlString,
|
||||||
NewValue: '' as IntlString
|
NewValue: '' as IntlString,
|
||||||
|
Leave: '' as IntlString,
|
||||||
|
LeaveDescr: '' as IntlString
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -83,6 +83,9 @@
|
|||||||
{
|
{
|
||||||
_class: contact.class.Employee,
|
_class: contact.class.Employee,
|
||||||
selected: value?._id,
|
selected: value?._id,
|
||||||
|
docQuery: {
|
||||||
|
active: true
|
||||||
|
},
|
||||||
allowDeselect: true,
|
allowDeselect: true,
|
||||||
placeholder: task.string.AssignThisTask
|
placeholder: task.string.AssignThisTask
|
||||||
},
|
},
|
||||||
|
@ -16,10 +16,9 @@
|
|||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import { Employee } from '@anticrm/contact'
|
import { Employee } from '@anticrm/contact'
|
||||||
import { AttachedData, Ref } from '@anticrm/core'
|
import { AttachedData, Ref } from '@anticrm/core'
|
||||||
import { getClient, UserBox } from '@anticrm/presentation'
|
import { getClient, EmployeeBox } from '@anticrm/presentation'
|
||||||
import { Issue } from '@anticrm/tracker'
|
import { Issue } from '@anticrm/tracker'
|
||||||
import { ButtonKind, ButtonSize, TooltipAlignment } from '@anticrm/ui'
|
import { ButtonKind, ButtonSize, TooltipAlignment } from '@anticrm/ui'
|
||||||
import contact from '@anticrm/contact'
|
|
||||||
import tracker from '../../plugin'
|
import tracker from '../../plugin'
|
||||||
|
|
||||||
export let value: Issue | AttachedData<Issue>
|
export let value: Issue | AttachedData<Issue>
|
||||||
@ -45,8 +44,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if value}
|
{#if value}
|
||||||
<UserBox
|
<EmployeeBox
|
||||||
_class={contact.class.Employee}
|
|
||||||
label={tracker.string.Assignee}
|
label={tracker.string.Assignee}
|
||||||
placeholder={tracker.string.Assignee}
|
placeholder={tracker.string.Assignee}
|
||||||
value={value.assignee}
|
value={value.assignee}
|
||||||
|
@ -83,6 +83,9 @@
|
|||||||
{
|
{
|
||||||
_class: contact.class.Employee,
|
_class: contact.class.Employee,
|
||||||
selected: value?._id,
|
selected: value?._id,
|
||||||
|
docQuery: {
|
||||||
|
active: true
|
||||||
|
},
|
||||||
allowDeselect: true,
|
allowDeselect: true,
|
||||||
placeholder: tracker.string.AssignTo
|
placeholder: tracker.string.AssignTo
|
||||||
},
|
},
|
||||||
|
@ -72,6 +72,9 @@
|
|||||||
{
|
{
|
||||||
_class: contact.class.Employee,
|
_class: contact.class.Employee,
|
||||||
selected: value?._id,
|
selected: value?._id,
|
||||||
|
docQuery: {
|
||||||
|
active: true
|
||||||
|
},
|
||||||
allowDeselect: true,
|
allowDeselect: true,
|
||||||
placeholder: tracker.string.ProjectLeadSearchPlaceholder
|
placeholder: tracker.string.ProjectLeadSearchPlaceholder
|
||||||
},
|
},
|
||||||
|
@ -13,10 +13,9 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact from '@anticrm/contact'
|
|
||||||
import { Data, Ref } from '@anticrm/core'
|
import { Data, Ref } from '@anticrm/core'
|
||||||
import { IntlString } from '@anticrm/platform'
|
import { IntlString } from '@anticrm/platform'
|
||||||
import { Card, getClient, SpaceSelector, UserBox, UserBoxList } from '@anticrm/presentation'
|
import { Card, getClient, SpaceSelector, EmployeeBox, UserBoxList } from '@anticrm/presentation'
|
||||||
import { Project, ProjectStatus, Team } from '@anticrm/tracker'
|
import { Project, ProjectStatus, Team } from '@anticrm/tracker'
|
||||||
import { DatePresenter, EditBox } from '@anticrm/ui'
|
import { DatePresenter, EditBox } from '@anticrm/ui'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
@ -83,19 +82,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div slot="pool" class="flex-row-center text-sm gap-1-5">
|
<div slot="pool" class="flex-row-center text-sm gap-1-5">
|
||||||
<ProjectStatusSelector selectedProjectStatus={object.status} onProjectStatusChange={handleProjectStatusChanged} />
|
<ProjectStatusSelector selectedProjectStatus={object.status} onProjectStatusChange={handleProjectStatusChanged} />
|
||||||
<UserBox
|
<EmployeeBox
|
||||||
_class={contact.class.Employee}
|
|
||||||
label={tracker.string.ProjectLead}
|
label={tracker.string.ProjectLead}
|
||||||
placeholder={tracker.string.AssignTo}
|
placeholder={tracker.string.AssignTo}
|
||||||
bind:value={object.lead}
|
bind:value={object.lead}
|
||||||
allowDeselect
|
allowDeselect
|
||||||
titleDeselect={tracker.string.Unassigned}
|
titleDeselect={tracker.string.Unassigned}
|
||||||
/>
|
/>
|
||||||
<UserBoxList
|
<UserBoxList bind:items={object.members} label={tracker.string.ProjectStatusPlaceholder} />
|
||||||
_class={contact.class.Employee}
|
|
||||||
bind:items={object.members}
|
|
||||||
label={tracker.string.ProjectStatusPlaceholder}
|
|
||||||
/>
|
|
||||||
<!-- TODO: add labels after customize IssueNeedsToBeCompletedByThisDate -->
|
<!-- TODO: add labels after customize IssueNeedsToBeCompletedByThisDate -->
|
||||||
<DatePresenter bind:value={object.startDate} labelNull={tracker.string.StartDate} editable />
|
<DatePresenter bind:value={object.startDate} labelNull={tracker.string.StartDate} editable />
|
||||||
<DatePresenter bind:value={object.targetDate} labelNull={tracker.string.TargetDate} editable />
|
<DatePresenter bind:value={object.targetDate} labelNull={tracker.string.TargetDate} editable />
|
||||||
|
@ -61,6 +61,9 @@
|
|||||||
selectedUsers: value.members,
|
selectedUsers: value.members,
|
||||||
allowDeselect: true,
|
allowDeselect: true,
|
||||||
multiSelect: true,
|
multiSelect: true,
|
||||||
|
docQuery: {
|
||||||
|
active: true
|
||||||
|
},
|
||||||
placeholder: tracker.string.ProjectMembersSearchPlaceholder
|
placeholder: tracker.string.ProjectMembersSearchPlaceholder
|
||||||
},
|
},
|
||||||
eventToHTMLElement(event),
|
eventToHTMLElement(event),
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
"Leave": "Leave",
|
"Leave": "Leave",
|
||||||
"Joined": "Joined",
|
"Joined": "Joined",
|
||||||
"Join": "Join",
|
"Join": "Join",
|
||||||
"BrowseSpaces": "Browse spaces"
|
"BrowseSpaces": "Browse spaces",
|
||||||
|
"AccountDisabled": "Account is disabled",
|
||||||
|
"AccountDisabledDescr": "Please contact the workspace administrator"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,6 +14,8 @@
|
|||||||
"Leave": "Покинуть",
|
"Leave": "Покинуть",
|
||||||
"Joined": "Вы присоеденились",
|
"Joined": "Вы присоеденились",
|
||||||
"Join": "Присоедениться",
|
"Join": "Присоедениться",
|
||||||
"BrowseSpaces": "Обзор пространств"
|
"BrowseSpaces": "Обзор пространств",
|
||||||
|
"AccountDisabled": "Аккаунт отключен",
|
||||||
|
"AccountDisabledDescr": "Пожалуйста свяжитесь с администратором"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -28,6 +28,7 @@
|
|||||||
DatePickerPopup,
|
DatePickerPopup,
|
||||||
fetchMetadataLocalStorage,
|
fetchMetadataLocalStorage,
|
||||||
getCurrentLocation,
|
getCurrentLocation,
|
||||||
|
Label,
|
||||||
location,
|
location,
|
||||||
Location,
|
Location,
|
||||||
navigate,
|
navigate,
|
||||||
@ -358,7 +359,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:resize={windowResize} />
|
<svelte:window on:resize={windowResize} />
|
||||||
{#if client}
|
{#if employee?.active === true}
|
||||||
<ActionHandler />
|
<ActionHandler />
|
||||||
<svg class="svg-mask">
|
<svg class="svg-mask">
|
||||||
<clipPath id="notify-normal">
|
<clipPath id="notify-normal">
|
||||||
@ -426,9 +427,7 @@
|
|||||||
showPopup(AccountPopup, {}, 'account')
|
showPopup(AccountPopup, {}, 'account')
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{#if employee}
|
<Avatar avatar={employee.avatar} size={'medium'} />
|
||||||
<Avatar avatar={employee.avatar} size={'medium'} />
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -507,7 +506,10 @@
|
|||||||
</Popup>
|
</Popup>
|
||||||
<DatePickerPopup />
|
<DatePickerPopup />
|
||||||
{:else}
|
{:else}
|
||||||
No client
|
<div class="flex-col-center justify-center h-full flex-grow">
|
||||||
|
<h1><Label label={workbench.string.AccountDisabled} /></h1>
|
||||||
|
<Label label={workbench.string.AccountDisabledDescr} />
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@ -34,7 +34,9 @@ export default mergeIds(workbenchId, workbench, {
|
|||||||
Leave: '' as IntlString,
|
Leave: '' as IntlString,
|
||||||
Joined: '' as IntlString,
|
Joined: '' as IntlString,
|
||||||
Join: '' as IntlString,
|
Join: '' as IntlString,
|
||||||
BrowseSpaces: '' as IntlString
|
BrowseSpaces: '' as IntlString,
|
||||||
|
AccountDisabled: '' as IntlString,
|
||||||
|
AccountDisabledDescr: '' as IntlString
|
||||||
},
|
},
|
||||||
component: {
|
component: {
|
||||||
SpacePanel: '' as AnyComponent
|
SpacePanel: '' as AnyComponent
|
||||||
|
@ -14,12 +14,12 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import contact, { Employee } from '@anticrm/contact'
|
import contact, { Employee } from '@anticrm/contact'
|
||||||
import core, { Ref, SortingOrder, Tx, TxFactory, TxMixin } from '@anticrm/core'
|
import core, { Ref, SortingOrder, Tx, TxFactory, TxMixin, TxUpdateDoc } from '@anticrm/core'
|
||||||
import hr, { Department, DepartmentMember, Staff } from '@anticrm/hr'
|
import hr, { Department, DepartmentMember, Staff } from '@anticrm/hr'
|
||||||
import { extractTx, TriggerControl } from '@anticrm/server-core'
|
import { extractTx, TriggerControl } from '@anticrm/server-core'
|
||||||
|
|
||||||
async function getOldDepartment (
|
async function getOldDepartment (
|
||||||
currentTx: TxMixin<Employee, Staff>,
|
currentTx: TxMixin<Employee, Staff> | TxUpdateDoc<Employee>,
|
||||||
control: TriggerControl
|
control: TriggerControl
|
||||||
): Promise<Ref<Department> | undefined> {
|
): Promise<Ref<Department> | undefined> {
|
||||||
const txes = await control.findAll<TxMixin<Employee, Staff>>(
|
const txes = await control.findAll<TxMixin<Employee, Staff>>(
|
||||||
@ -109,6 +109,12 @@ export async function OnDepartmentStaff (tx: Tx, control: TriggerControl): Promi
|
|||||||
const lastDepartment = await getOldDepartment(ctx, control)
|
const lastDepartment = await getOldDepartment(ctx, control)
|
||||||
|
|
||||||
const departmentId = ctx.attributes.department
|
const departmentId = ctx.attributes.department
|
||||||
|
if (departmentId === null) {
|
||||||
|
if (lastDepartment !== undefined) {
|
||||||
|
const removed = await buildHierarchy(lastDepartment, control)
|
||||||
|
return getTxes(control.txFactory, targetAccount._id, [], removed)
|
||||||
|
}
|
||||||
|
}
|
||||||
const push = await buildHierarchy(departmentId, control)
|
const push = await buildHierarchy(departmentId, control)
|
||||||
|
|
||||||
if (lastDepartment === undefined) {
|
if (lastDepartment === undefined) {
|
||||||
@ -124,9 +130,36 @@ export async function OnDepartmentStaff (tx: Tx, control: TriggerControl): Promi
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export async function OnEmployeeDeactivate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
|
||||||
|
const actualTx = extractTx(tx)
|
||||||
|
if (core.class.TxUpdateDoc !== actualTx._class) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
const ctx = actualTx as TxUpdateDoc<Employee>
|
||||||
|
if (ctx.objectClass !== contact.class.Employee || ctx.operations.active !== false) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetAccount = (
|
||||||
|
await control.modelDb.findAll(contact.class.EmployeeAccount, {
|
||||||
|
employee: ctx.objectId
|
||||||
|
})
|
||||||
|
)[0]
|
||||||
|
if (targetAccount === undefined) return []
|
||||||
|
const lastDepartment = await getOldDepartment(ctx, control)
|
||||||
|
if (lastDepartment === undefined) return []
|
||||||
|
|
||||||
|
const removed = await buildHierarchy(lastDepartment, control)
|
||||||
|
return getTxes(control.txFactory, targetAccount._id, [], removed)
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||||
export default async () => ({
|
export default async () => ({
|
||||||
trigger: {
|
trigger: {
|
||||||
OnDepartmentStaff
|
OnDepartmentStaff,
|
||||||
|
OnEmployeeDeactivate
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
// limitations under the f.
|
// limitations under the f.
|
||||||
//
|
//
|
||||||
|
|
||||||
import contact, { combineName } from '@anticrm/contact'
|
import contact, { combineName, Employee } from '@anticrm/contact'
|
||||||
import core, { TxOperations } from '@anticrm/core'
|
import core, { Ref, TxOperations } from '@anticrm/core'
|
||||||
import platform, {
|
import platform, {
|
||||||
getMetadata,
|
getMetadata,
|
||||||
PlatformError,
|
PlatformError,
|
||||||
@ -461,6 +461,14 @@ export async function assignWorkspace (db: Db, email: string, workspace: string)
|
|||||||
if (account !== null) await createEmployeeAccount(account, workspace)
|
if (account !== null) await createEmployeeAccount(account, workspace)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function createEmployee (ops: TxOperations, name: string): Promise<Ref<Employee>> {
|
||||||
|
return await ops.createDoc(contact.class.Employee, contact.space.Employee, {
|
||||||
|
name,
|
||||||
|
city: '',
|
||||||
|
active: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async function createEmployeeAccount (account: Account, workspace: string): Promise<void> {
|
async function createEmployeeAccount (account: Account, workspace: string): Promise<void> {
|
||||||
const connection = await connect(getTransactor(), workspace, account.email)
|
const connection = await connect(getTransactor(), workspace, account.email)
|
||||||
try {
|
try {
|
||||||
@ -472,10 +480,7 @@ async function createEmployeeAccount (account: Account, workspace: string): Prom
|
|||||||
const existingAccount = await ops.findOne(contact.class.EmployeeAccount, { email: account.email })
|
const existingAccount = await ops.findOne(contact.class.EmployeeAccount, { email: account.email })
|
||||||
|
|
||||||
if (existingAccount === undefined) {
|
if (existingAccount === undefined) {
|
||||||
const employee = await ops.createDoc(contact.class.Employee, contact.space.Employee, {
|
const employee = await createEmployee(ops, name)
|
||||||
name,
|
|
||||||
city: ''
|
|
||||||
})
|
|
||||||
|
|
||||||
await ops.createDoc(contact.class.EmployeeAccount, core.space.Model, {
|
await ops.createDoc(contact.class.EmployeeAccount, core.space.Model, {
|
||||||
email: account.email,
|
email: account.email,
|
||||||
@ -486,13 +491,15 @@ async function createEmployeeAccount (account: Account, workspace: string): Prom
|
|||||||
const employee = await ops.findOne(contact.class.Employee, { _id: existingAccount.employee })
|
const employee = await ops.findOne(contact.class.Employee, { _id: existingAccount.employee })
|
||||||
if (employee === undefined) {
|
if (employee === undefined) {
|
||||||
// Employee was deleted, let's restore it.
|
// Employee was deleted, let's restore it.
|
||||||
const employeeId = await ops.createDoc(contact.class.Employee, contact.space.Employee, {
|
const employeeId = await createEmployee(ops, name)
|
||||||
name,
|
|
||||||
city: ''
|
|
||||||
})
|
|
||||||
await ops.updateDoc(contact.class.EmployeeAccount, existingAccount.space, existingAccount._id, {
|
await ops.updateDoc(contact.class.EmployeeAccount, existingAccount.space, existingAccount._id, {
|
||||||
employee: employeeId
|
employee: employeeId
|
||||||
})
|
})
|
||||||
|
} else if (!employee.active) {
|
||||||
|
await ops.update(employee, {
|
||||||
|
active: true
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -599,12 +606,81 @@ export async function dropAccount (db: Db, email: string): Promise<void> {
|
|||||||
if (account === null) {
|
if (account === null) {
|
||||||
throw new PlatformError(new Status(Severity.ERROR, accountPlugin.status.AccountNotFound, { account: email }))
|
throw new PlatformError(new Status(Severity.ERROR, accountPlugin.status.AccountNotFound, { account: email }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const workspaces = await db
|
||||||
|
.collection<Workspace>(WORKSPACE_COLLECTION)
|
||||||
|
.find({ _id: { $in: account.workspaces } })
|
||||||
|
.toArray()
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
workspaces.map(async (ws) => {
|
||||||
|
return await deactivateEmployeeAccount(account, ws.workspace)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
await db.collection(ACCOUNT_COLLECTION).deleteOne({ _id: account._id })
|
await db.collection(ACCOUNT_COLLECTION).deleteOne({ _id: account._id })
|
||||||
await db
|
await db
|
||||||
.collection<Workspace>(WORKSPACE_COLLECTION)
|
.collection<Workspace>(WORKSPACE_COLLECTION)
|
||||||
.updateMany({ _id: { $in: account.workspaces } }, { $pull: { accounts: account._id } })
|
.updateMany({ _id: { $in: account.workspaces } }, { $pull: { accounts: account._id } })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export async function leaveWorkspace (db: Db, token: string, email: string): Promise<void> {
|
||||||
|
const tokenData = decodeToken(token)
|
||||||
|
|
||||||
|
const account = await getAccount(db, email)
|
||||||
|
if (account === null) {
|
||||||
|
throw new PlatformError(new Status(Severity.ERROR, accountPlugin.status.AccountNotFound, { account: email }))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tokenData.email !== email) {
|
||||||
|
const currentAccount = await getAccount(db, tokenData.email)
|
||||||
|
if (currentAccount === null) {
|
||||||
|
throw new PlatformError(
|
||||||
|
new Status(Severity.ERROR, accountPlugin.status.AccountNotFound, { account: tokenData.email })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const workspace = await getWorkspace(db, tokenData.workspace)
|
||||||
|
if (workspace === null) {
|
||||||
|
throw new PlatformError(
|
||||||
|
new Status(Severity.ERROR, accountPlugin.status.WorkspaceNotFound, { workspace: tokenData.workspace })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await deactivateEmployeeAccount(account, workspace.workspace)
|
||||||
|
|
||||||
|
await db
|
||||||
|
.collection<Workspace>(WORKSPACE_COLLECTION)
|
||||||
|
.updateOne({ _id: workspace._id }, { $pull: { accounts: account._id } })
|
||||||
|
await db
|
||||||
|
.collection<Account>(ACCOUNT_COLLECTION)
|
||||||
|
.updateOne({ _id: account._id }, { $pull: { workspaces: workspace._id } })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deactivateEmployeeAccount (account: Account, workspace: string): Promise<void> {
|
||||||
|
const connection = await connect(getTransactor(), workspace, account.email)
|
||||||
|
try {
|
||||||
|
const ops = new TxOperations(connection, core.account.System)
|
||||||
|
|
||||||
|
const existingAccount = await ops.findOne(contact.class.EmployeeAccount, { email: account.email })
|
||||||
|
|
||||||
|
if (existingAccount !== undefined) {
|
||||||
|
const employee = await ops.findOne(contact.class.Employee, { _id: existingAccount.employee })
|
||||||
|
if (employee !== undefined) {
|
||||||
|
await ops.update(employee, {
|
||||||
|
active: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await connection.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function wrap (f: (db: Db, ...args: any[]) => Promise<any>) {
|
function wrap (f: (db: Db, ...args: any[]) => Promise<any>) {
|
||||||
return async function (db: Db, request: Request<any[]>, token?: string): Promise<Response<any>> {
|
return async function (db: Db, request: Request<any[]>, token?: string): Promise<Response<any>> {
|
||||||
if (token !== undefined) request.params.unshift(token)
|
if (token !== undefined) request.params.unshift(token)
|
||||||
@ -634,6 +710,7 @@ export const methods = {
|
|||||||
createWorkspace: wrap(createUserWorkspace),
|
createWorkspace: wrap(createUserWorkspace),
|
||||||
assignWorkspace: wrap(assignWorkspace),
|
assignWorkspace: wrap(assignWorkspace),
|
||||||
removeWorkspace: wrap(removeWorkspace),
|
removeWorkspace: wrap(removeWorkspace),
|
||||||
|
leaveWorkspace: wrap(leaveWorkspace),
|
||||||
listWorkspaces: wrap(listWorkspaces),
|
listWorkspaces: wrap(listWorkspaces),
|
||||||
changeName: wrap(changeName),
|
changeName: wrap(changeName),
|
||||||
changePassword: wrap(changePassword)
|
changePassword: wrap(changePassword)
|
||||||
|
Loading…
Reference in New Issue
Block a user