Add members to space type for easy management members, and add autojo… (#5612)

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov 2024-05-17 11:05:05 +05:00 committed by GitHub
parent fa3df9981d
commit 8f52663a11
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 297 additions and 44 deletions

View File

@ -75,8 +75,7 @@ export class TSpace extends TDoc implements Space {
@Prop(ArrOf(TypeRef(core.class.Account)), core.string.Owners)
owners?: Ref<Account>[]
@Prop(TypeBoolean(), getEmbeddedLabel('Auto-Join members'))
@Hidden() // let's hide it for now
@Prop(TypeBoolean(), core.string.AutoJoin)
autoJoin?: boolean
}
@ -119,6 +118,12 @@ export class TSpaceType extends TDoc implements SpaceType {
@Prop(Collection(core.class.Role), core.string.Roles)
roles!: CollectionSize<Role>
@Prop(ArrOf(TypeRef(core.class.Account)), core.string.Members)
members!: Arr<Ref<Account>>
@Prop(TypeBoolean(), core.string.AutoJoin)
autoJoin?: boolean
}
@Model(core.class.Role, core.class.AttachedDoc, DOMAIN_MODEL)

View File

@ -89,6 +89,14 @@ export function createModel (builder: Builder): void {
}
})
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
trigger: serverContact.trigger.OnSpaceTypeMembers,
txMatch: {
objectClass: core.class.SpaceType,
_class: core.class.TxUpdateDoc
}
})
builder.mixin(
contact.templateField.CurrentEmployeeName,
templates.class.TemplateField,

View File

@ -58,6 +58,8 @@
"DeleteObjectDescription": "Grants users ability to delete objects in the space",
"ForbidDeleteObjectDescription": "Forbid users deleting objects in the space",
"UpdateSpaceDescription": "Grants users ability to update the space",
"ArchiveSpaceDescription": "Grants users ability to archive the space"
"ArchiveSpaceDescription": "Grants users ability to archive the space",
"AutoJoin": "Auto join",
"AutoJoinDescr": "Automatically join new employees to this space"
}
}

View File

@ -38,6 +38,21 @@
"StatusCategory": "Categoría de estado",
"Account": "Cuenta",
"Rank": "Rango",
"Owners": "Propietarios"
"Owners": "Propietarios",
"Permission": "Permiso",
"CreateObject": "Crear objeto",
"UpdateObject": "Actualizar objeto",
"DeleteObject": "Eliminar objeto",
"ForbidDeleteObject": "Prohibir eliminar objeto",
"UpdateSpace": "Actualizar espacio",
"ArchiveSpace": "Archivar espacio",
"CreateObjectDescription": "Concede a los usuarios la capacidad de crear objetos en el espacio",
"UpdateObjectDescription": "Concede a los usuarios la capacidad de actualizar objetos en el espacio",
"DeleteObjectDescription": "Concede a los usuarios la capacidad de eliminar objetos en el espacio",
"ForbidDeleteObjectDescription": "Prohíbe a los usuarios eliminar objetos en el espacio",
"UpdateSpaceDescription": "Concede a los usuarios la capacidad de actualizar el espacio",
"ArchiveSpaceDescription": "Concede a los usuarios la capacidad de archivar el espacio",
"AutoJoin": "Auto unirse",
"AutoJoinDescr": "Unirse automáticamente a los nuevos empleados a este espacio"
}
}

View File

@ -38,6 +38,21 @@
"StatusCategory": "Categoria de estado",
"Account": "Conta",
"Rank": "Ranking",
"Owners": "Proprietários"
"Owners": "Proprietários",
"Permission": "Permissão",
"CreateObject": "Criar objeto",
"UpdateObject": "Atualizar objeto",
"DeleteObject": "Apagar objeto",
"ForbidDeleteObject": "Proibir apagar objeto",
"UpdateSpace": "Atualizar espaço",
"ArchiveSpace": "Arquivar espaço",
"CreateObjectDescription": "Concede aos usuários a capacidade de criar objetos no espaço",
"UpdateObjectDescription": "Concede aos usuários a capacidade de atualizar objetos no espaço",
"DeleteObjectDescription": "Concede aos usuários a capacidade de apagar objetos no espaço",
"ForbidDeleteObjectDescription": "Proíbe aos usuários a capacidade de apagar objetos no espaço",
"UpdateSpaceDescription": "Concede aos usuários a capacidade de atualizar o espaço",
"ArchiveSpaceDescription": "Concede aos usuários a capacidade de arquivar o espaço",
"AutoJoin": "Auto adesão",
"AutoJoinDescr": "Adesão automática de novos funcionários a este espaço"
}
}

