UBER-665: Rename EmployeeAccount->PersonAccount (#3550)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2023-08-05 01:06:21 +07:00 committed by GitHub
parent 9acc1b86a0
commit 161d26ebeb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
181 changed files with 1215 additions and 1159 deletions

View File

@ -49,7 +49,7 @@ export async function generateIssues (
options: IssueOptions
): Promise<void> {
const connection = await connect(transactorUrl, workspaceId)
const accounts = await connection.findAll(contact.class.EmployeeAccount, {})
const accounts = await connection.findAll(contact.class.PersonAccount, {})
const account = faker.random.arrayElement(accounts)
const client = new TxOperations(connection, account._id)
const ctx = new MeasureMetricsContext('recruit', {})

View File

@ -1,4 +1,4 @@
import contact, { Channel, Employee, EmployeeAccount, Person } from '@hcengineering/contact'
import contact, { Channel, PersonAccount, Person, Employee } from '@hcengineering/contact'
import core, {
AttachedData,
Data,
@ -48,9 +48,9 @@ export async function generateContacts (
): Promise<void> {
const connection = await connect(transactorUrl, workspaceId)
const accounts = await connection.findAll(contact.class.EmployeeAccount, {})
const accounts = await connection.findAll(contact.class.PersonAccount, {})
const accountIds = accounts.map((a) => a._id)
const emoloyeeIds = accounts.map((a) => a.employee)
const emoloyeeIds = accounts.map((a) => a.person as Ref<Employee>)
const account = faker.random.arrayElement(accounts)
@ -78,7 +78,7 @@ export async function generateContacts (
async function genVacansyApplicants (
ctx: MeasureContext,
accountIds: Ref<EmployeeAccount>[],
accountIds: Ref<PersonAccount>[],
options: RecruitOptions,
i: number,
client: TxOperations,

View File

@ -207,7 +207,7 @@ export async function benchmark (
'avg',
{},
async () =>
await monitorConnection?.findAll(contact.class.Employee, {}).then((res) => {
await monitorConnection?.findAll(contact.mixin.Employee, {}).then((res) => {
const cur = Date.now() - st
opTime += cur
moment = cur

View File

@ -76,7 +76,7 @@ export async function cleanWorkspace (
if (opt.recruit) {
const contacts = await ops.findAll(recruit.mixin.Candidate, {})
console.log('removing Talents', contacts.length)
const filter = contacts.filter((it) => !hierarchy.isDerived(it._class, contact.class.Employee))
const filter = contacts.filter((it) => !hierarchy.isDerived(it._class, contact.mixin.Employee))
while (filter.length > 0) {
const part = filter.splice(0, 100)

View File

@ -33,7 +33,7 @@ export async function diffWorkspace (mongoUrl: string, workspace: WorkspaceId, r
.find<Tx>({
objectSpace: core.space.Model,
modifiedBy: core.account.System,
objectClass: { $ne: contact.class.EmployeeAccount }
objectClass: { $ne: contact.class.PersonAccount }
})
.toArray()
@ -41,7 +41,7 @@ export async function diffWorkspace (mongoUrl: string, workspace: WorkspaceId, r
return (
tx.objectSpace === core.space.Model &&
tx.modifiedBy === core.account.System &&
(tx as any).objectClass !== contact.class.EmployeeAccount
(tx as any).objectClass !== contact.class.PersonAccount
)
})

View File

@ -88,10 +88,10 @@ export class TCard extends TTask implements Card {
@Index(IndexKind.FullText)
location?: string
@Prop(TypeRef(contact.class.Employee), board.string.Assignee)
@Prop(TypeRef(contact.mixin.Employee), board.string.Assignee)
declare assignee: Ref<Employee> | null
@Prop(ArrOf(TypeRef(contact.class.Employee)), board.string.Members)
@Prop(ArrOf(TypeRef(contact.mixin.Employee)), board.string.Members)
members?: Ref<Employee>[]
@Prop(TypeCardCover(), board.string.Cover)

View File

@ -22,7 +22,7 @@ import contact from '@hcengineering/contact'
async function migrateCalendars (tx: TxOperations): Promise<void> {
const existCalendars = new Set((await tx.findAll(calendar.class.Calendar, {})).map((p) => p._id))
const users = await tx.findAll(contact.class.EmployeeAccount, {})
const users = await tx.findAll(contact.class.PersonAccount, {})
for (const user of users) {
if (!existCalendars.has(`${user._id}_calendar` as Ref<Calendar>)) {
await tx.createDoc(

View File

@ -27,7 +27,7 @@ import {
SavedMessages,
ThreadMessage
} from '@hcengineering/chunter'
import contact, { Employee } from '@hcengineering/contact'
import contact, { Person } from '@hcengineering/contact'
import type { Account, Class, Doc, Domain, Ref, Space, Timestamp } from '@hcengineering/core'
import { IndexKind } from '@hcengineering/core'
import {
@ -113,8 +113,8 @@ export class TMessage extends TChunterMessage implements Message {
declare attachedToClass: Ref<Class<Space>>
@Prop(ArrOf(TypeRef(contact.class.Employee)), chunter.string.Replies)
replies?: Ref<Employee>[]
@Prop(ArrOf(TypeRef(contact.class.Person)), chunter.string.Replies)
replies?: Ref<Person>[]
repliesCount?: number

View File

@ -23,7 +23,7 @@ import {
Contact,
ContactsTab,
Employee,
EmployeeAccount,
PersonAccount,
GetAvatarUrl,
Member,
Organization,
@ -39,8 +39,11 @@ import {
Collection,
Hidden,
Index,
Mixin,
Model,
Prop,
ReadOnly,
TypeBoolean,
TypeDate,
TypeRef,
TypeString,
@ -153,27 +156,30 @@ export class TStatus extends TAttachedDoc implements Status {
dueDate!: Timestamp
}
@Model(contact.class.Employee, contact.class.Person)
@Mixin(contact.mixin.Employee, contact.class.Person)
@UX(contact.string.Employee, contact.icon.Person, 'EMP', 'name')
export class TEmployee extends TPerson implements Employee {
active!: boolean
@Prop(TypeBoolean(), contact.string.Active)
@ReadOnly()
@Hidden()
active!: boolean
@Prop(Collection(contact.class.Status), contact.string.Status)
@Hidden()
statuses?: number
mergedTo?: Ref<Employee>
@Prop(TypeString(), contact.string.DisplayName)
@Hidden()
displayName?: string | null
@Prop(TypeString(), contact.string.Position)
@Hidden()
position?: string | null
}
@Model(contact.class.EmployeeAccount, core.class.Account)
export class TEmployeeAccount extends TAccount implements EmployeeAccount {
employee!: Ref<Employee>
mergedTo!: Ref<EmployeeAccount>
@Model(contact.class.PersonAccount, core.class.Account)
export class TPersonAccount extends TAccount implements PersonAccount {
person!: Ref<Person>
}
@Model(contact.class.Organizations, core.class.Space)
@ -201,14 +207,14 @@ export function createModel (builder: Builder): void {
TOrganization,
TOrganizations,
TEmployee,
TEmployeeAccount,
TPersonAccount,
TChannel,
TStatus,
TMember,
TContactsTab
)
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.ObjectFactory, {
builder.mixin(contact.mixin.Employee, core.class.Class, view.mixin.ObjectFactory, {
component: contact.component.CreateEmployee
})
@ -239,7 +245,7 @@ export function createModel (builder: Builder): void {
icon: contact.icon.Person,
label: contact.string.Employee,
componentProps: {
_class: contact.class.Employee,
_class: contact.mixin.Employee,
icon: contact.icon.Person,
label: contact.string.Employee,
createLabel: contact.string.CreateEmployee,
@ -253,6 +259,9 @@ export function createModel (builder: Builder): void {
label: contact.string.Person,
componentProps: {
_class: contact.class.Person,
baseQuery: {
[contact.mixin.Employee]: { $exists: false }
},
icon: contact.icon.Person,
label: contact.string.Person,
createLabel: contact.string.CreatePerson,
@ -337,7 +346,7 @@ export function createModel (builder: Builder): void {
baseQuery: {
_class: {
$in: [contact.class.Person],
$nin: [contact.class.Employee]
$nin: [contact.mixin.Employee]
}
}
},
@ -347,7 +356,7 @@ export function createModel (builder: Builder): void {
view.class.Viewlet,
core.space.Model,
{
attachTo: contact.class.Employee,
attachTo: contact.mixin.Employee,
descriptor: view.viewlet.Table,
config: [
'',
@ -400,7 +409,7 @@ export function createModel (builder: Builder): void {
pinned: true
})
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.ObjectEditor, {
builder.mixin(contact.mixin.Employee, core.class.Class, view.mixin.ObjectEditor, {
editor: contact.component.EditEmployee,
pinned: true
})
@ -418,7 +427,7 @@ export function createModel (builder: Builder): void {
editor: contact.component.Members
})
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.ArrayEditor, {
builder.mixin(contact.mixin.Employee, core.class.Class, view.mixin.ArrayEditor, {
inlineEditor: contact.component.EmployeeArrayEditor
})
@ -450,7 +459,7 @@ export function createModel (builder: Builder): void {
inlineEditor: contact.component.PersonEditor
})
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.AttributeEditor, {
builder.mixin(contact.mixin.Employee, core.class.Class, view.mixin.AttributeEditor, {
inlineEditor: contact.component.EmployeeEditor
})
@ -466,15 +475,15 @@ export function createModel (builder: Builder): void {
encode: contact.function.GetContactLink
})
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.AttributeFilterPresenter, {
builder.mixin(contact.mixin.Employee, core.class.Class, view.mixin.AttributeFilterPresenter, {
presenter: contact.component.EmployeeFilterValuePresenter
})
builder.mixin(core.class.Account, core.class.Class, view.mixin.AttributeFilterPresenter, {
presenter: contact.component.EmployeeAccountFilterValuePresenter
presenter: contact.component.PersonAccountFilterValuePresenter
})
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.AttributeFilter, {
builder.mixin(contact.mixin.Employee, core.class.Class, view.mixin.AttributeFilter, {
component: contact.component.EmployeeFilter
})
@ -621,15 +630,15 @@ export function createModel (builder: Builder): void {
inlineEditor: contact.component.AccountArrayEditor
})
builder.mixin(contact.class.EmployeeAccount, core.class.Class, view.mixin.ArrayEditor, {
builder.mixin(contact.class.PersonAccount, core.class.Class, view.mixin.ArrayEditor, {
inlineEditor: contact.component.AccountArrayEditor
})
builder.mixin(core.class.Account, core.class.Class, view.mixin.ObjectPresenter, {
presenter: contact.component.EmployeeAccountPresenter
presenter: contact.component.PersonAccountPresenter
})
builder.mixin(core.class.Account, core.class.Class, view.mixin.AttributePresenter, {
presenter: contact.component.EmployeeAccountRefPresenter
presenter: contact.component.PersonAccountRefPresenter
})
builder.mixin(contact.class.Organization, core.class.Class, view.mixin.ObjectPresenter, {
@ -640,11 +649,11 @@ export function createModel (builder: Builder): void {
presenter: contact.component.ContactPresenter
})
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.ObjectPresenter, {
builder.mixin(contact.mixin.Employee, core.class.Class, view.mixin.ObjectPresenter, {
presenter: contact.component.EmployeePresenter
})
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.SortFuncs, {
builder.mixin(contact.mixin.Employee, core.class.Class, view.mixin.SortFuncs, {
func: contact.function.EmployeeSort
})
@ -656,11 +665,11 @@ export function createModel (builder: Builder): void {
presenter: contact.component.ContactRefPresenter
})
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.AttributePresenter, {
builder.mixin(contact.mixin.Employee, core.class.Class, view.mixin.AttributePresenter, {
presenter: contact.component.EmployeeRefPresenter
})
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.IgnoreActions, {
builder.mixin(contact.mixin.Employee, core.class.Class, view.mixin.IgnoreActions, {
actions: [view.action.Delete]
})
@ -671,7 +680,7 @@ export function createModel (builder: Builder): void {
builder.mixin(contact.class.Person, core.class.Class, view.mixin.ClassFilters, {
filters: []
})
builder.mixin(contact.class.Employee, core.class.Class, view.mixin.ClassFilters, {
builder.mixin(contact.mixin.Employee, core.class.Class, view.mixin.ClassFilters, {
filters: []
})
builder.mixin(contact.class.Organization, core.class.Class, view.mixin.ClassFilters, {
@ -737,7 +746,7 @@ export function createModel (builder: Builder): void {
active: true
},
category: contact.category.Contact,
target: contact.class.Employee,
target: contact.mixin.Employee,
input: 'focus',
context: {
mode: ['context'],
@ -757,7 +766,7 @@ export function createModel (builder: Builder): void {
active: false
},
category: contact.category.Contact,
target: contact.class.Employee,
target: contact.mixin.Employee,
input: 'focus',
context: {
mode: ['context'],
@ -773,18 +782,16 @@ export function createModel (builder: Builder): void {
{
action: view.actionImpl.ShowPopup,
actionProps: {
component: contact.component.MergeEmployee,
component: contact.component.MergePersons,
element: 'top',
fillProps: {
_object: 'value'
}
},
query: {
active: false
},
label: contact.string.MergeEmployee,
query: {},
label: contact.string.MergePersons,
category: contact.category.Contact,
target: contact.class.Employee,
target: contact.class.Person,
input: 'focus',
context: {
mode: ['context'],
@ -792,7 +799,7 @@ export function createModel (builder: Builder): void {
},
secured: true
},
contact.action.MergeEmployee
contact.action.MergePersons
)
// Allow to use fuzzy search for mixins

View File

@ -1,9 +1,11 @@
//
import { TxOperations } from '@hcengineering/core'
import { Class, DOMAIN_TX, Doc, Domain, Ref, TxOperations } from '@hcengineering/core'
import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@hcengineering/model'
import { DOMAIN_COMMENT } from '@hcengineering/model-chunter'
import core from '@hcengineering/model-core'
import contact from './index'
import { DOMAIN_VIEW } from '@hcengineering/model-view'
import contact, { DOMAIN_CONTACT } from './index'
async function createSpace (tx: TxOperations): Promise<void> {
const current = await tx.findOne(core.class.Space, {
@ -43,14 +45,14 @@ async function createSpace (tx: TxOperations): Promise<void> {
}
async function createEmployeeEmail (client: TxOperations): Promise<void> {
const employees = await client.findAll(contact.class.Employee, {})
const employees = await client.findAll(contact.mixin.Employee, {})
const channels = await client.findAll(contact.class.Channel, {
provider: contact.channelProvider.Email,
attachedTo: { $in: employees.map((p) => p._id) }
})
const channelsMap = new Map(channels.map((p) => [p.attachedTo, p]))
for (const employee of employees) {
const acc = await client.findOne(contact.class.EmployeeAccount, { employee: employee._id })
const acc = await client.findOne(contact.class.PersonAccount, { person: employee._id })
if (acc === undefined) continue
const current = channelsMap.get(employee._id)
if (current === undefined) {
@ -58,7 +60,7 @@ async function createEmployeeEmail (client: TxOperations): Promise<void> {
contact.class.Channel,
contact.space.Contacts,
employee._id,
contact.class.Employee,
contact.mixin.Employee,
'channels',
{
provider: contact.channelProvider.Email,
@ -74,7 +76,88 @@ async function createEmployeeEmail (client: TxOperations): Promise<void> {
}
export const contactOperation: MigrateOperation = {
async migrate (client: MigrationClient): Promise<void> {},
async migrate (client: MigrationClient): Promise<void> {
await client.update(
DOMAIN_TX,
{
objectClass: 'contact:class:EmployeeAccount'
},
{
$rename: { 'attributes.employee': 'attributes.person' },
$set: { objectClass: contact.class.PersonAccount }
}
)
await client.update(
DOMAIN_TX,
{
objectClass: 'contact:class:Employee'
},
{
$set: { objectClass: contact.mixin.Employee }
}
)
await client.update(
DOMAIN_TX,
{
'tx.attributes.backlinkClass': 'contact:class:Employee'
},
{
$set: { 'tx.attributes.backlinkClass': contact.mixin.Employee }
}
)
await client.update(
DOMAIN_TX,
{
'tx.attributes.backlinkClass': 'contact:class:Employee'
},
{
$set: { 'tx.attributes.backlinkClass': contact.mixin.Employee }
}
)
for (const d of client.hierarchy.domains()) {
await client.update(
d,
{ attachedToClass: 'contact:class:Employee' },
{ $set: { attachedToClass: contact.mixin.Employee } }
)
}
await client.update(
DOMAIN_COMMENT,
{ backlinkClass: 'contact:class:Employee' },
{ $set: { backlinkClass: contact.mixin.Employee } }
)
await client.update(
'tags' as Domain,
{ targetClass: 'contact:class:Employee' },
{ $set: { targetClass: contact.mixin.Employee } }
)
await client.update(
DOMAIN_VIEW,
{ filterClass: 'contact:class:Employee' },
{ $set: { filterClass: contact.mixin.Employee } }
)
await client.update(
DOMAIN_CONTACT,
{
_class: 'contact:class:Employee' as Ref<Class<Doc>>
},
{
$rename: {
active: `${contact.mixin.Employee as string}.active`,
statuses: `${contact.mixin.Employee as string}.statuses`,
displayName: `${contact.mixin.Employee as string}.displayName`,
position: `${contact.mixin.Employee as string}.position`
},
$set: {
_class: contact.class.Person
}
}
)
},
async upgrade (client: MigrationUpgradeClient): Promise<void> {
const tx = new TxOperations(client, core.account.System)
await createSpace(tx)

View File

@ -36,8 +36,8 @@ export default mergeIds(contactId, contact, {
OrganizationPresenter: '' as AnyComponent,
Contacts: '' as AnyComponent,
ContactsTabs: '' as AnyComponent,
EmployeeAccountPresenter: '' as AnyComponent,
EmployeeAccountRefPresenter: '' as AnyComponent,
PersonAccountPresenter: '' as AnyComponent,
PersonAccountRefPresenter: '' as AnyComponent,
OrganizationEditor: '' as AnyComponent,
EmployeePresenter: '' as AnyComponent,
EmployeeRefPresenter: '' as AnyComponent,
@ -52,13 +52,13 @@ export default mergeIds(contactId, contact, {
CreateEmployee: '' as AnyComponent,
AccountArrayEditor: '' as AnyComponent,
ChannelFilter: '' as AnyComponent,
MergeEmployee: '' as AnyComponent,
MergePersons: '' as AnyComponent,
ActivityChannelMessage: '' as AnyComponent,
ChannelPanel: '' as AnyComponent,
ActivityChannelPresenter: '' as AnyComponent,
EmployeeFilter: '' as AnyComponent,
EmployeeFilterValuePresenter: '' as AnyComponent,
EmployeeAccountFilterValuePresenter: '' as AnyComponent
PersonAccountFilterValuePresenter: '' as AnyComponent
},
string: {
Persons: '' as IntlString,
@ -114,7 +114,7 @@ export default mergeIds(contactId, contact, {
action: {
KickEmployee: '' as Ref<Action>,
DeleteEmployee: '' as Ref<Action>,
MergeEmployee: '' as Ref<Action>
MergePersons: '' as Ref<Action>
},
actionImpl: {
KickEmployee: '' as ViewAction,

View File

@ -133,13 +133,13 @@ export class TDocument extends TDoc implements Document {
@Hidden()
versions!: number
@Prop(ArrOf(TypeRef(contact.class.Employee)), document.string.Authors)
@Prop(ArrOf(TypeRef(contact.mixin.Employee)), document.string.Authors)
authors!: Ref<Employee>[]
@Prop(ArrOf(TypeRef(contact.class.Employee)), document.string.Reviewers)
@Prop(ArrOf(TypeRef(contact.mixin.Employee)), document.string.Reviewers)
reviewers!: Ref<Employee>[]
@Prop(ArrOf(TypeRef(contact.class.Employee)), document.string.Approvers)
@Prop(ArrOf(TypeRef(contact.mixin.Employee)), document.string.Approvers)
approvers!: Ref<Employee>[]
@Prop(Collection(document.class.DocumentRequest), document.string.Requests)

View File

@ -43,7 +43,7 @@ import {
import attachment from '@hcengineering/model-attachment'
import calendar from '@hcengineering/model-calendar'
import chunter from '@hcengineering/model-chunter'
import contact, { TEmployee, TEmployeeAccount } from '@hcengineering/model-contact'
import contact, { TEmployee, TPersonAccount } from '@hcengineering/model-contact'
import core, { TAttachedDoc, TDoc, TSpace, TType } from '@hcengineering/model-core'
import view, { classPresenter, createAction } from '@hcengineering/model-view'
import workbench from '@hcengineering/model-workbench'
@ -80,7 +80,7 @@ export class TDepartment extends TSpace implements Department {
avatar?: string | null
@Prop(TypeRef(contact.class.Employee), hr.string.TeamLead)
@Prop(TypeRef(contact.mixin.Employee), hr.string.TeamLead)
teamLead!: Ref<Employee> | null
@Prop(ArrOf(TypeRef(hr.class.DepartmentMember)), contact.string.Members)
@ -89,15 +89,15 @@ export class TDepartment extends TSpace implements Department {
@Prop(ArrOf(TypeRef(contact.class.Contact)), hr.string.Subscribers)
subscribers?: Arr<Ref<Contact>>
@Prop(ArrOf(TypeRef(contact.class.Employee)), hr.string.Managers)
@Prop(ArrOf(TypeRef(contact.mixin.Employee)), hr.string.Managers)
managers!: Arr<Ref<Employee>>
}
@Model(hr.class.DepartmentMember, contact.class.EmployeeAccount)
@Model(hr.class.DepartmentMember, contact.class.PersonAccount)
@UX(contact.string.Employee, hr.icon.HR)
export class TDepartmentMember extends TEmployeeAccount implements DepartmentMember {}
export class TDepartmentMember extends TPersonAccount implements DepartmentMember {}
@Mixin(hr.mixin.Staff, contact.class.Employee)
@Mixin(hr.mixin.Staff, contact.mixin.Employee)
@UX(hr.string.Staff, hr.icon.HR, 'STFF', 'name')
export class TStaff extends TEmployee implements Staff {
@Prop(TypeRef(hr.class.Department), hr.string.Department)

View File

@ -78,7 +78,7 @@ export class TLead extends TTask implements Lead {
@Index(IndexKind.FullText)
title!: string
@Prop(TypeRef(contact.class.Employee), lead.string.Assignee)
@Prop(TypeRef(contact.mixin.Employee), lead.string.Assignee)
declare assignee: Ref<Employee> | null
@Prop(TypeRef(task.class.State), task.string.TaskState, { _id: task.attribute.State })

View File

@ -162,7 +162,7 @@ export class TApplicant extends TTask implements Applicant {
@Prop(TypeDate(), task.string.StartDate)
startDate!: Timestamp | null
@Prop(TypeRef(contact.class.Employee), recruit.string.AssignedRecruiter)
@Prop(TypeRef(contact.mixin.Employee), recruit.string.AssignedRecruiter)
declare assignee: Ref<Employee> | null
@Prop(TypeRef(task.class.State), task.string.TaskState, { _id: task.attribute.State })
@ -1105,7 +1105,7 @@ export function createModel (builder: Builder): void {
actionPopup: view.component.ValueSelector,
actionProps: {
attribute: 'assignee',
_class: contact.class.Employee,
_class: contact.mixin.Employee,
query: {},
placeholder: recruit.string.AssignRecruiter
},

View File

@ -13,7 +13,7 @@ import { generateClassNotificationTypes } from '@hcengineering/model-notificatio
export const reviewTableOptions: FindOptions<Review> = {
lookup: {
attachedTo: recruit.mixin.Candidate,
participants: contact.class.Employee,
participants: contact.mixin.Employee,
company: contact.class.Organization
}
}

View File

@ -15,7 +15,7 @@
import activity from '@hcengineering/activity'
import chunter from '@hcengineering/chunter'
import type { EmployeeAccount } from '@hcengineering/contact'
import type { PersonAccount } from '@hcengineering/contact'
import contact from '@hcengineering/contact'
import { Domain, IndexKind, Ref, Tx } from '@hcengineering/core'
import {
@ -48,13 +48,13 @@ export const DOMAIN_REQUEST = 'request' as Domain
@Model(request.class.Request, core.class.AttachedDoc, DOMAIN_REQUEST)
@UX(request.string.Request, request.icon.Requests)
export class TRequest extends TAttachedDoc implements Request {
@Prop(ArrOf(TypeRef(contact.class.EmployeeAccount)), request.string.Requested)
@Prop(ArrOf(TypeRef(contact.class.PersonAccount)), request.string.Requested)
@Index(IndexKind.Indexed)
requested!: Ref<EmployeeAccount>[]
requested!: Ref<PersonAccount>[]
@Prop(ArrOf(TypeRef(contact.class.EmployeeAccount)), request.string.Approved)
@Prop(ArrOf(TypeRef(contact.class.PersonAccount)), request.string.Approved)
@ReadOnly()
approved!: Ref<EmployeeAccount>[]
approved!: Ref<PersonAccount>[]
requiredApprovesCount!: number
@ -64,9 +64,9 @@ export class TRequest extends TAttachedDoc implements Request {
tx!: Tx
@Prop(TypeRef(contact.class.EmployeeAccount), request.string.Rejected)
@Prop(TypeRef(contact.class.PersonAccount), request.string.Rejected)
@ReadOnly()
rejected?: Ref<EmployeeAccount>
rejected?: Ref<PersonAccount>
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
comments?: number

View File

@ -34,10 +34,10 @@ export function createModel (builder: Builder): void {
})
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverCalendar.trigger.OnEmployeeAccountCreate,
trigger: serverCalendar.trigger.OnPersonAccountCreate,
txMatch: {
_class: core.class.TxCreateDoc,
objectClass: contact.class.EmployeeAccount
objectClass: contact.class.PersonAccount
}
})

View File

@ -55,12 +55,4 @@ export function createModel (builder: Builder): void {
_class: core.class.TxUpdateDoc
}
})
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverContact.trigger.OnEmployeeUpdate,
txMatch: {
objectClass: contact.class.Employee,
_class: core.class.TxUpdateDoc
}
})
}

View File

@ -13,7 +13,7 @@
// limitations under the License.
//
import type { Employee } from '@hcengineering/contact'
import type { Employee, Person } from '@hcengineering/contact'
import contact from '@hcengineering/contact'
import attachment from '@hcengineering/model-attachment'
import chunter from '@hcengineering/model-chunter'
@ -101,8 +101,8 @@ export class TTask extends TAttachedDoc implements Task {
@Hidden()
number!: number
// @Prop(TypeRef(contact.class.Employee), task.string.TaskAssignee)
assignee!: Ref<Employee> | null
// @Prop(TypeRef(contact.mixin.Employee), task.string.TaskAssignee)
assignee!: Ref<Person> | null
@Prop(TypeDate(), task.string.DueDate, { editor: task.component.DueDateEditor })
dueDate!: Timestamp | null
@ -126,7 +126,7 @@ export class TTodoItem extends TAttachedDoc implements TodoItem {
@Index(IndexKind.FullText)
name!: string
@Prop(TypeRef(contact.class.Employee), task.string.TaskAssignee)
@Prop(TypeRef(contact.mixin.Employee), task.string.TaskAssignee)
assignee!: Ref<Employee> | null
@Prop(TypeBoolean(), task.string.TaskDone)

View File

@ -14,7 +14,7 @@
//
import activity from '@hcengineering/activity'
import contact, { Employee } from '@hcengineering/contact'
import contact, { Employee, Person } from '@hcengineering/contact'
import {
DOMAIN_MODEL,
DateRangeMode,
@ -135,7 +135,7 @@ export class TProject extends TSpaceWithStates implements Project {
@Prop(TypeRef(tracker.class.IssueStatus), tracker.string.DefaultIssueStatus)
defaultIssueStatus!: Ref<IssueStatus>
@Prop(TypeRef(contact.class.Employee), tracker.string.DefaultAssignee)
@Prop(TypeRef(contact.mixin.Employee), tracker.string.DefaultAssignee)
defaultAssignee!: Ref<Employee>
declare defaultTimeReportDay: TimeReportDayType
@ -183,9 +183,9 @@ export class TIssue extends TTask implements Issue {
@ReadOnly()
number!: number
@Prop(TypeRef(contact.class.Employee), tracker.string.Assignee)
@Prop(TypeRef(contact.class.Person), tracker.string.Assignee)
@Index(IndexKind.Indexed)
assignee!: Ref<Employee> | null
assignee!: Ref<Person> | null
@Prop(TypeRef(tracker.class.Component), tracker.string.Component, { icon: tracker.icon.Component })
@Index(IndexKind.Indexed)
@ -256,8 +256,8 @@ export class TIssueTemplate extends TDoc implements IssueTemplate {
@Prop(TypeIssuePriority(), tracker.string.Priority)
priority!: IssuePriority
@Prop(TypeRef(contact.class.Employee), tracker.string.Assignee)
assignee!: Ref<Employee> | null
@Prop(TypeRef(contact.class.Person), tracker.string.Assignee)
assignee!: Ref<Person> | null
@Prop(TypeRef(tracker.class.Component), tracker.string.Component)
component!: Ref<Component> | null
@ -298,7 +298,7 @@ export class TTimeSpendReport extends TAttachedDoc implements TimeSpendReport {
@Prop(TypeRef(tracker.class.Issue), tracker.string.Parent)
declare attachedTo: Ref<Issue>
@Prop(TypeRef(contact.class.Employee), contact.string.Employee)
@Prop(TypeRef(contact.mixin.Employee), contact.string.Employee)
employee!: Ref<Employee>
@Prop(TypeDate(), tracker.string.TimeSpendReportDate)
@ -324,7 +324,7 @@ export class TComponent extends TDoc implements Component {
@Prop(TypeMarkup(), tracker.string.Description)
description?: Markup
@Prop(TypeRef(contact.class.Employee), tracker.string.ComponentLead)
@Prop(TypeRef(contact.mixin.Employee), tracker.string.ComponentLead)
lead!: Ref<Employee> | null
@Prop(Collection(chunter.class.Comment), chunter.string.Comments)
@ -1584,7 +1584,7 @@ export function createModel (builder: Builder): void {
actionPopup: view.component.ValueSelector,
actionProps: {
attribute: 'assignee',
_class: contact.class.Employee,
_class: contact.mixin.Employee,
query: {},
placeholder: tracker.string.AssignTo
},
@ -2006,7 +2006,7 @@ export function createModel (builder: Builder): void {
dividerBefore: true,
key: 'lead'
},
props: { _class: tracker.class.Component, defaultClass: contact.class.Employee, shouldShowLabel: false }
props: { _class: tracker.class.Component, defaultClass: contact.mixin.Employee, shouldShowLabel: false }
}
]
},

View File

@ -279,16 +279,17 @@ async function loadModel (
console.log('find' + (lastTxTime >= 0 ? 'full model' : 'model diff'), atxes.length, Date.now() - t)
// Ignore Employee accounts.
function isEmployeeAccount (tx: Tx): boolean {
function isPersonAccount (tx: Tx): boolean {
return (
(tx._class === core.class.TxCreateDoc ||
tx._class === core.class.TxUpdateDoc ||
tx._class === core.class.TxRemoveDoc) &&
(tx as TxCUD<Doc>).objectClass === 'contact:class:EmployeeAccount'
((tx as TxCUD<Doc>).objectClass === 'contact:class:PersonAccount' ||
(tx as TxCUD<Doc>).objectClass === 'contact:class:Account')
)
}
atxes.forEach((tx) => (tx.modifiedBy === core.account.System && !isEmployeeAccount(tx) ? systemTx : userTx).push(tx))
atxes.forEach((tx) => (tx.modifiedBy === core.account.System && !isPersonAccount(tx) ? systemTx : userTx).push(tx))
if (allowedPlugins != null) {
fillConfiguration(systemTx, configs)

View File

@ -227,10 +227,11 @@ export class TxOperations implements Omit<Client, 'notify'> {
modifiedBy?: Ref<Account>
): Promise<TxResult> {
const hierarchy = this.client.getHierarchy()
if (hierarchy.isMixin(doc._class)) {
const mixClass = Hierarchy.mixinOrClass(doc)
if (hierarchy.isMixin(mixClass)) {
// TODO: Rework it is wrong, we need to split values to mixin update and original document update if mixed.
const baseClass = hierarchy.getBaseClass(doc._class)
return this.updateMixin(doc._id, baseClass, doc.space, doc._class, update, modifiedOn, modifiedBy)
return this.updateMixin(doc._id, baseClass, doc.space, mixClass, update, modifiedOn, modifiedBy)
}
if (hierarchy.isDerived(doc._class, core.class.AttachedDoc)) {
const adoc = doc as unknown as AttachedDoc

View File

@ -16,7 +16,6 @@
"AccountAlreadyExists": "Account already exists",
"WorkspaceRateLimit": "Server is busy, Please wait a bit and try again",
"AccountAlreadyConfirmed": "Account already confirmed",
"AccountWasMerged": "Account was merged",
"WorkspaceAlreadyExists": "Workspace already exists",
"ProductIdMismatch": "Product Mismatch"
}

View File

@ -16,7 +16,6 @@
"AccountAlreadyExists": "Аккаунт уже существует",
"WorkspaceRateLimit": "Сервер перегружен, Пожалуйста подождите",
"AccountAlreadyConfirmed": "Аккаунт уже подтвержден",
"AccountWasMerged": "Аккаунт был объединен",
"WorkspaceAlreadyExists": "Рабочее пространство уже существует",
"ProductIdMismatch": "Продукт не соответсвует"
}

View File

@ -150,7 +150,6 @@ export default plugin(platformId, {
InvalidPassword: '' as StatusCode<{ account: string }>,
AccountAlreadyExists: '' as StatusCode<{ account: string }>,
AccountAlreadyConfirmed: '' as StatusCode<{ account: string }>,
AccountWasMerged: '' as StatusCode<{ account: string }>,
WorkspaceAlreadyExists: '' as StatusCode<{ workspace: string }>,
WorkspaceRateLimit: '' as StatusCode<{ workspace: string }>,
ProductIdMismatch: '' as StatusCode<{ productId: string }>

View File

@ -76,6 +76,7 @@
<svelte:component
this={editor}
readonly={isReadonly}
disabled="(isReadonly)"
label={attribute?.label}
placeholder={attribute?.label}
{kind}
@ -101,6 +102,7 @@
{attributeKey}
value={getAttribute(client, object, { key: attributeKey, attr: attribute })}
readonly={isReadonly}
disabled="(isReadonly)"
space={object.space}
{onChange}
{focus}

View File

@ -47,6 +47,10 @@
export let readonly = false
export let disallowDeselect: Ref<Doc>[] | undefined = undefined
export let filter: (it: Doc) => boolean = () => {
return true
}
const created: Doc[] = []
const dispatch = createEventDispatcher()
@ -75,9 +79,9 @@
})
if (created.length > 0) {
const cmap = new Set(created.map((it) => it._id))
objects = [...created, ...result.filter((d) => !cmap.has(d._id))]
objects = [...created, ...result.filter((d) => !cmap.has(d._id))].filter(filter)
} else {
objects = result
objects = result.filter(filter)
}
},
{ ...(options ?? {}), limit: 200 }

View File

@ -48,6 +48,12 @@
}
} catch {}
}
function correctClass (clName: string): string {
if (clName === 'contact:class:Employee') {
return 'contact:mixin:Employee'
}
return clName
}
</script>
{#if nodes}
@ -121,7 +127,7 @@
props={{
objectId: node.getAttribute('data-id'),
title: node.getAttribute('data-label'),
_class: node.getAttribute('data-objectclass'),
_class: correctClass(node.getAttribute('data-objectclass')),
inline: true
}}
/>

View File

@ -17,7 +17,7 @@
import type { DisplayTx, TxViewlet } from '@hcengineering/activity'
import attachment from '@hcengineering/attachment'
import chunter from '@hcengineering/chunter'
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
import contact, { Employee, PersonAccount, getName } from '@hcengineering/contact'
import core, { AnyAttribute, Class, Doc, Ref, TxCUD, getCurrentAccount } from '@hcengineering/core'
import { Asset } from '@hcengineering/platform'
import { createQuery, getClient } from '@hcengineering/presentation'
@ -55,7 +55,7 @@
let viewlet: TxDisplayViewlet | undefined
let props: any
let account: EmployeeAccount | undefined
let account: PersonAccount | undefined
let employee: Employee | undefined
let model: AttributeModel[] = []
let modelIcon: Asset | undefined = undefined
@ -97,8 +97,8 @@
})
$: query.query(
contact.class.EmployeeAccount,
{ _id: tx.tx.modifiedBy as Ref<EmployeeAccount> },
contact.class.PersonAccount,
{ _id: tx.tx.modifiedBy as Ref<PersonAccount> },
(res) => {
;[account] = res
},
@ -107,8 +107,8 @@
$: account &&
employeeQuery.query(
contact.class.Employee,
{ _id: account.employee },
contact.mixin.Employee,
{ _id: account.person as Ref<Employee> },
(res) => {
;[employee] = res
},
@ -213,7 +213,7 @@
<div class="msgactivity-content__title labels-row">
<span class={withAvatar ? 'bold' : 'strong'}>
{#if employee}
{getName(employee)}
{getName(client.getHierarchy(), employee)}
{:else}
<Label label={core.string.System} />
{/if}

View File

@ -14,7 +14,7 @@
-->
<script lang="ts">
import { Attachment } from '@hcengineering/attachment'
import contact, { Employee, EmployeeAccount } from '@hcengineering/contact'
import contact, { Person, PersonAccount } from '@hcengineering/contact'
import core, { Class, getCurrentAccount, Ref, Space } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { Label, Loading, navigate, TabList, SearchEdit, getLocation } from '@hcengineering/ui'
@ -38,8 +38,8 @@
navigate(loc)
}
export let requestedSpaceClasses: Ref<Class<Space>>[] = []
const currentUser = getCurrentAccount() as EmployeeAccount
let selectedParticipants: Ref<Employee>[] = [currentUser.employee]
const currentUser = getCurrentAccount() as PersonAccount
let selectedParticipants: Ref<Person>[] = [currentUser.person]
let selectedSpaces: Ref<Space>[] = []
export let search: string = ''
let isLoading = false
@ -58,14 +58,14 @@
selectedSort_: FileBrowserSortMode,
selectedFileTypeId_: string,
selectedDateId_: string,
selectedParticipants_: Ref<Employee>[],
selectedParticipants_: Ref<Person>[],
selectedSpaces_: Ref<Space>[]
) {
isLoading = true
const nameQuery = searchQuery_ ? { name: { $like: '%' + searchQuery_ + '%' } } : {}
const accounts = await client.findAll(contact.class.EmployeeAccount, { employee: { $in: selectedParticipants_ } })
const accounts = await client.findAll(contact.class.PersonAccount, { person: { $in: selectedParticipants_ } })
const senderQuery = accounts.length ? { modifiedBy: { $in: accounts.map((a) => a._id) } } : {}
let spaceQuery: { space: any }

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import { Employee } from '@hcengineering/contact'
import { Person } from '@hcengineering/contact'
import { Class, Ref, Space } from '@hcengineering/core'
import { SpaceMultiBoxList } from '@hcengineering/presentation'
import { Component, DropdownLabelsIntl } from '@hcengineering/ui'
@ -23,7 +23,7 @@
export let requestedSpaceClasses: Ref<Class<Space>>[]
export let spaceId: Ref<Space> | undefined
export let selectedParticipants: Ref<Employee>[]
export let selectedParticipants: Ref<Person>[]
export let selectedSpaces: Ref<Space>[]
export let selectedDateId: string
export let selectedFileTypeId: string

View File

@ -1,6 +1,6 @@
import attachment, { Attachment } from '@hcengineering/attachment'
import chunter, { Comment } from '@hcengineering/chunter'
import contact, { Channel, combineName, Contact, EmployeeAccount } from '@hcengineering/contact'
import contact, { Channel, combineName, Contact, Employee, PersonAccount } from '@hcengineering/contact'
import core, {
Account,
AccountRole,
@ -489,8 +489,8 @@ interface SyncOptionsExtra {
ownerTypeValues: BitrixOwnerType[]
commentFieldKeys: string[]
allMappings: FindResult<BitrixEntityMapping>
allEmployee: FindResult<EmployeeAccount>
userList: Map<string, Ref<EmployeeAccount>>
allEmployee: FindResult<PersonAccount>
userList: Map<string, Ref<PersonAccount>>
}
/**
@ -505,7 +505,7 @@ export async function performSynchronization (ops: SyncOptions): Promise<BitrixS
const commentFieldKeys = Object.keys(commentFields.result)
const allEmployee = await ops.client.findAll(contact.class.EmployeeAccount, {})
const allEmployee = await ops.client.findAll(contact.class.PersonAccount, {})
const allMappings = await ops.client.findAll<BitrixEntityMapping>(
bitrix.class.EntityMapping,
@ -519,7 +519,7 @@ export async function performSynchronization (ops: SyncOptions): Promise<BitrixS
}
)
const userList = new Map<string, Ref<EmployeeAccount>>()
const userList = new Map<string, Ref<PersonAccount>>()
// Fill all users and create new ones, if required.
await synchronizeUsers(userList, ops, allEmployee)
@ -764,7 +764,7 @@ async function downloadComments (
syncEmails?: boolean
},
commentFieldKeys: string[],
userList: Map<string, Ref<EmployeeAccount>>,
userList: Map<string, Ref<PersonAccount>>,
ownerTypeValues: BitrixOwnerType[]
): Promise<void> {
const entityType = ops.mapping.type.replace('crm.', '')
@ -887,7 +887,7 @@ async function downloadComments (
}
async function synchronizeUsers (
userList: Map<string, Ref<EmployeeAccount>>,
userList: Map<string, Ref<PersonAccount>>,
ops: {
client: TxOperations
bitrixClient: BitrixClient
@ -900,13 +900,13 @@ async function synchronizeUsers (
monitor: (total: number) => void
blobProvider?: ((blobRef: { file: string, id: string }) => Promise<Blob | undefined>) | undefined
},
allEmployee: FindResult<EmployeeAccount>
allEmployee: FindResult<PersonAccount>
): Promise<void> {
let totalUsers = 1
let next = 0
const employeesList = await ops.client.findAll(
contact.class.Employee,
contact.mixin.Employee,
{},
{
lookup: {
@ -929,23 +929,25 @@ async function synchronizeUsers (
// Try to find from employee
employeesList.forEach((it) => {
if ((it.$lookup?.channels as Channel[])?.some((q) => q.value === u.EMAIL)) {
account = allEmployee.find((qit) => qit.employee === it._id)
account = allEmployee.find((qit) => qit.person === it._id)
}
})
}
let accountId = account?._id
if (accountId === undefined) {
const employeeId = await ops.client.createDoc(contact.class.Employee, contact.space.Contacts, {
const employeeId = await ops.client.createDoc(contact.class.Person, contact.space.Contacts, {
name: combineName(u.NAME, u.LAST_NAME),
avatar: u.PERSONAL_PHOTO,
active: u.ACTIVE,
city: u.PERSONAL_CITY
})
accountId = await ops.client.createDoc(contact.class.EmployeeAccount, core.space.Model, {
await ops.client.createMixin(employeeId, contact.class.Person, contact.space.Contacts, contact.mixin.Employee, {
active: u.ACTIVE
})
accountId = await ops.client.createDoc(contact.class.PersonAccount, core.space.Model, {
email: u.EMAIL,
name: combineName(u.NAME, u.LAST_NAME),
employee: employeeId,
person: employeeId,
role: AccountRole.User
})
if (u.EMAIL !== undefined && u.EMAIL !== null) {
@ -953,7 +955,7 @@ async function synchronizeUsers (
contact.class.Channel,
contact.space.Contacts,
employeeId,
contact.class.Employee,
contact.mixin.Employee,
'channels',
{
provider: contact.channelProvider.Email,
@ -963,7 +965,7 @@ async function synchronizeUsers (
}
await ops.client.createMixin<Doc, BitrixSyncDoc>(
employeeId,
contact.class.Employee,
contact.mixin.Employee,
contact.space.Contacts,
bitrix.mixin.BitrixSyncDoc,
{
@ -973,7 +975,7 @@ async function synchronizeUsers (
}
)
} else if (account != null) {
const emp = employees.get(account.employee)
const emp = employees.get(account.person as unknown as Ref<Employee>)
if (emp !== undefined && !ops.client.getHierarchy().hasMixin(emp, bitrix.mixin.BitrixSyncDoc)) {
await ops.client.createMixin<Doc, BitrixSyncDoc>(emp._id, emp._class, emp.space, bitrix.mixin.BitrixSyncDoc, {
type: 'employee',

View File

@ -1,5 +1,5 @@
import attachment, { Attachment } from '@hcengineering/attachment'
import contact, { Channel, EmployeeAccount, Organization } from '@hcengineering/contact'
import contact, { Channel, PersonAccount, Organization } from '@hcengineering/contact'
import core, {
AnyAttribute,
AttachedDoc,
@ -102,7 +102,7 @@ export async function convert (
space: Ref<Space>,
fields: BitrixFieldMapping[],
rawDocument: any,
userList: Map<string, Ref<EmployeeAccount>>,
userList: Map<string, Ref<PersonAccount>>,
existingDoc: WithLookup<Doc> | undefined,
defaultCategories: TagCategory[],
allTagElements: TagElement[],

View File

@ -3,8 +3,8 @@
import { Ref } from '@hcengineering/core'
import contact from '@hcengineering/contact'
import board from '../plugin'
import { Component } from '@hcengineering/ui'
import board from '../plugin'
export let value: Ref<Employee>[]
export let readonly = false

View File

@ -16,20 +16,20 @@
import { Employee } from '@hcengineering/contact'
import { EmployeeBox } from '@hcengineering/contact-resources'
import { Ref } from '@hcengineering/core'
import { createQuery, getClient, MessageBox } from '@hcengineering/presentation'
import { MessageBox, createQuery, getClient } from '@hcengineering/presentation'
import type { TodoItem } from '@hcengineering/task'
import task, { calcRank } from '@hcengineering/task'
import {
Button,
CheckBox,
DateRangePresenter,
getEventPopupPositionElement,
IconAdd,
IconDelete,
IconMoreH,
Progress,
showPopup,
TextAreaEditor
TextAreaEditor,
getEventPopupPositionElement,
showPopup
} from '@hcengineering/ui'
import { ContextMenu, HTMLPresenter } from '@hcengineering/view-resources'

View File

@ -1,5 +1,5 @@
import { Card } from '@hcengineering/board'
import { Employee, EmployeeAccount } from '@hcengineering/contact'
import { Employee, PersonAccount } from '@hcengineering/contact'
import {
TxOperations as Client,
TxResult,
@ -86,9 +86,9 @@ export function canAddCurrentUser (card: Card): boolean {
if (card.members == null) {
return true
}
const employee = (getCurrentAccount() as EmployeeAccount).employee
const employee = (getCurrentAccount() as PersonAccount).person
return !card.members.includes(employee)
return !card.members.includes(employee as Ref<Employee>)
}
export function hasCover (card: Card): boolean {
@ -100,13 +100,13 @@ export function hasDate (card: Card): boolean {
}
export function addCurrentUser (card: Card, client: Client): Promise<TxResult> | undefined {
const employee = (getCurrentAccount() as EmployeeAccount).employee
const employee = (getCurrentAccount() as PersonAccount).person
if (card.members?.includes(employee) === true) {
if (card.members?.includes(employee as Ref<Employee>) === true) {
return
}
return client.update(card, { $push: { members: employee } })
return client.update(card, { $push: { members: employee as Ref<Employee> } })
}
export function archiveCard (card: Card, client: Client): Promise<TxResult> | undefined {

View File

@ -14,7 +14,7 @@
-->
<script lang="ts">
import { Calendar, Event, getAllEvents } from '@hcengineering/calendar'
import { EmployeeAccount } from '@hcengineering/contact'
import { PersonAccount } from '@hcengineering/contact'
import {
Class,
Doc,
@ -58,7 +58,7 @@
CalendarMode.Year
]
const me = getCurrentAccount() as EmployeeAccount
const me = getCurrentAccount() as PersonAccount
const mondayStart = true
let mode: CalendarMode = allowedModes.includes(CalendarMode.Days) ? CalendarMode.Days : allowedModes[0]

View File

@ -14,7 +14,7 @@
-->
<script lang="ts">
import { Calendar, generateEventId } from '@hcengineering/calendar'
import { Employee, EmployeeAccount } from '@hcengineering/contact'
import { Employee, PersonAccount } from '@hcengineering/contact'
import { UserBoxList } from '@hcengineering/contact-resources'
import { Class, DateRangeMode, Doc, Ref, getCurrentAccount } from '@hcengineering/core'
import { Card, getClient } from '@hcengineering/presentation'
@ -40,8 +40,8 @@
let dueDateRef: DateRangePresenter
let allDay = false
const currentUser = getCurrentAccount() as EmployeeAccount
let participants: Ref<Employee>[] = [currentUser.employee]
const currentUser = getCurrentAccount() as PersonAccount
let participants: Ref<Employee>[] = [currentUser.person as Ref<Employee>]
const dispatch = createEventDispatcher()
const client = getClient()

View File

@ -14,7 +14,7 @@
-->
<script lang="ts">
import { Calendar, Event, generateEventId } from '@hcengineering/calendar'
import { Employee, EmployeeAccount } from '@hcengineering/contact'
import { Employee, PersonAccount } from '@hcengineering/contact'
import { UserBoxList } from '@hcengineering/contact-resources'
import { Class, DateRangeMode, Doc, Ref, getCurrentAccount } from '@hcengineering/core'
import { Card, getClient } from '@hcengineering/presentation'
@ -29,8 +29,8 @@
let _title = title
let value: number | null | undefined = null
const currentUser = getCurrentAccount() as EmployeeAccount
let participants: Ref<Employee>[] = [currentUser.employee]
const currentUser = getCurrentAccount() as PersonAccount
let participants: Ref<Employee>[] = [currentUser.person as Ref<Employee>]
const defaultDuration = 30 * 60 * 1000
const dispatch = createEventDispatcher()

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import { EmployeeAccount } from '@hcengineering/contact'
import { PersonAccount } from '@hcengineering/contact'
import { Doc, getCurrentAccount } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { Button, showPopup } from '@hcengineering/ui'
@ -34,10 +34,10 @@
if (isEvent) {
showPopup(SaveEventReminder, { objectId: value._id, objectClass: value._class }, ev.target as HTMLElement)
} else {
const currentUser = getCurrentAccount() as EmployeeAccount
const currentUser = getCurrentAccount() as PersonAccount
const current = await client.findOne(calendar.class.Event, {
attachedTo: value._id,
participants: currentUser.employee
participants: currentUser.person
})
if (current === undefined) {
showPopup(

View File

@ -14,7 +14,7 @@
-->
<script lang="ts">
import { Event } from '@hcengineering/calendar'
import { EmployeeAccount } from '@hcengineering/contact'
import { PersonAccount } from '@hcengineering/contact'
import { Class, Doc, getCurrentAccount, Ref } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation'
import { Button, deviceOptionsStore as deviceInfo, IconAdd, Label, Scroller, showPopup } from '@hcengineering/ui'
@ -26,14 +26,14 @@
export let attachedToClass: Ref<Class<Doc>>
export let title: string | undefined
const currentUser = getCurrentAccount() as EmployeeAccount
const currentUser = getCurrentAccount() as PersonAccount
let events: Event[] = []
const query = createQuery()
$: query.query(
calendar.class.Event,
{
attachedTo,
participants: currentUser.employee
participants: currentUser.person
},
(res) => {
events = res.filter((p) => p.reminders !== undefined && p.reminders.length > 0)

View File

@ -20,6 +20,7 @@
import calendar from '../plugin'
import { showPanel, tooltip } from '@hcengineering/ui'
import view from '@hcengineering/view'
import { getClient } from '@hcengineering/presentation'
export let value: Person | Person[]
export let inline: boolean = false
@ -30,15 +31,17 @@
async function onClick (p: Person) {
showPanel(view.component.EditDoc, p._id, Hierarchy.mixinOrClass(p), 'content')
}
const client = getClient()
</script>
{#if value}
<div class="flex persons">
{#each persons as p}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="flex-presenter"
class:inline-presenter={inline}
use:tooltip={{ label: calendar.string.PersonsLabel, props: { name: getName(p) } }}
use:tooltip={{ label: calendar.string.PersonsLabel, props: { name: getName(client.getHierarchy(), p) } }}
on:click={() => onClick(p)}
>
<div class="icon">

View File

@ -1,13 +1,13 @@
import { Backlink } from '@hcengineering/chunter'
import contact, { EmployeeAccount } from '@hcengineering/contact'
import contact, { PersonAccount } from '@hcengineering/contact'
import { Account, Class, Client, Data, Doc, DocumentQuery, Ref, TxOperations } from '@hcengineering/core'
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> })
user: Ref<PersonAccount> | Ref<Account>
): Promise<PersonAccount | undefined> {
return await client.findOne(contact.class.PersonAccount, { _id: user as Ref<PersonAccount> })
}
export function getTime (time: number): string {

View File

@ -43,7 +43,7 @@
requestedSpaceClasses: [plugin.class.Channel, plugin.class.DirectMessage]
}
},
{ searchType: SearchType.Contacts, component: EmployeeBrowser, filterClass: contact.class.Employee }
{ searchType: SearchType.Contacts, component: EmployeeBrowser, filterClass: contact.mixin.Employee }
]
</script>

View File

@ -15,6 +15,7 @@
<script lang="ts">
import { getName, Person } from '@hcengineering/contact'
import { Avatar } from '@hcengineering/contact-resources'
import { getClient } from '@hcengineering/presentation'
interface IMessage {
text: string
@ -23,12 +24,13 @@
export let user: Person
export let message: IMessage
const client = getClient()
</script>
<div class="flex-nowrap">
<div class="avatar"><Avatar size={'medium'} /></div>
<div class="flex-col-stretch message">
<div class="header">{getName(user)}<span>{message.createDate}</span></div>
<div class="header">{getName(client.getHierarchy(), 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 { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
import { Avatar, employeeAccountByIdStore, employeeByIdStore } from '@hcengineering/contact-resources'
import { Person, PersonAccount, getName } from '@hcengineering/contact'
import { Avatar, personByIdStore, personAccountByIdStore } from '@hcengineering/contact-resources'
import { IdMap, Ref } from '@hcengineering/core'
import { MessageViewer } from '@hcengineering/presentation'
import { MessageViewer, getClient } from '@hcengineering/presentation'
import { Icon, ShowMore, TimeSince } from '@hcengineering/ui'
import { LinkPresenter } from '@hcengineering/view-resources'
@ -28,18 +28,20 @@
export let inline: boolean = false
export let disabled = false
const client = getClient()
const cutId = (str: string): string => {
return str.slice(0, 4) + '...' + str.slice(-4)
}
async function getEmployee (
value: Comment,
employees: IdMap<Employee>,
accounts: IdMap<EmployeeAccount>
): Promise<Employee | undefined> {
const acc = accounts.get(value.modifiedBy as Ref<EmployeeAccount>)
employees: IdMap<Person>,
accounts: IdMap<PersonAccount>
): Promise<Person | undefined> {
const acc = accounts.get(value.modifiedBy as Ref<PersonAccount>)
if (acc !== undefined) {
const emp = employees.get(acc.employee)
const emp = employees.get(acc.person)
return emp
}
}
@ -76,14 +78,14 @@
&nbsp;<span class="content-dark-color">#{cutId(value._id.toString())}</span>
{:else}
<div class="flex-row-top">
{#await getEmployee(value, $employeeByIdStore, $employeeAccountByIdStore) then employee}
{#await getEmployee(value, $personByIdStore, $personAccountByIdStore) then employee}
<div class="avatar">
<Avatar size={'medium'} avatar={employee?.avatar} />
</div>
<div class="flex-grow flex-col select-text">
<div class="header">
<div class="fs-title">
{#if employee}{getName(employee)}{/if}
{#if employee}{getName(client.getHierarchy(), employee)}{/if}
</div>
<div class="content-dark-color ml-4"><TimeSince value={value.modifiedOn} /></div>
</div>

View File

@ -32,7 +32,7 @@
let employeeIds: Ref<Employee>[] = []
async function createDirectMessage () {
const employeeAccounts = await client.findAll(contact.class.EmployeeAccount, { employee: { $in: employeeIds } })
const employeeAccounts = await client.findAll(contact.class.PersonAccount, { person: { $in: employeeIds } })
const accIds = [myAccId, ...employeeAccounts.filter((ea) => ea._id !== myAccId).map((ea) => ea._id)].sort()
const existingDms = await client.findAll(chunter.class.DirectMessage, {})

View File

@ -18,7 +18,7 @@
import { getCurrentAccount } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { CombineAvatars } from '@hcengineering/contact-resources'
import contact, { EmployeeAccount } from '@hcengineering/contact'
import contact, { PersonAccount } from '@hcengineering/contact'
import { SearchEdit, showPanel } from '@hcengineering/ui'
import chunter from '../plugin'
import { getDmName, navigateToSpecial } from '../utils'
@ -42,11 +42,11 @@
async function getEmpolyeeIds () {
const empAccIds = dm?.members.length !== 1 ? dm?.members.filter((accId) => accId !== myAccId) : dm?.members
const employeeAccounts = await client.findAll(contact.class.EmployeeAccount, {
_id: { $in: empAccIds as Ref<EmployeeAccount>[] }
const employeeAccounts = await client.findAll(contact.class.PersonAccount, {
_id: { $in: empAccIds as Ref<PersonAccount>[] }
})
return employeeAccounts.map((ea) => ea.employee)
return employeeAccounts.map((ea) => ea.person)
}
async function onSpaceEdit (): Promise<void> {
@ -62,7 +62,7 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div class="ac-header__wrap-title" on:click={onSpaceEdit}>
<div class="ac-header__icon">
<CombineAvatars _class={contact.class.Employee} items={empolyeeIds} size={'x-small'} />
<CombineAvatars _class={contact.mixin.Employee} items={empolyeeIds} size={'x-small'} />
</div>
<span class="ac-header__title">{name}</span>
</div>

View File

@ -16,8 +16,8 @@
import { Attachment } from '@hcengineering/attachment'
import { AttachmentList, AttachmentRefInput } from '@hcengineering/attachment-resources'
import type { ChunterMessage, Message, Reaction } from '@hcengineering/chunter'
import { EmployeeAccount } from '@hcengineering/contact'
import { Avatar, employeeByIdStore, EmployeePresenter } from '@hcengineering/contact-resources'
import { PersonAccount } from '@hcengineering/contact'
import { Avatar, personByIdStore, EmployeePresenter } from '@hcengineering/contact-resources'
import { getCurrentAccount, Ref, WithLookup } from '@hcengineering/core'
import { getResource } from '@hcengineering/platform'
import { getClient, MessageViewer } from '@hcengineering/presentation'
@ -46,8 +46,8 @@
let refInput: AttachmentRefInput
$: empRef = (message.$lookup?.createBy as EmployeeAccount)?.employee
$: employee = empRef !== undefined ? $employeeByIdStore.get(empRef) : undefined
$: empRef = (message.$lookup?.createBy as PersonAccount)?.person
$: employee = empRef !== undefined ? $personByIdStore.get(empRef) : undefined
$: attachments = (message.$lookup?.attachments ?? []) as Attachment[]
const client = getClient()

View File

@ -1,9 +1,9 @@
<script lang="ts">
import chunter, { ChunterMessage } from '@hcengineering/chunter'
import { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
import { Avatar, employeeAccountByIdStore, employeeByIdStore } from '@hcengineering/contact-resources'
import { Person, PersonAccount, getName } from '@hcengineering/contact'
import { Avatar, personAccountByIdStore, personByIdStore } from '@hcengineering/contact-resources'
import { IdMap, Ref, Space } from '@hcengineering/core'
import { MessageViewer, createQuery } from '@hcengineering/presentation'
import { MessageViewer, createQuery, getClient } from '@hcengineering/presentation'
import { IconClose } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import { UnpinMessage } from '../index'
@ -11,6 +11,8 @@
export let space: Ref<Space>
const client = getClient()
const pinnedQuery = createQuery()
let pinnedIds: Ref<ChunterMessage>[] = []
pinnedQuery.query(
@ -34,27 +36,28 @@
function getEmployee (
message: ChunterMessage,
employeeAccounts: IdMap<EmployeeAccount>,
employees: IdMap<Employee>
): Employee | undefined {
const acc = employeeAccounts.get(message.createBy as Ref<EmployeeAccount>)
employeeAccounts: IdMap<PersonAccount>,
employees: IdMap<Person>
): Person | undefined {
const acc = employeeAccounts.get(message.createBy as Ref<PersonAccount>)
if (acc) {
return employees.get(acc.employee)
return employees.get(acc.person)
}
}
</script>
<div class="antiPopup vScroll popup">
{#each pinnedMessages as message}
{@const employee = getEmployee(message, $employeeAccountByIdStore, $employeeByIdStore)}
{@const employee = getEmployee(message, $personAccountByIdStore, $personByIdStore)}
<div class="message">
<div class="header">
<div class="avatar">
<Avatar size={'medium'} avatar={employee?.avatar} />
</div>
<span class="name">
{employee ? getName(employee) : ''}
{employee ? getName(client.getHierarchy(), employee) : ''}
</span>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="cross"
on:click={async () => {

View File

@ -1,15 +1,17 @@
<script lang="ts">
import { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
import { employeeAccountByIdStore, employeeByIdStore } from '@hcengineering/contact-resources'
import { Person, PersonAccount, getName } from '@hcengineering/contact'
import { personAccountByIdStore, personByIdStore } from '@hcengineering/contact-resources'
import { Account, IdMap, Ref } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
export let reactionAccounts: Ref<Account>[]
function getAccName (acc: Ref<Account>, accounts: IdMap<EmployeeAccount>, employees: IdMap<Employee>): string {
const account = accounts.get(acc as Ref<EmployeeAccount>)
const client = getClient()
function getAccName (acc: Ref<Account>, accounts: IdMap<PersonAccount>, employees: IdMap<Person>): string {
const account = accounts.get(acc as Ref<PersonAccount>)
if (account !== undefined) {
const emp = employees.get(account.employee)
return emp ? getName(emp) : ''
const emp = employees.get(account.person)
return emp ? getName(client.getHierarchy(), emp) : ''
}
return ''
}
@ -17,6 +19,6 @@
{#each reactionAccounts as acc}
<div>
{getAccName(acc, $employeeAccountByIdStore, $employeeByIdStore)}
{getAccName(acc, $personAccountByIdStore, $personByIdStore)}
</div>
{/each}

View File

@ -14,8 +14,8 @@
-->
<script lang="ts">
import { Message } from '@hcengineering/chunter'
import { Employee } from '@hcengineering/contact'
import { employeeByIdStore } from '@hcengineering/contact-resources'
import { Person } from '@hcengineering/contact'
import { personByIdStore } from '@hcengineering/contact-resources'
import { IdMap, Ref } from '@hcengineering/core'
import { Avatar } from '@hcengineering/contact-resources'
import { Label, TimeSince } from '@hcengineering/ui'
@ -26,11 +26,11 @@
$: employees = new Set(message.replies)
const shown: number = 4
let showReplies: Employee[] = []
let showReplies: Person[] = []
$: updateQuery(employees, $employeeByIdStore)
$: updateQuery(employees, $personByIdStore)
function updateQuery (employees: Set<Ref<Employee>>, map: IdMap<Employee>) {
function updateQuery (employees: Set<Ref<Person>>, map: IdMap<Person>) {
showReplies = []
for (const employee of employees) {
const emp = map.get(employee)

View File

@ -2,8 +2,8 @@
import attachment, { Attachment } from '@hcengineering/attachment'
import { AttachmentPreview } from '@hcengineering/attachment-resources'
import { ChunterMessage } from '@hcengineering/chunter'
import { EmployeeAccount, getName as getContactName } from '@hcengineering/contact'
import { employeeAccountByIdStore, employeeByIdStore } from '@hcengineering/contact-resources'
import { Person, PersonAccount, getName as getContactName } from '@hcengineering/contact'
import { personAccountByIdStore, personByIdStore } from '@hcengineering/contact-resources'
import core, { IdMap, Ref, WithLookup } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { Label, Scroller } from '@hcengineering/ui'
@ -74,12 +74,16 @@
})
}
function getName (a: Attachment, employeeAccountByIdStore: IdMap<EmployeeAccount>): string | undefined {
const acc = employeeAccountByIdStore.get(a.modifiedBy as Ref<EmployeeAccount>)
function getName (
a: Attachment,
personAccountByIdStore: IdMap<PersonAccount>,
personByIdStore: IdMap<Person>
): string | undefined {
const acc = personAccountByIdStore.get(a.modifiedBy as Ref<PersonAccount>)
if (acc !== undefined) {
const emp = $employeeByIdStore.get(acc?.employee)
const emp = personByIdStore.get(acc?.person)
if (emp !== undefined) {
return getContactName(emp)
return getContactName(client.getHierarchy(), emp)
}
}
}
@ -112,7 +116,7 @@
<div class="label">
<Label
label={chunter.string.SharedBy}
params={{ name: getName(att, $employeeAccountByIdStore), time: getTime(att.modifiedOn) }}
params={{ name: getName(att, $personAccountByIdStore, $personByIdStore), time: getTime(att.modifiedOn) }}
/>
</div>
</div>

View File

@ -16,8 +16,8 @@
import attachment, { Attachment } from '@hcengineering/attachment'
import { AttachmentRefInput } from '@hcengineering/attachment-resources'
import type { ChunterSpace, Message, ThreadMessage } from '@hcengineering/chunter'
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
import { employeeByIdStore } from '@hcengineering/contact-resources'
import contact, { Person, PersonAccount, getName } from '@hcengineering/contact'
import { personByIdStore } from '@hcengineering/contact-resources'
import core, { FindOptions, IdMap, Ref, SortingOrder, generateId, getCurrentAccount } from '@hcengineering/core'
import { NotificationClientImpl } from '@hcengineering/notification-resources'
import { createQuery, getClient } from '@hcengineering/presentation'
@ -98,21 +98,21 @@
async function getParticipants (
comments: ThreadMessage[],
parent: Message | undefined,
employees: IdMap<Employee>
employees: IdMap<Person>
): Promise<string[]> {
const refs = new Set(comments.map((p) => p.createBy))
if (parent !== undefined) {
refs.add(parent.createBy)
}
refs.delete(getCurrentAccount()._id)
const accounts = await client.findAll(contact.class.EmployeeAccount, {
_id: { $in: Array.from(refs) as Ref<EmployeeAccount>[] }
const accounts = await client.findAll(contact.class.PersonAccount, {
_id: { $in: Array.from(refs) as Ref<PersonAccount>[] }
})
const res: string[] = []
for (const account of accounts) {
const employee = employees.get(account.employee)
const employee = employees.get(account.person)
if (employee !== undefined) {
res.push(getName(employee))
res.push(getName(client.getHierarchy(), employee))
}
}
return res
@ -159,7 +159,7 @@
<DmPresenter value={channel} />
{/if}
{/await}
{#await getParticipants(comments, parent, $employeeByIdStore) then participants}
{#await getParticipants(comments, parent, $personByIdStore) then participants}
{participants.join(', ')}
<Label label={chunter.string.AndYou} params={{ participants: participants.length }} />
{/await}

View File

@ -1,5 +1,5 @@
import { chunterId, ChunterMessage, Comment, ThreadMessage } from '@hcengineering/chunter'
import contact, { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
import contact, { Employee, PersonAccount, getName } from '@hcengineering/contact'
import { employeeByIdStore } from '@hcengineering/contact-resources'
import { Class, Client, Doc, getCurrentAccount, IdMap, Obj, Ref, Space, Timestamp } from '@hcengineering/core'
import { Asset } from '@hcengineering/platform'
@ -48,8 +48,8 @@ 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
let employeeAccounts: EmployeeAccount[] = await client.findAll(contact.class.EmployeeAccount, {
_id: { $in: dm.members as Array<Ref<EmployeeAccount>> }
let employeeAccounts: PersonAccount[] = await client.findAll(contact.class.PersonAccount, {
_id: { $in: dm.members as Array<Ref<PersonAccount>> }
})
if (dm.members.length > 1) {
@ -72,9 +72,9 @@ export async function getDmName (client: Client, dm: Space): Promise<string> {
const names: string[] = []
for (const acc of employeeAccounts) {
const employee = map.get(acc.employee)
const employee = map.get(acc.person as unknown as Ref<Employee>)
if (employee !== undefined) {
names.push(getName(employee))
names.push(getName(client.getHierarchy(), employee))
}
}
const name = names.join(', ')

View File

@ -13,7 +13,7 @@
// limitations under the License.
//
import type { Employee } from '@hcengineering/contact'
import type { Person } from '@hcengineering/contact'
import type { Account, AttachedDoc, Class, Doc, Ref, RelatedDocument, Space, Timestamp } from '@hcengineering/core'
import { NotificationType } from '@hcengineering/notification'
import type { Asset, Plugin, Resource } from '@hcengineering/platform'
@ -66,7 +66,7 @@ export interface ThreadMessage extends ChunterMessage {
export interface Message extends ChunterMessage {
attachedTo: Ref<Space>
attachedToClass: Ref<Class<Space>>
replies?: Ref<Employee>[]
replies?: Ref<Person>[]
repliesCount?: number
lastReply?: Timestamp
}

View File

@ -67,6 +67,7 @@
"Email": "Email",
"CreateEmployee": "Employee",
"Inactive": "Inactive",
"Active": "Active",
"Birthday": "Birthday",
"UseImage": "Attached photo",
"UseGravatar": "Gravatar",
@ -79,9 +80,9 @@
"Profile": "Profile",
"ProfilePlaceholder": "Profile...",
"CurrentEmployee": "Current employee",
"MergeEmployee": "Merge employee",
"MergeEmployeeFrom": "From(inactive)",
"MergeEmployeeTo": "To employee",
"MergePersons": "Merge contacts",
"MergePersonsFrom": "Source contact",
"MergePersonsTo": "Final contact",
"DisplayName": "Display name",
"SelectAvatar": "Select avatar",
"AvatarProvider": "Avatar provider",

View File

@ -67,6 +67,7 @@
"Email": "Email",
"CreateEmployee": "Сотрудник",
"Inactive": "Не активный",
"Active": "Активный",
"Birthday": "День рождения",
"UseImage": "Загруженное Фото",
"UseGravatar": "Граватар",
@ -80,9 +81,9 @@
"Profile": "Профиль",
"ProfilePlaceholder": "Профиль...",
"CurrentEmployee": "Текущий сотрудник",
"MergeEmployee": "Объединить сотрудника",
"MergeEmployeeFrom": "Из (неактивный)",
"MergeEmployeeTo": "В сотрудника",
"MergePersons": "Объеденить контакта",
"MergePersonsFrom": "Исходный контакт",
"MergePersonsTo": "Финальный контакт",
"DisplayName": "Отображаемое имя",
"SelectAvatar": "Выбрать аватар",
"GravatarsManaged": "Граватары управляются",

View File

@ -13,12 +13,12 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { Employee, EmployeeAccount } from '@hcengineering/contact'
import contact, { Employee, PersonAccount } from '@hcengineering/contact'
import core, { Account, Ref } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import { createQuery, getClient } from '@hcengineering/presentation'
import { ButtonKind, ButtonSize } from '@hcengineering/ui'
import { employeeAccountByIdStore } from '../utils'
import { personAccountByIdStore } from '../utils'
import UserBoxList from './UserBoxList.svelte'
export let label: IntlString
@ -36,7 +36,7 @@
function onUpdate (evt: CustomEvent<Ref<Employee>[]>): void {
clearTimeout(timer)
timer = setTimeout(async () => {
const accounts = await client.findAll(contact.class.EmployeeAccount, { employee: { $in: evt.detail } })
const accounts = await client.findAll(contact.class.PersonAccount, { person: { $in: evt.detail } })
onChange(accounts.map((it) => it._id))
}, 500)
}
@ -55,14 +55,14 @@
}
$: employees = Array.from(
(value ?? []).map((it) => $employeeAccountByIdStore.get(it as Ref<EmployeeAccount>)?.employee)
(value ?? []).map((it) => $personAccountByIdStore.get(it as Ref<PersonAccount>)?.person)
).filter((it) => it !== undefined) as Ref<Employee>[]
$: docQuery =
excluded.length > 0
? {
active: true,
_id: { $nin: excluded.map((p) => (p as EmployeeAccount).employee) }
_id: { $nin: excluded.map((p) => (p as PersonAccount).person as Ref<Employee>) }
}
: {
active: true

View File

@ -13,13 +13,13 @@
// limitations under the License.
-->
<script lang="ts">
import { Employee, EmployeeAccount } from '@hcengineering/contact'
import { Person, PersonAccount } from '@hcengineering/contact'
import core, { Account, DocumentQuery, Ref, matchQuery } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import { ButtonKind, ButtonSize } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import contact from '../plugin'
import { employeeAccountByIdStore } from '../utils'
import { personAccountByIdStore } from '../utils'
import UserBox from './UserBox.svelte'
import { getClient } from '@hcengineering/presentation'
@ -33,21 +33,21 @@
const client = getClient()
const hierarchy = client.getHierarchy()
$: accounts = matchQuery<Account>(
Array.from($employeeAccountByIdStore.values()),
Array.from($personAccountByIdStore.values()),
docQuery,
core.class.Account,
hierarchy
) as EmployeeAccount[]
) as PersonAccount[]
let map: Map<Ref<Employee>, Ref<Account>> = new Map()
$: map = new Map(accounts.map((p) => [p.employee, p._id]))
let map: Map<Ref<Person>, Ref<Account>> = new Map()
$: map = new Map(accounts.map((p) => [p.person, p._id]))
$: employees = accounts.map((p) => p.employee)
$: selectedEmp = value && accounts.find((p) => p._id === value)?.employee
$: employees = accounts.map((p) => p.person)
$: selectedEmp = value && accounts.find((p) => p._id === value)?.person
const dispatch = createEventDispatcher()
function change (e: CustomEvent<Ref<Employee> | null>) {
function change (e: CustomEvent<Ref<Person> | null>) {
if (e.detail === null) {
dispatch('change', null)
} else {
@ -58,7 +58,7 @@
</script>
<UserBox
_class={contact.class.Employee}
_class={contact.mixin.Employee}
docQuery={{ _id: { $in: employees } }}
showNavigate={false}
{kind}

View File

@ -1,5 +1,5 @@
<script lang="ts">
import { Employee, EmployeeAccount, getName } from '@hcengineering/contact'
import { Person, PersonAccount, getName } from '@hcengineering/contact'
import core, { IdMap, Ref, Space } from '@hcengineering/core'
import presentation, { getClient } from '@hcengineering/presentation'
import { ActionIcon, Button, IconClose, Label } from '@hcengineering/ui'
@ -11,19 +11,17 @@
const dispatch = createEventDispatcher()
const client = getClient()
const employees: IdMap<Employee> = new Map()
const employees: IdMap<Person> = new Map()
let membersToAdd: EmployeeAccount[] = []
let channelMembers: Ref<Employee>[] = []
let membersToAdd: PersonAccount[] = []
let channelMembers: Ref<Person>[] = []
client.findAll(core.class.Account, { _id: { $in: value.members } }).then((res) => {
channelMembers = res
.filter((e) => e._class === contact.class.EmployeeAccount)
.map((e) => (e as EmployeeAccount).employee)
channelMembers = res.filter((e) => e._class === contact.class.PersonAccount).map((e) => (e as PersonAccount).person)
})
async function changeMembersToAdd (employees: Ref<Employee>[]) {
async function changeMembersToAdd (employees: Ref<Person>[]) {
if (employees) {
await client.findAll(contact.class.EmployeeAccount, { employee: { $in: employees } }).then((res) => {
await client.findAll(contact.class.PersonAccount, { person: { $in: employees } }).then((res) => {
if (res) {
membersToAdd = res
}
@ -31,9 +29,9 @@
}
}
$: selectedEmployees = membersToAdd.map((e) => e.employee)
$: selectedEmployees = membersToAdd.map((e) => e.person)
function removeMember (_id: Ref<EmployeeAccount>) {
function removeMember (_id: Ref<PersonAccount>) {
membersToAdd = membersToAdd.filter((m) => m._id !== _id)
}
</script>
@ -56,9 +54,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)}
{@const employee = employees.get(m.person)}
<div class="mr-2 p-1 item">
{employee ? getName(employee) : ''}
{employee ? getName(client.getHierarchy(), employee) : ''}
<div class="tool">
<ActionIcon
icon={IconClose}
@ -75,7 +73,7 @@
<div class="ml-8 mr-8 mb-6 mt-4">
<UsersPopup
selected={undefined}
_class={contact.class.Employee}
_class={contact.mixin.Employee}
docQuery={{
active: true
}}

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { Contact, Employee, getName } from '@hcengineering/contact'
import contact, { Contact, Employee, Person, getName } from '@hcengineering/contact'
import { Class, DocumentQuery, FindOptions, Hierarchy, Ref } from '@hcengineering/core'
import { getEmbeddedLabel, IntlString } from '@hcengineering/platform'
import {
@ -34,13 +34,13 @@
import view from '@hcengineering/view'
import { createEventDispatcher } from 'svelte'
import presentation, { getClient } from '@hcengineering/presentation'
import { PersonLabelTooltip, employeeByIdStore } from '..'
import { PersonLabelTooltip, personByIdStore } from '..'
import AssigneePopup from './AssigneePopup.svelte'
import IconPerson from './icons/Person.svelte'
import UserInfo from './UserInfo.svelte'
import EmployeePresenter from './EmployeePresenter.svelte'
export let _class: Ref<Class<Employee>> = contact.class.Employee
export let _class: Ref<Class<Employee>> = contact.mixin.Employee
export let excluded: Ref<Contact>[] | undefined = undefined
export let options: FindOptions<Employee> | undefined = undefined
export let docQuery: DocumentQuery<Employee> | undefined = {
@ -48,8 +48,8 @@
}
export let label: IntlString
export let placeholder: IntlString = presentation.string.Search
export let value: Ref<Employee> | null | undefined
export let prevAssigned: Ref<Employee>[] | undefined = []
export let value: Ref<Person> | null | undefined
export let prevAssigned: Ref<Person>[] | undefined = []
export let componentLead: Ref<Employee> | undefined = undefined
export let members: Ref<Employee>[] | undefined = []
export let allowDeselect = true
@ -71,13 +71,15 @@
const dispatch = createEventDispatcher()
let selected: Employee | undefined
let selected: Person | undefined
let container: HTMLElement
const client = getClient()
async function updateSelected (value: Ref<Employee> | null | undefined) {
selected = value ? $employeeByIdStore.get(value) ?? (await client.findOne(_class, { _id: value })) : undefined
async function updateSelected (value: Ref<Person> | null | undefined) {
selected = value
? $personByIdStore.get(value) ?? (await client.findOne(contact.class.Person, { _id: value }))
: undefined
}
$: updateSelected(value)
@ -132,7 +134,9 @@
<div
class="w-full h-full flex-streatch"
on:click={_click}
use:tooltip={selected !== undefined ? { label: getEmbeddedLabel(getName(selected)) } : undefined}
use:tooltip={selected !== undefined
? { label: getEmbeddedLabel(getName(client.getHierarchy(), selected)) }
: undefined}
>
<slot name="content" />
</div>
@ -158,13 +162,15 @@
style:width={showNavigate && selected
? `calc(${width ?? 'min-content'} - 1.5rem)`
: `${width ?? 'min-content'}`}
use:tooltip={selected !== undefined ? { label: getEmbeddedLabel(getName(selected)) } : undefined}
use:tooltip={selected !== undefined
? { label: getEmbeddedLabel(getName(client.getHierarchy(), selected)) }
: undefined}
>
{#if selected}
{#if hideIcon || selected}
<UserInfo value={selected} size={avatarSize} {icon} {short} on:accent-color />
{:else}
{getName(selected)}
{getName(client.getHierarchy(), selected)}
{/if}
{:else}
<div class="flex-presenter not-selected">

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { Contact, Employee, EmployeeAccount, Person } from '@hcengineering/contact'
import contact, { Contact, PersonAccount, Person, Employee } from '@hcengineering/contact'
import { DocumentQuery, FindOptions, getCurrentAccount, Ref } from '@hcengineering/core'
import type { Asset, IntlString } from '@hcengineering/platform'
import {
@ -52,9 +52,7 @@
export let showCategories: boolean = true
export let icon: Asset | AnySvelteComponent | undefined = undefined
// const client = getClient()
// const hierarchy = client.getHierarchy()
const currentEmployee = (getCurrentAccount() as EmployeeAccount).employee
const currentEmployee = (getCurrentAccount() as PersonAccount).person
let search: string = ''
let objects: Contact[] = []
@ -66,7 +64,7 @@
const query = createQuery()
$: query.query<Contact>(
contact.class.Employee,
contact.mixin.Employee,
{
...(docQuery ?? {}),
[searchField]: { $like: '%' + search + '%' },

View File

@ -28,9 +28,7 @@
export let items: Ref<Contact>[] = []
export let _class: Ref<Class<Contact>> = contact.class.Contact
export let label: IntlString
export let docQuery: DocumentQuery<Contact> | undefined = {
active: true
}
export let docQuery: DocumentQuery<Contact> | undefined = {}
export let kind: ButtonKind = 'no-border'
export let size: ButtonSize = 'small'

View File

@ -38,7 +38,7 @@
function isEmployee (value: Contact): boolean {
const client = getClient()
const hierarchy = client.getHierarchy()
return hierarchy.isDerived(value._class, contact.class.Employee)
return hierarchy.isDerived(value._class, contact.mixin.Employee)
}
const toOrg = (contact: Contact) => contact as Organization
const toEmployee = (contact: Contact) => contact as Employee

View File

@ -48,20 +48,22 @@
async function createPerson () {
changeEmail()
const name = combineName(firstName, lastName)
const person: Data<Employee> = {
const person: Data<Person> = {
name,
city: object.city,
active: true
city: object.city
}
person.avatar = await avatarEditor.createAvatar()
await client.createDoc(contact.class.Employee, contact.space.Contacts, person, id)
await client.createDoc(contact.class.Person, contact.space.Contacts, person, id)
await client.createMixin(id, contact.class.Person, contact.space.Contacts, contact.mixin.Employee, {
active: true
})
await client.createDoc(contact.class.EmployeeAccount, core.space.Model, {
await client.createDoc(contact.class.PersonAccount, core.space.Model, {
email: email.trim(),
name,
employee: id,
person: id,
role: AccountRole.User
})

View File

@ -17,22 +17,22 @@
import { AccountRole, Doc, getCurrentAccount, Ref } from '@hcengineering/core'
import view from '@hcengineering/view-resources/src/plugin'
import { createEventDispatcher } from 'svelte'
import { EmployeeAccount } from '@hcengineering/contact'
import { employeeAccountByIdStore } from '../utils'
import { PersonAccount } from '@hcengineering/contact'
import { personAccountByIdStore } from '../utils'
import ui, { Button, Label } from '@hcengineering/ui'
import EmployeeAccountRefPresenter from './EmployeeAccountRefPresenter.svelte'
import EmployeeAccountPresenter from './EmployeeAccountPresenter.svelte'
import PersonAccountRefPresenter from './PersonAccountRefPresenter.svelte'
import PersonAccountPresenter from './PersonAccountPresenter.svelte'
export let object: Doc | Doc[]
export let deleteAction: () => void
const objectArray = Array.isArray(object) ? object : [object]
const owners: EmployeeAccount[] = Array.from($employeeAccountByIdStore.values()).filter(
const owners: PersonAccount[] = Array.from($personAccountByIdStore.values()).filter(
(acc) => acc.role === AccountRole.Owner
)
const dispatch = createEventDispatcher()
$: creators = [...new Set(objectArray.map((obj) => obj.createdBy as Ref<EmployeeAccount>))]
$: creators = [...new Set(objectArray.map((obj) => obj.createdBy as Ref<PersonAccount>))]
$: canDelete =
(creators.length === 1 && creators.includes(getCurrentAccount()._id as Ref<EmployeeAccount>)) ||
(creators.length === 1 && creators.includes(getCurrentAccount()._id as Ref<PersonAccount>)) ||
getCurrentAccount().role === AccountRole.Owner
$: label = canDelete ? view.string.DeleteObject : view.string.DeletePopupNoPermissionTitle
</script>
@ -62,7 +62,7 @@
<Label label={view.string.DeletePopupCreatorLabel} />
{#each creators as account}
<div class="my-2">
<EmployeeAccountRefPresenter value={account} />
<PersonAccountRefPresenter value={account} />
</div>
{/each}
</div>
@ -70,7 +70,7 @@
<Label label={view.string.DeletePopupOwnerLabel} />
{#each owners as owner}
<div class="my-2">
<EmployeeAccountPresenter value={owner} />
<PersonAccountPresenter value={owner} />
</div>
{/each}
</div>

View File

@ -14,7 +14,7 @@
// limitations under the License.
-->
<script lang="ts">
import { Channel, Employee, EmployeeAccount, getFirstName, getLastName, Person } from '@hcengineering/contact'
import { Channel, Employee, PersonAccount, getFirstName, getLastName, Person } from '@hcengineering/contact'
import { AccountRole, getCurrentAccount, Ref } from '@hcengineering/core'
import login from '@hcengineering/login'
import { getResource } from '@hcengineering/platform'
@ -28,28 +28,27 @@
import ChannelsEditor from './ChannelsEditor.svelte'
import EditableAvatar from './EditableAvatar.svelte'
export let object: Employee
export let object: Person
export let readonly = false
export let channels: Channel[] | undefined = undefined
const client = getClient()
const account = getCurrentAccount() as EmployeeAccount
const account = getCurrentAccount() as PersonAccount
let avatarEditor: EditableAvatar
$: owner = account.employee === object._id
$: owner = account.person === object._id
$: editable = !readonly && (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) => {
client.findOne(contact.class.PersonAccount, { person: (object as Employee)._id }).then((acc) => {
email = acc?.email
})
}
@ -71,12 +70,6 @@
await changeName(getFirstName(object.name), lastName)
}
function changeDisplayName () {
client.update(object, {
displayName: displayName.trim() === '' ? null : displayName
})
}
let integrations: Set<Ref<IntegrationType>> = new Set<Ref<IntegrationType>>()
const settingsQuery = createQuery()
$: settingsQuery.query(setting.class.Integration, { createdBy: account._id, disabled: false }, (res) => {
@ -146,19 +139,6 @@
{lastName}
{/if}
</div>
<div class="name">
{#if editable}
<EditBox
placeholder={contact.string.DisplayName}
bind:value={displayName}
on:change={changeDisplayName}
disabled={!editable}
focusIndex={1}
/>
{:else}
{displayName}
{/if}
</div>
<div class="location">
<AttributeEditor maxWidth="20rem" _class={contact.class.Person} {editable} {object} key="city" focusIndex={3} />
</div>

View File

@ -14,7 +14,7 @@
// limitations under the License.
-->
<script lang="ts">
import { combineName, EmployeeAccount, getFirstName, getLastName, Person } from '@hcengineering/contact'
import { combineName, PersonAccount, getFirstName, getLastName, Person } from '@hcengineering/contact'
import { getCurrentAccount, Ref } from '@hcengineering/core'
import { AttributeEditor, createQuery, getClient } from '@hcengineering/presentation'
import setting, { IntegrationType } from '@hcengineering/setting'
@ -27,7 +27,7 @@
export let object: Person
const client = getClient()
const account = getCurrentAccount() as EmployeeAccount
const account = getCurrentAccount() as PersonAccount
let avatarEditor: EditableAvatar

View File

@ -14,22 +14,22 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { Employee } from '@hcengineering/contact'
import contact, { Employee, Person } from '@hcengineering/contact'
import type { Class, DocumentQuery, FindOptions, Ref } from '@hcengineering/core'
import type { IntlString } from '@hcengineering/platform'
import { ButtonKind, ButtonSize, IconSize, LabelAndProps } from '@hcengineering/ui'
import presentation from '@hcengineering/presentation'
import IconPerson from './icons/Person.svelte'
import { ButtonKind, ButtonSize, IconSize, LabelAndProps } from '@hcengineering/ui'
import UserBox from './UserBox.svelte'
import IconPerson from './icons/Person.svelte'
export let _class: Ref<Class<Employee>> = contact.class.Employee
export let _class: Ref<Class<Employee>> = contact.mixin.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 value: Ref<Person> | null | undefined
export let allowDeselect = false
export let titleDeselect: IntlString | undefined = undefined
export let kind: ButtonKind = 'no-border'

View File

@ -14,7 +14,7 @@
async function updateEmployees (resultQuery: DocumentQuery<Employee>) {
employees = await client.findAll(
contact.class.Employee,
contact.mixin.Employee,
{
...resultQuery
},

View File

@ -31,7 +31,7 @@
export let readonly = false
export let showNavigate = true
$: _class = type?.to ?? contact.class.Employee
$: _class = type?.to ?? contact.mixin.Employee
const query: DocumentQuery<Employee> = {
active: true

View File

@ -78,7 +78,7 @@
: {
_id: { $in: Array.from(targets.keys()) }
}
objectsPromise = client.findAll(contact.class.Employee, resultQuery, { sort: { name: SortingOrder.Ascending } })
objectsPromise = client.findAll(contact.mixin.Employee, resultQuery, { sort: { name: SortingOrder.Ascending } })
values = await objectsPromise
if (targets.has(undefined)) {
values.unshift(undefined)

View File

@ -13,12 +13,12 @@
// limitations under the License.
-->
<script lang="ts">
import { Employee } from '@hcengineering/contact'
import { Person } from '@hcengineering/contact'
import { Ref } from '@hcengineering/core'
import CombineAvatars from './CombineAvatars.svelte'
import contact from '../plugin'
export let value: Ref<Employee>[]
export let value: Ref<Person>[]
</script>
<CombineAvatars _class={contact.class.Employee} items={value} limit={5} size={'x-small'} />
<CombineAvatars _class={contact.mixin.Employee} items={value} limit={5} size={'x-small'} />

View File

@ -1,13 +1,13 @@
<script lang="ts">
import { Employee } from '@hcengineering/contact'
import { Employee, Person } from '@hcengineering/contact'
import { Ref, WithLookup } from '@hcengineering/core'
import { IntlString } from '@hcengineering/platform'
import ui, { IconSize } from '@hcengineering/ui'
import { PersonLabelTooltip, employeeByIdStore } from '..'
import { PersonLabelTooltip, employeeByIdStore, personByIdStore } from '..'
import PersonPresenter from '../components/PersonPresenter.svelte'
import contact from '../plugin'
export let value: Ref<Employee> | WithLookup<Employee> | null | undefined
export let value: Ref<Person> | WithLookup<Person> | null | undefined
export let tooltipLabels: PersonLabelTooltip | undefined = undefined
export let shouldShowAvatar: boolean = true
export let shouldShowName: boolean = true
@ -21,7 +21,10 @@
export let defaultName: IntlString | undefined = ui.string.NotSelected
export let element: HTMLElement | undefined = undefined
$: employeeValue = typeof value === 'string' ? $employeeByIdStore.get(value) : value
$: employeeValue = typeof value === 'string' ? $personByIdStore.get(value) : value
$: active =
employeeValue !== undefined ? $employeeByIdStore.get(employeeValue?._id as Ref<Employee>)?.active ?? false : false
</script>
<PersonPresenter
@ -38,6 +41,6 @@
{colorInherit}
{accent}
{defaultName}
statusLabel={employeeValue?.active === false && shouldShowName ? contact.string.Inactive : undefined}
statusLabel={active === false && shouldShowName ? contact.string.Inactive : undefined}
on:accent-color
/>

View File

@ -1,5 +1,5 @@
<script lang="ts">
import { Employee, EmployeeAccount, getName, Status } from '@hcengineering/contact'
import { Employee, PersonAccount, getName, Status } from '@hcengineering/contact'
import { getCurrentAccount, Ref } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import Avatar from './Avatar.svelte'
@ -15,12 +15,12 @@
export let employeeId: Ref<Employee>
const client = getClient()
const me = (getCurrentAccount() as EmployeeAccount).employee
const me = (getCurrentAccount() as PersonAccount).person
$: editable = employeeId === me
const statusesQuery = createQuery()
let status: Status | undefined = undefined
$: employee = $employeeByIdStore.get(employeeId)
$: employee = $employeeByIdStore.get(employeeId) as Employee
statusesQuery.query(contact.class.Status, { attachedTo: employeeId }, (res) => (status = res[0]))
const dispatch = createEventDispatcher()
@ -39,7 +39,7 @@
} else if (status && !newStatus) {
client.removeDoc(contact.class.Status, status.space, status._id)
} else {
client.addCollection(contact.class.Status, employee!.space, employeeId, contact.class.Employee, 'statuses', {
client.addCollection(contact.class.Status, employee!.space, employeeId, contact.mixin.Employee, 'statuses', {
name: newStatus.name,
dueDate: newStatus.dueDate
})
@ -60,7 +60,7 @@
<div class="flex-col-center pb-2">
<Avatar size="x-large" avatar={employee.avatar} />
</div>
<div class="pb-2">{getName(employee)}</div>
<div class="pb-2">{getName(client.getHierarchy(), employee)}</div>
<DocNavLink object={employee}>
<Label label={contact.string.ViewFullProfile} />
</DocNavLink>

View File

@ -61,7 +61,7 @@
showPopup(
UsersPopup,
{
_class: contact.class.Employee,
_class: contact.mixin.Employee,
selectedUsers: members,
allowDeselect: true,
multiSelect: true,

View File

@ -13,14 +13,14 @@
// limitations under the License.
-->
<script lang="ts">
import { Employee } from '@hcengineering/contact'
import { Person } from '@hcengineering/contact'
import { Class, Doc, Ref } from '@hcengineering/core'
import { getAttribute, getAttributeEditor, getClient } from '@hcengineering/presentation'
import MergeComparer from './MergeComparer.svelte'
export let value: Employee
export let value: Person
export let _class: Ref<Class<Doc>>
export let targetEmp: Employee
export let targetEmp: Person
export let key: string
export let onChange: (key: string, value: boolean) => void
export let selected = false

View File

@ -13,13 +13,13 @@
// limitations under the License.
-->
<script lang="ts">
import { Employee } from '@hcengineering/contact'
import { Person } from '@hcengineering/contact'
import { Doc, Mixin, Ref } from '@hcengineering/core'
import { getClient } from '@hcengineering/presentation'
import { CheckBox, Label } from '@hcengineering/ui'
export let value: Employee
export let targetEmp: Employee
export let value: Person
export let targetEmp: Person
export let cast: Ref<Mixin<Doc>> | undefined = undefined
export let key: string
export let selected = false
@ -28,7 +28,7 @@
const client = getClient()
const hierarchy = client.getHierarchy()
function isEqual (value: Employee, targetEmp: Employee, key: string) {
function isEqual (value: Person, targetEmp: Person, key: string) {
if (cast !== undefined) {
value = hierarchy.as(value, cast)
targetEmp = hierarchy.as(targetEmp, cast)

View File

@ -13,9 +13,9 @@
// limitations under the License.
-->
<script lang="ts">
import { Channel, Employee, getName } from '@hcengineering/contact'
import core, { Doc, DocumentUpdate, Mixin, Ref, TxProcessor } from '@hcengineering/core'
import { Card, createQuery, getClient } from '@hcengineering/presentation'
import { Channel, Person, getName } from '@hcengineering/contact'
import core, { Doc, DocumentUpdate, Mixin, Ref, RefTo, Tx, TxOperations, TxProcessor } from '@hcengineering/core'
import { Card, createQuery, getClient, updateAttribute } from '@hcengineering/presentation'
import { Toggle } from '@hcengineering/ui'
import { isCollectionAttr } from '@hcengineering/view-resources'
import { createEventDispatcher } from 'svelte'
@ -24,40 +24,40 @@
import ChannelPresenter from './ChannelPresenter.svelte'
import ChannelsDropdown from './ChannelsDropdown.svelte'
import EditEmployee from './EditEmployee.svelte'
import EmployeeBox from './EmployeeBox.svelte'
import MergeAttributeComparer from './MergeAttributeComparer.svelte'
import MergeComparer from './MergeComparer.svelte'
import UserBox from './UserBox.svelte'
export let value: Employee
export let value: Person
const dispatch = createEventDispatcher()
const client = getClient()
const hierarchy = client.getHierarchy()
const parent = hierarchy.getParentClass(contact.class.Employee)
const parent = hierarchy.getParentClass(contact.class.Person)
const mixins = hierarchy.getDescendants(parent).filter((p) => hierarchy.isMixin(p))
let sourceEmployee = value._id
let sourceEmp: Employee | undefined = undefined
let sourcePersonRef = value._id
let sourcePerson: Person | undefined = undefined
let targetEmployee: Ref<Employee> | undefined = undefined
let targetEmp: Employee | undefined = undefined
let targetPersonRf: Ref<Person> | undefined = undefined
let targetPerson: Person | undefined = undefined
const targetQuery = createQuery()
$: targetEmployee &&
sourceEmployee &&
targetQuery.query(contact.class.Employee, { _id: { $in: [sourceEmployee, targetEmployee] } }, (res) => {
$: targetPersonRf &&
sourcePersonRef &&
targetQuery.query(contact.class.Person, { _id: { $in: [sourcePersonRef, targetPersonRf] } }, (res) => {
// ;[targetEmp] = res
sourceEmp = res.find((it) => it._id === sourceEmployee)
targetEmp = res.find((it) => it._id === targetEmployee)
if (sourceEmp && targetEmp) {
update = fillUpdate(sourceEmp, targetEmp)
mixinUpdate = fillMixinUpdate(sourceEmp, targetEmp)
sourcePerson = res.find((it) => it._id === sourcePersonRef)
targetPerson = res.find((it) => it._id === targetPersonRf)
if (sourcePerson && targetPerson) {
update = fillUpdate(sourcePerson, targetPerson)
mixinUpdate = fillMixinUpdate(sourcePerson, targetPerson)
applyUpdate(update)
}
})
function fillUpdate (source: Employee, target: Employee): DocumentUpdate<Employee> {
const res: DocumentUpdate<Employee> = {}
const attributes = hierarchy.getOwnAttributes(contact.class.Employee)
function fillUpdate (source: Person, target: Person): DocumentUpdate<Person> {
const res: DocumentUpdate<Person> = {}
const attributes = hierarchy.getOwnAttributes(contact.class.Person)
for (const attribute of attributes) {
const key = attribute[0]
if (attribute[1].hidden) continue
@ -69,7 +69,7 @@
return res
}
function fillMixinUpdate (source: Employee, target: Employee): Record<Ref<Mixin<Doc>>, DocumentUpdate<Doc>> {
function fillMixinUpdate (source: Person, target: Person): Record<Ref<Mixin<Doc>>, DocumentUpdate<Doc>> {
const res: Record<Ref<Mixin<Doc>>, DocumentUpdate<Doc>> = {}
for (const mixin of mixins) {
if (!hierarchy.hasMixin(source, mixin)) continue
@ -88,30 +88,31 @@
return res
}
let update: DocumentUpdate<Employee> = {}
let update: DocumentUpdate<Person> = {}
let mixinUpdate: Record<Ref<Mixin<Doc>>, DocumentUpdate<Doc>> = {}
let result: Employee = { ...value }
let result: Person = { ...value }
function applyUpdate (update: DocumentUpdate<Employee>): void {
const r = hierarchy.clone(targetEmp)
function applyUpdate (update: DocumentUpdate<Person>): void {
const r = hierarchy.clone(targetPerson)
TxProcessor.applyUpdate(r, update)
result = r
}
async function merge (): Promise<void> {
if (sourceEmp === undefined || targetEmp === undefined) return
if (sourcePerson === undefined || targetPerson === undefined) return
if (Object.keys(update).length > 0) {
if (update.avatar !== undefined || sourceEmp.avatar === targetEmp.avatar) {
if (update.avatar !== undefined || sourcePerson.avatar === targetPerson.avatar) {
// We replace avatar, we need to update source with target
await client.update(sourceEmp, { avatar: sourceEmp.avatar === targetEmp.avatar ? '' : targetEmp.avatar })
await client.update(sourcePerson, {
avatar: sourcePerson.avatar === targetPerson.avatar ? '' : targetPerson.avatar
})
}
await client.update(targetEmp, update)
await client.update(targetPerson, update)
}
await client.update(sourceEmp, { mergedTo: targetEmp._id, active: false })
for (const channel of resultChannels.values()) {
if (channel.attachedTo !== targetEmp._id) continue
await client.update(channel, { attachedTo: targetEmp._id })
if (channel.attachedTo !== targetPerson._id) continue
await client.update(channel, { attachedTo: targetPerson._id })
}
for (const old of oldChannels) {
if ((enabledChannels.get(old._id) ?? true) === false) {
@ -121,18 +122,31 @@
for (const mixin in mixinUpdate) {
const attrs = (mixinUpdate as any)[mixin]
if (Object.keys(attrs).length > 0) {
await client.updateMixin(targetEmp._id, targetEmp._class, targetEmp.space, mixin as Ref<Mixin<Doc>>, attrs)
} else if (!hierarchy.hasMixin(targetEmp, mixin as Ref<Mixin<Doc>>)) {
await client.createMixin(targetEmp._id, targetEmp._class, targetEmp.space, mixin as Ref<Mixin<Doc>>, {})
await client.updateMixin(
targetPerson._id,
targetPerson._class,
targetPerson.space,
mixin as Ref<Mixin<Doc>>,
attrs
)
} else if (!hierarchy.hasMixin(targetPerson, mixin as Ref<Mixin<Doc>>)) {
await client.createMixin(
targetPerson._id,
targetPerson._class,
targetPerson.space,
mixin as Ref<Mixin<Doc>>,
{}
)
}
}
await updateAllRefs(client, sourcePerson, targetPerson)
dispatch('close')
}
function select (field: string, targetValue: boolean) {
if (!targetValue) {
;(update as any)[field] = (sourceEmp as any)[field]
;(update as any)[field] = (sourcePerson as any)[field]
} else {
delete (update as any)[field]
}
@ -166,20 +180,20 @@
let oldChannels: Channel[] = []
const valueChannelsQuery = createQuery()
$: valueChannelsQuery.query(contact.class.Channel, { attachedTo: sourceEmployee }, (res) => {
$: valueChannelsQuery.query(contact.class.Channel, { attachedTo: sourcePersonRef }, (res) => {
oldChannels = res
})
let targetChannels: Channel[] = []
const targetChannelsQuery = createQuery()
$: targetEmployee &&
targetChannelsQuery.query(contact.class.Channel, { attachedTo: targetEmployee }, (res) => {
$: targetPersonRf &&
targetChannelsQuery.query(contact.class.Channel, { attachedTo: targetPersonRf }, (res) => {
targetChannels = res
})
$: resultChannels = mergeChannels(oldChannels, targetChannels, enabledChannels)
const attributes = hierarchy.getAllAttributes(contact.class.Employee, core.class.Doc)
const attributes = hierarchy.getAllAttributes(contact.mixin.Employee, core.class.Doc)
const ignoreKeys = ['name', 'avatar', 'createdOn']
const objectAttributes = Array.from(attributes.entries()).filter(
(p) => !p[1].hidden && !ignoreKeys.includes(p[0]) && !isCollectionAttr(hierarchy, { key: p[0], attr: p[1] })
@ -200,25 +214,111 @@
}
mixinUpdate[mixin] = upd
}
async function updateAllRefs (client: TxOperations, sourceAccount: Person, targetAccount: Person): Promise<Tx[]> {
const accounts = await client.findAll(contact.class.PersonAccount, { person: sourceAccount._id })
const h = client.getHierarchy()
console.log('merge employee:', sourceAccount.name, 'to', targetAccount.name)
// Move all possible references to Account and Employee and replace to target one.
const reftos = (await client.findAll(core.class.Attribute, { 'type._class': core.class.RefTo })).filter((it) => {
const to = it.type as RefTo<Doc>
return (
to.to === contact.class.Person ||
to.to === contact.mixin.Employee ||
to.to === core.class.Account ||
to.to === contact.class.PersonAccount
)
})
for (const attr of reftos) {
if (attr.name === '_id') {
continue
}
const to = attr.type as RefTo<Doc>
if (to.to === contact.mixin.Employee || to.to === contact.class.Person) {
const descendants = h.getDescendants(attr.attributeOf)
for (const d of descendants) {
if (h.isDerived(d, core.class.Tx)) {
continue
}
if (h.findDomain(d) !== undefined) {
while (true) {
const values = await client.findAll(d, { [attr.name]: sourceAccount._id }, { limit: 100 })
if (values.length === 0) {
break
}
const builder = client.apply(sourceAccount._id)
for (const v of values) {
await updateAttribute(builder, v, d, { key: attr.name, attr }, targetAccount._id)
}
if (builder.txes.length > 0) {
console.log('merge employee:', sourceAccount.name, 'to', targetAccount.name, d, builder.txes.length)
await builder.commit()
}
}
}
}
}
}
const arrs = await client.findAll(core.class.Attribute, { 'type._class': core.class.ArrOf })
for (const attr of arrs) {
if (attr.name === '_id') {
continue
}
const to = attr.type as RefTo<Doc>
if (to.to === contact.mixin.Employee || to.to === contact.class.Person) {
const descendants = h.getDescendants(attr.attributeOf)
for (const d of descendants) {
if (h.isDerived(d, core.class.Tx)) {
continue
}
if (h.findDomain(d) !== undefined) {
while (true) {
const values = await client.findAll(attr.attributeOf, { [attr.name]: sourceAccount._id }, { limit: 100 })
if (values.length === 0) {
break
}
const builder = client.apply(sourceAccount._id)
for (const v of values) {
await updateAttribute(builder, v, d, { key: attr.name, attr }, targetAccount._id)
}
console.log('merge employee:', sourceAccount.name, 'to', targetAccount.name, d, builder.txes.length)
await builder.commit()
}
}
}
}
}
await client.remove(sourceAccount)
for (const account of accounts) {
await client.update(account, { person: targetAccount._id })
}
return []
}
const toAny = (a: any) => a
</script>
<Card
label={contact.string.MergeEmployee}
okLabel={contact.string.MergeEmployee}
label={contact.string.MergePersons}
okLabel={contact.string.MergePersons}
fullSize
okAction={merge}
canSave={targetEmp !== undefined}
canSave={targetPerson !== undefined}
onCancel={() => dispatch('close')}
on:changeContent
>
<div class="flex-row flex-between">
<div class="flex-row-center">
<EmployeeBox
<UserBox
_class={contact.class.Person}
showNavigate={false}
label={contact.string.MergeEmployeeFrom}
docQuery={{ active: { $in: [true, false] } }}
bind:value={sourceEmployee}
label={contact.string.MergePersonsFrom}
docQuery={{}}
bind:value={sourcePersonRef}
/>
<ChannelsDropdown
value={oldChannels}
@ -231,11 +331,12 @@
</div>
>>
<div class="flex-row-center">
<EmployeeBox
<UserBox
_class={contact.class.Person}
showNavigate={false}
label={contact.string.MergeEmployeeTo}
docQuery={{ active: { $in: [true, false] } }}
bind:value={targetEmployee}
label={contact.string.MergePersonsTo}
docQuery={{ _id: { $ne: sourcePersonRef } }}
bind:value={targetPersonRf}
/>
<ChannelsDropdown
value={targetChannels}
@ -247,13 +348,13 @@
/>
</div>
</div>
{#key [targetEmployee, sourceEmployee]}
{#if targetEmp && sourceEmp}
{#key [targetPersonRf, sourcePersonRef]}
{#if targetPerson && sourcePerson}
<div class="flex-col flex-grow">
<MergeComparer
key="avatar"
value={sourceEmp}
{targetEmp}
value={sourcePerson}
targetEmp={targetPerson}
onChange={select}
selected={update.avatar !== undefined}
>
@ -261,18 +362,24 @@
<Avatar avatar={item.avatar} size={'x-large'} icon={contact.icon.Person} />
</svelte:fragment>
</MergeComparer>
<MergeComparer key="name" value={sourceEmp} {targetEmp} onChange={select} selected={update.name !== undefined}>
<MergeComparer
key="name"
value={sourcePerson}
targetEmp={targetPerson}
onChange={select}
selected={update.name !== undefined}
>
<svelte:fragment slot="item" let:item>
{getName(item)}
{getName(client.getHierarchy(), item)}
</svelte:fragment>
</MergeComparer>
{#each objectAttributes as attribute}
<MergeAttributeComparer
key={attribute[0]}
value={sourceEmp}
{targetEmp}
value={sourcePerson}
targetEmp={targetPerson}
onChange={select}
_class={contact.class.Employee}
_class={contact.mixin.Employee}
selected={toAny(update)[attribute[0]] !== undefined}
/>
{/each}
@ -281,8 +388,8 @@
{#each attributes as attribute}
<MergeAttributeComparer
key={attribute}
value={sourceEmp}
{targetEmp}
value={sourcePerson}
targetEmp={targetPerson}
onChange={(key, value) => selectMixin(mixin, key, value)}
_class={mixin}
selected={toAny(mixinUpdate)?.[mixin]?.[attribute] !== undefined}

View File

@ -13,17 +13,17 @@
// limitations under the License.
-->
<script lang="ts">
import { Employee, EmployeeAccount } from '@hcengineering/contact'
import { Employee, PersonAccount } from '@hcengineering/contact'
import { Ref } from '@hcengineering/core'
import contact from '../plugin'
import { employeeAccountByIdStore } from '../utils'
import { personAccountByIdStore } from '../utils'
import CombineAvatars from './CombineAvatars.svelte'
export let value: Ref<EmployeeAccount>[]
export let value: Ref<PersonAccount>[]
$: employees = value
.map((p) => $employeeAccountByIdStore.get(p)?.employee)
.map((p) => $personAccountByIdStore.get(p)?.person)
.filter((p) => p !== undefined) as Ref<Employee>[]
</script>
<CombineAvatars _class={contact.class.Employee} items={employees} limit={5} size={'x-small'} />
<CombineAvatars _class={contact.mixin.Employee} items={employees} limit={5} size={'x-small'} />

View File

@ -14,13 +14,14 @@
// limitations under the License.
-->
<script lang="ts">
import { EmployeeAccount } from '@hcengineering/contact'
import core, { Account, systemAccountEmail } from '@hcengineering/core'
import { Employee, PersonAccount } from '@hcengineering/contact'
import core, { Account, Ref, systemAccountEmail } from '@hcengineering/core'
import { getEmbeddedLabel } from '@hcengineering/platform'
import { Label, tooltip, IconSize } from '@hcengineering/ui'
import { employeeByIdStore } from '../utils'
import { IconSize, Label, tooltip } from '@hcengineering/ui'
import { employeeByIdStore, personAccountPersonByIdStore } from '../utils'
import Avatar from './Avatar.svelte'
import EmployeePresenter from './EmployeePresenter.svelte'
import PersonPresenter from './PersonPresenter.svelte'
export let value: Account
export let avatarSize: IconSize = 'x-small'
@ -28,8 +29,9 @@
export let inline: boolean = false
export let accent: boolean = false
$: employee = $employeeByIdStore.get((value as EmployeeAccount)?.employee)
$: employee = $employeeByIdStore.get((value as PersonAccount)?.person as Ref<Employee>)
$: person = $personAccountPersonByIdStore.get((value as PersonAccount)?.person)
const valueLabel = value?.email === systemAccountEmail ? core.string.System : getEmbeddedLabel(value?.email)
</script>
@ -37,6 +39,8 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
{#if employee}
<EmployeePresenter value={employee} {disabled} {inline} {accent} {avatarSize} on:accent-color />
{:else if person}
<PersonPresenter value={person} {disabled} {inline} {accent} {avatarSize} on:accent-color />
{:else}
<div class="flex-row-center">
<Avatar size={avatarSize} />

View File

@ -14,21 +14,21 @@
// limitations under the License.
-->
<script lang="ts">
import { EmployeeAccount } from '@hcengineering/contact'
import { PersonAccount } from '@hcengineering/contact'
import { Ref } from '@hcengineering/core'
import { IconSize } from '@hcengineering/ui'
import { employeeAccountByIdStore } from '../utils'
import EmployeeAccountPresenter from './EmployeeAccountPresenter.svelte'
import { personAccountByIdStore } from '../utils'
import PersonAccountPresenter from './PersonAccountPresenter.svelte'
export let value: Ref<EmployeeAccount>
export let value: Ref<PersonAccount>
export let avatarSize: IconSize = 'x-small'
export let disabled: boolean = false
export let inline: boolean = false
export let accent: boolean = false
$: account = $employeeAccountByIdStore.get(value)
$: account = $personAccountByIdStore.get(value)
</script>
{#if account}
<EmployeeAccountPresenter value={account} {disabled} {inline} {avatarSize} on:accent-color />
<PersonAccountPresenter value={account} {disabled} {inline} {avatarSize} on:accent-color />
{/if}

View File

@ -15,7 +15,7 @@
<script lang="ts">
import attachment from '@hcengineering/attachment'
import contact, { Channel, Contact, getName } from '@hcengineering/contact'
import { createQuery } from '@hcengineering/presentation'
import { createQuery, getClient } from '@hcengineering/presentation'
import Avatar from './Avatar.svelte'
import { Component, Label } from '@hcengineering/ui'
import { DocNavLink } from '@hcengineering/view-resources'
@ -24,6 +24,8 @@
export let object: Contact
export let disabled: boolean = false
const client = getClient()
let channels: Channel[] = []
const channelsQuery = createQuery()
channelsQuery.query(
@ -46,7 +48,7 @@
<!-- svelte-ignore a11y-click-events-have-key-events -->
<DocNavLink {object} {disabled}>
<div class="name lines-limit-2">
{getName(object)}
{getName(client.getHierarchy(), object)}
</div>
</DocNavLink>
<div class="description overflow-label">{object.city ?? ''}</div>

View File

@ -27,6 +27,7 @@
import { DocNavLink } from '@hcengineering/view-resources'
import { createEventDispatcher, onMount } from 'svelte'
import Avatar from './Avatar.svelte'
import { getClient } from '@hcengineering/presentation'
export let value: Person | Employee | undefined | null
export let inline: boolean = false
@ -45,6 +46,8 @@
export let accent: boolean = false
export let maxWidth = ''
const client = getClient()
const onEditClick = (evt: MouseEvent) => {
if (!disabled) {
onEdit?.(evt)
@ -83,7 +86,9 @@
</span>
{/if}
{#if shouldShowName}
<span class="eContentPresenterLabel" class:colorInherit class:fs-bold={accent}>{getName(value)}</span>
<span class="eContentPresenterLabel" class:colorInherit class:fs-bold={accent}
>{getName(client.getHierarchy(), value)}</span
>
{/if}
</span>
</DocNavLink>

View File

@ -18,6 +18,7 @@
import type { LabelAndProps, IconSize } from '@hcengineering/ui'
import { PersonLabelTooltip } from '..'
import PersonContent from './PersonContent.svelte'
import { getClient } from '@hcengineering/presentation'
export let value: Person | null | undefined
export let inline = false
@ -36,6 +37,8 @@
export let accent: boolean = false
export let maxWidth = ''
const client = getClient()
function getTooltip (
tooltipLabels: PersonLabelTooltip | undefined,
value: Person | null | undefined
@ -44,7 +47,7 @@
return !value
? undefined
: {
label: getEmbeddedLabel(getName(value))
label: getEmbeddedLabel(getName(client.getHierarchy(), value))
}
}
const direction = tooltipLabels?.direction
@ -52,11 +55,15 @@
const label = value
? tooltipLabels.personLabel
? tooltipLabels.personLabel
: getEmbeddedLabel(getName(value))
: getEmbeddedLabel(getName(client.getHierarchy(), value))
: tooltipLabels.placeholderLabel
? tooltipLabels.placeholderLabel
: undefined
const props = tooltipLabels.props ? tooltipLabels.props : value ? { value: getName(value) } : undefined
const props = tooltipLabels.props
? tooltipLabels.props
: value
? { value: getName(client.getHierarchy(), value) }
: undefined
return {
component,
label,

View File

@ -25,7 +25,7 @@
import UsersPopup from './UsersPopup.svelte'
export let items: Ref<Employee>[] = []
export let _class: Ref<Class<Employee>> = contact.class.Employee
export let _class: Ref<Class<Employee>> = contact.mixin.Employee
export let docQuery: DocumentQuery<Employee> | undefined = {
active: true
}

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import contact, { Employee, EmployeeAccount } from '@hcengineering/contact'
import contact, { Person, PersonAccount, Employee } from '@hcengineering/contact'
import { Account, AccountRole, DocumentQuery, getCurrentAccount, Ref, SortingOrder, Space } from '@hcengineering/core'
import { translate } from '@hcengineering/platform'
import presentation, { getClient } from '@hcengineering/presentation'
@ -33,29 +33,24 @@
}
let search: string = ''
$: isSearch = search.trim().length
let members: Set<Ref<Employee>> = new Set<Ref<Employee>>()
let members: Set<Ref<Person>> = new Set<Ref<Person>>()
async function getUsers (accounts: Ref<Account>[], search: string): Promise<Employee[]> {
const query: DocumentQuery<EmployeeAccount> =
isSearch > 0 ? { name: { $like: '%' + search + '%' } } : { _id: { $in: accounts as Ref<EmployeeAccount>[] } }
const employess = await client.findAll(contact.class.EmployeeAccount, query)
members = new Set(
employess
.filter((it) => it.mergedTo == null)
.filter((p) => accounts.includes(p._id))
.map((p) => p.employee)
)
async function getUsers (accounts: Ref<Account>[], search: string): Promise<Person[]> {
const query: DocumentQuery<PersonAccount> =
isSearch > 0 ? { name: { $like: '%' + search + '%' } } : { _id: { $in: accounts as Ref<PersonAccount>[] } }
const employess = await client.findAll(contact.class.PersonAccount, query)
members = new Set(employess.filter((p) => accounts.includes(p._id)).map((p) => p.person))
return await client.findAll(
contact.class.Employee,
contact.mixin.Employee,
{
_id: { $in: employess.map((e) => e.employee) }
_id: { $in: employess.map((e) => e.person as Ref<Employee>) }
},
{ sort: { name: SortingOrder.Descending } }
)
}
async function add (employee: Ref<Employee>): Promise<void> {
const account = await client.findOne(contact.class.EmployeeAccount, { employee })
async function add (employee: Ref<Person>): Promise<void> {
const account = await client.findOne(contact.class.PersonAccount, { employee })
if (account === undefined) return
await client.update(space, {
$push: {
@ -64,14 +59,14 @@
})
}
async function removeMember (employee: Ref<Employee>): Promise<void> {
const account = await client.findOne(contact.class.EmployeeAccount, { employee })
async function removeMember (employee: Ref<Person>): Promise<void> {
const account = await client.findOne(contact.class.PersonAccount, { employee })
if (account === undefined) return
await client.update(space, { $pull: { members: account._id } })
}
function openAddMembersPopup () {
showPopup(AddMembersPopup, { value: space }, undefined, async (membersIds: Ref<EmployeeAccount>[]) => {
showPopup(AddMembersPopup, { value: space }, undefined, async (membersIds: Ref<PersonAccount>[]) => {
if (membersIds) {
for (const member of membersIds) {
if (space.members.includes(member)) continue

View File

@ -14,8 +14,8 @@
// limitations under the License.
-->
<script lang="ts">
import { Contact, getName } from '@hcengineering/contact'
import { Class, DocumentQuery, FindOptions, Hierarchy, Ref } from '@hcengineering/core'
import contact, { Contact, getName } from '@hcengineering/contact'
import { Class, DocumentQuery, FindOptions, Hierarchy, Ref, Doc } from '@hcengineering/core'
import { Asset, getEmbeddedLabel, IntlString } from '@hcengineering/platform'
import { getClient, ObjectCreate } from '@hcengineering/presentation'
import {
@ -42,6 +42,7 @@
import UsersPopup from './UsersPopup.svelte'
export let _class: Ref<Class<Contact>>
export let _previewClass: Ref<Class<Contact>> = contact.class.Contact
export let excluded: Ref<Contact>[] | undefined = undefined
export let options: FindOptions<Contact> | undefined = undefined
export let docQuery: DocumentQuery<Contact> | undefined = undefined
@ -61,6 +62,9 @@
export let showTooltip: LabelAndProps | undefined = undefined
export let showNavigate = true
export let id: string | undefined = undefined
export let filter: (it: Doc) => boolean = () => {
return true
}
export let create: ObjectCreate | undefined = undefined
@ -72,7 +76,7 @@
const client = getClient()
async function updateSelected (value: Ref<Contact> | null | undefined) {
selected = value ? await client.findOne(_class, { _id: value }) : undefined
selected = value ? await client.findOne(_previewClass, { _id: value }) : undefined
}
$: updateSelected(value)
@ -93,7 +97,8 @@
selected: value,
titleDeselect,
placeholder,
create
create,
filter
},
!$$slots.content ? container : getEventPositionElement(ev),
(result) => {
@ -121,7 +126,9 @@
class="w-full h-full flex-streatch"
on:click={_click}
class:content-color={selected === undefined}
use:tooltip={selected !== undefined ? { label: getEmbeddedLabel(getName(selected)) } : undefined}
use:tooltip={selected !== undefined
? { label: getEmbeddedLabel(getName(client.getHierarchy(), selected)) }
: undefined}
>
<slot name="content" />
</div>
@ -148,13 +155,15 @@
style:width={showNavigate && selected
? `calc(${width ?? 'min-content'} - 1.5rem)`
: `${width ?? 'min-content'}`}
use:tooltip={selected !== undefined ? { label: getEmbeddedLabel(getName(selected)) } : undefined}
use:tooltip={selected !== undefined
? { label: getEmbeddedLabel(getName(client.getHierarchy(), selected)) }
: undefined}
>
{#if selected}
{#if hideIcon || selected}
<UserInfo value={selected} size={avatarSize} {icon} />
{:else}
{getName(selected)}
{getName(client.getHierarchy(), selected)}
{/if}
{:else}
<div class="flex-presenter not-selected">

View File

@ -14,7 +14,7 @@
-->
<script lang="ts">
import contact, { Employee } from '@hcengineering/contact'
import type { Class, DocumentQuery, IdMap, Ref } from '@hcengineering/core'
import type { Class, Doc, DocumentQuery, IdMap, Ref } from '@hcengineering/core'
import type { IntlString } from '@hcengineering/platform'
import { Label, showPopup, ActionIcon, IconClose, IconAdd, Icon } from '@hcengineering/ui'
import type { IconSize } from '@hcengineering/ui'
@ -23,13 +23,12 @@
import { employeeByIdStore } from '../utils'
import UserInfo from './UserInfo.svelte'
import UsersPopup from './UsersPopup.svelte'
import { getClient } from '@hcengineering/presentation'
export let items: Ref<Employee>[] = []
export let readonlyItems: Ref<Employee>[] = []
export let _class: Ref<Class<Employee>> = contact.class.Employee
export let docQuery: DocumentQuery<Employee> | undefined = {
active: true
}
export let _class: Ref<Class<Employee>> = contact.mixin.Employee
export let docQuery: DocumentQuery<Employee> | undefined = {}
export let label: IntlString | undefined = undefined
export let actionLabel: IntlString = plugin.string.AddMember
@ -42,6 +41,8 @@
let readonlyPersons: Employee[] = getPersons(readonlyItems, $employeeByIdStore)
$: readonlyPersons = getPersons(readonlyItems, $employeeByIdStore)
const client = getClient()
const dispatch = createEventDispatcher()
async function addPerson (evt: Event): Promise<void> {
@ -55,7 +56,13 @@
allowDeselect: false,
selectedUsers: items,
ignoreUsers: readonlyItems,
readonly
readonly,
filter: (it: Doc) => {
if (client.getHierarchy().hasMixin(it, contact.mixin.Employee)) {
return client.getHierarchy().as(it, contact.mixin.Employee).active
}
return true
}
},
evt.target as HTMLElement,
undefined,

View File

@ -14,23 +14,22 @@
-->
<script lang="ts">
import contact, { Employee } from '@hcengineering/contact'
import type { Class, DocumentQuery, Ref } from '@hcengineering/core'
import type { Class, DocumentQuery, Ref, Doc } from '@hcengineering/core'
import type { IntlString } from '@hcengineering/platform'
import { Button, Label, showPopup } from '@hcengineering/ui'
import type { ButtonKind, ButtonSize, TooltipAlignment } from '@hcengineering/ui'
import { Button, Label, showPopup } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'
import plugin from '../plugin'
import { employeeByIdStore } from '../utils'
import CombineAvatars from './CombineAvatars.svelte'
import Members from './icons/Members.svelte'
import UserInfo from './UserInfo.svelte'
import UsersPopup from './UsersPopup.svelte'
import Members from './icons/Members.svelte'
import { getClient } from '@hcengineering/presentation'
export let items: Ref<Employee>[] = []
export let _class: Ref<Class<Employee>> = contact.class.Employee
export let docQuery: DocumentQuery<Employee> | undefined = {
active: true
}
export let _class: Ref<Class<Employee>> = contact.mixin.Employee
export let docQuery: DocumentQuery<Employee> | undefined = {}
export let label: IntlString | undefined = undefined
export let kind: ButtonKind = 'no-border'
@ -45,6 +44,7 @@
$: persons = items.map((p) => $employeeByIdStore.get(p)).filter((p) => p !== undefined) as Employee[]
const dispatch = createEventDispatcher()
const client = getClient()
async function addPerson (evt: Event): Promise<void> {
showPopup(
@ -56,6 +56,12 @@
multiSelect: true,
allowDeselect: false,
selectedUsers: items,
filter: (it: Doc) => {
const h = client.getHierarchy()
if (h.hasMixin(it, contact.mixin.Employee)) {
return h.as(it, contact.mixin.Employee).active
}
},
readonly
},
evt.target as HTMLElement,

View File

@ -17,6 +17,7 @@
import { getName, Person } from '@hcengineering/contact'
import { Asset } from '@hcengineering/platform'
import { getClient } from '@hcengineering/presentation'
import { AnySvelteComponent, IconSize } from '@hcengineering/ui'
export let value: Person
@ -24,6 +25,8 @@
export let size: IconSize
export let icon: Asset | AnySvelteComponent | undefined = undefined
export let short: boolean = false
const client = getClient()
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
@ -31,6 +34,6 @@
<Avatar avatar={value.avatar} {size} {icon} on:accent-color />
<div class="flex-col min-w-0 {size === 'tiny' || size === 'inline' ? 'ml-1' : 'ml-2'}" class:max-w-20={short}>
{#if subtitle}<div class="content-dark-color text-sm">{subtitle}</div>{/if}
<div class="label overflow-label text-left">{getName(value)}</div>
<div class="label overflow-label text-left">{getName(client.getHierarchy(), value)}</div>
</div>
</div>

View File

@ -13,7 +13,7 @@
// limitations under the License.
-->
<script lang="ts">
import { Contact, getFirstName, getLastName, Person } from '@hcengineering/contact'
import contact, { Contact, getFirstName, getLastName, Person } from '@hcengineering/contact'
import type { Class, Doc, DocumentQuery, FindOptions, Ref } from '@hcengineering/core'
import type { Asset, IntlString } from '@hcengineering/platform'
import presentation, { getClient, ObjectCreate, ObjectPopup } from '@hcengineering/presentation'
@ -26,6 +26,15 @@
export let selected: Ref<Person> | undefined
export let docQuery: DocumentQuery<Contact> | undefined = undefined
const client = getClient()
export let filter: (it: Doc) => boolean = (it) => {
if (client.getHierarchy().hasMixin(it, contact.mixin.Employee)) {
return client.getHierarchy().as(it, contact.mixin.Employee).active
}
return true
}
export let multiSelect: boolean = false
export let allowDeselect: boolean = false
export let titleDeselect: IntlString | undefined = undefined
@ -61,6 +70,7 @@
{titleDeselect}
{placeholder}
{docQuery}
{filter}
groupBy={'_class'}
bind:selectedObjects={selectedUsers}
bind:ignoreObjects={ignoreUsers}

View File

@ -16,7 +16,7 @@
import contact, { Channel, Contact, getName } from '@hcengineering/contact'
import { Ref } from '@hcengineering/core'
import { getEmbeddedLabel } from '@hcengineering/platform'
import { createQuery } from '@hcengineering/presentation'
import { createQuery, getClient } from '@hcengineering/presentation'
import { CircleButton, tooltip } from '@hcengineering/ui'
import { DocNavLink } from '@hcengineering/view-resources'
import { channelProviders } from '../../utils'
@ -27,6 +27,8 @@
let target: Contact | undefined
const query = createQuery()
$: query.query(contact.class.Contact, { _id: value.attachedTo as Ref<Contact> }, (res) => ([target] = res))
const client = getClient()
</script>
<div class="flex-row-center" use:tooltip={{ label: getEmbeddedLabel(value.value) }}>
@ -36,7 +38,7 @@
{#if target}
<div class="ml-1">
<DocNavLink object={target}>
{getName(target)}
{getName(client.getHierarchy(), target)}
</DocNavLink>
</div>
{/if}

View File

@ -14,7 +14,7 @@
// limitations under the License.
//
import { Channel, Contact, Employee, getGravatarUrl, getName } from '@hcengineering/contact'
import { Channel, Contact, getGravatarUrl, getName, Person } from '@hcengineering/contact'
import { Class, Client, DocumentQuery, Ref, RelatedDocument, WithLookup } from '@hcengineering/core'
import login from '@hcengineering/login'
import { IntlString, Resources, getResource } from '@hcengineering/platform'
@ -55,9 +55,9 @@ import EditMember from './components/EditMember.svelte'
import EditOrganization from './components/EditOrganization.svelte'
import EditPerson from './components/EditPerson.svelte'
import EditableAvatar from './components/EditableAvatar.svelte'
import EmployeeAccountFilterValuePresenter from './components/EmployeeAccountFilterValuePresenter.svelte'
import EmployeeAccountPresenter from './components/EmployeeAccountPresenter.svelte'
import EmployeeAccountRefPresenter from './components/EmployeeAccountRefPresenter.svelte'
import PersonAccountFilterValuePresenter from './components/PersonAccountFilterValuePresenter.svelte'
import PersonAccountPresenter from './components/PersonAccountPresenter.svelte'
import PersonAccountRefPresenter from './components/PersonAccountRefPresenter.svelte'
import EmployeeArrayEditor from './components/EmployeeArrayEditor.svelte'
import EmployeeBox from './components/EmployeeBox.svelte'
import EmployeeBrowser from './components/EmployeeBrowser.svelte'
@ -70,7 +70,7 @@ import MemberPresenter from './components/MemberPresenter.svelte'
import Members from './components/Members.svelte'
import MembersBox from './components/MembersBox.svelte'
import MembersPresenter from './components/MembersPresenter.svelte'
import MergeEmployee from './components/MergeEmployee.svelte'
import MergePersons from './components/MergePersons.svelte'
import OrganizationEditor from './components/OrganizationEditor.svelte'
import OrganizationPresenter from './components/OrganizationPresenter.svelte'
import PersonEditor from './components/PersonEditor.svelte'
@ -121,7 +121,7 @@ export {
EmployeeBrowser,
MemberPresenter,
EmployeeEditor,
EmployeeAccountRefPresenter,
PersonAccountRefPresenter,
MembersPresenter,
EditPerson,
EmployeeRefPresenter,
@ -148,7 +148,7 @@ export {
const toObjectSearchResult = (e: WithLookup<Contact>): ObjectSearchResult => ({
doc: e,
title: getName(e),
title: getName(getClient().getHierarchy(), e),
icon: Avatar,
iconProps: { size: 'x-small', avatar: e.avatar },
component: UserInfo,
@ -171,13 +171,13 @@ async function queryEmployee (
filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] }
): Promise<ObjectSearchResult[]> {
const q1 = await doContactQuery(
contact.class.Employee,
contact.mixin.Employee,
{ name: { $like: `%${search}%` }, active: true },
filter,
client
)
const q2 = await doContactQuery(
contact.class.Employee,
contact.mixin.Employee,
{ displayName: { $like: `%${search}%` }, active: true },
{
in: filter?.in,
@ -195,7 +195,7 @@ async function doContactQuery<T extends Contact> (
filter: { in?: RelatedDocument[] | undefined, nin?: RelatedDocument[] | undefined } | undefined,
client: Client
): Promise<ObjectSearchResult[]> {
if (_class === contact.class.Employee) {
if (_class === contact.mixin.Employee) {
q = { ...q, active: true }
}
if (filter?.in !== undefined || filter?.nin !== undefined) {
@ -210,10 +210,12 @@ async function doContactQuery<T extends Contact> (
return (await client.findAll(_class, q, { limit: 200 })).map(toObjectSearchResult)
}
async function kickEmployee (doc: Employee): Promise<void> {
async function kickEmployee (doc: Person): Promise<void> {
const client = getClient()
const email = await client.findOne(contact.class.EmployeeAccount, { employee: doc._id })
if (!doc.active) {
const employee = client.getHierarchy().as(doc, contact.mixin.Employee)
const email = await client.findOne(contact.class.PersonAccount, { person: doc._id })
if (!employee.active) {
showPopup(
MessageBox,
{
@ -230,7 +232,7 @@ async function kickEmployee (doc: Employee): Promise<void> {
return
}
if (email === undefined) {
await client.update(doc, { active: false })
await client.update(employee, { active: false })
} else {
showPopup(
MessageBox,
@ -288,7 +290,7 @@ export default async (): Promise<Resources> => ({
SocialEditor,
Contacts,
ContactsTabs,
EmployeeAccountPresenter,
PersonAccountPresenter,
EmployeePresenter,
EmployeeRefPresenter,
Members,
@ -300,7 +302,7 @@ export default async (): Promise<Resources> => ({
CreateEmployee,
AccountArrayEditor,
ChannelFilter,
MergeEmployee,
MergePersons,
Avatar,
UserBoxList,
ActivityChannelMessage,
@ -312,9 +314,9 @@ export default async (): Promise<Resources> => ({
UserBoxItems,
EmployeeFilter,
EmployeeFilterValuePresenter,
EmployeeAccountFilterValuePresenter,
PersonAccountFilterValuePresenter,
DeleteConfirmationPopup,
EmployeeAccountRefPresenter
PersonAccountRefPresenter
},
completion: {
EmployeeQuery: async (

View File

@ -60,10 +60,11 @@ export default mergeIds(contactId, contact, {
Email: '' as IntlString,
CreateEmployee: '' as IntlString,
Inactive: '' as IntlString,
Active: '' as IntlString,
NotSpecified: '' as IntlString,
MergeEmployee: '' as IntlString,
MergeEmployeeFrom: '' as IntlString,
MergeEmployeeTo: '' as IntlString,
MergePersons: '' as IntlString,
MergePersonsFrom: '' as IntlString,
MergePersonsTo: '' as IntlString,
SelectAvatar: '' as IntlString,
GravatarsManaged: '' as IntlString,
Through: '' as IntlString,

View File

@ -18,25 +18,26 @@ import {
AvatarProvider,
AvatarType,
ChannelProvider,
Contact,
contactId,
Employee,
EmployeeAccount,
Contact,
PersonAccount,
contactId,
formatName,
getFirstName,
getLastName,
getName
getName,
Person
} from '@hcengineering/contact'
import { Client, Doc, getCurrentAccount, IdMap, ObjQueryType, Ref, Timestamp, toIdMap } from '@hcengineering/core'
import { Client, Doc, IdMap, ObjQueryType, Ref, Timestamp, getCurrentAccount, toIdMap } from '@hcengineering/core'
import notification, { DocUpdateTx, DocUpdates } from '@hcengineering/notification'
import { getResource } from '@hcengineering/platform'
import { createQuery, getClient } from '@hcengineering/presentation'
import { TemplateDataProvider } from '@hcengineering/templates'
import { TabItem, getCurrentResolvedLocation, getPanelURI, Location, ResolvedLocation } from '@hcengineering/ui'
import { Location, ResolvedLocation, TabItem, getCurrentResolvedLocation, getPanelURI } from '@hcengineering/ui'
import view, { Filter } from '@hcengineering/view'
import { FilterQuery } from '@hcengineering/view-resources'
import { get, writable } from 'svelte/store'
import { derived, get, writable } from 'svelte/store'
import contact from './plugin'
import notification, { DocUpdates, DocUpdateTx } from '@hcengineering/notification'
import { getResource } from '@hcengineering/platform'
export function formatDate (dueDateMs: Timestamp): string {
return new Date(dueDateMs).toLocaleString('default', {
@ -48,6 +49,7 @@ export function formatDate (dueDateMs: Timestamp): string {
}
export async function employeeSort (value: Array<Ref<Employee>>): Promise<Array<Ref<Employee>>> {
const h = getClient().getHierarchy()
return value.sort((a, b) => {
const employeeId1 = a as Ref<Employee> | null | undefined
const employeeId2 = b as Ref<Employee> | null | undefined
@ -63,8 +65,8 @@ export async function employeeSort (value: Array<Ref<Employee>>): Promise<Array<
if (employeeId1 != null && employeeId2 != null) {
const employee1 = get(employeeByIdStore).get(employeeId1)
const employee2 = get(employeeByIdStore).get(employeeId2)
const name1 = employee1 != null ? getName(employee1) : ''
const name2 = employee2 != null ? getName(employee2) : ''
const name1 = employee1 != null ? getName(h, employee1) : ''
const name2 = employee2 != null ? getName(h, employee2) : ''
return name1.localeCompare(name2)
}
@ -137,19 +139,19 @@ export async function getRefs (
}
export async function getCurrentEmployeeName (): Promise<string> {
const me = getCurrentAccount() as EmployeeAccount
const me = getCurrentAccount() as PersonAccount
return formatName(me.name)
}
export async function getCurrentEmployeeEmail (): Promise<string> {
const me = getCurrentAccount() as EmployeeAccount
const me = getCurrentAccount() as PersonAccount
return me.email
}
export async function getCurrentEmployeePosition (): Promise<string | undefined> {
const me = getCurrentAccount() as EmployeeAccount
const me = getCurrentAccount() as PersonAccount
const client = getClient()
const employee = await client.findOne(contact.class.Employee, { _id: me.employee })
const employee = await client.findOne<Employee>(contact.mixin.Employee, { _id: me.person as Ref<Employee> })
if (employee !== undefined) {
return employee.position ?? ''
}
@ -161,7 +163,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 getName(value)
return getName(client.getHierarchy(), value)
} else {
return value.name
}
@ -234,7 +236,7 @@ async function generateLocation (loc: Location, id: Ref<Contact>): Promise<Resol
const workspace = loc.path[1] ?? ''
const special = client.getHierarchy().isDerived(doc._class, contact.class.Organization)
? 'companies'
: client.getHierarchy().isDerived(doc._class, contact.class.Employee)
: client.getHierarchy().isDerived(doc._class, contact.mixin.Employee)
? 'employees'
: 'persons'
return {
@ -252,33 +254,43 @@ async function generateLocation (loc: Location, id: Ref<Contact>): Promise<Resol
export const employeeByIdStore = writable<IdMap<Employee>>(new Map())
export const employeesStore = writable<Employee[]>([])
export const employeeAccountByIdStore = writable<IdMap<EmployeeAccount>>(new Map())
export const personAccountByIdStore = writable<IdMap<PersonAccount>>(new Map())
export const channelProviders = writable<ChannelProvider[]>([])
export const personAccountPersonByIdStore = writable<IdMap<Person>>(new Map())
export const personByIdStore = derived([personAccountPersonByIdStore, employeeByIdStore], (vals) => {
const m1 = Array.from(vals[0].entries())
const m2 = Array.from(vals[1].entries())
return new Map([...m1, ...m2])
})
function fillStores (): void {
const client = getClient()
if (client !== undefined) {
const accountPersonQuery = createQuery(true)
const query = createQuery(true)
query.query(contact.class.Employee, {}, (res) => {
query.query(contact.mixin.Employee, {}, (res) => {
employeesStore.set(res)
employeeByIdStore.set(toIdMap(res))
})
const accountQ = createQuery(true)
accountQ.query(contact.class.EmployeeAccount, {}, (res) => {
const mergedEmployees = res.filter((it) => it.mergedTo !== undefined)
const activeEmployees = res.filter((it) => it.mergedTo === undefined)
const ids = toIdMap(activeEmployees)
for (const e of mergedEmployees) {
if (e.mergedTo !== undefined) {
const mergeTo = ids.get(e.mergedTo)
if (mergeTo !== undefined) {
ids.set(e._id, mergeTo)
}
accountQ.query(contact.class.PersonAccount, {}, (res) => {
personAccountByIdStore.set(toIdMap(res))
const persons = res.map((it) => it.person)
accountPersonQuery.query(
contact.class.Person,
{ _id: { $in: persons }, [contact.mixin.Employee]: { $exists: false } },
(res) => {
personAccountPersonByIdStore.set(toIdMap(res))
}
}
employeeAccountByIdStore.set(ids)
)
})
const providerQuery = createQuery(true)
@ -332,5 +344,5 @@ export function getAvatarProviderId (avatar?: string | null): Ref<AvatarProvider
export async function contactTitleProvider (client: Client, ref: Ref<Contact>): Promise<string> {
const object = await client.findOne(contact.class.Contact, { _id: ref })
if (object === undefined) return ''
return getName(object)
return getName(client.getHierarchy(), object)
}

View File

@ -138,7 +138,6 @@ export interface Status extends AttachedDoc {
*/
export interface Employee extends Person {
active: boolean
mergedTo?: Ref<Employee>
statuses?: number
displayName?: string | null
position?: string | null
@ -147,9 +146,8 @@ export interface Employee extends Person {
/**
* @public
*/
export interface EmployeeAccount extends Account {
employee: Ref<Employee>
mergedTo?: Ref<EmployeeAccount>
export interface PersonAccount extends Account {
person: Ref<Person>
}
/**
@ -180,11 +178,13 @@ export const contactPlugin = plugin(contactId, {
Member: '' as Ref<Class<Member>>,
Organization: '' as Ref<Class<Organization>>,
Organizations: '' as Ref<Class<Organizations>>,
Employee: '' as Ref<Class<Employee>>,
EmployeeAccount: '' as Ref<Class<EmployeeAccount>>,
PersonAccount: '' as Ref<Class<PersonAccount>>,
Status: '' as Ref<Class<Status>>,
ContactsTab: '' as Ref<Class<ContactsTab>>
},
mixin: {
Employee: '' as Ref<Class<Employee>>
},
component: {
SocialEditor: '' as AnyComponent,
CreateOrganization: '' as AnyComponent,

View File

@ -13,7 +13,7 @@
// limitations under the License.
//
import { AttachedData, Class, Client, Doc, FindResult, Ref } from '@hcengineering/core'
import { AttachedData, Class, Client, Doc, FindResult, Ref, Hierarchy } from '@hcengineering/core'
import { IconSize } from '@hcengineering/ui'
import { MD5 } from 'crypto-js'
import { Channel, Contact, contactPlugin, Employee, Person } from '.'
@ -211,9 +211,9 @@ export function formatName (name: string): string {
/**
* @public
*/
export function getName (value: Contact): string {
if (isEmployee(value)) {
return value.displayName ?? formatName(value.name)
export function getName (hierarchy: Hierarchy, value: Contact): string {
if (isEmployee(hierarchy, value)) {
return hierarchy.as(value, contactPlugin.mixin.Employee).displayName ?? formatName(value.name)
}
if (isPerson(value)) {
return formatName(value.name)
@ -221,8 +221,8 @@ export function getName (value: Contact): string {
return value.name
}
function isEmployee (value: Contact): value is Employee {
return value._class === contactPlugin.class.Employee
function isEmployee (hierarchy: Hierarchy, value: Contact): value is Employee {
return hierarchy.hasMixin(value, contactPlugin.mixin.Employee)
}
function isPerson (value: Contact): value is Person {

Some files were not shown because too many files have changed in this diff Show More