mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-13 03:40:48 +00:00
Fix TSK-101 navigation (#2068)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
5d33062176
commit
665ef78199
@ -17,13 +17,9 @@
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { getPlatformColor, ScrollBox, Scroller } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { slide } from 'svelte/transition'
|
||||
import { DocWithRank, StateType, TypeState } from '../types'
|
||||
import { CardDragEvent, ExtItem, Item, StateType, TypeState } from '../types'
|
||||
import { calcRank } from '../utils'
|
||||
|
||||
type Item = DocWithRank & { state: StateType; doneState: StateType | null }
|
||||
type ExtItem = { prev?: Item; it: Item; next?: Item; pos: number }
|
||||
type CardDragEvent = DragEvent & { currentTarget: EventTarget & HTMLDivElement }
|
||||
import KanbanRow from './KanbanRow.svelte'
|
||||
|
||||
export let _class: Ref<Class<Item>>
|
||||
export let space: Ref<Space>
|
||||
@ -144,7 +140,6 @@
|
||||
return calcRank(object.it, object.next)
|
||||
}
|
||||
}
|
||||
const slideD = (node: any, args: any) => (args.isDragging ? slide(node, args) : {})
|
||||
|
||||
function panelDragOver (event: Event, state: TypeState): void {
|
||||
event.preventDefault()
|
||||
@ -184,11 +179,14 @@
|
||||
let stateObjects: ExtItem[]
|
||||
|
||||
const stateRefs: HTMLElement[] = []
|
||||
const stateRows: KanbanRow[] = []
|
||||
|
||||
$: stateRefs.length = states.length
|
||||
$: stateRows.length = states.length
|
||||
|
||||
function scrollInto (statePos: number): void {
|
||||
function scrollInto (statePos: number, obj: Item): void {
|
||||
stateRefs[statePos]?.scrollIntoView({ behavior: 'auto', block: 'nearest' })
|
||||
stateRows[statePos]?.scroll(obj)
|
||||
}
|
||||
|
||||
export function select (offset: 1 | -1 | 0, of?: Doc, dir?: 'vertical' | 'horizontal'): void {
|
||||
@ -227,16 +225,18 @@
|
||||
|
||||
if (offset === -1) {
|
||||
if (dir === undefined || dir === 'vertical') {
|
||||
scrollInto(objState)
|
||||
dispatch('obj-focus', (stateObjs[statePos - 1] ?? stateObjs[0]).it)
|
||||
const obj = (stateObjs[statePos - 1] ?? stateObjs[0]).it
|
||||
scrollInto(objState, obj)
|
||||
dispatch('obj-focus', obj)
|
||||
return
|
||||
} else {
|
||||
while (objState > 0) {
|
||||
objState--
|
||||
const nstateObjs = getStateObjects(objects, states[objState])
|
||||
if (nstateObjs.length > 0) {
|
||||
scrollInto(objState)
|
||||
dispatch('obj-focus', (nstateObjs[statePos] ?? nstateObjs[nstateObjs.length - 1]).it)
|
||||
const obj = (nstateObjs[statePos] ?? nstateObjs[nstateObjs.length - 1]).it
|
||||
scrollInto(objState, obj)
|
||||
dispatch('obj-focus', obj)
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -244,23 +244,25 @@
|
||||
}
|
||||
if (offset === 1) {
|
||||
if (dir === undefined || dir === 'vertical') {
|
||||
scrollInto(objState)
|
||||
dispatch('obj-focus', (stateObjs[statePos + 1] ?? stateObjs[stateObjs.length - 1]).it)
|
||||
const obj = (stateObjs[statePos + 1] ?? stateObjs[stateObjs.length - 1]).it
|
||||
scrollInto(objState, obj)
|
||||
dispatch('obj-focus', obj)
|
||||
return
|
||||
} else {
|
||||
while (objState < states.length - 1) {
|
||||
objState++
|
||||
const nstateObjs = getStateObjects(objects, states[objState])
|
||||
if (nstateObjs.length > 0) {
|
||||
scrollInto(objState)
|
||||
dispatch('obj-focus', (nstateObjs[statePos] ?? nstateObjs[nstateObjs.length - 1]).it)
|
||||
const obj = (nstateObjs[statePos] ?? nstateObjs[nstateObjs.length - 1]).it
|
||||
scrollInto(objState, obj)
|
||||
dispatch('obj-focus', obj)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (offset === 0) {
|
||||
scrollInto(objState)
|
||||
scrollInto(objState, obj)
|
||||
dispatch('obj-focus', obj)
|
||||
}
|
||||
}
|
||||
@ -309,35 +311,25 @@
|
||||
{/if}
|
||||
<Scroller padding={'.5rem 0'} on:dragover on:drop>
|
||||
<slot name="beforeCard" {state} />
|
||||
{#each stateObjects as object}
|
||||
{@const dragged = isDragging && object.it._id === dragCard?._id}
|
||||
<div
|
||||
transition:slideD|local={{ isDragging }}
|
||||
class="step-tb75"
|
||||
on:dragover|preventDefault={(evt) => cardDragOver(evt, object)}
|
||||
on:drop|preventDefault={(evt) => cardDrop(evt, object)}
|
||||
>
|
||||
<div
|
||||
class="card-container"
|
||||
class:selection={selection !== undefined ? objects[selection]?._id === object.it._id : false}
|
||||
class:checked={checkedSet.has(object.it._id)}
|
||||
on:mouseover={() => dispatch('obj-focus', object.it)}
|
||||
on:focus={() => {}}
|
||||
on:contextmenu={(evt) => showMenu(evt, object)}
|
||||
draggable={true}
|
||||
class:draggable={true}
|
||||
on:dragstart
|
||||
on:dragend
|
||||
class:dragged
|
||||
on:dragstart={() => onDragStart(object, state)}
|
||||
on:dragend={() => {
|
||||
isDragging = false
|
||||
}}
|
||||
>
|
||||
<slot name="card" object={toAny(object.it)} {dragged} />
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
<KanbanRow
|
||||
bind:this={stateRows[si]}
|
||||
{stateObjects}
|
||||
{isDragging}
|
||||
{dragCard}
|
||||
{objects}
|
||||
{selection}
|
||||
{checkedSet}
|
||||
{state}
|
||||
{cardDragOver}
|
||||
{cardDrop}
|
||||
{onDragStart}
|
||||
{showMenu}
|
||||
>
|
||||
<svelte:fragment slot="card" let:object let:dragged>
|
||||
<slot name="card" {object} {dragged} />
|
||||
</svelte:fragment>
|
||||
</KanbanRow>
|
||||
|
||||
<slot name="afterCard" {space} {state} />
|
||||
</Scroller>
|
||||
</div>
|
||||
@ -361,42 +353,6 @@
|
||||
padding: 1.5rem 2rem 0;
|
||||
}
|
||||
|
||||
.card-container {
|
||||
background-color: var(--board-card-bg-color);
|
||||
border-radius: 0.25rem;
|
||||
// transition: box-shadow .15s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--board-card-bg-hover);
|
||||
}
|
||||
&.checked {
|
||||
background-color: var(--highlight-select);
|
||||
box-shadow: inset 0 0 1px 1px var(--highlight-select-border);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--highlight-select-hover);
|
||||
}
|
||||
}
|
||||
&.selection,
|
||||
&.checked.selection {
|
||||
box-shadow: inset 0 0 1px 1px var(--primary-bg-color);
|
||||
animation: anim-border 1s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--highlight-hover);
|
||||
}
|
||||
}
|
||||
&.checked.selection:hover {
|
||||
background-color: var(--highlight-select-hover);
|
||||
}
|
||||
|
||||
&.draggable {
|
||||
cursor: grab;
|
||||
}
|
||||
&.dragged {
|
||||
background-color: var(--board-bg-color);
|
||||
}
|
||||
}
|
||||
@keyframes anim-border {
|
||||
from {
|
||||
box-shadow: inset 0 0 1px 1px var(--primary-edit-border-color);
|
||||
|
129
packages/kanban/src/components/KanbanRow.svelte
Normal file
129
packages/kanban/src/components/KanbanRow.svelte
Normal file
@ -0,0 +1,129 @@
|
||||
<!--
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Doc, Ref } from '@anticrm/core'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { slide } from 'svelte/transition'
|
||||
import { CardDragEvent, ExtItem, Item, TypeState } from '../types'
|
||||
|
||||
export let stateObjects: ExtItem[]
|
||||
export let isDragging: boolean
|
||||
export let dragCard: Item | undefined
|
||||
export let objects: Item[]
|
||||
export let selection: number | undefined = undefined
|
||||
export let checkedSet: Set<Ref<Doc>>
|
||||
export let state: TypeState
|
||||
|
||||
export let cardDragOver: (evt: CardDragEvent, object: ExtItem) => void
|
||||
export let cardDrop: (evt: CardDragEvent, object: ExtItem) => void
|
||||
export let onDragStart: (object: ExtItem, state: TypeState) => void
|
||||
export let showMenu: (evt: MouseEvent, object: ExtItem) => void
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function toAny (object: any): any {
|
||||
return object
|
||||
}
|
||||
|
||||
const slideD = (node: any, args: any) => (args.isDragging ? slide(node, args) : {})
|
||||
|
||||
const stateRefs: HTMLElement[] = []
|
||||
|
||||
$: stateRefs.length = stateObjects.length
|
||||
export function scroll (item: Item): void {
|
||||
const pos = stateObjects.findIndex((it) => it.it._id === item._id)
|
||||
if (pos >= 0) {
|
||||
stateRefs[pos]?.scrollIntoView({ behavior: 'auto', block: 'nearest' })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#each stateObjects as object, i}
|
||||
{@const dragged = isDragging && object.it._id === dragCard?._id}
|
||||
<div
|
||||
bind:this={stateRefs[i]}
|
||||
transition:slideD|local={{ isDragging }}
|
||||
class="step-tb75"
|
||||
on:dragover|preventDefault={(evt) => cardDragOver(evt, object)}
|
||||
on:drop|preventDefault={(evt) => cardDrop(evt, object)}
|
||||
>
|
||||
<div
|
||||
class="card-container"
|
||||
class:selection={selection !== undefined ? objects[selection]?._id === object.it._id : false}
|
||||
class:checked={checkedSet.has(object.it._id)}
|
||||
on:mouseover={() => dispatch('obj-focus', object.it)}
|
||||
on:focus={() => {}}
|
||||
on:contextmenu={(evt) => showMenu(evt, object)}
|
||||
draggable={true}
|
||||
class:draggable={true}
|
||||
on:dragstart
|
||||
on:dragend
|
||||
class:dragged
|
||||
on:dragstart={() => onDragStart(object, state)}
|
||||
on:dragend={() => {
|
||||
isDragging = false
|
||||
}}
|
||||
>
|
||||
<slot name="card" object={toAny(object.it)} {dragged} />
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
<style lang="scss">
|
||||
.card-container {
|
||||
background-color: var(--board-card-bg-color);
|
||||
border-radius: 0.25rem;
|
||||
// transition: box-shadow .15s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--board-card-bg-hover);
|
||||
}
|
||||
&.checked {
|
||||
background-color: var(--highlight-select);
|
||||
box-shadow: inset 0 0 1px 1px var(--highlight-select-border);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--highlight-select-hover);
|
||||
}
|
||||
}
|
||||
&.selection,
|
||||
&.checked.selection {
|
||||
box-shadow: inset 0 0 1px 1px var(--primary-bg-color);
|
||||
animation: anim-border 1s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--highlight-hover);
|
||||
}
|
||||
}
|
||||
&.checked.selection:hover {
|
||||
background-color: var(--highlight-select-hover);
|
||||
}
|
||||
|
||||
&.draggable {
|
||||
cursor: grab;
|
||||
}
|
||||
&.dragged {
|
||||
background-color: var(--board-bg-color);
|
||||
}
|
||||
}
|
||||
@keyframes anim-border {
|
||||
from {
|
||||
box-shadow: inset 0 0 1px 1px var(--primary-edit-border-color);
|
||||
}
|
||||
to {
|
||||
box-shadow: inset 0 0 1px 1px var(--primary-bg-color);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -19,3 +19,20 @@ export interface TypeState {
|
||||
color: number
|
||||
icon?: Asset
|
||||
}
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type Item = DocWithRank & { state: StateType, doneState: StateType | null }
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ExtItem {
|
||||
prev?: Item
|
||||
it: Item
|
||||
next?: Item
|
||||
pos: number
|
||||
}
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type CardDragEvent = DragEvent & { currentTarget: EventTarget & HTMLDivElement }
|
||||
|
@ -45,7 +45,6 @@
|
||||
<Avatar size={'x-small'} avatar={employee.avatar} />
|
||||
<div class="overflow-label user">{formatName(employee.name)}</div>
|
||||
{:else}
|
||||
{JSON.stringify(value)}
|
||||
<div class="overflow-label user">{value.email}</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -14,11 +14,11 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref, SortingOrder } from '@anticrm/core'
|
||||
import { Project } from '@anticrm/tracker'
|
||||
import { IntlString, translate } from '@anticrm/platform'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { Button, showPopup, SelectPopup, eventToHTMLElement, ButtonShape } from '@anticrm/ui'
|
||||
import { getClient } from '@anticrm/presentation'
|
||||
import { Project } from '@anticrm/tracker'
|
||||
import type { ButtonKind, ButtonSize } from '@anticrm/ui'
|
||||
import { Button, ButtonShape, eventToHTMLElement, SelectPopup, showPopup } from '@anticrm/ui'
|
||||
import tracker from '../plugin'
|
||||
|
||||
export let value: Ref<Project> | null | undefined
|
||||
@ -33,23 +33,10 @@
|
||||
export let width: string | undefined = 'min-content'
|
||||
|
||||
const client = getClient()
|
||||
const projectsQuery = createQuery()
|
||||
|
||||
let projects: Project[] = []
|
||||
let selectedProject: Project | undefined
|
||||
let defaultProjectLabel = ''
|
||||
|
||||
$: projectsQuery.query(
|
||||
tracker.class.Project,
|
||||
{},
|
||||
(currentProjects) => {
|
||||
projects = currentProjects
|
||||
},
|
||||
{
|
||||
sort: { modifiedOn: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
|
||||
$: if (value !== undefined) {
|
||||
handleSelectedProjectIdUpdated(value)
|
||||
}
|
||||
@ -58,15 +45,6 @@
|
||||
$: projectIcon = selectedProject?.icon ?? tracker.icon.Projects
|
||||
$: projectText = shouldShowLabel ? selectedProject?.label ?? defaultProjectLabel : undefined
|
||||
|
||||
$: projectsInfo = [
|
||||
{ id: null, icon: tracker.icon.Projects, label: tracker.string.NoProject },
|
||||
...projects.map((p) => ({
|
||||
id: p._id,
|
||||
icon: p.icon,
|
||||
text: p.label
|
||||
}))
|
||||
]
|
||||
|
||||
const handleSelectedProjectIdUpdated = async (newProjectId: Ref<Project> | null) => {
|
||||
if (newProjectId === null) {
|
||||
selectedProject = undefined
|
||||
@ -77,12 +55,28 @@
|
||||
selectedProject = await client.findOne(tracker.class.Project, { _id: newProjectId })
|
||||
}
|
||||
|
||||
const handleProjectEditorOpened = (event: MouseEvent) => {
|
||||
const handleProjectEditorOpened = async (event: MouseEvent): Promise<void> => {
|
||||
event.stopPropagation()
|
||||
if (!isEditable) {
|
||||
return
|
||||
}
|
||||
|
||||
const projects = await client.findAll(
|
||||
tracker.class.Project,
|
||||
{},
|
||||
{
|
||||
sort: { modifiedOn: SortingOrder.Ascending }
|
||||
}
|
||||
)
|
||||
const projectsInfo = [
|
||||
{ id: null, icon: tracker.icon.Projects, label: tracker.string.NoProject },
|
||||
...projects.map((p) => ({
|
||||
id: p._id,
|
||||
icon: p.icon,
|
||||
text: p.label
|
||||
}))
|
||||
]
|
||||
|
||||
showPopup(
|
||||
SelectPopup,
|
||||
{ value: projectsInfo, placeholder: popupPlaceholder, searchable: true },
|
||||
|
@ -69,7 +69,11 @@
|
||||
|
||||
const options: FindOptions<Issue> = {
|
||||
lookup: {
|
||||
assignee: contact.class.Employee
|
||||
assignee: contact.class.Employee,
|
||||
space: tracker.class.Team,
|
||||
_id: {
|
||||
subIssues: tracker.class.Issue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,7 +161,7 @@
|
||||
}}
|
||||
>
|
||||
<div class="flex-col mr-6">
|
||||
<IssuePresenter value={object} {currentTeam} />
|
||||
<IssuePresenter value={object} />
|
||||
<span class="fs-bold caption-color mt-1 lines-limit-2">
|
||||
{object.title}
|
||||
</span>
|
||||
|
@ -13,40 +13,29 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { WithLookup } from '@anticrm/core'
|
||||
import type { Issue, Team } from '@anticrm/tracker'
|
||||
import { Icon, showPanel } from '@anticrm/ui'
|
||||
import tracker from '../../plugin'
|
||||
import { getIssueId } from '../../utils'
|
||||
|
||||
export let value: Issue
|
||||
export let currentTeam: Team | undefined
|
||||
export let value: WithLookup<Issue>
|
||||
export let inline: boolean = false
|
||||
|
||||
const client = getClient()
|
||||
const spaceQuery = createQuery()
|
||||
const shortLabel = client.getHierarchy().getClass(value._class).shortLabel
|
||||
|
||||
function handleIssueEditorOpened () {
|
||||
showPanel(tracker.component.EditIssue, value._id, value._class, 'content')
|
||||
}
|
||||
|
||||
$: if (!currentTeam) {
|
||||
spaceQuery.query(tracker.class.Team, { _id: value.space }, (res) => ([currentTeam] = res))
|
||||
} else {
|
||||
spaceQuery.unsubscribe()
|
||||
}
|
||||
$: issueName = currentTeam && getIssueId(currentTeam, value)
|
||||
$: title = `${(value?.$lookup?.space as Team)?.identifier}-${value.number}`
|
||||
</script>
|
||||
|
||||
{#if value && shortLabel}
|
||||
{#if value}
|
||||
<div class="flex-presenter issuePresenterRoot" class:inline-presenter={inline} on:click={handleIssueEditorOpened}>
|
||||
<div class="icon">
|
||||
<Icon icon={tracker.icon.Issue} size={'small'} />
|
||||
</div>
|
||||
{#if issueName !== undefined}
|
||||
<span title={issueName} class="label nowrap issueLabel">{issueName}</span>
|
||||
{/if}
|
||||
<span title="title" class="label nowrap issueLabel">
|
||||
{title}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
|
@ -91,7 +91,11 @@
|
||||
const options: FindOptions<Issue> = {
|
||||
sort: { [issuesOrderKeyMap[orderingKey]]: issuesSortOrderMap[issuesOrderKeyMap[orderingKey]] },
|
||||
limit: ENTRIES_LIMIT,
|
||||
lookup: { assignee: contact.class.Employee, status: tracker.class.IssueStatus }
|
||||
lookup: {
|
||||
assignee: contact.class.Employee,
|
||||
status: tracker.class.IssueStatus,
|
||||
space: tracker.class.Team
|
||||
}
|
||||
}
|
||||
|
||||
$: baseQuery = {
|
||||
|
@ -57,7 +57,8 @@
|
||||
const baseOptions: FindOptions<Issue> = {
|
||||
lookup: {
|
||||
assignee: contact.class.Employee,
|
||||
status: tracker.class.IssueStatus
|
||||
status: tracker.class.IssueStatus,
|
||||
space: tracker.class.Team
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,11 @@
|
||||
{
|
||||
sort: { [orderByKey]: issuesSortOrderMap[orderByKey] },
|
||||
limit: 200,
|
||||
lookup: { assignee: contact.class.Employee, status: tracker.class.IssueStatus }
|
||||
lookup: {
|
||||
assignee: contact.class.Employee,
|
||||
status: tracker.class.IssueStatus,
|
||||
space: tracker.class.Team
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
@ -13,15 +13,14 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { SortingOrder, WithLookup, Ref, Doc } from '@anticrm/core'
|
||||
import { createQuery } from '@anticrm/presentation'
|
||||
import { Doc, Ref, WithLookup } from '@anticrm/core'
|
||||
import { Issue, IssueStatus, Team } from '@anticrm/tracker'
|
||||
import { Button, ProgressCircle, showPopup, SelectPopup, closeTooltip, showPanel } from '@anticrm/ui'
|
||||
import type { ButtonKind, ButtonSize } from '@anticrm/ui'
|
||||
import { Button, closeTooltip, ProgressCircle, SelectPopup, showPanel, showPopup } from '@anticrm/ui'
|
||||
import tracker from '../../../plugin'
|
||||
import { getIssueId } from '../../../utils'
|
||||
|
||||
export let issue: Issue
|
||||
export let issue: WithLookup<Issue>
|
||||
export let currentTeam: Team | undefined
|
||||
export let issueStatuses: WithLookup<IssueStatus>[] | undefined
|
||||
|
||||
@ -30,7 +29,6 @@
|
||||
export let justify: 'left' | 'center' = 'left'
|
||||
export let width: string | undefined = 'min-contet'
|
||||
|
||||
const subIssuesQuery = createQuery()
|
||||
let btn: HTMLElement
|
||||
|
||||
let subIssues: Issue[] | undefined
|
||||
@ -38,9 +36,10 @@
|
||||
let countComplate: number = 0
|
||||
|
||||
$: hasSubIssues = issue.subIssues > 0
|
||||
$: subIssuesQuery.query(tracker.class.Issue, { attachedTo: issue._id }, async (result) => (subIssues = result), {
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
})
|
||||
$: if (issue.$lookup?.subIssues !== undefined) {
|
||||
subIssues = issue.$lookup.subIssues as Issue[]
|
||||
subIssues.sort((a, b) => a.rank.localeCompare(b.rank))
|
||||
}
|
||||
$: if (issueStatuses && subIssues) {
|
||||
doneStatus = issueStatuses.find((s) => s.category === tracker.issueStatusCategory.Completed)?._id ?? undefined
|
||||
if (doneStatus) countComplate = subIssues.filter((si) => si.status === doneStatus).length
|
||||
|
@ -54,8 +54,6 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{projectId}
|
||||
{JSON.stringify(project)}
|
||||
{#if project}
|
||||
<EditProject {project} />
|
||||
{:else}
|
||||
|
@ -69,13 +69,12 @@ test.describe('recruit tests', () => {
|
||||
await page.click('button:has-text("Vacancy")')
|
||||
await page.fill('[placeholder="Software\\ Engineer"]', vacancyId)
|
||||
await page.click('button:has-text("Create")')
|
||||
await page.locator(`text=${vacancyId}`).click()
|
||||
await page.click(`tr > :has-text("${vacancyId}")`)
|
||||
|
||||
await page.click('text=Talents')
|
||||
|
||||
await page.click('text=Talents')
|
||||
await page.click('text=Andrey P.')
|
||||
// await page.locator('.mixin-selector').locator('text="Candidate"').click()
|
||||
|
||||
// Click on Add button
|
||||
await page.click('.applications-container .flex-row-center .flex-center')
|
||||
@ -87,7 +86,7 @@ test.describe('recruit tests', () => {
|
||||
|
||||
await page.click('button:has-text("Create")')
|
||||
|
||||
await page.locator(`tr:has-text("${vacancyId}") >> text=APP-`).click()
|
||||
await page.click(`tr:has-text("${vacancyId}") >> text=APP-`)
|
||||
await page.click('button:has-text("Assigned recruiter")')
|
||||
await page.click('button:has-text("Rosamund Chen")')
|
||||
})
|
||||
@ -100,9 +99,9 @@ test.describe('recruit tests', () => {
|
||||
await page.locator('text=Vacancies').click()
|
||||
|
||||
await page.click('button:has-text("Vacancy")')
|
||||
await page.fill('[placeholder="Software\\ Engineer"]', vacancyId)
|
||||
await page.click('button:has-text("Create")')
|
||||
await page.locator(`text=${vacancyId}`).click()
|
||||
await page.fill('form [placeholder="Software\\ Engineer"]', vacancyId)
|
||||
await page.click('form button:has-text("Create")')
|
||||
await page.click(`tr > :has-text("${vacancyId}")`)
|
||||
|
||||
// Create Applicatio n1
|
||||
await page.click('button:has-text("Application")')
|
||||
|
@ -90,17 +90,17 @@ test.describe('recruit tests', () => {
|
||||
// Click [placeholder="John"]
|
||||
await page.click('[placeholder="John"]')
|
||||
// Fill [placeholder="John"]
|
||||
const first = 'first-' + generateId().slice(0, 4)
|
||||
const first = 'first-' + generateId(4)
|
||||
await page.fill('[placeholder="John"]', first)
|
||||
// Click [placeholder="Appleseed"]
|
||||
await page.click('[placeholder="Appleseed"]')
|
||||
// Fill [placeholder="Appleseed"]
|
||||
const last = 'last-' + generateId().slice(0, 4)
|
||||
const last = 'last-' + generateId(4)
|
||||
await page.fill('[placeholder="Appleseed"]', last)
|
||||
// Click button:has-text("Create")
|
||||
await page.click('button:has-text("Create")')
|
||||
// Click text=q w
|
||||
await page.click(`text=${first} ${last}`)
|
||||
await page.click(`tr > :has-text("${first} ${last}")`)
|
||||
// Click text=java
|
||||
await expect(page.locator('text=java').first()).toBeVisible()
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user