View File

@ -58,6 +58,8 @@
"DeleteObjectDescription": "Дает пользователям разрешение удалять объекты в пространстве",
"ForbidDeleteObjectDescription": "Запрещает пользователям удалять объекты в пространстве",
"UpdateSpaceDescription": "Дает пользователям разрешение обновлять пространство",
"ArchiveSpaceDescription": "Дает пользователям разрешение архивировать пространство"
"ArchiveSpaceDescription": "Дает пользователям разрешение архивировать пространство",
"AutoJoin": "Автоприсоединение",
"AutoJoinDescr": "Автоматически присоединять новых сотрудников к этому пространству"
}
}

View File

@ -401,6 +401,8 @@ export interface SpaceType extends Doc {
name: string
shortDescription?: string
descriptor: Ref<SpaceTypeDescriptor>
members?: Ref<Account>[] // this members will be added automatically to new space, also change this fiield will affect existing spaces
autoJoin?: boolean // if true, all new users will be added to space automatically
targetClass: Ref<Class<Space>> // A dynamic mixin for Spaces to hold custom attributes and roles assignment of the space type
roles: CollectionSize<Role>
}

View File

@ -230,7 +230,9 @@ export default plugin(coreId, {
DeleteObjectDescription: '' as IntlString,
ForbidDeleteObjectDescription: '' as IntlString,
UpdateSpaceDescription: '' as IntlString,
ArchiveSpaceDescription: '' as IntlString
ArchiveSpaceDescription: '' as IntlString,
AutoJoin: '' as IntlString,
AutoJoinDescr: '' as IntlString
},
descriptor: {
SpacesType: '' as Ref<SpaceTypeDescriptor>

View File

@ -17,6 +17,7 @@
import { LabelAndProps } from '../types'
import { tooltip } from '../tooltips'
export let id: string | undefined = undefined
export let on: boolean = false
export let disabled: boolean = false
export let showTooltip: LabelAndProps | undefined = undefined
@ -24,7 +25,7 @@
const dispatch = createEventDispatcher()
</script>
<label class="toggle" use:tooltip={showTooltip}>
<label {id} class="toggle" use:tooltip={showTooltip} class:disabled>
<input
class="chBox"
type="checkbox"
@ -47,6 +48,12 @@
vertical-align: middle;
font-size: inherit;
user-select: none;
cursor: pointer;
&.disabled {
cursor: default;
}
.chBox {
position: absolute;
width: 1px;
@ -67,9 +74,6 @@
background: var(--theme-toggle-on-sw-color);
}
}
&:not(:disabled) + .toggle-switch {
cursor: pointer;
}
&:disabled + .toggle-switch {
filter: grayscale(70%);
&:before {

View File

@ -25,7 +25,7 @@
export let label: IntlString
export let value: Ref<Account>[]
export let onChange: ((refs: Ref<Account>[]) => void) | undefined
export let onChange: ((refs: Ref<Account>[]) => void | Promise<void>) | undefined
export let readonly = false
export let kind: ButtonKind = 'link'
export let size: ButtonSize = 'large'

View File

@ -51,7 +51,6 @@ export default mergeIds(contactId, contact, {
CopyToClipboard: '' as IntlString,
ViewFullProfile: '' as IntlString,
Member: '' as IntlString,
Members: '' as IntlString,
NoMembers: '' as IntlString,
AddMember: '' as IntlString,
KickEmployee: '' as IntlString,

View File

@ -263,7 +263,8 @@ export const contactPlugin = plugin(contactId, {
Position: '' as IntlString,
For: '' as IntlString,
SelectUsers: '' as IntlString,
AddGuest: '' as IntlString
AddGuest: '' as IntlString,
Members: '' as IntlString
},
viewlet: {
TableMember: '' as Ref<Viewlet>,

View File

@ -118,6 +118,7 @@
private: isPrivate,
members,
owners,
autoJoin,
archived: false,
icon,
color
@ -146,6 +147,9 @@
if (teamspaceData.color !== teamspace?.color) {
update.color = teamspaceData.color
}
if (teamspaceData.autoJoin !== teamspace?.autoJoin) {
update.autoJoin = teamspaceData.autoJoin
}
if (teamspaceData.members.length !== teamspace?.members.length) {
update.members = teamspaceData.members
} else {
@ -236,6 +240,7 @@
}
function handleMembersChanged (newMembers: Ref<Account>[]): void {
membersChanged = true
// If a member was removed we need to remove it from any roles assignments as well
const newMembersSet = new Set(newMembers)
const removedMembersSet = new Set(members.filter((m) => !newMembersSet.has(m)))
@ -257,6 +262,21 @@
rolesAssignment[roleId] = newMembers
}
let autoJoin = teamspace?.autoJoin ?? spaceType?.autoJoin ?? false
$: setDefaultMembers(spaceType)
let membersChanged: boolean = false
function setDefaultMembers (typeType: SpaceType | undefined): void {
if (typeType === undefined) return
if (membersChanged) return
if (teamspace !== undefined) return
autoJoin = typeType.autoJoin ?? false
if (typeType.members === undefined || typeType.members.length === 0) return
members = typeType.members
}
$: canSave =
name.trim().length > 0 &&
!(members.length === 0 && isPrivate) &&
@ -366,7 +386,7 @@
<Label label={presentation.string.MakePrivate} />
<span><Label label={presentation.string.MakePrivateDescription} /></span>
</div>
<Toggle bind:on={isPrivate} disabled={!isPrivate && members.length === 0} />
<Toggle id={'teamspace-private'} bind:on={isPrivate} disabled={!isPrivate && members.length === 0} />
</div>
<div class="antiGrid-row">
@ -383,6 +403,14 @@
/>
</div>
<div class="antiGrid-row">
<div class="antiGrid-row__header withDesciption">
<Label label={core.string.AutoJoin} />
<span><Label label={core.string.AutoJoinDescr} /></span>
</div>
<Toggle bind:on={autoJoin} />
</div>
{#each roles as role}
<div class="antiGrid-row">
<div class="antiGrid-row__header">

View File

@ -27,7 +27,7 @@
import lead, { Funnel } from '@hcengineering/lead'
import presentation, { getClient, SpaceCreateCard } from '@hcengineering/presentation'
import task, { ProjectType } from '@hcengineering/task'
import ui, { Component, EditBox, Grid, Label, ToggleWithLabel } from '@hcengineering/ui'
import ui, { Component, EditBox, Label, Toggle, ToggleWithLabel } from '@hcengineering/ui'
import { deepEqual } from 'fast-equals'
import { createEventDispatcher } from 'svelte'
@ -99,6 +99,7 @@
private: isPrivate,
archived: false,
members,
autoJoin,
owners,
type: typeId
})
@ -111,7 +112,11 @@
if (isNew) {
await createFunnel()
} else if (funnel !== undefined && spaceType?.targetClass !== undefined) {
await client.diffUpdate<Funnel>(funnel, { name, description, members, owners, private: isPrivate }, Date.now())
await client.diffUpdate<Funnel>(
funnel,
{ name, description, members, owners, private: isPrivate, autoJoin },
Date.now()
)
if (!deepEqual(rolesAssignment, getRolesAssignment())) {
await client.updateMixin(
@ -133,6 +138,7 @@
}
function handleMembersChanged (newMembers: Ref<Account>[]): void {
membersChanged = true
// If a member was removed we need to remove it from any roles assignments as well
const newMembersSet = new Set(newMembers)
const removedMembersSet = new Set(members.filter((m) => !newMembersSet.has(m)))
@ -156,6 +162,21 @@
$: canSave =
name.trim().length > 0 && members.length > 0 && owners.length > 0 && owners.some((o) => members.includes(o))
let autoJoin = funnel?.autoJoin ?? spaceType?.autoJoin ?? false
$: setDefaultMembers(spaceType)
let membersChanged: boolean = false
function setDefaultMembers (typeType: SpaceType | undefined): void {
if (typeType === undefined) return
if (membersChanged) return
if (funnel !== undefined) return
autoJoin = typeType.autoJoin ?? false
if (typeType.members === undefined || typeType.members.length === 0) return
members = typeType.members
}
</script>
<SpaceCreateCard
@ -167,27 +188,37 @@
dispatch('close')
}}
>
<Grid column={1} rowGap={1.5}>
<div class="antiGrid-row">
<EditBox label={leadRes.string.FunnelName} bind:value={name} placeholder={leadRes.string.FunnelName} autoFocus />
</div>
<div class="antiGrid-row">
<ToggleWithLabel
label={presentation.string.MakePrivate}
description={presentation.string.MakePrivateDescription}
bind:on={isPrivate}
/>
</div>
<div class="antiGrid-row">
<div class="antiGrid-row__header">
<Label label={task.string.ProjectType} />
</div>
<Component
is={task.component.ProjectTypeSelector}
disabled={!isNew}
props={{
descriptors: [leadRes.descriptors.FunnelType],
type: typeId,
kind: 'regular',
size: 'large',
disabled: funnel !== undefined
}}
on:change={(evt) => {
typeId = evt.detail
}}
/>
</Grid>
</div>
<div class="antiGrid-row">
<div class="antiGrid-row__header">
<Label label={core.string.Owners} />
@ -201,7 +232,7 @@
/>
</div>
<div class="antiGrid-row mt-4">
<div class="antiGrid-row">
<div class="antiGrid-row__header">
<Label label={leadRes.string.Members} />
</div>
@ -214,6 +245,13 @@
size={'large'}
/>
</div>
<div class="antiGrid-row">
<div class="antiGrid-row__header withDesciption">
<Label label={core.string.AutoJoin} />
<span><Label label={core.string.AutoJoinDescr} /></span>
</div>
<Toggle bind:on={autoJoin} />
</div>
{#each roles as role}
<div class="antiGrid-row">

View File

@ -40,6 +40,8 @@
EditBox,
FocusHandler,
IconAttachment,
Label,
Toggle,
createFocusManager,
showPopup
} from '@hcengineering/ui'
@ -61,13 +63,25 @@
let issueTemplates: IssueTemplate[] = []
let fullDescription: string = ''
let members = [getCurrentAccount()._id]
let membersChanged: boolean = false
$: setDefaultMembers(typeType)
function setDefaultMembers (typeType: ProjectType | undefined): void {
if (typeType === undefined) return
if (membersChanged) return
if (typeType.members === undefined || typeType.members.length === 0) return
members = typeType.members
}
export let company: Ref<Organization> | undefined
export let preserveCompany: boolean = false
let vacancyData: Data<VacancyClass> = {
archived: false,
description: '',
members: [getCurrentAccount()._id],
members,
name: '',
number: 0,
private: false,
@ -219,7 +233,8 @@
archived: false,
number: (incResult as any).object.sequence,
company,
members: [getCurrentAccount()._id],
members,
autoJoin: typeType.autoJoin ?? false,
owners: [getCurrentAccount()._id],
type: typeId
},
@ -365,6 +380,16 @@
extraProps={{ showNavigate: false }}
/>
<AccountArrayEditor
bind:value={members}
label={contact.string.Members}
onChange={() => {
membersChanged = true
}}
kind={'regular'}
size={'large'}
/>
{#each roles as role}
<AccountArrayEditor
value={rolesAssignment?.[role._id] ?? []}

View File

@ -13,9 +13,11 @@
// limitations under the License.
-->
<script lang="ts">
import core, { type SpaceType, type SpaceTypeDescriptor } from '@hcengineering/core'
import { ButtonIcon, IconSquareExpand, ModernButton, ModernEditbox, TextArea } from '@hcengineering/ui'
import contact from '@hcengineering/contact'
import { AccountArrayEditor } from '@hcengineering/contact-resources'
import core, { Account, Ref, type SpaceType, type SpaceTypeDescriptor } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { ButtonIcon, IconSquareExpand, Label, ModernButton, ModernEditbox, TextArea, Toggle } from '@hcengineering/ui'
import settingRes from '../../../plugin'
@ -50,6 +52,33 @@
await client.update(type, { [field]: value })
}
async function changeMembers (members: Ref<Account>[]): Promise<void> {
if (disabled || type === undefined) {
return
}
const push = new Set<Ref<Account>>(members)
const pull = new Set<Ref<Account>>()
for (const member of type.members ?? []) {
if (!push.has(member)) {
pull.add(member)
} else {
push.delete(member)
}
}
if (push.size === 0 && pull.size === 0) {
return
}
const ops = client.apply(`typeMembers_${type._id}`)
for (const pushMem of push) {
ops.update(type, { $push: { members: pushMem } })
}
for (const pullMem of pull) {
ops.update(type, { $pull: { members: pullMem } })
}
await ops.commit()
}
</script>
{#if descriptor !== undefined}
@ -90,6 +119,24 @@
attributeUpdated('shortDescription', shortDescription)
}}
/>
<div class="flex-between">
<AccountArrayEditor
value={type?.members ?? []}
label={contact.string.Members}
onChange={changeMembers}
readonly={disabled}
/>
<div class="flex-row-center flex-gap-2">
<Label label={core.string.AutoJoin} />
<Toggle
on={type?.autoJoin ?? false}
on:change={(evt) => {
attributeUpdated('autoJoin', evt.detail)
}}
{disabled}
/>
</div>
</div>
<slot name="extra" />
</div>
{/if}

View File

@ -76,7 +76,9 @@
let defaultStatus: Ref<IssueStatus> | undefined = project?.defaultIssueStatus
let rolesAssignment: RolesAssignment | undefined
let changeIdentityRef: HTMLElement
let typeId: Ref<ProjectType> | undefined = project?.type
$: typeType = typeId !== undefined ? $typeStore.get(typeId) : undefined
let autoJoin = project?.autoJoin ?? typeType?.autoJoin ?? false
const dispatch = createEventDispatcher()
@ -100,6 +102,7 @@
members,
owners,
archived: false,
autoJoin,
identifier: identifier.toUpperCase(),
sequence: 0,
defaultAssignee: defaultAssignee ?? undefined,
@ -155,6 +158,9 @@
if (projectData.defaultTimeReportDay !== project?.defaultTimeReportDay) {
update.defaultTimeReportDay = projectData.defaultTimeReportDay
}
if (projectData.autoJoin !== project?.autoJoin) {
update.autoJoin = projectData.autoJoin
}
if (projectData.members.length !== project?.members.length) {
update.members = projectData.members
} else {
@ -195,8 +201,16 @@
close()
}
let typeId: Ref<ProjectType> | undefined = project?.type
$: typeType = typeId !== undefined ? $typeStore.get(typeId) : undefined
$: setDefaultMembers(typeType)
function setDefaultMembers (typeType: ProjectType | undefined): void {
if (typeType === undefined) return
if (membersChanged) return
if (project !== undefined) return
autoJoin = typeType.autoJoin ?? false
if (typeType.members === undefined || typeType.members.length === 0) return
members = typeType.members
}
function findTaskTypes (typeId: Ref<SpaceType>): TaskType[] {
return Array.from($taskTypeStore.values()).filter(
@ -254,6 +268,8 @@
dispatch('close', id)
}
let membersChanged: boolean = false
$: projectsQuery.query(tracker.class.Project, { _id: { $nin: project ? [project._id] : [] } }, (res) => {
projectsIdentifiers = new Set(res.map(({ identifier }) => identifier))
})
@ -296,6 +312,7 @@
}
function handleMembersChanged (newMembers: Ref<Account>[]): void {
membersChanged = true
// If a member was removed we need to remove it from any roles assignments as well
const newMembersSet = new Set(newMembers)
const removedMembersSet = new Set(members.filter((m) => !newMembersSet.has(m)))
@ -382,7 +399,7 @@
<Label label={tracker.string.Identifier} />
<span><Label label={tracker.string.UsedInIssueIDs} /></span>
</div>
<div bind:this={changeIdentityRef} class="padding flex-row-center relative">
<div class="padding flex-row-center relative">
<EditBox
id="project-identifier"
bind:value={identifier}
@ -483,7 +500,7 @@
<Label label={presentation.string.MakePrivate} />
<span><Label label={presentation.string.MakePrivateDescription} /></span>
</div>
<Toggle bind:on={isPrivate} disabled={!isPrivate && members.length === 0} />
<Toggle id={'project-private'} bind:on={isPrivate} disabled={!isPrivate && members.length === 0} />
</div>
<div class="antiGrid-row">
@ -499,6 +516,14 @@
/>
</div>
<div class="antiGrid-row">
<div class="antiGrid-row__header withDesciption">
<Label label={core.string.AutoJoin} />
<span><Label label={core.string.AutoJoinDescr} /></span>
</div>
<Toggle bind:on={autoJoin} />
</div>
{#each roles as role}
<div class="antiGrid-row">
<div class="antiGrid-row__header">

View File

@ -18,7 +18,7 @@
import { CheckBox, resizeObserver } from '@hcengineering/ui'
import BooleanPresenter from './BooleanPresenter.svelte'
export let value: boolean
export let value: boolean | null | undefined
export let withoutUndefined: boolean = false
const dispatch = createEventDispatcher()
@ -31,7 +31,7 @@
<BooleanPresenter value={true} />
{#if value}
<div class="check">
<CheckBox checked={value} kind={'primary'} />
<CheckBox checked kind={'primary'} />
</div>
{/if}
</div>
@ -39,9 +39,9 @@
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="menu-item" on:click={() => dispatch('close', 2)}>
<BooleanPresenter value={false} />
{#if withoutUndefined ? !value : !value}
{#if withoutUndefined ? !value : value === false}
<div class="check">
<CheckBox checked={withoutUndefined ? !value : !value} kind={'primary'} />
<CheckBox checked kind={'primary'} />
</div>
{/if}
</div>
@ -50,9 +50,9 @@
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="menu-item" on:click={() => dispatch('close', 3)}>
<BooleanPresenter value={undefined} />
{#if value === undefined}
{#if value == null}
<div class="check">
<CheckBox checked={value === undefined} kind={'primary'} />
<CheckBox checked kind={'primary'} />
</div>
{/if}
</div>

View File

@ -17,7 +17,7 @@
import { BooleanIcon, Label } from '@hcengineering/ui'
import { getBooleanLabel } from '../utils'
export let value: any
export let value: boolean | null | undefined
export let inline: boolean = false
</script>

View File

@ -591,7 +591,7 @@ function getLookup (
return current !== undefined ? [current, parent, false] : undefined
}
export function getBooleanLabel (value: boolean | undefined): IntlString {
export function getBooleanLabel (value: boolean | undefined | null): IntlString {
if (value === true) return plugin.string.LabelYes
if (value === false) return plugin.string.LabelNo
return plugin.string.LabelNA

View File

@ -29,10 +29,12 @@ import contact, {
getName
} from '@hcengineering/contact'
import core, {
Account,
Class,
Doc,
Hierarchy,
Ref,
SpaceType,
Tx,
TxCreateDoc,
TxMixin,
@ -46,6 +48,38 @@ import { getMetadata } from '@hcengineering/platform'
import serverCore, { TriggerControl } from '@hcengineering/server-core'
import { workbenchId } from '@hcengineering/workbench'
export async function OnSpaceTypeMembers (tx: Tx, control: TriggerControl): Promise<Tx[]> {
const ctx = tx as TxUpdateDoc<SpaceType>
const result: Tx[] = []
const newMember = ctx.operations.$push?.members as Ref<Account>
if (newMember !== undefined) {
const spaces = await control.findAll(core.class.Space, { type: ctx.objectId })
for (const space of spaces) {
if (space.members.includes(newMember)) continue
const pushTx = control.txFactory.createTxUpdateDoc(space._class, space.space, space._id, {
$push: {
members: newMember
}
})
result.push(pushTx)
}
}
const oldMember = ctx.operations.$pull?.members as Ref<Account>
if (ctx.operations.$pull?.members !== undefined) {
const spaces = await control.findAll(core.class.Space, { type: ctx.objectId })
for (const space of spaces) {
if (!space.members.includes(oldMember)) continue
const pullTx = control.txFactory.createTxUpdateDoc(space._class, space.space, space._id, {
$pull: {
members: oldMember
}
})
result.push(pullTx)
}
}
return result
}
export async function OnEmployeeCreate (tx: Tx, control: TriggerControl): Promise<Tx[]> {
const mixinTx = tx as TxMixin<Person, Employee>
if (mixinTx.attributes.active !== true) return []
@ -305,7 +339,8 @@ export default async () => ({
OnEmployeeCreate,
OnPersonAccountCreate,
OnContactDelete,
OnChannelUpdate
OnChannelUpdate,
OnSpaceTypeMembers
},
function: {
PersonHTMLPresenter: personHTMLPresenter,

View File

@ -33,7 +33,8 @@ export default plugin(serverContactId, {
OnContactDelete: '' as Resource<TriggerFunc>,
OnChannelUpdate: '' as Resource<TriggerFunc>,
OnEmployeeCreate: '' as Resource<TriggerFunc>,
OnPersonAccountCreate: '' as Resource<TriggerFunc>
OnPersonAccountCreate: '' as Resource<TriggerFunc>,
OnSpaceTypeMembers: '' as Resource<TriggerFunc>
},
function: {
PersonHTMLPresenter: '' as Resource<Presenter>,

View File

@ -30,7 +30,7 @@ export class DocumentsPage extends CommonPage {
readonly inputModalNewTeamspaceDescription = (): Locator =>
this.formNewTeamspace().locator('div[id="teamspace-description"] input')
readonly inputModalNewTeamspacePrivate = (): Locator => this.formNewTeamspace().locator('div.antiGrid label.toggle')
readonly inputModalNewTeamspacePrivate = (): Locator => this.formNewTeamspace().locator('[id="teamspace-private"]')
readonly buttonModalNewTeamspaceCreate = (): Locator => this.formNewTeamspace().locator('button[type="submit"]')
readonly buttonModalEditTeamspaceTitle = (): Locator =>
this.formEditTeamspace().locator('div[id="teamspace-title"] input')
@ -38,8 +38,7 @@ export class DocumentsPage extends CommonPage {
readonly buttonModalEditTeamspaceDescription = (): Locator =>
this.formEditTeamspace().locator('div[id="teamspace-description"] input')
readonly buttonModalEditTeamspacePrivate = (): Locator =>
this.formEditTeamspace().locator('div.antiGrid label.toggle')
readonly buttonModalEditTeamspacePrivate = (): Locator => this.formEditTeamspace().locator('[id="teamspace-private"]')
readonly buttonModalEditTeamspaceSave = (): Locator => this.formEditTeamspace().locator('button[type="submit"]')
readonly buttonModalEditTeamspaceClose = (): Locator => this.formEditTeamspace().locator('button#card-close')

View File

@ -16,7 +16,7 @@ export class EditProjectPage extends CommonTrackerPage {
this.page.locator('form[id="tracker:string:EditProject"] div[id="project-description"] input')
buttonChooseIcon = (): Locator => this.page.locator('div.antiGrid-row button.only-icon')
buttonMakePrivate = (): Locator => this.page.locator('div.antiGrid-row span.toggle-switch')
buttonMakePrivate = (): Locator => this.page.locator('[id="project-private"]')
buttonSaveProject = (): Locator => this.page.locator('form[id="tracker:string:EditProject"] button[type="submit"]')
buttonIcons = (): Locator => this.page.locator('form[id="view:string:ChooseIcon"] div.float-left > button')
buttonSaveIcons = (): Locator =>

View File

@ -12,7 +12,7 @@ export class NewProjectPage extends CommonTrackerPage {
inputIdentifier = (): Locator => this.page.locator('div[id="project-identifier"] input')
inputDescription = (): Locator => this.page.locator('div[id="project-description"] input')
buttonChooseIcon = (): Locator => this.page.locator('div.antiGrid-row button.only-icon')
buttonMakePrivate = (): Locator => this.page.locator('div.antiGrid-row span.toggle-switch')
buttonMakePrivate = (): Locator => this.page.locator('[id="project-private"]')
buttonCreateProject = (): Locator => this.page.locator('form[id="tracker:string:NewProject"] button[type="submit"]')
projectTypeButton = (): Locator =>
this.page.locator('div[class*="header"]', { hasText: 'Project type' }).locator('xpath=..').locator('button')