mirror of
https://github.com/hcengineering/platform.git
synced 2025-01-07 04:11:17 +03:00
UBERF-8993: Part2 (#7532)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
0db7adccd6
commit
35981be705
1
.gitignore
vendored
1
.gitignore
vendored
@ -106,3 +106,4 @@ tests/profiles
|
|||||||
dump
|
dump
|
||||||
**/logs/**
|
**/logs/**
|
||||||
dev/tool/history.json
|
dev/tool/history.json
|
||||||
|
.aider*
|
||||||
|
@ -28,22 +28,23 @@ function $push (document: Doc, keyval: Record<string, PropertyType>): void {
|
|||||||
if (doc[key] === undefined) {
|
if (doc[key] === undefined) {
|
||||||
doc[key] = []
|
doc[key] = []
|
||||||
}
|
}
|
||||||
const val = keyval[key]
|
const kvk = keyval[key]
|
||||||
if (typeof val === 'object') {
|
if (typeof kvk === 'object' && kvk != null) {
|
||||||
const arr = doc[key] as Array<any>
|
const arr = doc[key] as Array<any>
|
||||||
const desc = val as Position<PropertyType>
|
const desc = kvk as Position<PropertyType>
|
||||||
if ('$each' in desc) {
|
if ('$each' in desc) {
|
||||||
if (arr != null) {
|
if (arr != null && Array.isArray(arr)) {
|
||||||
arr.splice(desc.$position ?? 0, 0, ...desc.$each)
|
arr.splice(desc.$position ?? 0, 0, ...desc.$each)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
arr.push(val)
|
arr.push(kvk)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (doc[key] == null) {
|
if (doc[key] === null || doc[key] === undefined) {
|
||||||
doc[key] = []
|
doc[key] = [kvk]
|
||||||
|
} else {
|
||||||
|
doc[key].push(kvk)
|
||||||
}
|
}
|
||||||
doc[key].push(val)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,15 +56,16 @@ function $pull (document: Doc, keyval: Record<string, PropertyType>): void {
|
|||||||
doc[key] = []
|
doc[key] = []
|
||||||
}
|
}
|
||||||
const arr = doc[key] as Array<any>
|
const arr = doc[key] as Array<any>
|
||||||
if (typeof keyval[key] === 'object' && keyval[key] !== null) {
|
const kvk = keyval[key]
|
||||||
const { $in } = keyval[key] as PullArray<PropertyType>
|
if (typeof kvk === 'object' && kvk !== null) {
|
||||||
|
const { $in } = kvk as PullArray<PropertyType>
|
||||||
|
|
||||||
doc[key] = (arr ?? []).filter((val) => {
|
doc[key] = (arr ?? []).filter((val) => {
|
||||||
if ($in !== undefined) {
|
if ($in !== undefined) {
|
||||||
return !$in.includes(val)
|
return !$in.includes(val)
|
||||||
} else {
|
} else {
|
||||||
// We need to match all fields
|
// We need to match all fields
|
||||||
for (const [kk, kv] of Object.entries(keyval[key])) {
|
for (const [kk, kv] of Object.entries(kvk)) {
|
||||||
if (val[kk] !== kv) {
|
if (val[kk] !== kv) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -72,7 +74,7 @@ function $pull (document: Doc, keyval: Record<string, PropertyType>): void {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
doc[key] = (arr ?? []).filter((val) => val !== keyval[key])
|
doc[key] = (arr ?? []).filter((val) => val !== kvk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,13 +93,14 @@
|
|||||||
dispatch('update', selectedObjects)
|
dispatch('update', selectedObjects)
|
||||||
}
|
}
|
||||||
|
|
||||||
const client = getClient()
|
|
||||||
|
|
||||||
let selection = 0
|
let selection = 0
|
||||||
let list: ListView
|
let list: ListView
|
||||||
|
|
||||||
async function handleSelection (evt: Event | undefined, objects: Doc[], selection: number): Promise<void> {
|
async function handleSelection (evt: Event | undefined, objects: Doc[], selection: number): Promise<void> {
|
||||||
const item = objects[selection]
|
const item = objects[selection]
|
||||||
|
if (item === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (!multiSelect) {
|
if (!multiSelect) {
|
||||||
if (allowDeselect) {
|
if (allowDeselect) {
|
||||||
@ -140,7 +141,7 @@
|
|||||||
showPopup(c.component, c.props ?? {}, 'top', async (res) => {
|
showPopup(c.component, c.props ?? {}, 'top', async (res) => {
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
// We expect reference to new object.
|
// We expect reference to new object.
|
||||||
const newPerson = await client.findOne(_class, { _id: res })
|
const newPerson = await getClient().findOne(_class, { _id: res })
|
||||||
if (newPerson !== undefined) {
|
if (newPerson !== undefined) {
|
||||||
search = c.update?.(newPerson) ?? ''
|
search = c.update?.(newPerson) ?? ''
|
||||||
dispatch('created', newPerson)
|
dispatch('created', newPerson)
|
||||||
@ -163,7 +164,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function findObjectPresenter (_class: Ref<Class<Doc>>): void {
|
function findObjectPresenter (_class: Ref<Class<Doc>>): void {
|
||||||
const presenterMixin = client.getHierarchy().classHierarchyMixin(_class, view.mixin.ObjectPresenter)
|
const presenterMixin = getClient().getHierarchy().classHierarchyMixin(_class, view.mixin.ObjectPresenter)
|
||||||
if (presenterMixin?.presenter !== undefined) {
|
if (presenterMixin?.presenter !== undefined) {
|
||||||
getResource(presenterMixin.presenter)
|
getResource(presenterMixin.presenter)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
|
@ -46,7 +46,6 @@
|
|||||||
|
|
||||||
let categories: ObjectSearchCategory[] = []
|
let categories: ObjectSearchCategory[] = []
|
||||||
let categoryStatus: Record<Ref<ObjectSearchCategory>, number> = {}
|
let categoryStatus: Record<Ref<ObjectSearchCategory>, number> = {}
|
||||||
const client = getClient()
|
|
||||||
|
|
||||||
let category: ObjectSearchCategory | undefined
|
let category: ObjectSearchCategory | undefined
|
||||||
const categoryQuery: DocumentQuery<ObjectSearchCategory> = {
|
const categoryQuery: DocumentQuery<ObjectSearchCategory> = {
|
||||||
@ -56,10 +55,12 @@
|
|||||||
categoryQuery._id = { $in: allowCategory }
|
categoryQuery._id = { $in: allowCategory }
|
||||||
}
|
}
|
||||||
|
|
||||||
client.findAll(presentation.class.ObjectSearchCategory, categoryQuery).then((r) => {
|
void getClient()
|
||||||
categories = r.filter((it) => hasResource(it.query))
|
.findAll(presentation.class.ObjectSearchCategory, categoryQuery)
|
||||||
category = categories[0]
|
.then((r) => {
|
||||||
})
|
categories = r.filter((it) => hasResource(it.query))
|
||||||
|
category = categories[0]
|
||||||
|
})
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
@ -96,7 +97,7 @@
|
|||||||
key.preventDefault()
|
key.preventDefault()
|
||||||
key.stopPropagation()
|
key.stopPropagation()
|
||||||
const item = items[selection]
|
const item = items[selection]
|
||||||
if (item) {
|
if (item != null) {
|
||||||
dispatchItem(item)
|
dispatchItem(item)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
@ -106,7 +107,7 @@
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
export function done () {}
|
export function done (): void {}
|
||||||
|
|
||||||
const updateItems = reduceCalls(async function updateItems (
|
const updateItems = reduceCalls(async function updateItems (
|
||||||
cat: ObjectSearchCategory | undefined,
|
cat: ObjectSearchCategory | undefined,
|
||||||
@ -120,6 +121,7 @@
|
|||||||
const newCategoryStatus: Record<Ref<ObjectSearchCategory>, number> = {}
|
const newCategoryStatus: Record<Ref<ObjectSearchCategory>, number> = {}
|
||||||
const f = await getResource(cat.query)
|
const f = await getResource(cat.query)
|
||||||
|
|
||||||
|
const client = getClient()
|
||||||
const result = await f(client, query, { in: relatedDocuments, nin: ignore })
|
const result = await f(client, query, { in: relatedDocuments, nin: ignore })
|
||||||
// We need to sure, results we return is for proper category.
|
// We need to sure, results we return is for proper category.
|
||||||
if (cat._id === category?._id) {
|
if (cat._id === category?._id) {
|
||||||
|
@ -53,7 +53,7 @@ import { getRawCurrentLocation, workspaceId, type AnyComponent, type AnySvelteCo
|
|||||||
import view, { type AttributeCategory, type AttributeEditor } from '@hcengineering/view'
|
import view, { type AttributeCategory, type AttributeEditor } from '@hcengineering/view'
|
||||||
import { deepEqual } from 'fast-equals'
|
import { deepEqual } from 'fast-equals'
|
||||||
import { onDestroy } from 'svelte'
|
import { onDestroy } from 'svelte'
|
||||||
import { get, writable, type Writable } from 'svelte/store'
|
import { get, writable } from 'svelte/store'
|
||||||
|
|
||||||
import { type KeyedAttribute } from '..'
|
import { type KeyedAttribute } from '..'
|
||||||
import { OptimizeQueryMiddleware, PresentationPipelineImpl, type PresentationPipeline } from './pipeline'
|
import { OptimizeQueryMiddleware, PresentationPipelineImpl, type PresentationPipeline } from './pipeline'
|
||||||
@ -63,7 +63,7 @@ export { reduceCalls } from '@hcengineering/core'
|
|||||||
|
|
||||||
let liveQuery: LQ
|
let liveQuery: LQ
|
||||||
let rawLiveQuery: LQ
|
let rawLiveQuery: LQ
|
||||||
let client: TxOperations & Client & OptimisticTxes
|
let client: TxOperations & Client
|
||||||
let pipeline: PresentationPipeline
|
let pipeline: PresentationPipeline
|
||||||
|
|
||||||
const txListeners: Array<(...tx: Tx[]) => void> = []
|
const txListeners: Array<(...tx: Tx[]) => void> = []
|
||||||
@ -89,13 +89,11 @@ export function removeTxListener (l: (tx: Tx) => void): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OptimisticTxes {
|
|
||||||
pendingCreatedDocs: Writable<Record<Ref<Doc>, boolean>>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const uiContext = new MeasureMetricsContext('client-ui', {})
|
export const uiContext = new MeasureMetricsContext('client-ui', {})
|
||||||
|
|
||||||
class UIClient extends TxOperations implements Client, OptimisticTxes {
|
export const pendingCreatedDocs = writable<Record<Ref<Doc>, boolean>>({})
|
||||||
|
|
||||||
|
class UIClient extends TxOperations implements Client {
|
||||||
hook = getMetadata(plugin.metadata.ClientHook)
|
hook = getMetadata(plugin.metadata.ClientHook)
|
||||||
constructor (
|
constructor (
|
||||||
client: Client,
|
client: Client,
|
||||||
@ -105,14 +103,9 @@ class UIClient extends TxOperations implements Client, OptimisticTxes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected pendingTxes = new Set<Ref<Tx>>()
|
protected pendingTxes = new Set<Ref<Tx>>()
|
||||||
protected _pendingCreatedDocs = writable<Record<Ref<Doc>, boolean>>({})
|
|
||||||
|
|
||||||
get pendingCreatedDocs (): typeof this._pendingCreatedDocs {
|
|
||||||
return this._pendingCreatedDocs
|
|
||||||
}
|
|
||||||
|
|
||||||
async doNotify (...tx: Tx[]): Promise<void> {
|
async doNotify (...tx: Tx[]): Promise<void> {
|
||||||
const pending = get(this._pendingCreatedDocs)
|
const pending = get(pendingCreatedDocs)
|
||||||
let pendingUpdated = false
|
let pendingUpdated = false
|
||||||
tx.forEach((t) => {
|
tx.forEach((t) => {
|
||||||
if (this.pendingTxes.has(t._id)) {
|
if (this.pendingTxes.has(t._id)) {
|
||||||
@ -129,7 +122,7 @@ class UIClient extends TxOperations implements Client, OptimisticTxes {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (pendingUpdated) {
|
if (pendingUpdated) {
|
||||||
this._pendingCreatedDocs.set(pending)
|
pendingCreatedDocs.set(pending)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We still want to notify about all transactions because there might be queries created after
|
// We still want to notify about all transactions because there might be queries created after
|
||||||
@ -214,9 +207,9 @@ class UIClient extends TxOperations implements Client, OptimisticTxes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (innerTx._class === core.class.TxCreateDoc) {
|
if (innerTx._class === core.class.TxCreateDoc) {
|
||||||
const pending = get(this._pendingCreatedDocs)
|
const pending = get(pendingCreatedDocs)
|
||||||
pending[innerTx.objectId] = true
|
pending[innerTx.objectId] = true
|
||||||
this._pendingCreatedDocs.set(pending)
|
pendingCreatedDocs.set(pending)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pendingTxes.add(tx._id)
|
this.pendingTxes.add(tx._id)
|
||||||
@ -231,11 +224,33 @@ class UIClient extends TxOperations implements Client, OptimisticTxes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hierarchyProxy = new Proxy(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
get (target, p, receiver) {
|
||||||
|
const h = client.getHierarchy()
|
||||||
|
return Reflect.get(h, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) as TxOperations & Client
|
||||||
|
|
||||||
|
// We need a proxy to handle all the calls to the proper client.
|
||||||
|
const clientProxy = new Proxy(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
get (target, p, receiver) {
|
||||||
|
if (p === 'getHierarchy') {
|
||||||
|
return () => hierarchyProxy
|
||||||
|
}
|
||||||
|
return Reflect.get(client, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) as TxOperations & Client
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export function getClient (): TxOperations & Client & OptimisticTxes {
|
export function getClient (): TxOperations & Client {
|
||||||
return client
|
return clientProxy
|
||||||
}
|
}
|
||||||
|
|
||||||
let txQueue: Tx[] = []
|
let txQueue: Tx[] = []
|
||||||
@ -252,6 +267,7 @@ export function addRefreshListener (r: RefreshListener): void {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export async function setClient (_client: Client): Promise<void> {
|
export async function setClient (_client: Client): Promise<void> {
|
||||||
|
pendingCreatedDocs.set({})
|
||||||
if (liveQuery !== undefined) {
|
if (liveQuery !== undefined) {
|
||||||
await liveQuery.close()
|
await liveQuery.close()
|
||||||
}
|
}
|
||||||
@ -700,7 +716,7 @@ export function isSpace (space: Doc): space is Space {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isSpaceClass (_class: Ref<Class<Doc>>): boolean {
|
export function isSpaceClass (_class: Ref<Class<Doc>>): boolean {
|
||||||
return getClient().getHierarchy().isDerived(_class, core.class.Space)
|
return client.getHierarchy().isDerived(_class, core.class.Space)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setPresentationCookie (token: string, workspaceId: string): void {
|
export function setPresentationCookie (token: string, workspaceId: string): void {
|
||||||
|
@ -59,6 +59,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeydown (e: KeyboardEvent): void {
|
function onKeydown (e: KeyboardEvent): void {
|
||||||
|
if (e.key === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const key = e.key.toLowerCase()
|
const key = e.key.toLowerCase()
|
||||||
const target = e.target as HTMLInputElement
|
const target = e.target as HTMLInputElement
|
||||||
if (key !== 'backspace' && key !== 'delete') return
|
if (key !== 'backspace' && key !== 'delete') return
|
||||||
|
@ -63,7 +63,7 @@ class FocusManagerImpl implements FocusManager {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.current = this.elements.findIndex((it) => it.id === idx) ?? 0
|
this.current = this.elements.findIndex((it) => it.id === idx) ?? 0
|
||||||
this.elements[Math.abs(this.current) % this.elements.length].focus()
|
this.elements[Math.abs(this.current) % this.elements.length]?.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
setFocusPos (order: number): void {
|
setFocusPos (order: number): void {
|
||||||
@ -73,7 +73,7 @@ class FocusManagerImpl implements FocusManager {
|
|||||||
const idx = this.elements.findIndex((it) => it.order === order)
|
const idx = this.elements.findIndex((it) => it.order === order)
|
||||||
if (idx !== undefined) {
|
if (idx !== undefined) {
|
||||||
this.current = idx
|
this.current = idx
|
||||||
this.elements[Math.abs(this.current) % this.elements.length].focus()
|
this.elements[Math.abs(this.current) % this.elements.length]?.focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,16 +149,17 @@ export function closePopup (category?: string): void {
|
|||||||
} else {
|
} else {
|
||||||
for (let i = popups.length - 1; i >= 0; i--) {
|
for (let i = popups.length - 1; i >= 0; i--) {
|
||||||
if (popups[i].type !== 'popup') continue
|
if (popups[i].type !== 'popup') continue
|
||||||
if ((popups[i] as CompAndProps).options.fixed !== true) {
|
const popi = popups[i] as CompAndProps
|
||||||
const isClosing = (popups[i] as CompAndProps).closing ?? false
|
if (popi.options.fixed !== true) {
|
||||||
|
const isClosing = popi.closing ?? false
|
||||||
if (popups[i].type === 'popup') {
|
if (popups[i].type === 'popup') {
|
||||||
;(popups[i] as CompAndProps).closing = true
|
popi.closing = true
|
||||||
}
|
}
|
||||||
if (!isClosing) {
|
if (!isClosing) {
|
||||||
// To prevent possible recursion, we need to check if we call some code from popup close, to do close.
|
// To prevent possible recursion, we need to check if we call some code from popup close, to do close.
|
||||||
;(popups[i] as CompAndProps).onClose?.(undefined)
|
popi.onClose?.(undefined)
|
||||||
}
|
}
|
||||||
;(popups[i] as CompAndProps).closing = false
|
popi.closing = false
|
||||||
popups.splice(i, 1)
|
popups.splice(i, 1)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -214,8 +214,12 @@ export function replaceURLs (text: string): string {
|
|||||||
* @returns {string} string with parsed URL
|
* @returns {string} string with parsed URL
|
||||||
*/
|
*/
|
||||||
export function parseURL (text: string): string {
|
export function parseURL (text: string): string {
|
||||||
const matches = autolinker.parse(text, { urls: true })
|
try {
|
||||||
return matches.length > 0 ? matches[0].getAnchorHref() : ''
|
const matches = autolinker.parse(text ?? '', { urls: true })
|
||||||
|
return matches.length > 0 ? matches[0].getAnchorHref() : ''
|
||||||
|
} catch (err: any) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
import contact, { Person, PersonAccount } from '@hcengineering/contact'
|
import contact, { Person, PersonAccount } from '@hcengineering/contact'
|
||||||
import { personAccountByIdStore, personByIdStore } from '@hcengineering/contact-resources'
|
import { personAccountByIdStore, personByIdStore } from '@hcengineering/contact-resources'
|
||||||
import { Class, Doc, getCurrentAccount, Markup, Ref, Space, WithLookup } from '@hcengineering/core'
|
import { Class, Doc, getCurrentAccount, Markup, Ref, Space, WithLookup } from '@hcengineering/core'
|
||||||
import { getClient, MessageViewer } from '@hcengineering/presentation'
|
import { getClient, MessageViewer, pendingCreatedDocs } from '@hcengineering/presentation'
|
||||||
import { AttachmentDocList, AttachmentImageSize } from '@hcengineering/attachment-resources'
|
import { AttachmentDocList, AttachmentImageSize } from '@hcengineering/attachment-resources'
|
||||||
import { getDocLinkTitle } from '@hcengineering/view-resources'
|
import { getDocLinkTitle } from '@hcengineering/view-resources'
|
||||||
import { Action, Button, IconEdit, ShowMore } from '@hcengineering/ui'
|
import { Action, Button, IconEdit, ShowMore } from '@hcengineering/ui'
|
||||||
@ -58,7 +58,6 @@
|
|||||||
export let onReply: ((message: ActivityMessage) => void) | undefined = undefined
|
export let onReply: ((message: ActivityMessage) => void) | undefined = undefined
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const { pendingCreatedDocs } = client
|
|
||||||
const hierarchy = client.getHierarchy()
|
const hierarchy = client.getHierarchy()
|
||||||
const STALE_TIMEOUT_MS = 5000
|
const STALE_TIMEOUT_MS = 5000
|
||||||
const currentAccount = getCurrentAccount()
|
const currentAccount = getCurrentAccount()
|
||||||
|
@ -82,9 +82,11 @@
|
|||||||
included = []
|
included = []
|
||||||
}
|
}
|
||||||
|
|
||||||
$: employees = Array.from(
|
$: employees = Array.isArray(value)
|
||||||
(value ?? []).map((it) => $personAccountByIdStore.get(it as Ref<PersonAccount>)?.person)
|
? (Array.from((value ?? []).map((it) => $personAccountByIdStore.get(it as Ref<PersonAccount>)?.person)).filter(
|
||||||
).filter((it) => it !== undefined) as Ref<Employee>[]
|
(it) => it !== undefined
|
||||||
|
) as Ref<Employee>[])
|
||||||
|
: []
|
||||||
|
|
||||||
$: docQuery =
|
$: docQuery =
|
||||||
excluded.length === 0 && included.length === 0
|
excluded.length === 0 && included.length === 0
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact, { Contact, Employee, Person } from '@hcengineering/contact'
|
import contact, { Person } from '@hcengineering/contact'
|
||||||
import type { Class, Doc, DocumentQuery, Ref } from '@hcengineering/core'
|
import type { Class, Doc, DocumentQuery, Ref } from '@hcengineering/core'
|
||||||
import type { IntlString } from '@hcengineering/platform'
|
import type { IntlString } from '@hcengineering/platform'
|
||||||
import { ObjectCreate, getClient } from '@hcengineering/presentation'
|
import { ObjectCreate, getClient } from '@hcengineering/presentation'
|
||||||
@ -43,8 +43,8 @@
|
|||||||
|
|
||||||
export let sort: ((a: Person, b: Person) => number) | undefined = undefined
|
export let sort: ((a: Person, b: Person) => number) | undefined = undefined
|
||||||
|
|
||||||
function filter (items: Ref<Person>[]): Ref<Person>[] {
|
function filter (items: Ref<Person>[] | undefined): Ref<Person>[] {
|
||||||
return items.filter((it, idx, arr) => arr.indexOf(it) === idx)
|
return (items ?? []).filter((it, idx, arr) => arr.indexOf(it) === idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
let persons: Person[] = filter(items)
|
let persons: Person[] = filter(items)
|
||||||
@ -55,11 +55,10 @@
|
|||||||
.filter((p) => p !== undefined) as Person[]
|
.filter((p) => p !== undefined) as Person[]
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const client = getClient()
|
|
||||||
|
|
||||||
async function addPerson (evt: Event): Promise<void> {
|
async function addPerson (evt: Event): Promise<void> {
|
||||||
const accounts = new Set(
|
const accounts = new Set(
|
||||||
client
|
getClient()
|
||||||
.getModel()
|
.getModel()
|
||||||
.findAllSync(contact.class.PersonAccount, {})
|
.findAllSync(contact.class.PersonAccount, {})
|
||||||
.map((p) => p.person)
|
.map((p) => p.person)
|
||||||
@ -72,7 +71,7 @@
|
|||||||
allowDeselect: false,
|
allowDeselect: false,
|
||||||
selectedUsers: filter(items),
|
selectedUsers: filter(items),
|
||||||
filter: (it: Doc) => {
|
filter: (it: Doc) => {
|
||||||
const h = client.getHierarchy()
|
const h = getClient().getHierarchy()
|
||||||
if (h.hasMixin(it, contact.mixin.Employee)) {
|
if (h.hasMixin(it, contact.mixin.Employee)) {
|
||||||
const isActive = h.as(it, contact.mixin.Employee).active
|
const isActive = h.as(it, contact.mixin.Employee).active
|
||||||
const isSelected = items.some((selectedItem) => selectedItem === it._id)
|
const isSelected = items.some((selectedItem) => selectedItem === it._id)
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
if (space !== value.space) {
|
if (space !== value.space) {
|
||||||
const children = await findChildren(value)
|
const children = await findChildren(value)
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
await client.updateDoc(document.class.Document, value.space, child, {
|
await ops.updateDoc(document.class.Document, value.space, child, {
|
||||||
space
|
space
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1058,7 +1058,11 @@ export async function restorePassword (token: string, password: string): Promise
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleStatusError (message: string, err: Status): Promise<void> {
|
async function handleStatusError (message: string, err: Status): Promise<void> {
|
||||||
if (err.code === platform.status.InvalidPassword || err.code === platform.status.AccountNotFound) {
|
if (
|
||||||
|
err.code === platform.status.InvalidPassword ||
|
||||||
|
err.code === platform.status.AccountNotFound ||
|
||||||
|
err.code === platform.status.InvalidOtp
|
||||||
|
) {
|
||||||
// No need to send to analytics
|
// No need to send to analytics
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -340,7 +340,7 @@
|
|||||||
console.log(`Error exiting fullscreen mode: ${err.message} (${err.name})`)
|
console.log(`Error exiting fullscreen mode: ${err.message} (${err.name})`)
|
||||||
$isFullScreen = false
|
$isFullScreen = false
|
||||||
})
|
})
|
||||||
} else if (!document.fullscreenElement && needFullScreen) {
|
} else if (!document.fullscreenElement && needFullScreen && roomEl != null) {
|
||||||
roomEl
|
roomEl
|
||||||
.requestFullscreen()
|
.requestFullscreen()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -355,7 +355,7 @@
|
|||||||
|
|
||||||
function onFullScreen (): void {
|
function onFullScreen (): void {
|
||||||
const needFullScreen = !$isFullScreen
|
const needFullScreen = !$isFullScreen
|
||||||
if (!document.fullscreenElement && needFullScreen) {
|
if (!document.fullscreenElement && needFullScreen && roomEl != null) {
|
||||||
roomEl
|
roomEl
|
||||||
.requestFullscreen()
|
.requestFullscreen()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
@ -15,7 +15,15 @@
|
|||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { AttachmentStyleBoxCollabEditor } from '@hcengineering/attachment-resources'
|
import { AttachmentStyleBoxCollabEditor } from '@hcengineering/attachment-resources'
|
||||||
import core, { ClassifierKind, type CollaborativeDoc, Data, Doc, Mixin, Ref } from '@hcengineering/core'
|
import core, {
|
||||||
|
ClassifierKind,
|
||||||
|
type CollaborativeDoc,
|
||||||
|
Data,
|
||||||
|
Doc,
|
||||||
|
type MarkupBlobRef,
|
||||||
|
Mixin,
|
||||||
|
Ref
|
||||||
|
} from '@hcengineering/core'
|
||||||
import notification from '@hcengineering/notification'
|
import notification from '@hcengineering/notification'
|
||||||
import { Panel } from '@hcengineering/panel'
|
import { Panel } from '@hcengineering/panel'
|
||||||
import { getResource } from '@hcengineering/platform'
|
import { getResource } from '@hcengineering/platform'
|
||||||
@ -37,7 +45,7 @@
|
|||||||
let object: Required<Vacancy>
|
let object: Required<Vacancy>
|
||||||
let rawName: string = ''
|
let rawName: string = ''
|
||||||
let rawDesc: string = ''
|
let rawDesc: string = ''
|
||||||
let rawFullDesc: CollaborativeDoc
|
let rawFullDesc: MarkupBlobRef | null = null
|
||||||
let lastId: Ref<Vacancy> | undefined = undefined
|
let lastId: Ref<Vacancy> | undefined = undefined
|
||||||
|
|
||||||
let showAllMixins = false
|
let showAllMixins = false
|
||||||
|
@ -35,10 +35,8 @@
|
|||||||
export let groupByKey: string
|
export let groupByKey: string
|
||||||
export let config: (string | BuildModelKey)[]
|
export let config: (string | BuildModelKey)[]
|
||||||
|
|
||||||
const client = getClient()
|
const assigneeAttribute = getClient().getHierarchy().getAttribute(recruit.class.Applicant, 'assignee')
|
||||||
const hierarchy = client.getHierarchy()
|
const isTitleHidden = getClient().getHierarchy().getAttribute(recruit.mixin.Candidate, 'title').hidden
|
||||||
const assigneeAttribute = hierarchy.getAttribute(recruit.class.Applicant, 'assignee')
|
|
||||||
const isTitleHidden = client.getHierarchy().getAttribute(recruit.mixin.Candidate, 'title').hidden
|
|
||||||
|
|
||||||
$: channels = (object.$lookup?.attachedTo as WithLookup<Candidate>)?.$lookup?.channels
|
$: channels = (object.$lookup?.attachedTo as WithLookup<Candidate>)?.$lookup?.channels
|
||||||
|
|
||||||
@ -70,7 +68,7 @@
|
|||||||
<Avatar person={object.$lookup?.attachedTo} size={'medium'} name={object.$lookup?.attachedTo?.name} />
|
<Avatar person={object.$lookup?.attachedTo} size={'medium'} name={object.$lookup?.attachedTo?.name} />
|
||||||
<div class="flex-grow flex-col min-w-0 ml-2">
|
<div class="flex-grow flex-col min-w-0 ml-2">
|
||||||
<div class="fs-title over-underline lines-limit-2">
|
<div class="fs-title over-underline lines-limit-2">
|
||||||
{object.$lookup?.attachedTo ? getName(client.getHierarchy(), object.$lookup.attachedTo) : ''}
|
{object.$lookup?.attachedTo ? getName(getClient().getHierarchy(), object.$lookup.attachedTo) : ''}
|
||||||
</div>
|
</div>
|
||||||
{#if !isTitleHidden && enabledConfig(config, 'title')}
|
{#if !isTitleHidden && enabledConfig(config, 'title')}
|
||||||
<div class="text-sm lines-limit-2">{object.$lookup?.attachedTo?.title ?? ''}</div>
|
<div class="text-sm lines-limit-2">{object.$lookup?.attachedTo?.title ?? ''}</div>
|
||||||
@ -107,7 +105,7 @@
|
|||||||
shrink={1}
|
shrink={1}
|
||||||
value={object.status}
|
value={object.status}
|
||||||
onChange={(status) => {
|
onChange={(status) => {
|
||||||
client.update(object, { status })
|
getClient().update(object, { status })
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
@ -120,7 +118,7 @@
|
|||||||
shouldRender={object.dueDate !== null && object.dueDate !== undefined}
|
shouldRender={object.dueDate !== null && object.dueDate !== undefined}
|
||||||
shouldIgnoreOverdue={isDone}
|
shouldIgnoreOverdue={isDone}
|
||||||
onChange={async (e) => {
|
onChange={async (e) => {
|
||||||
await client.update(object, { dueDate: e })
|
await getClient().update(object, { dueDate: e })
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -122,7 +122,7 @@
|
|||||||
}
|
}
|
||||||
const handleSelect = async (event: CustomEvent): Promise<void> => {
|
const handleSelect = async (event: CustomEvent): Promise<void> => {
|
||||||
selected = event.detail as AnyAttribute
|
selected = event.detail as AnyAttribute
|
||||||
if (selected != null) {
|
if (selected?._id != null) {
|
||||||
const exist = (await client.findOne(selected.attributeOf, { [selected.name]: { $exists: true } })) !== undefined
|
const exist = (await client.findOne(selected.attributeOf, { [selected.name]: { $exists: true } })) !== undefined
|
||||||
$settingsStore = { id: selected._id, component: EditAttribute, props: { attribute: selected, exist, disabled } }
|
$settingsStore = { id: selected._id, component: EditAttribute, props: { attribute: selected, exist, disabled } }
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,9 @@
|
|||||||
specials = newSpecials
|
specials = newSpecials
|
||||||
}
|
}
|
||||||
|
|
||||||
$: updateSpecials(model, space)
|
$: if (model != null) {
|
||||||
|
void updateSpecials(model, space)
|
||||||
|
}
|
||||||
$: visible =
|
$: visible =
|
||||||
(!deselect && currentSpace !== undefined && currentSpecial !== undefined && space._id === currentSpace) ||
|
(!deselect && currentSpace !== undefined && currentSpecial !== undefined && space._id === currentSpace) ||
|
||||||
forciblyСollapsed
|
forciblyСollapsed
|
||||||
@ -64,7 +66,7 @@
|
|||||||
{#if specials}
|
{#if specials}
|
||||||
<TreeNode
|
<TreeNode
|
||||||
_id={space?._id}
|
_id={space?._id}
|
||||||
icon={space?.icon === view.ids.IconWithEmoji ? IconWithEmoji : space?.icon ?? model.icon}
|
icon={space?.icon === view.ids.IconWithEmoji ? IconWithEmoji : space?.icon ?? model?.icon}
|
||||||
iconProps={space?.icon === view.ids.IconWithEmoji
|
iconProps={space?.icon === view.ids.IconWithEmoji
|
||||||
? { icon: space.color }
|
? { icon: space.color }
|
||||||
: {
|
: {
|
||||||
|
@ -108,22 +108,22 @@ export const completionConfig: Partial<CompletionOptions> = {
|
|||||||
props: {
|
props: {
|
||||||
...props,
|
...props,
|
||||||
close: () => {
|
close: () => {
|
||||||
component.destroy()
|
component?.destroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onUpdate (props: SuggestionProps) {
|
onUpdate (props: SuggestionProps) {
|
||||||
component.updateProps(props)
|
component?.updateProps(props)
|
||||||
},
|
},
|
||||||
onKeyDown (props: SuggestionKeyDownProps) {
|
onKeyDown (props: SuggestionKeyDownProps) {
|
||||||
if (props.event.key === 'Escape') {
|
if (props.event.key === 'Escape') {
|
||||||
props.event.stopPropagation()
|
props.event.stopPropagation()
|
||||||
}
|
}
|
||||||
return component.onKeyDown(props)
|
return component?.onKeyDown(props)
|
||||||
},
|
},
|
||||||
onExit () {
|
onExit () {
|
||||||
component.destroy()
|
component?.destroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,22 +181,22 @@ export function inlineCommandsConfig (
|
|||||||
props: {
|
props: {
|
||||||
...props,
|
...props,
|
||||||
close: () => {
|
close: () => {
|
||||||
component.destroy()
|
component?.destroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
onUpdate (props: SuggestionProps) {
|
onUpdate (props: SuggestionProps) {
|
||||||
component.updateProps(props)
|
component?.updateProps(props)
|
||||||
},
|
},
|
||||||
onKeyDown (props: SuggestionKeyDownProps) {
|
onKeyDown (props: SuggestionKeyDownProps) {
|
||||||
if (props.event.key === 'Escape') {
|
if (props.event.key === 'Escape') {
|
||||||
props.event.stopPropagation()
|
props.event.stopPropagation()
|
||||||
}
|
}
|
||||||
return component.onKeyDown(props)
|
return component?.onKeyDown(props)
|
||||||
},
|
},
|
||||||
onExit () {
|
onExit () {
|
||||||
component.destroy()
|
component?.destroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,9 @@
|
|||||||
specials = newSpecials
|
specials = newSpecials
|
||||||
}
|
}
|
||||||
|
|
||||||
$: updateSpecials(model, space)
|
$: if (model != null) {
|
||||||
|
void updateSpecials(model, space)
|
||||||
|
}
|
||||||
$: visible =
|
$: visible =
|
||||||
(!deselect && currentSpace !== undefined && currentSpecial !== undefined && space._id === currentSpace) ||
|
(!deselect && currentSpace !== undefined && currentSpecial !== undefined && space._id === currentSpace) ||
|
||||||
forciblyСollapsed
|
forciblyСollapsed
|
||||||
|
@ -30,8 +30,6 @@
|
|||||||
|
|
||||||
let resActions = actions
|
let resActions = actions
|
||||||
|
|
||||||
const client = getClient()
|
|
||||||
|
|
||||||
let loaded = false
|
let loaded = false
|
||||||
|
|
||||||
const order: Record<ActionGroup, number> = {
|
const order: Record<ActionGroup, number> = {
|
||||||
@ -44,7 +42,7 @@
|
|||||||
remove: 7
|
remove: 7
|
||||||
}
|
}
|
||||||
|
|
||||||
void getActions(client, object, baseMenuClass, mode).then((result) => {
|
void getActions(getClient(), object, baseMenuClass, mode).then((result) => {
|
||||||
const filtered = result.filter((a) => {
|
const filtered = result.filter((a) => {
|
||||||
if (excludedActions.includes(a._id)) {
|
if (excludedActions.includes(a._id)) {
|
||||||
return false
|
return false
|
||||||
@ -63,7 +61,7 @@
|
|||||||
inline: a.inline,
|
inline: a.inline,
|
||||||
group: a.context.group ?? 'other',
|
group: a.context.group ?? 'other',
|
||||||
action: async (_: any, evt: Event) => {
|
action: async (_: any, evt: Event) => {
|
||||||
if (overrides.has(a._id)) {
|
if (overrides?.has(a._id)) {
|
||||||
overrides.get(a._id)?.(object, evt)
|
overrides.get(a._id)?.(object, evt)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -178,11 +178,11 @@
|
|||||||
|
|
||||||
const keyDown = (event: KeyboardEvent, index: number) => {
|
const keyDown = (event: KeyboardEvent, index: number) => {
|
||||||
if (event.key === 'ArrowDown') {
|
if (event.key === 'ArrowDown') {
|
||||||
actionElements[(index + 1) % actionElements.length].focus()
|
actionElements[(index + 1) % actionElements.length]?.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === 'ArrowUp') {
|
if (event.key === 'ArrowUp') {
|
||||||
actionElements[(actionElements.length + index - 1) % actionElements.length].focus()
|
actionElements[(actionElements.length + index - 1) % actionElements.length]?.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.key === 'ArrowLeft') {
|
if (event.key === 'ArrowLeft') {
|
||||||
|
@ -604,6 +604,7 @@ export class DocumentContentPage extends DocumentCommonPage {
|
|||||||
await expect(this.editDocumentSpace).not.toBeVisible()
|
await expect(this.editDocumentSpace).not.toBeVisible()
|
||||||
await expect(this.createNewTemplateFromSpace).not.toBeVisible()
|
await expect(this.createNewTemplateFromSpace).not.toBeVisible()
|
||||||
}
|
}
|
||||||
|
await this.page.keyboard.press('Escape')
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkSpaceFormIsCreated (spaceName: string): Promise<void> {
|
async checkSpaceFormIsCreated (spaceName: string): Promise<void> {
|
||||||
|
@ -31,6 +31,7 @@ import core, {
|
|||||||
Data,
|
Data,
|
||||||
generateId,
|
generateId,
|
||||||
getWorkspaceId,
|
getWorkspaceId,
|
||||||
|
isActiveMode,
|
||||||
isArchivingMode,
|
isArchivingMode,
|
||||||
isMigrationMode,
|
isMigrationMode,
|
||||||
isWorkspaceCreating,
|
isWorkspaceCreating,
|
||||||
@ -511,7 +512,7 @@ export async function selectWorkspace (
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
if (workspaceInfo.disabled === true && workspaceInfo.mode === 'active') {
|
if (workspaceInfo.disabled === true && isActiveMode(workspaceInfo.mode)) {
|
||||||
ctx.error('workspace disabled', { workspaceUrl, email })
|
ctx.error('workspace disabled', { workspaceUrl, email })
|
||||||
throw new PlatformError(
|
throw new PlatformError(
|
||||||
new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspace: workspaceUrl })
|
new Status(Severity.ERROR, platform.status.WorkspaceNotFound, { workspace: workspaceUrl })
|
||||||
@ -1770,14 +1771,14 @@ export async function getWorkspaceInfo (
|
|||||||
|
|
||||||
const [ws] = await ctx.with('get-workspace', {}, async () =>
|
const [ws] = await ctx.with('get-workspace', {}, async () =>
|
||||||
(await db.workspace.find(query)).filter(
|
(await db.workspace.find(query)).filter(
|
||||||
(it) => it.disabled !== true || account?.admin === true || it.mode !== 'active'
|
(it) => it.disabled !== true || account?.admin === true || !isActiveMode(it.mode)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if (ws == null) {
|
if (ws == null) {
|
||||||
ctx.error('no workspace', { workspace: workspace.name, email })
|
ctx.error('no workspace', { workspace: workspace.name, email })
|
||||||
throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))
|
throw new PlatformError(new Status(Severity.ERROR, platform.status.Forbidden, {}))
|
||||||
}
|
}
|
||||||
if (ws.mode !== 'archived' && _updateLastVisit && (isAccount(account) || email === systemAccountEmail)) {
|
if (!isArchivingMode(ws.mode) && _updateLastVisit && (isAccount(account) || email === systemAccountEmail)) {
|
||||||
void ctx.with('update-last-visit', {}, () => updateLastVisit(db, ws, account as Account))
|
void ctx.with('update-last-visit', {}, () => updateLastVisit(db, ws, account as Account))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,12 +204,17 @@ test.describe('Content in the Documents tests', () => {
|
|||||||
await expect(documentContentPage.imageInPopup()).toBeVisible()
|
await expect(documentContentPage.imageInPopup()).toBeVisible()
|
||||||
await documentContentPage.fullscreenButton().click()
|
await documentContentPage.fullscreenButton().click()
|
||||||
await expect(documentContentPage.fullscreenImage()).toBeVisible()
|
await expect(documentContentPage.fullscreenImage()).toBeVisible()
|
||||||
await documentContentPage.page.keyboard.press('Escape')
|
await expect(async () => {
|
||||||
await expect(documentContentPage.fullscreenImage()).toBeHidden()
|
await documentContentPage.page.keyboard.press('Escape')
|
||||||
|
await expect(documentContentPage.fullscreenImage()).toBeHidden()
|
||||||
|
await expect(documentContentPage.imageInPopup()).toBeHidden()
|
||||||
|
}).toPass(retryOptions)
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('User can open image original in the new tab', async () => {
|
await test.step('User can open image original in the new tab', async () => {
|
||||||
const pagePromise = context.waitForEvent('page')
|
const pagePromise = context.waitForEvent('page', {
|
||||||
|
timeout: 15000
|
||||||
|
})
|
||||||
await documentContentPage.clickImageOriginalButton()
|
await documentContentPage.clickImageOriginalButton()
|
||||||
const newPage = await pagePromise
|
const newPage = await pagePromise
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ test.describe('Tracker issue tests', () => {
|
|||||||
const newIssue: NewIssue = {
|
const newIssue: NewIssue = {
|
||||||
title: `Issue with all parameters and attachments-${generateId()}`,
|
title: `Issue with all parameters and attachments-${generateId()}`,
|
||||||
description: 'Created issue with all parameters and attachments description',
|
description: 'Created issue with all parameters and attachments description',
|
||||||
|
projectName: 'Default',
|
||||||
status: 'In Progress',
|
status: 'In Progress',
|
||||||
priority: 'Urgent',
|
priority: 'Urgent',
|
||||||
assignee: 'Appleseed John',
|
assignee: 'Appleseed John',
|
||||||
@ -48,7 +49,8 @@ test.describe('Tracker issue tests', () => {
|
|||||||
test('Edit an issue', async ({ page }) => {
|
test('Edit an issue', async ({ page }) => {
|
||||||
const newIssue: NewIssue = {
|
const newIssue: NewIssue = {
|
||||||
title: `Issue with all parameters and attachments-${generateId()}`,
|
title: `Issue with all parameters and attachments-${generateId()}`,
|
||||||
description: 'Created issue with all parameters and attachments description'
|
description: 'Created issue with all parameters and attachments description',
|
||||||
|
projectName: 'Default'
|
||||||
}
|
}
|
||||||
const editIssue: Issue = {
|
const editIssue: Issue = {
|
||||||
status: 'Done',
|
status: 'Done',
|
||||||
@ -86,7 +88,8 @@ test.describe('Tracker issue tests', () => {
|
|||||||
test.skip('Set parent issue', async ({ page }) => {
|
test.skip('Set parent issue', async ({ page }) => {
|
||||||
const parentIssue: NewIssue = {
|
const parentIssue: NewIssue = {
|
||||||
title: `PARENT ISSUE-${generateId(2)}`,
|
title: `PARENT ISSUE-${generateId(2)}`,
|
||||||
description: 'Created issue to be parent issue'
|
description: 'Created issue to be parent issue',
|
||||||
|
projectName: 'Default'
|
||||||
}
|
}
|
||||||
|
|
||||||
await issuesPage.clickModelSelectorAll()
|
await issuesPage.clickModelSelectorAll()
|
||||||
@ -96,7 +99,8 @@ test.describe('Tracker issue tests', () => {
|
|||||||
const newIssue: NewIssue = {
|
const newIssue: NewIssue = {
|
||||||
title: `Set parent issue during creation-${generateId(2)}`,
|
title: `Set parent issue during creation-${generateId(2)}`,
|
||||||
description: 'Set parent issue during creation',
|
description: 'Set parent issue during creation',
|
||||||
parentIssue: parentIssue.title
|
parentIssue: parentIssue.title,
|
||||||
|
projectName: 'Default'
|
||||||
}
|
}
|
||||||
|
|
||||||
await issuesPage.clickModelSelectorAll()
|
await issuesPage.clickModelSelectorAll()
|
||||||
@ -116,7 +120,8 @@ test.describe('Tracker issue tests', () => {
|
|||||||
await test.step('Set parent issue from issues page', async () => {
|
await test.step('Set parent issue from issues page', async () => {
|
||||||
const newIssue: NewIssue = {
|
const newIssue: NewIssue = {
|
||||||
title: `Set parent issue from issues page-${generateId(2)}`,
|
title: `Set parent issue from issues page-${generateId(2)}`,
|
||||||
description: 'Set parent issue from issues page'
|
description: 'Set parent issue from issues page',
|
||||||
|
projectName: 'Default'
|
||||||
}
|
}
|
||||||
await issuesPage.clickModelSelectorAll()
|
await issuesPage.clickModelSelectorAll()
|
||||||
await issuesPage.createNewIssue(newIssue)
|
await issuesPage.createNewIssue(newIssue)
|
||||||
@ -139,7 +144,8 @@ test.describe('Tracker issue tests', () => {
|
|||||||
await test.step('Set parent issue from issue details page', async () => {
|
await test.step('Set parent issue from issue details page', async () => {
|
||||||
const newIssue: NewIssue = {
|
const newIssue: NewIssue = {
|
||||||
title: `Set parent issue from issue details page-${generateId(2)}`,
|
title: `Set parent issue from issue details page-${generateId(2)}`,
|
||||||
description: 'Set parent issue from issue details page'
|
description: 'Set parent issue from issue details page',
|
||||||
|
projectName: 'Default'
|
||||||
}
|
}
|
||||||
await issuesPage.clickModelSelectorAll()
|
await issuesPage.clickModelSelectorAll()
|
||||||
await issuesPage.createNewIssue(newIssue)
|
await issuesPage.createNewIssue(newIssue)
|
||||||
@ -160,7 +166,8 @@ test.describe('Tracker issue tests', () => {
|
|||||||
const secondProjectName = 'Second Project'
|
const secondProjectName = 'Second Project'
|
||||||
const moveIssue: NewIssue = {
|
const moveIssue: NewIssue = {
|
||||||
title: `Issue to another project-${generateId()}`,
|
title: `Issue to another project-${generateId()}`,
|
||||||
description: 'Issue to move to another project'
|
description: 'Issue to move to another project',
|
||||||
|
projectName: 'Default'
|
||||||
}
|
}
|
||||||
await prepareNewIssueWithOpenStep(page, moveIssue)
|
await prepareNewIssueWithOpenStep(page, moveIssue)
|
||||||
await issuesDetailsPage.moreActionOnIssue('Move to project')
|
await issuesDetailsPage.moreActionOnIssue('Move to project')
|
||||||
@ -181,7 +188,8 @@ test.describe('Tracker issue tests', () => {
|
|||||||
const commentText = `Comment should be stored after reload-${generateId()}`
|
const commentText = `Comment should be stored after reload-${generateId()}`
|
||||||
const commentIssue: NewIssue = {
|
const commentIssue: NewIssue = {
|
||||||
title: `Issue for stored comment-${generateId()}`,
|
title: `Issue for stored comment-${generateId()}`,
|
||||||
description: 'Issue for comment stored after reload the page'
|
description: 'Issue for comment stored after reload the page',
|
||||||
|
projectName: 'Default'
|
||||||
}
|
}
|
||||||
await prepareNewIssueWithOpenStep(page, commentIssue)
|
await prepareNewIssueWithOpenStep(page, commentIssue)
|
||||||
|
|
||||||
@ -203,7 +211,8 @@ test.describe('Tracker issue tests', () => {
|
|||||||
priority: 'Medium',
|
priority: 'Medium',
|
||||||
estimation: '8',
|
estimation: '8',
|
||||||
component: 'Default component',
|
component: 'Default component',
|
||||||
milestone: 'Edit Milestone'
|
milestone: 'Edit Milestone',
|
||||||
|
projectName: 'Default'
|
||||||
}
|
}
|
||||||
await issuesPage.clickModelSelectorAll()
|
await issuesPage.clickModelSelectorAll()
|
||||||
await issuesPage.buttonCreateNewIssue().click()
|
await issuesPage.buttonCreateNewIssue().click()
|
||||||
@ -219,8 +228,9 @@ test.describe('Tracker issue tests', () => {
|
|||||||
|
|
||||||
test('Delete an issue', async ({ page }) => {
|
test('Delete an issue', async ({ page }) => {
|
||||||
const deleteIssue: NewIssue = {
|
const deleteIssue: NewIssue = {
|
||||||
title: `Issue-to-delete-${generateId()}`,
|
title: `Issue-to-delete-${generateId(4)}`,
|
||||||
description: 'Description Issue for deletion'
|
description: 'Description Issue for deletion',
|
||||||
|
projectName: 'Default'
|
||||||
}
|
}
|
||||||
await prepareNewIssueWithOpenStep(page, deleteIssue)
|
await prepareNewIssueWithOpenStep(page, deleteIssue)
|
||||||
await issuesPage.navigateToIssues()
|
await issuesPage.navigateToIssues()
|
||||||
@ -238,7 +248,8 @@ test.describe('Tracker issue tests', () => {
|
|||||||
const additionalDescription = 'New row for the additional description'
|
const additionalDescription = 'New row for the additional description'
|
||||||
const changedDescriptionIssue: NewIssue = {
|
const changedDescriptionIssue: NewIssue = {
|
||||||
title: `Check the changed description activity-${generateId()}`,
|
title: `Check the changed description activity-${generateId()}`,
|
||||||
description: 'Check the changed description activity description'
|
description: 'Check the changed description activity description',
|
||||||
|
projectName: 'Default'
|
||||||
}
|
}
|
||||||
await prepareNewIssueWithOpenStep(page, changedDescriptionIssue)
|
await prepareNewIssueWithOpenStep(page, changedDescriptionIssue)
|
||||||
await issuesDetailsPage.waitDetailsOpened(changedDescriptionIssue.title)
|
await issuesDetailsPage.waitDetailsOpened(changedDescriptionIssue.title)
|
||||||
@ -251,7 +262,8 @@ test.describe('Tracker issue tests', () => {
|
|||||||
test('Add comment with image attachment', async ({ page }) => {
|
test('Add comment with image attachment', async ({ page }) => {
|
||||||
const commentImageIssue: NewIssue = {
|
const commentImageIssue: NewIssue = {
|
||||||
title: `Add comment with image attachment-${generateId()}`,
|
title: `Add comment with image attachment-${generateId()}`,
|
||||||
description: 'Add comment with image attachment'
|
description: 'Add comment with image attachment',
|
||||||
|
projectName: 'Default'
|
||||||
}
|
}
|
||||||
await prepareNewIssueWithOpenStep(page, commentImageIssue)
|
await prepareNewIssueWithOpenStep(page, commentImageIssue)
|
||||||
await issuesDetailsPage.waitDetailsOpened(commentImageIssue.title)
|
await issuesDetailsPage.waitDetailsOpened(commentImageIssue.title)
|
||||||
@ -264,7 +276,8 @@ test.describe('Tracker issue tests', () => {
|
|||||||
const commentPopup = `Comment for the popup-${generateId()}`
|
const commentPopup = `Comment for the popup-${generateId()}`
|
||||||
const commentIssue: NewIssue = {
|
const commentIssue: NewIssue = {
|
||||||
title: `Issue for add comment by popup-${generateId()}`,
|
title: `Issue for add comment by popup-${generateId()}`,
|
||||||
description: 'Issue for add comment by popup'
|
description: 'Issue for add comment by popup',
|
||||||
|
projectName: 'Default'
|
||||||
}
|
}
|
||||||
await prepareNewIssueWithOpenStep(page, commentIssue)
|
await prepareNewIssueWithOpenStep(page, commentIssue)
|
||||||
await issuesDetailsPage.waitDetailsOpened(commentIssue.title)
|
await issuesDetailsPage.waitDetailsOpened(commentIssue.title)
|
||||||
|
@ -9,7 +9,8 @@ test.describe('Tracker public link issues tests', () => {
|
|||||||
test('Public link generate', async ({ browser }) => {
|
test('Public link generate', async ({ browser }) => {
|
||||||
const publicLinkIssue: NewIssue = {
|
const publicLinkIssue: NewIssue = {
|
||||||
title: `Public link generate issue-${generateId()}`,
|
title: `Public link generate issue-${generateId()}`,
|
||||||
description: 'Public link generate issue'
|
description: 'Public link generate issue',
|
||||||
|
projectName: 'Default'
|
||||||
}
|
}
|
||||||
|
|
||||||
let link: string
|
let link: string
|
||||||
@ -60,7 +61,8 @@ test.describe('Tracker public link issues tests', () => {
|
|||||||
test('Public link Revoke', async ({ browser }) => {
|
test('Public link Revoke', async ({ browser }) => {
|
||||||
const publicLinkIssue: NewIssue = {
|
const publicLinkIssue: NewIssue = {
|
||||||
title: `Public link Revoke issue-${generateId()}`,
|
title: `Public link Revoke issue-${generateId()}`,
|
||||||
description: 'Public link Revoke issue'
|
description: 'Public link Revoke issue',
|
||||||
|
projectName: 'Default'
|
||||||
}
|
}
|
||||||
|
|
||||||
const newContext = await browser.newContext({ storageState: PlatformSetting })
|
const newContext = await browser.newContext({ storageState: PlatformSetting })
|
||||||
|
Loading…
Reference in New Issue
Block a user