Employee display name (#2717)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2023-03-09 15:05:42 +06:00 committed by GitHub
parent 7997d34d17
commit 16eec3b0a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 566 additions and 290 deletions

View File

@ -158,6 +158,9 @@ export class TEmployee extends TPerson implements Employee {
statuses?: number
mergedTo?: Ref<Employee>
@Prop(TypeString(), contact.string.DisplayName)
displayName?: string | null
}
@Model(contact.class.EmployeeAccount, core.class.Account)
@ -284,7 +287,7 @@ export function createModel (builder: Builder): void {
})
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.ObjectEditor, {
editor: contact.component.EditPerson,
editor: contact.component.EditEmployee,
pinned: true
})

View File

@ -29,6 +29,7 @@ export default mergeIds(contactId, contact, {
ContactRefPresenter: '' as AnyComponent,
ContactPresenter: '' as AnyComponent,
EditPerson: '' as AnyComponent,
EditEmployee: '' as AnyComponent,
EditOrganization: '' as AnyComponent,
OrganizationPresenter: '' as AnyComponent,
Contacts: '' as AnyComponent,

View File

@ -1,15 +1,19 @@
<script lang="ts">
import contact, { Employee, EmployeeAccount, formatName } from '@hcengineering/contact'
import core, { Ref, Space } from '@hcengineering/core'
import { Label, Button, ActionIcon, IconClose } from '@hcengineering/ui'
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
import core, { IdMap, Ref, Space, toIdMap } from '@hcengineering/core'
import { ActionIcon, Button, IconClose, Label } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import { getClient } from '../utils'
import presentation from '../plugin'
import { createQuery, getClient } from '../utils'
import UsersPopup from './UsersPopup.svelte'
export let value: Space
const dispatch = createEventDispatcher()
const client = getClient()
const query = createQuery()
let employees: IdMap<Employee> = new Map()
query.query(contact.class.Employee, {}, (res) => (employees = toIdMap(res)))
let membersToAdd: EmployeeAccount[] = []
let channelMembers: Ref<Employee>[] = []
@ -54,8 +58,9 @@
{#if membersToAdd.length}
<div class="flex-row-top flex-wrap ml-6 mr-6 mt-4">
{#each membersToAdd as m}
{@const employee = employees.get(m.employee)}
<div class="mr-2 p-1 item">
{formatName(m.name)}
{employee ? getName(employee) : ''}
<div class="tool">
<ActionIcon
icon={IconClose}

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { Contact, Employee, formatName } from '@hcengineering/contact'
import contact, { Contact, Employee, getName } from '@hcengineering/contact'
import { Class, DocumentQuery, FindOptions, Hierarchy, Ref } from '@hcengineering/core'
import { getEmbeddedLabel, IntlString } from '@hcengineering/platform'
import {
@ -33,7 +33,7 @@
} from '@hcengineering/ui'
import view from '@hcengineering/view'
import { createEventDispatcher } from 'svelte'
import presentation, { IconPerson, UserInfo, getClient } from '..'
import presentation, { getClient, IconPerson, UserInfo } from '..'
import AssigneePopup from './AssigneePopup.svelte'
export let _class: Ref<Class<Employee>> = contact.class.Employee
@ -76,10 +76,6 @@
$: updateSelected(value)
function getName (obj: Contact): string {
const isPerson = client.getHierarchy().isDerived(obj._class, contact.class.Person)
return isPerson ? formatName(obj.name) : obj.name
}
const mgr = getFocusManager()
const _click = (ev: MouseEvent): void => {

View File

@ -14,7 +14,7 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { Contact, formatName } from '@hcengineering/contact'
import { Contact, getName } from '@hcengineering/contact'
import { Class, DocumentQuery, FindOptions, Hierarchy, Ref } from '@hcengineering/core'
import { Asset, getEmbeddedLabel, IntlString } from '@hcengineering/platform'
import {
@ -77,10 +77,6 @@
$: updateSelected(value)
function getName (obj: Contact): string {
const isPerson = client.getHierarchy().isDerived(obj._class, contact.class.Person)
return isPerson ? formatName(obj.name) : obj.name
}
const mgr = getFocusManager()
const _click = (ev: MouseEvent): void => {

View File

@ -15,7 +15,7 @@
<script lang="ts">
import Avatar from './Avatar.svelte'
import { formatName, Person } from '@hcengineering/contact'
import { getName, Person } from '@hcengineering/contact'
import { Asset } from '@hcengineering/platform'
import { AnySvelteComponent, IconSize } from '@hcengineering/ui'
@ -30,6 +30,6 @@
<Avatar avatar={value.avatar} {size} {icon} />
<div class="flex-col min-w-0 {size === 'tiny' || size === 'inline' ? 'ml-1' : 'ml-2'}">
{#if subtitle}<div class="content-dark-color text-sm">{subtitle}</div>{/if}
<div class="content-accent-color overflow-label text-left">{formatName(value.name)}</div>
<div class="content-accent-color overflow-label text-left">{getName(value)}</div>
</div>
</div>

View File

@ -15,7 +15,7 @@
-->
<script lang="ts">
import type { TxViewlet } from '@hcengineering/activity'
import contact, { EmployeeAccount, formatName } from '@hcengineering/contact'
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
import core, { AnyAttribute, Doc, getCurrentAccount, Ref } from '@hcengineering/core'
import { Asset, getResource } from '@hcengineering/platform'
import { createQuery, getClient } from '@hcengineering/presentation'
@ -50,14 +50,16 @@
let viewlet: TxDisplayViewlet | undefined
let props: any
let employee: EmployeeAccount | undefined
let account: EmployeeAccount | undefined
let employee: Employee | undefined
let model: AttributeModel[] = []
let modelIcon: Asset | undefined = undefined
let edit = false
$: if (tx.tx._id !== ptx?.tx._id) {
if (tx.tx.modifiedBy !== employee?._id) {
if (tx.tx.modifiedBy !== account?._id) {
account = undefined
employee = undefined
}
viewlet = undefined
@ -68,6 +70,7 @@
const client = getClient()
const query = createQuery()
const employeeQuery = createQuery()
function getProps (props: any, edit: boolean): any {
return { ...props, edit, attr: tx.collectionAttribute }
@ -85,12 +88,22 @@
$: query.query(
contact.class.EmployeeAccount,
{ _id: tx.tx.modifiedBy as Ref<EmployeeAccount> },
(account) => {
;[employee] = account
(res) => {
;[account] = res
},
{ limit: 1 }
)
$: account &&
employeeQuery.query(
contact.class.Employee,
{ _id: account.employee },
(res) => {
;[employee] = res
},
{ limit: 1 }
)
const showMenu = async (ev: MouseEvent): Promise<void> => {
const actions = await getActions(client, tx.doc as Doc)
showPopup(
@ -167,7 +180,7 @@
<div class="flex-row-center flex-grow label">
<div class="bold">
{#if employee}
{formatName(employee.name)}
{getName(employee)}
{:else}
<Label label={activity.string.System} />
{/if}

View File

@ -14,7 +14,7 @@
// limitations under the License.
-->
<script lang="ts">
import { formatName, Person } from '@hcengineering/contact'
import { getName, Person } from '@hcengineering/contact'
import { Hierarchy } from '@hcengineering/core'
import { Avatar } from '@hcengineering/presentation'
import calendar from '../plugin'
@ -38,7 +38,7 @@
<div
class="flex-presenter"
class:inline-presenter={inline}
use:tooltip={{ label: calendar.string.PersonsLabel, props: { name: formatName(p.name) } }}
use:tooltip={{ label: calendar.string.PersonsLabel, props: { name: getName(p) } }}
on:click={() => onClick(p)}
>
<div class="icon">

View File

@ -13,8 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import type { Person } from '@hcengineering/contact'
import { formatName } from '@hcengineering/contact'
import { getName, Person } from '@hcengineering/contact'
import { Avatar } from '@hcengineering/presentation'
interface IMessage {
@ -29,7 +28,7 @@
<div class="flex-nowrap">
<div class="avatar"><Avatar size={'medium'} /></div>
<div class="flex-col-stretch message">
<div class="header">{formatName(user.name)}<span>{message.createDate}</span></div>
<div class="header">{getName(user)}<span>{message.createDate}</span></div>
<div class="text">{message.text}</div>
</div>
</div>

View File

@ -17,10 +17,10 @@
import { AttachmentDocList } from '@hcengineering/attachment-resources'
import type { Comment } from '@hcengineering/chunter'
import chunter from '@hcengineering/chunter'
import { formatName } from '@hcengineering/contact'
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
import { Ref } from '@hcengineering/core'
import { Avatar, getClient, MessageViewer } from '@hcengineering/presentation'
import { TimeSince, ShowMore, Icon } from '@hcengineering/ui'
import { getUser } from '../utils'
import { Icon, ShowMore, TimeSince } from '@hcengineering/ui'
export let value: Comment
export let inline: boolean = false
@ -31,6 +31,14 @@
const cutId = (str: string): string => {
return str.slice(0, 4) + '...' + str.slice(-4)
}
async function getEmployee (value: Comment): Promise<Employee | undefined> {
const acc = await client.findOne(contact.class.EmployeeAccount, { _id: value.modifiedBy as Ref<EmployeeAccount> })
if (acc !== undefined) {
const emp = await client.findOne(contact.class.Employee, { _id: acc.employee })
return emp
}
}
</script>
{#if inline}
@ -43,23 +51,23 @@
&nbsp;<span class="content-dark-color">#{cutId(value._id.toString())}</span>
{:else}
<div class="flex-row-top">
<div class="avatar">
<Avatar size={'medium'} />
</div>
<div class="flex-grow flex-col select-text">
<div class="header">
<div class="fs-title">
{#await getUser(client, value.modifiedBy) then user}
{#if user}{formatName(user.name)}{/if}
{/await}
</div>
<div class="content-trans-color ml-4"><TimeSince value={value.modifiedOn} /></div>
{#await getEmployee(value) then employee}
<div class="avatar">
<Avatar size={'medium'} avatar={employee?.avatar} />
</div>
<ShowMore limit={126} fixed>
<MessageViewer message={value.message} />
<AttachmentDocList {value} />
</ShowMore>
</div>
<div class="flex-grow flex-col select-text">
<div class="header">
<div class="fs-title">
{#if employee}{getName(employee)}{/if}
</div>
<div class="content-trans-color ml-4"><TimeSince value={value.modifiedOn} /></div>
</div>
<ShowMore limit={126} fixed>
<MessageViewer message={value.message} />
<AttachmentDocList {value} />
</ShowMore>
</div>
{/await}
</div>
{/if}

View File

@ -1,8 +1,8 @@
<script lang="ts">
import chunter, { ChunterMessage } from '@hcengineering/chunter'
import contact, { Employee, EmployeeAccount, formatName } from '@hcengineering/contact'
import { Ref, Space } from '@hcengineering/core'
import { Avatar, createQuery, getClient, MessageViewer } from '@hcengineering/presentation'
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
import { IdMap, Ref, Space, toIdMap } from '@hcengineering/core'
import { Avatar, createQuery, MessageViewer } from '@hcengineering/presentation'
import { IconClose } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import { UnpinMessage } from '../index'
@ -30,38 +30,40 @@
})
const employeeAccoutsQuery = createQuery()
let employeeAcounts: EmployeeAccount[]
let employeeAcounts: IdMap<EmployeeAccount> = new Map()
employeeAccoutsQuery.query(contact.class.EmployeeAccount, {}, (res) => (employeeAcounts = res))
employeeAccoutsQuery.query(contact.class.EmployeeAccount, {}, (res) => (employeeAcounts = toIdMap(res)))
const employeeQuery = createQuery()
let employees: IdMap<Employee> = new Map()
employeeQuery.query(contact.class.Employee, {}, (res) => (employees = toIdMap(res)))
const client = getClient()
const dispatch = createEventDispatcher()
async function getEmployee (_id?: Ref<Employee>): Promise<Employee | undefined> {
if (_id) {
return await client.findOne(contact.class.Employee, { _id })
function getEmployee (
message: ChunterMessage,
employeeAcounts: IdMap<EmployeeAccount>,
employees: IdMap<Employee>
): Employee | undefined {
const acc = employeeAcounts.get(message.createBy as Ref<EmployeeAccount>)
if (acc) {
return employees.get(acc.employee)
}
}
function getEmployeeAccount (message: ChunterMessage): EmployeeAccount | undefined {
return employeeAcounts?.find((e) => e._id === message.createBy)
}
</script>
<div class="antiPopup vScroll popup">
{#each pinnedMessages as message}
{@const employee = getEmployee(message, employeeAcounts, employees)}
<div class="message">
<div class="header">
{#await getEmployeeAccount(message) then employeeAccount}
{#await getEmployee(employeeAccount?.employee) then employee}
<div class="avatar">
<Avatar size={'medium'} avatar={employee?.avatar} />
</div>
{/await}
<span class="name">
{formatName(employeeAccount?.name ?? '')}
</span>
{/await}
<div class="avatar">
<Avatar size={'medium'} avatar={employee?.avatar} />
</div>
<span class="name">
{employee ? getName(employee) : ''}
</span>
<div
class="cross"
on:click={async () => {

View File

@ -1,24 +1,32 @@
<script lang="ts">
import { Account, Ref } from '@hcengineering/core'
import contact, { EmployeeAccount, formatName } from '@hcengineering/contact'
import { getClient } from '@hcengineering/presentation'
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
import { Account, IdMap, Ref, toIdMap } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation'
export let reactionAccounts: Ref<Account>[]
let accounts: IdMap<EmployeeAccount> = new Map()
let employees: IdMap<Employee> = new Map()
const client = getClient()
let accountNames: string[] = []
const query = createQuery()
const empQ = createQuery()
$: query.query(contact.class.EmployeeAccount, {}, (res) => {
accounts = toIdMap(res)
})
async function getAccountNames (reactionAccounts: Ref<EmployeeAccount>[]) {
client
.findAll(contact.class.EmployeeAccount, { _id: { $in: reactionAccounts } })
.then((res) => (accountNames = res.map((a) => a.name)))
empQ.query(contact.class.Employee, {}, (res) => (employees = toIdMap(res)))
function getAccName (acc: Ref<Account>, accounts: IdMap<EmployeeAccount>, employees: IdMap<Employee>): string {
const account = accounts.get(acc as Ref<EmployeeAccount>)
if (account !== undefined) {
const emp = employees.get(account.employee)
return emp ? getName(emp) : ''
}
return ''
}
$: getAccountNames(reactionAccounts as Ref<EmployeeAccount>[])
</script>
{#each accountNames as name}
{#each reactionAccounts as acc}
<div>
{formatName(name)}
{getAccName(acc, accounts, employees)}
</div>
{/each}

View File

@ -2,8 +2,8 @@
import attachment, { Attachment } from '@hcengineering/attachment'
import AttachmentPreview from '@hcengineering/attachment-resources/src/components/AttachmentPreview.svelte'
import { ChunterMessage } from '@hcengineering/chunter'
import { EmployeeAccount, formatName } from '@hcengineering/contact'
import core, { Ref, WithLookup } from '@hcengineering/core'
import contact, { Employee, EmployeeAccount, getName as getContactName } from '@hcengineering/contact'
import core, { IdMap, Ref, toIdMap, WithLookup } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { Label, Scroller } from '@hcengineering/ui'
import chunter from '../plugin'
@ -16,11 +16,18 @@
let savedMessages: WithLookup<ChunterMessage>[] = []
let savedAttachmentsIds: Ref<Attachment>[] = []
let savedAttachments: WithLookup<Attachment>[] = []
let accounts: IdMap<EmployeeAccount> = new Map()
let employees: IdMap<Employee> = new Map()
const messagesQuery = createQuery()
const attachmentsQuery = createQuery()
const savedMessagesQuery = createQuery()
const savedAttachmentsQuery = createQuery()
const accQ = createQuery()
const empQ = createQuery()
accQ.query(contact.class.EmployeeAccount, {}, (res) => (accounts = toIdMap(res)))
empQ.query(contact.class.Employee, {}, (res) => (employees = toIdMap(res)))
savedMessagesQuery.query(chunter.class.SavedMessages, {}, (res) => {
savedMessagesIds = res.map((r) => r.attachedTo)
@ -46,18 +53,9 @@
)
$: savedAttachmentsIds &&
attachmentsQuery.query(
attachment.class.Attachment,
{ _id: { $in: savedAttachmentsIds } },
(res) => {
savedAttachments = res
},
{
lookup: {
modifiedBy: core.class.Account
}
}
)
attachmentsQuery.query(attachment.class.Attachment, { _id: { $in: savedAttachmentsIds } }, (res) => {
savedAttachments = res
})
const pinnedQuery = createQuery()
const pinnedIds: Ref<ChunterMessage>[] = []
@ -82,10 +80,13 @@
})
}
function getName (a: WithLookup<Attachment>): string | undefined {
const name = (a.$lookup?.modifiedBy as EmployeeAccount).name
if (name !== undefined) {
return formatName(name)
function getName (a: Attachment): string | undefined {
const acc = accounts.get(a.modifiedBy as Ref<EmployeeAccount>)
if (acc !== undefined) {
const emp = employees.get(acc?.employee)
if (emp !== undefined) {
return getContactName(emp)
}
}
}
</script>

View File

@ -16,7 +16,7 @@
import attachment, { Attachment } from '@hcengineering/attachment'
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
import type { ChunterSpace, Message, ThreadMessage } from '@hcengineering/chunter'
import contact, { Employee, EmployeeAccount, formatName } from '@hcengineering/contact'
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
import core, {
FindOptions,
generateId,
@ -124,7 +124,7 @@
for (const account of accounts) {
const employee = employees.get(account.employee)
if (employee !== undefined) {
res.push(formatName(employee.name))
res.push(getName(employee))
}
}
return res

View File

@ -1,19 +1,12 @@
import { chunterId, ChunterMessage } from '@hcengineering/chunter'
import contact, { EmployeeAccount, formatName } from '@hcengineering/contact'
import { Account, Class, Client, Obj, Ref, Space, getCurrentAccount, Timestamp } from '@hcengineering/core'
import contact, { EmployeeAccount, getName } from '@hcengineering/contact'
import { Class, Client, getCurrentAccount, Obj, Ref, Space, Timestamp } from '@hcengineering/core'
import { Asset } from '@hcengineering/platform'
import { getCurrentLocation, locationToUrl, navigate } from '@hcengineering/ui'
import { writable, get } from 'svelte/store'
import { get, writable } from 'svelte/store'
import chunter from './plugin'
export async function getUser (
client: Client,
user: Ref<EmployeeAccount> | Ref<Account>
): Promise<EmployeeAccount | undefined> {
return await client.findOne(contact.class.EmployeeAccount, { _id: user as Ref<EmployeeAccount> })
}
export function getTime (time: number): string {
let options: Intl.DateTimeFormatOptions = { hour: 'numeric', minute: 'numeric' }
if (!isToday(time)) {
@ -44,13 +37,19 @@ export function classIcon (client: Client, _class: Ref<Class<Obj>>): Asset | und
export async function getDmName (client: Client, dm: Space): Promise<string> {
const myAccId = getCurrentAccount()._id
const employeeAccounts = await client.findAll(contact.class.EmployeeAccount, {
let employeeAccounts: EmployeeAccount[] = await client.findAll(contact.class.EmployeeAccount, {
_id: { $in: dm.members as Array<Ref<EmployeeAccount>> }
})
const name = (dm.members.length > 1 ? employeeAccounts.filter((a) => a._id !== myAccId) : employeeAccounts)
.map((a) => formatName(a.name))
.join(', ')
if (dm.members.length > 1) {
employeeAccounts = employeeAccounts.filter((p) => p._id !== myAccId)
}
const emloyees = await client.findAll(contact.class.Employee, {
_id: { $in: employeeAccounts.map((p) => p.employee) }
})
const name = emloyees.map((a) => getName(a)).join(', ')
return name
}

View File

@ -76,6 +76,7 @@
"Profile": "Profile",
"ProfilePlaceholder": "Profile...",
"CurrentEmployee": "Current employee",
"MergeEmployee": "Merge employee"
"MergeEmployee": "Merge employee",
"DisplayName": "Display name"
}
}

View File

@ -76,6 +76,7 @@
"Profile": "Профиль",
"ProfilePlaceholder": "Профиль...",
"CurrentEmployee": "Текущий сотрудник",
"MergeEmployee": "Объеденить сотрудника"
"MergeEmployee": "Объеденить сотрудника",
"DisplayName": "Отображаемое имя"
}
}

View File

@ -16,8 +16,8 @@
<script lang="ts">
import { Contact, Employee, Organization } from '@hcengineering/contact'
import { getClient } from '@hcengineering/presentation'
import { Label } from '@hcengineering/ui'
import contact from '../plugin'
import EmployeePresenter from './EmployeePresenter.svelte'
import OrganizationPresenter from './OrganizationPresenter.svelte'
import PersonPresenter from './PersonPresenter.svelte'
@ -39,13 +39,10 @@
const toEmployee = (contact: Contact) => contact as Employee
</script>
{#if isPerson(value)}
{#if isEmployee(value)}
<EmployeePresenter {isInteractive} value={toEmployee(value)} />
{:else if isPerson(value)}
<PersonPresenter {isInteractive} {value} />
{#if isEmployee(value) && toEmployee(value)?.active === false}
<div class="ml-1">
(<Label label={contact.string.Inactive} />)
</div>
{/if}
{:else}
<OrganizationPresenter value={toOrg(value)} />
{/if}

View File

@ -0,0 +1,197 @@
<!--
// 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 { Employee, EmployeeAccount, getFirstName, getLastName, Person } from '@hcengineering/contact'
import { AccountRole, getCurrentAccount, Ref, Space } from '@hcengineering/core'
import { changeName } from '@hcengineering/login-resources'
import { AttributeEditor, Avatar, createQuery, EditableAvatar, getClient } from '@hcengineering/presentation'
import setting, { IntegrationType } from '@hcengineering/setting'
import { createFocusManager, EditBox, FocusHandler } from '@hcengineering/ui'
import { createEventDispatcher, onMount } from 'svelte'
import contact from '../plugin'
import ChannelsEditor from './ChannelsEditor.svelte'
export let object: Employee
const client = getClient()
const account = getCurrentAccount() as EmployeeAccount
let avatarEditor: EditableAvatar
$: owner = account.employee === object._id
$: editable = account.role >= AccountRole.Maintainer || owner
let firstName = getFirstName(object.name)
let lastName = getLastName(object.name)
let displayName = object.displayName ?? ''
$: setName(object)
let email: string | undefined
$: if (editable) {
client.findOne(contact.class.EmployeeAccount, { employee: (object as Employee)._id }).then((acc) => {
email = acc?.email
})
}
function setName (object: Person) {
firstName = getFirstName(object.name)
lastName = getLastName(object.name)
}
const dispatch = createEventDispatcher()
function firstNameChange () {
changeName(firstName, getLastName(object.name))
}
function lastNameChange () {
changeName(getFirstName(object.name), lastName)
}
function changeDisplayName () {
client.update(object, {
displayName: displayName === '' ? null : displayName
})
}
let integrations: Set<Ref<IntegrationType>> = new Set<Ref<IntegrationType>>()
const settingsQuery = createQuery()
$: settingsQuery.query(
setting.class.Integration,
{ space: account._id as string as Ref<Space>, disabled: false },
(res) => {
integrations = new Set(res.map((p) => p.type))
}
)
const sendOpen = () => dispatch('open', { ignoreKeys: ['comments', 'name', 'channels', 'city', 'displayName'] })
onMount(sendOpen)
async function onAvatarDone () {
if (object.avatar != null) {
await avatarEditor.removeAvatar(object.avatar)
}
const avatar = await avatarEditor.createAvatar()
await client.update(object, {
avatar
})
}
const manager = createFocusManager()
</script>
<FocusHandler {manager} />
{#if object !== undefined}
<div class="flex-row-stretch flex-grow">
<div class="mr-8">
{#key object}
{#if editable}
<EditableAvatar
avatar={object.avatar}
{email}
id={object._id}
size={'x-large'}
bind:this={avatarEditor}
on:done={onAvatarDone}
/>
{:else}
<Avatar avatar={object.avatar} size={'x-large'} />
{/if}
{/key}
</div>
<div class="flex-grow flex-col">
<div class="flex-grow flex-col">
<div class="name">
{#if owner}
<EditBox
placeholder={contact.string.PersonFirstNamePlaceholder}
bind:value={firstName}
on:change={firstNameChange}
focusIndex={1}
/>
{:else}
{firstName}
{/if}
</div>
<div class="name">
{#if owner}
<EditBox
placeholder={contact.string.PersonLastNamePlaceholder}
bind:value={lastName}
on:change={lastNameChange}
focusIndex={2}
/>
{:else}
{lastName}
{/if}
</div>
<div class="name">
{#if editable}
<EditBox
placeholder={contact.string.DisplayName}
bind:value={displayName}
on:change={changeDisplayName}
focusIndex={1}
/>
{:else}
{displayName}
{/if}
</div>
<div class="location">
<AttributeEditor
maxWidth="20rem"
_class={contact.class.Person}
{editable}
{object}
key="city"
focusIndex={3}
/>
</div>
</div>
<div class="separator" />
<div class="flex-row-center">
<ChannelsEditor
attachedTo={object._id}
attachedClass={object._class}
{editable}
bind:integrations
shape={'circle'}
focusIndex={10}
/>
</div>
</div>
</div>
{/if}
<style lang="scss">
.name {
font-weight: 500;
font-size: 1.25rem;
color: var(--theme-caption-color);
}
.location {
margin-top: 0.25rem;
font-size: 0.75rem;
}
.separator {
margin: 1rem 0;
height: 1px;
background-color: var(--divider-color);
}
</style>

View File

@ -14,11 +14,11 @@
// limitations under the License.
-->
<script lang="ts">
import { combineName, Employee, EmployeeAccount, getFirstName, getLastName, Person } from '@hcengineering/contact'
import { combineName, EmployeeAccount, getFirstName, getLastName, Person } from '@hcengineering/contact'
import { getCurrentAccount, Ref, Space } from '@hcengineering/core'
import { AttributeEditor, Avatar, createQuery, EditableAvatar, getClient } from '@hcengineering/presentation'
import { AttributeEditor, createQuery, EditableAvatar, getClient } from '@hcengineering/presentation'
import setting, { IntegrationType } from '@hcengineering/setting'
import { EditBox, createFocusManager, FocusHandler } from '@hcengineering/ui'
import { createFocusManager, EditBox, FocusHandler } from '@hcengineering/ui'
import { createEventDispatcher, onMount } from 'svelte'
import contact from '../plugin'
import ChannelsEditor from './ChannelsEditor.svelte'
@ -26,24 +26,15 @@
export let object: Person
const client = getClient()
const hierarchy = client.getHierarchy()
const account = getCurrentAccount() as EmployeeAccount
let avatarEditor: EditableAvatar
$: editable = !hierarchy.isDerived(object._class, contact.class.Employee)
let firstName = getFirstName(object.name)
let lastName = getLastName(object.name)
$: setName(object)
let email: string | undefined
$: if (editable && hierarchy.isDerived(object._class, contact.class.Employee)) {
client.findOne(contact.class.EmployeeAccount, { employee: (object as Employee)._id }).then((acc) => {
email = acc?.email
})
}
function setName (object: Person) {
firstName = getFirstName(object.name)
lastName = getLastName(object.name)
@ -52,13 +43,13 @@
const dispatch = createEventDispatcher()
function firstNameChange () {
client.updateDoc(object._class, object.space, object._id, {
client.update(object, {
name: combineName(firstName, getLastName(object.name))
})
}
function lastNameChange () {
client.updateDoc(object._class, object.space, object._id, {
client.update(object, {
name: combineName(getFirstName(object.name), lastName)
})
}
@ -76,12 +67,12 @@
const sendOpen = () => dispatch('open', { ignoreKeys: ['comments', 'name', 'channels', 'city'] })
onMount(sendOpen)
async function onAvatarDone (e: any) {
async function onAvatarDone () {
if (object.avatar != null) {
await avatarEditor.removeAvatar(object.avatar)
}
const avatar = await avatarEditor.createAvatar()
await client.updateDoc(object._class, object.space, object._id, {
await client.update(object, {
avatar
})
}
@ -95,55 +86,35 @@
<div class="flex-row-stretch flex-grow">
<div class="mr-8">
{#key object}
{#if editable}
<EditableAvatar
avatar={object.avatar}
{email}
id={object._id}
size={'x-large'}
bind:this={avatarEditor}
on:done={onAvatarDone}
/>
{:else}
<Avatar avatar={object.avatar} size={'x-large'} />
{/if}
<EditableAvatar
avatar={object.avatar}
id={object._id}
size={'x-large'}
bind:this={avatarEditor}
on:done={onAvatarDone}
/>
{/key}
</div>
<div class="flex-grow flex-col">
<div class="flex-grow flex-col">
<div class="name">
{#if editable}
<EditBox
placeholder={contact.string.PersonFirstNamePlaceholder}
bind:value={firstName}
on:change={firstNameChange}
focusIndex={1}
/>
{:else}
{firstName}
{/if}
<EditBox
placeholder={contact.string.PersonFirstNamePlaceholder}
bind:value={firstName}
on:change={firstNameChange}
focusIndex={1}
/>
</div>
<div class="name">
{#if editable}
<EditBox
placeholder={contact.string.PersonLastNamePlaceholder}
bind:value={lastName}
on:change={lastNameChange}
focusIndex={2}
/>
{:else}
{lastName}
{/if}
<EditBox
placeholder={contact.string.PersonLastNamePlaceholder}
bind:value={lastName}
on:change={lastNameChange}
focusIndex={2}
/>
</div>
<div class="location">
<AttributeEditor
maxWidth="20rem"
_class={contact.class.Person}
{editable}
{object}
key="city"
focusIndex={3}
/>
<AttributeEditor maxWidth="20rem" _class={contact.class.Person} {object} key="city" focusIndex={3} />
</div>
</div>
@ -152,7 +123,6 @@
<ChannelsEditor
attachedTo={object._id}
attachedClass={object._class}
{editable}
bind:integrations
shape={'circle'}
focusIndex={10}

View File

@ -14,7 +14,7 @@
// limitations under the License.
-->
<script lang="ts">
import { Employee, EmployeeAccount, formatName } from '@hcengineering/contact'
import { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
import { Account } from '@hcengineering/core'
import { Avatar, createQuery } from '@hcengineering/presentation'
import { showPopup } from '@hcengineering/ui'
@ -41,7 +41,7 @@
<div class="flex-row-center" class:user-container={employee !== undefined} on:click={onClick}>
{#if employee}
<Avatar size={'x-small'} avatar={employee.avatar} />
<div class="overflow-label user">{formatName(employee.name)}</div>
<div class="overflow-label user">{getName(employee)}</div>
{:else}
<div class="overflow-label user">{value.email}</div>
{/if}

View File

@ -2,11 +2,11 @@
import { Employee } from '@hcengineering/contact'
import { WithLookup } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import { showPopup } from '@hcengineering/ui'
import { Label, showPopup } from '@hcengineering/ui'
import { PersonLabelTooltip } from '..'
import PersonPresenter from '../components/PersonPresenter.svelte'
import contact from '../plugin'
import EmployeePreviewPopup from './EmployeePreviewPopup.svelte'
import EmployeeStatusPresenter from './EmployeeStatusPresenter.svelte'
export let value: WithLookup<Employee> | null | undefined
export let tooltipLabels: PersonLabelTooltip | undefined = undefined
@ -56,9 +56,9 @@
{defaultName}
/>
</div>
{#if value?.$lookup?.statuses?.length}
<div class="pl-2 status content-color">
<EmployeeStatusPresenter employee={value} />
{#if value?.active === false}
<div class="ml-1">
(<Label label={contact.string.Inactive} />)
</div>
{/if}

View File

@ -1,14 +1,14 @@
<script lang="ts">
import { Employee, EmployeeAccount, formatName, Status } from '@hcengineering/contact'
import { getCurrentAccount, Ref, Hierarchy, WithLookup } from '@hcengineering/core'
import { Employee, EmployeeAccount, getName, Status } from '@hcengineering/contact'
import { getCurrentAccount, Hierarchy, Ref, WithLookup } from '@hcengineering/core'
import { Avatar, createQuery, getClient } from '@hcengineering/presentation'
import { Button, getPanelURI, Label, showPopup, resizeObserver } from '@hcengineering/ui'
import EmployeeSetStatusPopup from './EmployeeSetStatusPopup.svelte'
import { Button, getPanelURI, Label, resizeObserver, showPopup } from '@hcengineering/ui'
import view from '@hcengineering/view'
import { createEventDispatcher } from 'svelte'
import contact from '../plugin'
import EmployeeSetStatusPopup from './EmployeeSetStatusPopup.svelte'
import EmployeeStatusPresenter from './EmployeeStatusPresenter.svelte'
import Edit from './icons/Edit.svelte'
import { createEventDispatcher } from 'svelte'
import view from '@hcengineering/view'
export let employeeId: Ref<Employee>
@ -60,9 +60,9 @@
>
{#if employee}
<div class="flex-col-center pb-2">
<Avatar size="x-large" avatar={employee?.avatar} />
<Avatar size="x-large" avatar={employee.avatar} />
</div>
<div class="pb-2">{formatName(employee?.name ?? '')}</div>
<div class="pb-2">{getName(employee)}</div>
<a href={`#${getPanelURI(view.component.EditDoc, employee._id, Hierarchy.mixinOrClass(employee), 'content')}`}
><Label label={contact.string.ViewFullProfile} /></a
>

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import { Channel, ChannelProvider, Employee, formatName } from '@hcengineering/contact'
import { Channel, ChannelProvider, Employee, getName } from '@hcengineering/contact'
import core, { Doc, DocumentUpdate, Mixin, Ref, TxProcessor } from '@hcengineering/core'
import { leaveWorkspace } from '@hcengineering/login-resources'
import { Avatar, Card, createQuery, EmployeeBox, getClient } from '@hcengineering/presentation'
@ -217,7 +217,7 @@
</MergeComparer>
<MergeComparer key="name" {value} {targetEmp} onChange={select}>
<svelte:fragment slot="item" let:item>
{formatName(item.name)}
{getName(item)}
</svelte:fragment>
</MergeComparer>
{#each objectAttributes as attribute}
@ -263,7 +263,7 @@
</Grid>
<div class="flex-col-center">
<Avatar avatar={result.avatar} size={'large'} icon={contact.icon.Person} />
{formatName(result.name)}
{getName(result)}
<DatePresenter value={result.birthday} />
<StringEditor value={result.city} readonly placeholder={contact.string.Location} />
<ChannelsDropdown

View File

@ -14,7 +14,7 @@
-->
<script lang="ts">
import attachment from '@hcengineering/attachment'
import contact, { Channel, Contact, formatName } from '@hcengineering/contact'
import contact, { Channel, Contact, getName } from '@hcengineering/contact'
import { Hierarchy } from '@hcengineering/core'
import { Avatar, createQuery } from '@hcengineering/presentation'
import { Component, Label, showPanel } from '@hcengineering/ui'
@ -51,7 +51,7 @@
if (!disabled) showPanel(view.component.EditDoc, object._id, Hierarchy.mixinOrClass(object), 'content')
}}
>
{formatName(object.name)}
{getName(object)}
</div>
<div class="description overflow-label">{object.city ?? ''}</div>
<div class="footer flex flex-reverse flex-grow">

View File

@ -13,14 +13,14 @@
// limitations under the License.
-->
<script lang="ts">
import { formatName, Person } from '@hcengineering/contact'
import { Employee, getName, Person } from '@hcengineering/contact'
import { Hierarchy } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import { Avatar } from '@hcengineering/presentation'
import { getPanelURI, Label, LabelAndProps, tooltip } from '@hcengineering/ui'
import view from '@hcengineering/view'
export let value: Person | undefined | null
export let value: Person | Employee | undefined | null
export let inline: boolean = false
export let isInteractive = true
export let shouldShowAvatar: boolean = true
@ -78,7 +78,7 @@
</div>
{/if}
{#if value && shouldShowName}
<span class="eContentPresenterLabel">{formatName(value.name)}</span>
<span class="eContentPresenterLabel">{getName(value)}</span>
{/if}
{#if !value && shouldShowName && defaultName}
<div class="eContentPresenterLabel">

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import { formatName, Person } from '@hcengineering/contact'
import { getName, Person } from '@hcengineering/contact'
import { IntlString } from '@hcengineering/platform'
import presentation from '@hcengineering/presentation'
import { LabelAndProps } from '@hcengineering/ui'
@ -41,7 +41,7 @@
? undefined
: {
label: presentation.string.InltPropsValue,
props: { value: formatName(value.name) }
props: { value: getName(value) }
}
}
const component = value ? tooltipLabels.component : undefined
@ -50,7 +50,7 @@
? tooltipLabels.personLabel
: presentation.string.InltPropsValue
: undefined
const props = tooltipLabels.props ? tooltipLabels.props : value ? { value: formatName(value.name) } : undefined
const props = tooltipLabels.props ? tooltipLabels.props : value ? { value: getName(value) } : undefined
return {
component,
label,

View File

@ -14,48 +14,49 @@
// limitations under the License.
//
import { Channel, Contact, Employee, formatName, getGravatarUrl } from '@hcengineering/contact'
import { Channel, Contact, Employee, getGravatarUrl, getName } from '@hcengineering/contact'
import { Class, Client, DocumentQuery, Ref, RelatedDocument, WithLookup } from '@hcengineering/core'
import { leaveWorkspace } from '@hcengineering/login-resources'
import { IntlString, Resources } from '@hcengineering/platform'
import { Avatar, getClient, MessageBox, ObjectSearchResult, UserInfo, getFileUrl } from '@hcengineering/presentation'
import { Avatar, getClient, getFileUrl, MessageBox, ObjectSearchResult, UserInfo } from '@hcengineering/presentation'
import { AnyComponent, AnySvelteComponent, showPopup } from '@hcengineering/ui'
import AccountArrayEditor from './components/AccountArrayEditor.svelte'
import AccountBox from './components/AccountBox.svelte'
import ChannelFilter from './components/ChannelFilter.svelte'
import Channels from './components/Channels.svelte'
import ChannelsDropdown from './components/ChannelsDropdown.svelte'
import ChannelsEditor from './components/ChannelsEditor.svelte'
import ChannelsPresenter from './components/ChannelsPresenter.svelte'
import ChannelsView from './components/ChannelsView.svelte'
import ContactArrayEditor from './components/ContactArrayEditor.svelte'
import ContactPresenter from './components/ContactPresenter.svelte'
import ContactRefPresenter from './components/ContactRefPresenter.svelte'
import Contacts from './components/Contacts.svelte'
import ContactsTabs from './components/ContactsTabs.svelte'
import CreateEmployee from './components/CreateEmployee.svelte'
import CreateOrganization from './components/CreateOrganization.svelte'
import CreatePerson from './components/CreatePerson.svelte'
import EditEmployee from './components/EditEmployee.svelte'
import EditMember from './components/EditMember.svelte'
import EditOrganization from './components/EditOrganization.svelte'
import EditPerson from './components/EditPerson.svelte'
import EmployeeAccountPresenter from './components/EmployeeAccountPresenter.svelte'
import EmployeeAccountRefPresenter from './components/EmployeeAccountRefPresenter.svelte'
import EmployeeArrayEditor from './components/EmployeeArrayEditor.svelte'
import AccountArrayEditor from './components/AccountArrayEditor.svelte'
import EmployeeBrowser from './components/EmployeeBrowser.svelte'
import EmployeeEditor from './components/EmployeeEditor.svelte'
import EmployeePresenter from './components/EmployeePresenter.svelte'
import EmployeeRefPresenter from './components/EmployeeRefPresenter.svelte'
import MemberPresenter from './components/MemberPresenter.svelte'
import MembersPresenter from './components/MembersPresenter.svelte'
import Members from './components/Members.svelte'
import MembersPresenter from './components/MembersPresenter.svelte'
import MergeEmployee from './components/MergeEmployee.svelte'
import OrganizationEditor from './components/OrganizationEditor.svelte'
import OrganizationPresenter from './components/OrganizationPresenter.svelte'
import PersonEditor from './components/PersonEditor.svelte'
import PersonPresenter from './components/PersonPresenter.svelte'
import SocialEditor from './components/SocialEditor.svelte'
import ContactRefPresenter from './components/ContactRefPresenter.svelte'
import PersonRefPresenter from './components/PersonRefPresenter.svelte'
import EmployeeRefPresenter from './components/EmployeeRefPresenter.svelte'
import ChannelFilter from './components/ChannelFilter.svelte'
import AccountBox from './components/AccountBox.svelte'
import MergeEmployee from './components/MergeEmployee.svelte'
import ContactArrayEditor from './components/ContactArrayEditor.svelte'
import SocialEditor from './components/SocialEditor.svelte'
import contact from './plugin'
import {
employeeSort,
@ -88,7 +89,7 @@ export {
const toObjectSearchResult = (e: WithLookup<Contact>): ObjectSearchResult => ({
doc: e,
title: formatName(e.name),
title: getName(e),
icon: Avatar,
iconProps: { size: 'x-small', avatar: e.avatar },
component: UserInfo,
@ -167,6 +168,7 @@ export default async (): Promise<Resources> => ({
CreatePerson,
CreateOrganization,
EditPerson,
EditEmployee,
EditOrganization,
SocialEditor,
Contacts,

View File

@ -61,7 +61,8 @@ export default mergeIds(contactId, contact, {
CreateEmployee: '' as IntlString,
Inactive: '' as IntlString,
NotSpecified: '' as IntlString,
MergeEmployee: '' as IntlString
MergeEmployee: '' as IntlString,
DisplayName: '' as IntlString
},
function: {
EmployeeSort: '' as SortFunc,

View File

@ -14,7 +14,7 @@
// limitations under the License.
//
import { ChannelProvider, Contact, Employee, EmployeeAccount, formatName } from '@hcengineering/contact'
import { ChannelProvider, Contact, Employee, EmployeeAccount, formatName, getName } from '@hcengineering/contact'
import { Doc, getCurrentAccount, ObjQueryType, Ref, Timestamp, toIdMap } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { TemplateDataProvider } from '@hcengineering/templates'
@ -61,8 +61,10 @@ export async function employeeSort (value: Array<Ref<Employee>>): Promise<Array<
}
if (employeeId1 != null && employeeId2 != null) {
const name1 = formatName(employees.get(employeeId1)?.name ?? '')
const name2 = formatName(employees.get(employeeId2)?.name ?? '')
const employee1 = employees.get(employeeId1)
const employee2 = employees.get(employeeId2)
const name1 = employee1 != null ? getName(employee1) : ''
const name2 = employee2 != null ? getName(employee2) : ''
return name1.localeCompare(name2)
}
@ -126,7 +128,7 @@ export async function getContactName (provider: TemplateDataProvider): Promise<s
const client = getClient()
const hierarchy = client.getHierarchy()
if (hierarchy.isDerived(value._class, contact.class.Person)) {
return formatName(value.name)
return getName(value)
} else {
return value.name
}

View File

@ -144,6 +144,7 @@ export interface Employee extends Person {
active: boolean
mergedTo?: Ref<Employee>
statuses?: number
displayName?: string | null
}
/**
@ -193,6 +194,27 @@ export function formatName (name: string): string {
return getLastName(name) + ' ' + getFirstName(name)
}
/**
* @public
*/
export function getName (value: Contact): string {
if (isEmployee(value)) {
return value.displayName ?? formatName(value.name)
}
if (isPerson(value)) {
return formatName(value.name)
}
return value.name
}
function isEmployee (value: Contact): value is Employee {
return value._class === contactPlugin.class.Employee
}
function isPerson (value: Contact): value is Person {
return value._class === contactPlugin.class.Person
}
/**
* @public
*/

View File

@ -14,8 +14,14 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { Channel, Contact, EmployeeAccount, formatName } from '@hcengineering/contact'
import { Ref, SortingOrder } from '@hcengineering/core'
import contact, {
Channel,
Contact,
Employee,
EmployeeAccount,
getName as getContactName
} from '@hcengineering/contact'
import { IdMap, Ref, SortingOrder, toIdMap } from '@hcengineering/core'
import { Message, SharedMessage } from '@hcengineering/gmail'
import { NotificationClientImpl } from '@hcengineering/notification-resources'
import { createQuery, getClient } from '@hcengineering/presentation'
@ -33,12 +39,17 @@
/(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))/
let messages: Message[] = []
let accounts: EmployeeAccount[] = []
let accounts: IdMap<EmployeeAccount> = new Map()
let employees: IdMap<Employee> = new Map()
let selected: Set<Ref<SharedMessage>> = new Set<Ref<SharedMessage>>()
let selectable = false
const messagesQuery = createQuery()
const accauntsQuery = createQuery()
const accountsQuery = createQuery()
const employeesQuery = createQuery()
accountsQuery.query(contact.class.EmployeeAccount, {}, (res) => (accounts = toIdMap(res)))
employeesQuery.query(contact.class.Employee, {}, (res) => (employees = toIdMap(res)))
const notificationClient = NotificationClientImpl.getClient()
@ -49,8 +60,6 @@
(res) => {
messages = res
notificationClient.updateLastView(channelId, channel._class, undefined, true)
const accountsIds = new Set(messages.map((p) => p.modifiedBy as Ref<EmployeeAccount>))
updateAccountsQuery(accountsIds)
},
{ sort: { sendOn: SortingOrder.Descending } }
)
@ -58,12 +67,6 @@
$: updateMessagesQuery(channel._id)
function updateAccountsQuery (accountsIds: Set<Ref<EmployeeAccount>>): void {
accauntsQuery.query(contact.class.EmployeeAccount, { _id: { $in: Array.from(accountsIds) } }, (result) => {
accounts = result
})
}
const client = getClient()
async function share (): Promise<void> {
@ -75,7 +78,7 @@
object._class,
'gmailSharedMessages',
{
messages: convertMessages(selectedMessages, accounts)
messages: convertMessages(selectedMessages, accounts, employees)
}
)
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
@ -88,25 +91,36 @@
selected = selected
}
function convertMessages (messages: Message[], accounts: EmployeeAccount[]): SharedMessage[] {
function convertMessages (
messages: Message[],
accounts: IdMap<EmployeeAccount>,
employees: IdMap<Employee>
): SharedMessage[] {
return messages.map((m) => {
return {
...m,
_id: m._id as string as Ref<SharedMessage>,
sender: getName(m, accounts, true),
receiver: getName(m, accounts, false)
sender: getName(m, accounts, employees, true),
receiver: getName(m, accounts, employees, false)
}
})
}
function getName (message: Message, accounts: EmployeeAccount[], sender: boolean): string {
function getName (
message: Message,
accounts: IdMap<EmployeeAccount>,
employees: IdMap<Employee>,
sender: boolean
): string {
if (message.incoming === sender) {
return `${formatName(object.name)} (${channel.value})`
return `${getContactName(object)} (${channel.value})`
} else {
const account = accounts.find((p) => p._id === message.modifiedBy)
const account = accounts.get(message.modifiedBy as Ref<EmployeeAccount>)
const emp = account ? employees.get(account?.employee) : undefined
const value = message.incoming ? message.to : message.from
const email = value.match(EMAIL_REGEX)
return account ? `${formatName(account.name)} (${email?.[0] ?? value})` : email?.[0] ?? value
const emailVal = email?.[0] ?? value
return emp ? `${getContactName(emp)} (${emailVal})` : emailVal
}
}
</script>
@ -154,7 +168,7 @@
<Scroller>
<div class="popupPanel-body__main-content py-4 clear-mins flex-no-shrink">
{#if messages && messages.length > 0}
<Messages messages={convertMessages(messages, accounts)} {selectable} bind:selected on:select />
<Messages messages={convertMessages(messages, accounts, employees)} {selectable} bind:selected on:select />
<div class="clear-mins h-4 flex-no-shrink" />
{:else}
<div class="flex-col-center justify-center h-full">

View File

@ -14,8 +14,8 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { Channel, formatName } from '@hcengineering/contact'
import { Class, Doc, getCurrentAccount, Ref } from '@hcengineering/core'
import contact, { Channel, Contact, getName } from '@hcengineering/contact'
import { Class, getCurrentAccount, Ref } from '@hcengineering/core'
import { SharedMessage } from '@hcengineering/gmail'
import { NotificationClientImpl } from '@hcengineering/notification-resources'
import { getResource } from '@hcengineering/platform'
@ -31,10 +31,11 @@
import IntegrationSelector from './IntegrationSelector.svelte'
import NewMessage from './NewMessage.svelte'
export let _id: Ref<Doc>
export let _class: Ref<Class<Doc>>
export let _id: Ref<Contact>
export let _class: Ref<Class<Contact>>
let object: Contact
let object: any
let newMessage: boolean = false
let currentMessage: SharedMessage | undefined = undefined
let channel: Channel | undefined = undefined
@ -115,7 +116,7 @@
<span class="wrapped-title">Email</span>
<span class="wrapped-subtitle">
<Label label={gmail.string.YouAnd} />
<b>{formatName(object.name)}</b>
<b>{getName(object)}</b>
</span>
</div>
</div>

View File

@ -15,7 +15,7 @@
<script lang="ts">
import attachmentP, { Attachment } from '@hcengineering/attachment'
import { AttachmentPresenter } from '@hcengineering/attachment-resources'
import contact, { Channel, Contact, formatName } from '@hcengineering/contact'
import contact, { Channel, Contact, getName } from '@hcengineering/contact'
import { Account, Data, generateId, Ref } from '@hcengineering/core'
import { NewMessage, SharedMessage } from '@hcengineering/gmail'
import { NotificationClientImpl } from '@hcengineering/notification-resources'
@ -172,7 +172,7 @@
/>
<div class="flex-grow flex-col">
<Label label={plugin.string.NewMessage} />
<span class="content-accent-color"><b>{formatName(object.name)} ({channel.value})</b></span>
<span class="content-accent-color"><b>{getName(object)} ({channel.value})</b></span>
</div>
</div>
<div class="buttons-group small-gap">

View File

@ -15,7 +15,7 @@
<script lang="ts">
import attachmentP, { Attachment } from '@hcengineering/attachment'
import { AttachmentPresenter } from '@hcengineering/attachment-resources'
import contact, { Channel, Contact, formatName } from '@hcengineering/contact'
import contact, { Channel, Contact, getName as getContactName } from '@hcengineering/contact'
import { Account, generateId, getCurrentAccount, Ref, toIdMap } from '@hcengineering/core'
import { NotificationClientImpl } from '@hcengineering/notification-resources'
import { getResource, setPlatformStatus, unknownError } from '@hcengineering/platform'
@ -183,7 +183,7 @@
function getName (channel: Channel): string {
const contact = contactMap.get(channel.attachedTo as Ref<Contact>)
if (contact === undefined) return channel.value
return `${formatName(contact.name)} (${channel.value})`
return `${getContactName(contact)} (${channel.value})`
}
const settingsQuery = createQuery()

View File

@ -1,4 +1,4 @@
import { Employee, formatName } from '@hcengineering/contact'
import { Employee, getName } from '@hcengineering/contact'
import { Ref, TxOperations } from '@hcengineering/core'
import { Department, fromTzDate, Request, RequestType, Staff } from '@hcengineering/hr'
import { MessageBox } from '@hcengineering/presentation'
@ -30,7 +30,7 @@ export async function addMember (client: TxOperations, employee?: Employee, valu
MessageBox,
{
label: hr.string.MoveStaff,
labelProps: { name: formatName(employee.name) },
labelProps: { name: getName(employee) },
message: hr.string.MoveStaffDescr,
params: {
current: current.name,

View File

@ -14,7 +14,7 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { formatName, Person } from '@hcengineering/contact'
import contact, { getName, Person } from '@hcengineering/contact'
import { Ref } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import type { Applicant } from '@hcengineering/recruit'
@ -39,7 +39,7 @@
{#if shortLabel}<Label label={shortLabel} />-{/if}{value.number}
</div>
{#if person}
<div class="ml-1">{formatName(person.name)}</div>
<div class="ml-1">{getName(person)}</div>
{/if}
</div>

View File

@ -15,7 +15,7 @@
<script lang="ts">
import attachment from '@hcengineering/attachment'
import chunter from '@hcengineering/chunter'
import contact, { Channel, formatName, Person } from '@hcengineering/contact'
import contact, { Channel, getName, Person } from '@hcengineering/contact'
import { ChannelsEditor } from '@hcengineering/contact-resources'
import { Hierarchy } from '@hcengineering/core'
import { Avatar, createQuery, getClient } from '@hcengineering/presentation'
@ -58,7 +58,7 @@
}
}}
>
{formatName(candidate.name)}
{getName(candidate)}
</div>
{#if client.getHierarchy().hasMixin(candidate, recruit.mixin.Candidate)}
{@const cand = client.getHierarchy().as(candidate, recruit.mixin.Candidate)}

View File

@ -15,7 +15,7 @@
<script lang="ts">
import { AttachmentsPresenter } from '@hcengineering/attachment-resources'
import { CommentsPresenter } from '@hcengineering/chunter-resources'
import contact, { formatName } from '@hcengineering/contact'
import contact, { getName } from '@hcengineering/contact'
import { Hierarchy, WithLookup } from '@hcengineering/core'
import notification from '@hcengineering/notification'
import { Avatar } from '@hcengineering/presentation'
@ -43,7 +43,7 @@
<Avatar avatar={object.$lookup?.attachedTo?.avatar} size={'medium'} />
<div class="flex-grow flex-col min-w-0 ml-2">
<div class="fs-title over-underline lines-limit-2">
{formatName(object.$lookup?.attachedTo?.name ?? '')}
{object.$lookup?.attachedTo ? getName(object.$lookup.attachedTo) : ''}
</div>
<div class="text-sm lines-limit-2">{object.$lookup?.attachedTo?.title ?? ''}</div>
</div>

View File

@ -15,7 +15,7 @@
-->
<script lang="ts">
import calendar from '@hcengineering/calendar'
import { formatName, Person } from '@hcengineering/contact'
import { getName, Person } from '@hcengineering/contact'
import { Hierarchy } from '@hcengineering/core'
import { Avatar } from '@hcengineering/presentation'
import { showPanel, tooltip } from '@hcengineering/ui'
@ -38,7 +38,7 @@
<div
class="flex-presenter"
class:inline-presenter={inline}
use:tooltip={{ label: calendar.string.PersonsLabel, props: { name: formatName(p.name) } }}
use:tooltip={{ label: calendar.string.PersonsLabel, props: { name: getName(p) } }}
on:click={() => onClick(p)}
>
<div class="icon">

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { EmployeeAccount, formatName } from '@hcengineering/contact'
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
import { Ref } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation'
import { Request } from '@hcengineering/request'
@ -25,18 +25,31 @@
export let value: Request
let employee: EmployeeAccount | undefined
let account: EmployeeAccount | undefined
let employee: Employee | undefined
const query = createQuery()
$: query.query(
contact.class.EmployeeAccount,
{ _id: value.tx.modifiedBy as Ref<EmployeeAccount> },
(account) => {
;[employee] = account
(res) => {
;[account] = res
},
{ limit: 1 }
)
const employeeQuery = createQuery()
$: account &&
employeeQuery.query(
contact.class.Employee,
{ _id: account.employee },
(res) => {
;[employee] = res
},
{ limit: 1 }
)
</script>
<div class="container">
@ -44,7 +57,7 @@
<div class="label">
<div class="bold">
{#if employee}
{formatName(employee.name)}
{getName(employee)}
{/if}
</div>
<span class="lower">

View File

@ -14,7 +14,7 @@
-->
<script lang="ts">
import contact, { Employee, EmployeeAccount, formatName } from '@hcengineering/contact'
import { PersonPresenter } from '@hcengineering/contact-resources'
import { EmployeePresenter } from '@hcengineering/contact-resources'
import { AccountRole, getCurrentAccount, IdMap, SortingOrder, toIdMap } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { DropdownIntlItem, DropdownLabelsIntl, Icon, Label } from '@hcengineering/ui'
@ -70,7 +70,7 @@
{@const employee = employees.get(account.employee)}
<div class="flex-between">
{#if employee}
<PersonPresenter value={employee} isInteractive={false} />
<EmployeePresenter value={employee} isInteractive={false} />
{:else}
{formatName(account.name)}
{/if}

View File

@ -30,6 +30,7 @@
let employee: Employee | undefined
let firstName: string
let lastName: string
let displayName: string = ''
const employeeQ = createQuery()
const account = getCurrentAccount() as EmployeeAccount
@ -43,6 +44,7 @@
employee = res[0]
firstName = getFirstName(employee.name)
lastName = getLastName(employee.name)
displayName = employee.displayName ?? ''
},
{ limit: 1 }
)
@ -76,6 +78,14 @@
}
)
}
function changeDisplayName () {
if (employee) {
client.update(employee, {
displayName: displayName === '' ? null : displayName
})
}
}
</script>
<FocusHandler {manager} />
@ -118,6 +128,13 @@
changeName(firstName, lastName)
}}
/>
<EditBox
placeholder={contactRes.string.DisplayName}
bind:value={displayName}
kind={'large-style'}
focusIndex={2}
on:change={changeDisplayName}
/>
<div class="location">
<AttributeEditor
maxWidth="20rem"

View File

@ -1,4 +1,4 @@
import contact, { EmployeeAccount, formatName } from '@hcengineering/contact'
import contact, { EmployeeAccount, getName } from '@hcengineering/contact'
import { Class, Doc, Hierarchy, Ref } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import setting from '@hcengineering/setting'
@ -62,5 +62,10 @@ export async function getOwnerName (provider: TemplateDataProvider): Promise<str
const employeeAccount = await client.findOne(contact.class.EmployeeAccount, {
_id: value.modifiedBy as Ref<EmployeeAccount>
})
return employeeAccount != null ? formatName(employeeAccount.name) : undefined
if (employeeAccount !== undefined) {
const employee = await client.findOne(contact.class.Employee, {
_id: employeeAccount.employee
})
return employee != null ? getName(employee) : undefined
}
}

View File

@ -16,7 +16,7 @@
<script lang="ts">
import attachment from '@hcengineering/attachment'
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
import contact, { Channel, Contact, EmployeeAccount, formatName } from '@hcengineering/contact'
import contact, { Channel, Contact, EmployeeAccount, getName as getContactName } from '@hcengineering/contact'
import { Class, generateId, getCurrentAccount, Ref, SortingOrder, Space } from '@hcengineering/core'
import { NotificationClientImpl } from '@hcengineering/notification-resources'
import { getResource } from '@hcengineering/platform'
@ -221,7 +221,7 @@
<span class="wrapped-title">Telegram</span>
<span class="wrapped-subtitle">
<Label label={telegram.string.YouAnd} />
<b>{formatName(object.name)}</b>
<b>{getContactName(object)}</b>
</span>
</div>
</div>

View File

@ -13,7 +13,7 @@
// limitations under the License.
//
import { Employee, formatName } from '@hcengineering/contact'
import { Employee, getName } from '@hcengineering/contact'
import core, {
AttachedData,
Class,
@ -406,7 +406,7 @@ export async function mapKanbanCategories (
.map((employee) => {
return {
_id: employee._id,
title: formatName(employee.name),
title: getName(employee),
color: UNSET_COLOR,
icon: undefined
}

View File

@ -14,7 +14,7 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { formatName } from '@hcengineering/contact'
import contact, { Contact, getName } from '@hcengineering/contact'
import { Class, ClassifierKind, Doc, Mixin, Obj, Ref } from '@hcengineering/core'
import notification from '@hcengineering/notification'
import { Panel } from '@hcengineering/panel'
@ -40,6 +40,8 @@
export let _id: Ref<Doc>
export let _class: Ref<Class<Doc>>
console.log('OPEN EDIT DOC')
let realObjectClass: Ref<Class<Doc>> = _class
let lastId: Ref<Doc> = _id
let lastClass: Ref<Class<Doc>> = _class
@ -223,7 +225,7 @@
const name = (object as any).name
if (name !== undefined) {
if (hierarchy.isDerived(object._class, contact.class.Person)) {
return formatName(name)
return getName(object as Contact)
}
return name
}

View File

@ -14,7 +14,7 @@
// limitations under the License.
//
import contact, { Contact, contactId, Employee, formatName, Organization, Person } from '@hcengineering/contact'
import contact, { Contact, contactId, Employee, getName, Organization, Person } from '@hcengineering/contact'
import core, {
AnyAttribute,
ArrOf,
@ -279,7 +279,7 @@ export function personHTMLPresenter (doc: Doc, control: TriggerControl): string
const front = getMetadata(login.metadata.FrontUrl) ?? ''
const path = `${workbenchId}/${control.workspace.name}/${contactId}#${view.component.EditDoc}|${person._id}|${person._class}|content`
const link = concatLink(front, path)
return `<a href="${link}">${formatName(person.name)}</a>`
return `<a href="${link}">${getName(person)}</a>`
}
/**
@ -287,7 +287,7 @@ export function personHTMLPresenter (doc: Doc, control: TriggerControl): string
*/
export function personTextPresenter (doc: Doc): string {
const person = doc as Person
return `${formatName(person.name)}`
return `${getName(person)}`
}
/**

View File

@ -13,7 +13,7 @@
// limitations under the License.
//
import contact, { Contact, Employee, EmployeeAccount, formatName } from '@hcengineering/contact'
import contact, { Contact, Employee, EmployeeAccount, formatName, getName } from '@hcengineering/contact'
import core, {
Doc,
Ref,
@ -318,7 +318,7 @@ export async function OnRequestRemove (tx: Tx, control: TriggerControl): Promise
export async function RequestHTMLPresenter (doc: Doc, control: TriggerControl): Promise<string> {
const request = doc as Request
const employee = (await control.findAll(contact.class.Employee, { _id: request.attachedTo }))[0]
const who = formatName(employee.name)
const who = getName(employee)
const type = await translate(control.modelDb.getObject(request.type).label, {})
const date = tzDateEqual(request.tzDate, request.tzDueDate)
@ -336,7 +336,7 @@ export async function RequestHTMLPresenter (doc: Doc, control: TriggerControl):
export async function RequestTextPresenter (doc: Doc, control: TriggerControl): Promise<string> {
const request = doc as Request
const employee = (await control.findAll(contact.class.Employee, { _id: request.attachedTo }))[0]
const who = formatName(employee.name)
const who = getName(employee)
const type = await translate(control.modelDb.getObject(request.type).label, {})
const date = tzDateEqual(request.tzDate, request.tzDueDate)