From 1d8de7681f21cb50a6a64ac2a4c9e8ad98722f57 Mon Sep 17 00:00:00 2001 From: Denis Bykhov Date: Mon, 13 May 2024 20:31:55 +0500 Subject: [PATCH] Fix hr departments (#5589) Signed-off-by: Denis Bykhov --- models/hr/src/index.ts | 38 +++++++----- models/hr/src/migration.ts | 62 +++++++++++++++---- models/hr/src/plugin.ts | 3 +- packages/core/src/storage.ts | 2 +- plugins/guest-resources/package.json | 1 + plugins/hr-resources/package.json | 1 + .../src/components/CreateDepartment.svelte | 22 +++---- .../src/components/CreateRequest.svelte | 15 ++--- .../src/components/DepartmentEditor.svelte | 10 +-- .../src/components/DepartmentPresenter.svelte | 23 +++++++ .../src/components/DepartmentStaff.svelte | 4 +- .../src/components/EditRequestType.svelte | 53 +++++++--------- .../src/components/Schedule.svelte | 14 +++-- .../src/components/ScheduleView.svelte | 12 ++-- .../src/components/Structure.svelte | 10 +-- .../schedule/CreatePublicHoliday.svelte | 21 ++++--- .../components/schedule/ExportPopup.svelte | 16 +++-- .../components/schedule/MonthTableView.svelte | 7 ++- .../src/components/schedule/MonthView.svelte | 16 +++-- .../components/schedule/ReportsPopup.svelte | 1 - plugins/hr-resources/src/index.ts | 4 +- plugins/hr/src/index.ts | 13 ++-- plugins/hr/src/utils.ts | 13 ++++ server-plugins/hr-resources/src/index.ts | 36 +++-------- server/middleware/src/spaceSecurity.ts | 2 + 25 files changed, 238 insertions(+), 161 deletions(-) create mode 100644 plugins/hr-resources/src/components/DepartmentPresenter.svelte diff --git a/models/hr/src/index.ts b/models/hr/src/index.ts index 6a19a0700f..062943e8c4 100644 --- a/models/hr/src/index.ts +++ b/models/hr/src/index.ts @@ -15,29 +15,28 @@ import { type Contact, type Employee } from '@hcengineering/contact' import { + AccountRole, + DOMAIN_MODEL, + IndexKind, type Arr, type Class, - DOMAIN_MODEL, type Domain, - IndexKind, type Markup, type Ref, - type Type, - AccountRole + type Type } from '@hcengineering/core' import { + hrId, type Department, type DepartmentMember, type PublicHoliday, type Request, type RequestType, type Staff, - type TzDate, - hrId + type TzDate } from '@hcengineering/hr' import { ArrOf, - type Builder, Collection, Hidden, Index, @@ -48,13 +47,14 @@ import { TypeMarkup, TypeRef, TypeString, - UX + UX, + type Builder } from '@hcengineering/model' import attachment from '@hcengineering/model-attachment' import calendar from '@hcengineering/model-calendar' import chunter from '@hcengineering/model-chunter' import contact, { TEmployee, TPersonAccount } from '@hcengineering/model-contact' -import core, { TAttachedDoc, TDoc, TSpace, TType } from '@hcengineering/model-core' +import core, { TAttachedDoc, TDoc, TType } from '@hcengineering/model-core' import view, { classPresenter, createAction } from '@hcengineering/model-view' import workbench from '@hcengineering/model-workbench' import notification from '@hcengineering/notification' @@ -68,16 +68,20 @@ export { default } from './plugin' export const DOMAIN_HR = 'hr' as Domain -@Model(hr.class.Department, core.class.Space) +@Model(hr.class.Department, core.class.Doc, DOMAIN_HR) @UX(hr.string.Department, hr.icon.Department) -export class TDepartment extends TSpace implements Department { +export class TDepartment extends TDoc implements Department { @Prop(TypeRef(hr.class.Department), hr.string.ParentDepartmentLabel) @Index(IndexKind.Indexed) - declare space: Ref + parent?: Ref @Prop(TypeString(), core.string.Name) @Index(IndexKind.FullText) - declare name: string + name!: string + + @Prop(TypeString(), core.string.Description) + @Index(IndexKind.FullText) + description!: string @Prop(Collection(contact.class.Channel), contact.string.ContactInfo) channels?: number @@ -94,7 +98,7 @@ export class TDepartment extends TSpace implements Department { teamLead!: Ref | null @Prop(ArrOf(TypeRef(hr.class.DepartmentMember)), contact.string.Members) - declare members: Arr> + members!: Arr> @Prop(ArrOf(TypeRef(contact.class.Contact)), hr.string.Subscribers) subscribers?: Arr> @@ -154,7 +158,7 @@ export class TRequest extends TAttachedDoc implements Request { @Prop(TypeRef(hr.class.Department), hr.string.Department) @Index(IndexKind.Indexed) - declare space: Ref + department!: Ref @Prop(TypeRef(hr.class.RequestType), hr.string.RequestType) @Hidden() @@ -568,4 +572,8 @@ export function createModel (builder: Builder): void { domain: DOMAIN_HR, disabled: [{ modifiedOn: 1 }, { modifiedBy: 1 }, { createdBy: 1 }, { attachedToClass: 1 }, { createdOn: -1 }] }) + + builder.mixin(hr.class.Department, core.class.Class, view.mixin.ObjectPresenter, { + presenter: hr.component.DepartmentPresenter + }) } diff --git a/models/hr/src/migration.ts b/models/hr/src/migration.ts index 9cad566d56..e86e838440 100644 --- a/models/hr/src/migration.ts +++ b/models/hr/src/migration.ts @@ -13,29 +13,30 @@ // limitations under the License. // -import { TxOperations } from '@hcengineering/core' +import { type Ref, TxOperations } from '@hcengineering/core' import { + createDefaultSpace, + tryMigrate, tryUpgrade, type MigrateOperation, type MigrationClient, type MigrationUpgradeClient } from '@hcengineering/model' -import core from '@hcengineering/model-core' -import hr, { hrId } from './index' +import core, { DOMAIN_SPACE } from '@hcengineering/model-core' +import hr, { DOMAIN_HR, hrId } from './index' +import { type Department } from '@hcengineering/hr' -async function createSpace (tx: TxOperations): Promise { - const current = await tx.findOne(core.class.Space, { +async function createDepartment (tx: TxOperations): Promise { + const current = await tx.findOne(hr.class.Department, { _id: hr.ids.Head }) if (current === undefined) { await tx.createDoc( hr.class.Department, - core.space.Space, + hr.space.HR, { name: 'Organization', description: '', - private: false, - archived: false, members: [], teamLead: null, managers: [] @@ -45,15 +46,54 @@ async function createSpace (tx: TxOperations): Promise { } } +async function migrateDepartments (client: MigrationClient): Promise { + await client.update( + DOMAIN_HR, + { _class: hr.class.PublicHoliday, space: { $ne: hr.space.HR } }, + { space: hr.space.HR } + ) + const objects = await client.find(DOMAIN_HR, { space: { $ne: hr.space.HR }, _class: hr.class.Request }) + for (const obj of objects) { + await client.update(DOMAIN_HR, { _id: obj._id }, { space: hr.space.HR, department: obj.space }) + } + await client.move(DOMAIN_SPACE, { _class: hr.class.Department }, DOMAIN_HR) + const departments = await client.find(DOMAIN_HR, { + _class: hr.class.Department, + space: { $ne: hr.space.HR } + }) + for (const department of departments) { + const upd: Partial = { + space: hr.space.HR + } + if (department._id !== hr.ids.Head) { + upd.parent = department.space as unknown as Ref + } + await client.update(DOMAIN_HR, { _id: department._id }, upd) + } + await client.update( + DOMAIN_HR, + { _class: hr.class.Department }, + { $unset: { archived: true, private: true, owners: true, autoJoin: true } } + ) +} + export const hrOperation: MigrateOperation = { - async migrate (client: MigrationClient): Promise {}, + async migrate (client: MigrationClient): Promise { + await tryMigrate(client, hrId, [ + { + state: 'migrateDepartments', + func: migrateDepartments + } + ]) + }, async upgrade (client: MigrationUpgradeClient): Promise { await tryUpgrade(client, hrId, [ { - state: 'create-defaults', + state: 'create-defaults-v2', func: async (client) => { + await createDefaultSpace(client, hr.space.HR, { name: 'HR', description: 'Human Resources' }) const tx = new TxOperations(client, core.account.System) - await createSpace(tx) + await createDepartment(tx) } } ]) diff --git a/models/hr/src/plugin.ts b/models/hr/src/plugin.ts index c1c8987b37..44ad9f39c4 100644 --- a/models/hr/src/plugin.ts +++ b/models/hr/src/plugin.ts @@ -49,7 +49,8 @@ export default mergeIds(hrId, hr, { EditRequest: '' as AnyComponent, TzDatePresenter: '' as AnyComponent, TzDateEditor: '' as AnyComponent, - RequestPresenter: '' as AnyComponent + RequestPresenter: '' as AnyComponent, + DepartmentPresenter: '' as AnyComponent }, category: { HR: '' as Ref diff --git a/packages/core/src/storage.ts b/packages/core/src/storage.ts index c115faed4c..c26bf2d595 100644 --- a/packages/core/src/storage.ts +++ b/packages/core/src/storage.ts @@ -41,7 +41,7 @@ export type QuerySelector = { /** * @public */ -export type ObjQueryType = (T extends Array ? U | U[] : T) | QuerySelector +export type ObjQueryType = (T extends Array ? U | U[] | QuerySelector : T) | QuerySelector /** * @public diff --git a/plugins/guest-resources/package.json b/plugins/guest-resources/package.json index 57a606a0cd..990b344a8c 100644 --- a/plugins/guest-resources/package.json +++ b/plugins/guest-resources/package.json @@ -7,6 +7,7 @@ "scripts": { "build": "compile ui", "build:docs": "api-extractor run --local", + "svelte-check": "do-svelte-check", "format": "format src", "build:watch": "compile ui", "_phase:build": "compile ui", diff --git a/plugins/hr-resources/package.json b/plugins/hr-resources/package.json index faf4d237e2..bcc6afde77 100644 --- a/plugins/hr-resources/package.json +++ b/plugins/hr-resources/package.json @@ -7,6 +7,7 @@ "scripts": { "build": "compile ui", "build:docs": "api-extractor run --local", + "svelte-check": "do-svelte-check", "format": "format src", "build:watch": "compile ui", "_phase:build": "compile ui", diff --git a/plugins/hr-resources/src/components/CreateDepartment.svelte b/plugins/hr-resources/src/components/CreateDepartment.svelte index b58242fd84..42191853cd 100644 --- a/plugins/hr-resources/src/components/CreateDepartment.svelte +++ b/plugins/hr-resources/src/components/CreateDepartment.svelte @@ -14,14 +14,15 @@ --> - { onChange(e.detail) }} diff --git a/plugins/hr-resources/src/components/DepartmentPresenter.svelte b/plugins/hr-resources/src/components/DepartmentPresenter.svelte new file mode 100644 index 0000000000..fb6c8fbe25 --- /dev/null +++ b/plugins/hr-resources/src/components/DepartmentPresenter.svelte @@ -0,0 +1,23 @@ + + + + + {value.name} + diff --git a/plugins/hr-resources/src/components/DepartmentStaff.svelte b/plugins/hr-resources/src/components/DepartmentStaff.svelte index f9ca02ebd4..a7f5979792 100644 --- a/plugins/hr-resources/src/components/DepartmentStaff.svelte +++ b/plugins/hr-resources/src/components/DepartmentStaff.svelte @@ -37,7 +37,9 @@ { _id: objectId }, - (res) => ([value] = res) + (res) => { + value = res[0] + } ) function add (e: MouseEvent) { diff --git a/plugins/hr-resources/src/components/EditRequestType.svelte b/plugins/hr-resources/src/components/EditRequestType.svelte index 6a6d0ac112..4aed34956d 100644 --- a/plugins/hr-resources/src/components/EditRequestType.svelte +++ b/plugins/hr-resources/src/components/EditRequestType.svelte @@ -1,30 +1,14 @@ diff --git a/plugins/hr-resources/src/components/Schedule.svelte b/plugins/hr-resources/src/components/Schedule.svelte index dcb5a31f46..0022fc9e4a 100644 --- a/plugins/hr-resources/src/components/Schedule.svelte +++ b/plugins/hr-resources/src/components/Schedule.svelte @@ -17,7 +17,7 @@ import calendar from '@hcengineering/calendar-resources/src/plugin' import { Employee, PersonAccount } from '@hcengineering/contact' import { employeeByIdStore } from '@hcengineering/contact-resources' - import { DocumentQuery, getCurrentAccount, Ref } from '@hcengineering/core' + import { DocumentQuery, Ref, getCurrentAccount } from '@hcengineering/core' import { Department, Staff } from '@hcengineering/hr' import { createQuery } from '@hcengineering/presentation' import type { TabItem } from '@hcengineering/ui' @@ -27,10 +27,10 @@ IconForward, Label, SearchEdit, + Separator, TabList, - workbenchSeparators, defineSeparators, - Separator + workbenchSeparators } from '@hcengineering/ui' import view from '@hcengineering/view' @@ -77,9 +77,11 @@ departments.clear() descendants.clear() for (const doc of res) { - const current = descendants.get(doc.space) ?? [] - current.push(doc) - descendants.set(doc.space, current) + if (doc.parent !== undefined) { + const current = descendants.get(doc.parent) ?? [] + current.push(doc) + descendants.set(doc.parent, current) + } departments.set(doc._id, doc) } departments = departments diff --git a/plugins/hr-resources/src/components/ScheduleView.svelte b/plugins/hr-resources/src/components/ScheduleView.svelte index ab317ca7b2..7e56e75e43 100644 --- a/plugins/hr-resources/src/components/ScheduleView.svelte +++ b/plugins/hr-resources/src/components/ScheduleView.svelte @@ -16,8 +16,8 @@ import { CalendarMode } from '@hcengineering/calendar-resources' import { Employee, PersonAccount } from '@hcengineering/contact' import contact from '@hcengineering/contact-resources/src/plugin' - import { DocumentQuery, getCurrentAccount, Ref } from '@hcengineering/core' - import { Department, fromTzDate, Request, RequestType, Staff } from '@hcengineering/hr' + import { DocumentQuery, Ref, getCurrentAccount } from '@hcengineering/core' + import { Department, DepartmentMember, Request, RequestType, Staff, fromTzDate } from '@hcengineering/hr' import { createQuery, getClient } from '@hcengineering/presentation' import tracker, { Issue } from '@hcengineering/tracker' import { Label } from '@hcengineering/ui' @@ -138,7 +138,7 @@ } function isEditable (department: Department): boolean { - return department.teamLead === currentEmployee || department.managers.includes(currentEmployee) + return department.teamLead === currentEmployee || department.managers.includes(currentEmployee as Ref) } function checkDepartmentEditable ( @@ -170,7 +170,7 @@ departmentStaff: Staff[], descendants: Map, Department[]> ) { - editableList = [currentEmployee] + editableList = [currentEmployee as Ref] checkDepartmentEditable(departmentById, hr.ids.Head, departmentStaff, descendants) editableList = editableList } @@ -235,7 +235,7 @@ holidays = new Map() for (const groupKey in group) { holidays.set( - groupKey, + groupKey as Ref, group[groupKey].map((holiday) => new Date(fromTzDate(holiday.date))) ) } @@ -266,7 +266,7 @@ const ids = departmentStaff.map((staff) => staff._id) const staffs = await client.findAll(contact.class.PersonAccount, { person: { $in: ids } }) const departments = await client.findAll(hr.class.Department, { - members: { $in: staffs.map((staff) => staff._id) } + members: { $in: staffs.map((staff) => staff._id as Ref) } }) staffs.forEach((staff) => { const filteredDepartments = departments.filter((department) => department.members.includes(staff._id)) diff --git a/plugins/hr-resources/src/components/Structure.svelte b/plugins/hr-resources/src/components/Structure.svelte index 2e37f87ce4..bec789bdbe 100644 --- a/plugins/hr-resources/src/components/Structure.svelte +++ b/plugins/hr-resources/src/components/Structure.svelte @@ -17,7 +17,7 @@ import { DocumentQuery, Ref, WithLookup } from '@hcengineering/core' import type { Department, Staff } from '@hcengineering/hr' import { createQuery } from '@hcengineering/presentation' - import { Button, eventToHTMLElement, Label, Scroller, SearchEdit, showPopup, IconAdd } from '@hcengineering/ui' + import { Button, IconAdd, Label, Scroller, SearchEdit, eventToHTMLElement, showPopup } from '@hcengineering/ui' import hr from '../plugin' import CreateDepartment from './CreateDepartment.svelte' import DepartmentCard from './DepartmentCard.svelte' @@ -50,9 +50,11 @@ head = res.find((p) => p._id === hr.ids.Head) descendants.clear() for (const doc of res) { - const current = descendants.get(doc.space) ?? [] - current.push(doc) - descendants.set(doc.space, current) + if (doc.parent !== undefined) { + const current = descendants.get(doc.parent) ?? [] + current.push(doc) + descendants.set(doc.parent, current) + } } descendants = descendants }, diff --git a/plugins/hr-resources/src/components/schedule/CreatePublicHoliday.svelte b/plugins/hr-resources/src/components/schedule/CreatePublicHoliday.svelte index 98061d5a5a..db31eb3097 100644 --- a/plugins/hr-resources/src/components/schedule/CreatePublicHoliday.svelte +++ b/plugins/hr-resources/src/components/schedule/CreatePublicHoliday.svelte @@ -13,23 +13,24 @@ // limitations under the License. --> - dispatch('close', selectedSeparator)} - canSave - on:close -> +
- {#each values as value, i} + {#each values as value} {@const day = getDay(startDate, value)} {@const today = areDatesEqual(todayDate, day)} {@const weekend = isWeekend(day)} @@ -360,9 +366,11 @@ )} {@const requests = getRequests(employeeRequests, day, day, employee._id)} {@const tooltipValue = getTooltip(requests, day, employee)} + {@const clickHandler = getClickHandler(day, employee)} {#key [tooltipValue, editable]} +
{ - createRequest(e, day, employee) - }} + on:click={clickHandler} > {#if today}
diff --git a/plugins/hr-resources/src/components/schedule/ReportsPopup.svelte b/plugins/hr-resources/src/components/schedule/ReportsPopup.svelte index 6ccc8951d7..257e0d47fd 100644 --- a/plugins/hr-resources/src/components/schedule/ReportsPopup.svelte +++ b/plugins/hr-resources/src/components/schedule/ReportsPopup.svelte @@ -51,7 +51,6 @@ it._id) } }} config={['$lookup.attachedTo', '$lookup.attachedTo.title', '', 'employee', 'date']} diff --git a/plugins/hr-resources/src/index.ts b/plugins/hr-resources/src/index.ts index aae3bdb40f..679c6bc8ef 100644 --- a/plugins/hr-resources/src/index.ts +++ b/plugins/hr-resources/src/index.ts @@ -23,6 +23,7 @@ import Schedule from './components/Schedule.svelte' import Structure from './components/Structure.svelte' import TzDatePresenter from './components/TzDatePresenter.svelte' import TzDateEditor from './components/TzDateEditor.svelte' +import DepartmentPresenter from './components/DepartmentPresenter.svelte' import RequestPresenter from './components/RequestPresenter.svelte' import { showPopup } from '@hcengineering/ui' import { type Request } from '@hcengineering/hr' @@ -44,7 +45,8 @@ export default async (): Promise => ({ TzDatePresenter, TzDateEditor, RequestPresenter, - EditRequestType + EditRequestType, + DepartmentPresenter }, actionImpl: { EditRequestType: editRequestType diff --git a/plugins/hr/src/index.ts b/plugins/hr/src/index.ts index a8a4adf498..3f3a0c0d55 100644 --- a/plugins/hr/src/index.ts +++ b/plugins/hr/src/index.ts @@ -23,14 +23,16 @@ import { Viewlet } from '@hcengineering/view' /** * @public */ -export interface Department extends Space { - space: Ref +export interface Department extends Doc { + name: string + description: string + parent?: Ref avatar?: string | null teamLead: Ref | null attachments?: number comments?: number channels?: number - members: Arr> + members: Ref[] subscribers?: Arr> managers: Arr> } @@ -85,7 +87,7 @@ export interface Request extends AttachedDoc { attachedToClass: Ref> - space: Ref + department: Ref type: Ref @@ -121,6 +123,9 @@ const hr = plugin(hrId, { mixin: { Staff: '' as Ref> }, + space: { + HR: '' as Ref + }, icon: { HR: '' as Asset, Department: '' as Asset, diff --git a/plugins/hr/src/utils.ts b/plugins/hr/src/utils.ts index bf0b26767d..9f92c376b7 100644 --- a/plugins/hr/src/utils.ts +++ b/plugins/hr/src/utils.ts @@ -14,6 +14,19 @@ // import { TzDate } from '.' +/** + * @public + */ +export function timeToTzDate (val: number): TzDate { + const date = new Date(val) + return { + year: date.getFullYear(), + month: date.getMonth(), + day: date.getDate(), + offset: date.getTimezoneOffset() + } +} + /** * @public */ diff --git a/server-plugins/hr-resources/src/index.ts b/server-plugins/hr-resources/src/index.ts index ca3e1d9a54..98f0375eff 100644 --- a/server-plugins/hr-resources/src/index.ts +++ b/server-plugins/hr-resources/src/index.ts @@ -68,8 +68,8 @@ async function buildHierarchy (_id: Ref, control: TriggerControl): P const ancestors = new Map, Ref>() const departments = await control.findAll(hr.class.Department, {}) for (const department of departments) { - if (department._id === hr.ids.Head) continue - ancestors.set(department._id, department.space) + if (department._id === hr.ids.Head || department.parent === undefined) continue + ancestors.set(department._id, department.parent) } const departmentsMap = toIdMap(departments) while (true) { @@ -100,13 +100,13 @@ function getTxes ( removed?: Ref[] ): Tx[] { const pushTxes = added.map((dep) => - factory.createTxUpdateDoc(hr.class.Department, core.space.Space, dep, { + factory.createTxUpdateDoc(hr.class.Department, hr.space.HR, dep, { $push: { members: account } }) ) if (removed === undefined) return pushTxes const pullTxes = removed.map((dep) => - factory.createTxUpdateDoc(hr.class.Department, core.space.Space, dep, { + factory.createTxUpdateDoc(hr.class.Department, hr.space.HR, dep, { $pull: { members: account } }) ) @@ -162,8 +162,8 @@ export async function OnDepartmentStaff (tx: Tx, control: TriggerControl): Promi export async function OnDepartmentRemove (tx: Tx, control: TriggerControl): Promise { const ctx = TxProcessor.extractTx(tx) as TxRemoveDoc - const department = (await control.findAll(hr.class.Department, { _id: ctx.objectSpace as Ref }))[0] - + const department = control.removedMap.get(ctx.objectId) as Department + if (department === undefined) return [] const targetAccounts = await control.modelDb.findAll(contact.class.PersonAccount, { _id: { $in: department.members } }) @@ -298,13 +298,7 @@ export async function OnRequestCreate (tx: Tx, control: TriggerControl): Promise const request = TxProcessor.createDoc2Doc(ctx) - await sendEmailNotifications( - control, - sender, - request, - ctx.objectSpace as Ref, - hr.ids.CreateRequestNotification - ) + await sendEmailNotifications(control, sender, request, request.department, hr.ids.CreateRequestNotification) return [] } @@ -320,13 +314,7 @@ export async function OnRequestUpdate (tx: Tx, control: TriggerControl): Promise const request = (await control.findAll(hr.class.Request, { _id: ctx.objectId }))[0] as Request if (request === undefined) return [] - await sendEmailNotifications( - control, - sender, - request, - ctx.objectSpace as Ref, - hr.ids.UpdateRequestNotification - ) + await sendEmailNotifications(control, sender, request, request.department, hr.ids.UpdateRequestNotification) return [] } @@ -342,13 +330,7 @@ export async function OnRequestRemove (tx: Tx, control: TriggerControl): Promise const request = control.removedMap.get(ctx.objectId) as Request if (request === undefined) return [] - await sendEmailNotifications( - control, - sender, - request, - ctx.objectSpace as Ref, - hr.ids.RemoveRequestNotification - ) + await sendEmailNotifications(control, sender, request, request.department, hr.ids.RemoveRequestNotification) return [] } diff --git a/server/middleware/src/spaceSecurity.ts b/server/middleware/src/spaceSecurity.ts index 052f3aef96..b7df818244 100644 --- a/server/middleware/src/spaceSecurity.ts +++ b/server/middleware/src/spaceSecurity.ts @@ -325,6 +325,8 @@ export class SpaceSecurityMiddleware extends BaseMiddleware implements Middlewar const account = await getUser(this.storage, ctx) if (tx.objectSpace === (account._id as string)) { targets = [account.email, systemAccountEmail] + } else if ([...this.systemSpaces, ...this.mainSpaces].includes(tx.objectSpace)) { + return } else { const cudTx = tx as TxCUD const isSpace = h.isDerived(cudTx.objectClass, core.class.Space)