mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-13 19:58:09 +00:00
Fix tracker templates issues (#7590)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
1254f0154a
commit
54d9a6c847
@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Attachment } from '@hcengineering/attachment'
|
||||
import { RateLimiter, Account, Class, Doc, IdMap, Markup, Ref, Space, generateId, toIdMap } from '@hcengineering/core'
|
||||
import { Account, Class, Doc, IdMap, Markup, RateLimiter, Ref, Space, generateId, toIdMap } from '@hcengineering/core'
|
||||
import { Asset, IntlString, setPlatformStatus, unknownError } from '@hcengineering/platform'
|
||||
import {
|
||||
DraftController,
|
||||
@ -25,9 +25,9 @@
|
||||
getFileMetadata,
|
||||
uploadFile
|
||||
} from '@hcengineering/presentation'
|
||||
import { EmptyMarkup } from '@hcengineering/text'
|
||||
import textEditor, { type RefAction } from '@hcengineering/text-editor'
|
||||
import { AttachIcon, ReferenceInput } from '@hcengineering/text-editor-resources'
|
||||
import { EmptyMarkup } from '@hcengineering/text'
|
||||
import { Loading, type AnySvelteComponent } from '@hcengineering/ui'
|
||||
import { createEventDispatcher, onDestroy, tick } from 'svelte'
|
||||
import attachment from '../plugin'
|
||||
@ -196,11 +196,11 @@
|
||||
}
|
||||
|
||||
async function fileDrop (e: DragEvent): Promise<void> {
|
||||
progress = true
|
||||
const list = e.dataTransfer?.files
|
||||
const limiter = new RateLimiter(10)
|
||||
|
||||
if (list === undefined || list.length === 0) return
|
||||
progress = true
|
||||
for (let index = 0; index < list.length; index++) {
|
||||
const file = list.item(index)
|
||||
if (file !== null) {
|
||||
|
@ -12,6 +12,9 @@
|
||||
export let baseClass: Ref<Class<Doc>> | undefined = undefined
|
||||
export let kind: ButtonKind = 'regular'
|
||||
export let size: ButtonSize = 'medium'
|
||||
export let justify: 'left' | 'center' = 'center'
|
||||
export let width: string | undefined = undefined
|
||||
export let showAlways: boolean = false
|
||||
export let allTypes = false
|
||||
|
||||
const client = getClient()
|
||||
@ -46,12 +49,14 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if projectType !== undefined && items.length > 1}
|
||||
{#if projectType !== undefined && (items.length > 1 || showAlways)}
|
||||
<DropdownLabels
|
||||
{focusIndex}
|
||||
{kind}
|
||||
{size}
|
||||
{items}
|
||||
{justify}
|
||||
{width}
|
||||
dataId={'btnSelectTaskType'}
|
||||
bind:selected={value}
|
||||
enableSearch={false}
|
||||
|
@ -15,8 +15,10 @@
|
||||
<script lang="ts">
|
||||
import { Person } from '@hcengineering/contact'
|
||||
import { Data, Doc, Ref, generateId } from '@hcengineering/core'
|
||||
import { Card, KeyedAttribute, SpaceSelector, getClient } from '@hcengineering/presentation'
|
||||
import tags, { TagElement } from '@hcengineering/tags'
|
||||
import { Card, KeyedAttribute, SpaceSelector, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import tags, { TagElement, TagReference } from '@hcengineering/tags'
|
||||
import { TaskType } from '@hcengineering/task'
|
||||
import { TaskKindSelector } from '@hcengineering/task-resources'
|
||||
import { StyledTextBox } from '@hcengineering/text-editor-resources'
|
||||
import { Component as ComponentType, IssuePriority, IssueTemplate, Milestone, Project } from '@hcengineering/tracker'
|
||||
import { Component, EditBox, Label } from '@hcengineering/ui'
|
||||
@ -39,6 +41,7 @@
|
||||
export let relatedTo: Doc | undefined
|
||||
|
||||
let labels: TagElement[] = []
|
||||
let kind: Ref<TaskType> | undefined = undefined
|
||||
|
||||
let objectId: Ref<IssueTemplate> = generateId()
|
||||
let object: Data<IssueTemplate> = {
|
||||
@ -93,6 +96,7 @@
|
||||
comments: 0,
|
||||
attachments: 0,
|
||||
labels: labels.map((it) => it._id),
|
||||
kind,
|
||||
relations: relatedTo !== undefined ? [{ _id: relatedTo._id, _class: relatedTo._class }] : []
|
||||
}
|
||||
|
||||
@ -119,6 +123,19 @@
|
||||
function addTagRef (tag: TagElement): void {
|
||||
labels = [...labels, tag]
|
||||
}
|
||||
|
||||
let currentProject: Project | undefined
|
||||
const spaceQuery = createQuery()
|
||||
$: if (_space !== undefined) {
|
||||
spaceQuery.query(tracker.class.Project, { _id: _space }, (res) => {
|
||||
currentProject = res[0]
|
||||
})
|
||||
} else {
|
||||
spaceQuery.unsubscribe()
|
||||
currentProject = undefined
|
||||
}
|
||||
|
||||
$: labelRefs = labels.map((it) => ({ ...(it as unknown as TagReference), _id: generateId(), tag: it._id }))
|
||||
</script>
|
||||
|
||||
<Card
|
||||
@ -144,7 +161,17 @@
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="title" let:label>
|
||||
<div class="flex-row-center gap-2 pt-1 pb-1 pr-1">
|
||||
<span class="overflow-label">
|
||||
<Label {label} />
|
||||
</span>
|
||||
<TaskKindSelector
|
||||
projectType={currentProject?.type}
|
||||
bind:value={kind}
|
||||
baseClass={tracker.class.Issue}
|
||||
size={'small'}
|
||||
/>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
<EditBox
|
||||
@ -187,7 +214,7 @@
|
||||
<Component
|
||||
is={tags.component.TagsDropdownEditor}
|
||||
props={{
|
||||
items: labels,
|
||||
items: labelRefs,
|
||||
key,
|
||||
targetClass: tracker.class.Issue,
|
||||
countLabel: tracker.string.NumberLabels,
|
||||
|
@ -44,9 +44,11 @@
|
||||
const client = getClient()
|
||||
|
||||
let newIssue: IssueTemplateChild = childIssue !== undefined ? { ...childIssue } : getIssueDefaults()
|
||||
let thisRef: HTMLDivElement
|
||||
let thisRef: HTMLDivElement | undefined
|
||||
let focusIssueTitle: () => void
|
||||
let labels: TagElement[] = []
|
||||
let canSave = getTitle(newIssue.title ?? '').length > 0
|
||||
$: canSave = getTitle(newIssue.title ?? '').length > 0
|
||||
|
||||
const labelsQuery = createQuery()
|
||||
|
||||
@ -72,20 +74,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
function resetToDefaults () {
|
||||
function resetToDefaults (): void {
|
||||
newIssue = getIssueDefaults()
|
||||
labels = []
|
||||
focusIssueTitle?.()
|
||||
}
|
||||
|
||||
function getTitle (value: string) {
|
||||
function getTitle (value: string): string {
|
||||
return value.trim()
|
||||
}
|
||||
|
||||
function close () {
|
||||
function close (): void {
|
||||
dispatch('close')
|
||||
}
|
||||
|
||||
async function createIssue () {
|
||||
function onDelete (): void {
|
||||
dispatch('close', ['delete', newIssue])
|
||||
}
|
||||
|
||||
function createIssue (): void {
|
||||
if (!canSave) {
|
||||
return
|
||||
}
|
||||
@ -99,7 +106,7 @@
|
||||
if (childIssue === undefined) {
|
||||
dispatch('create', value)
|
||||
} else {
|
||||
dispatch('close', value)
|
||||
dispatch('close', ['update', value])
|
||||
}
|
||||
|
||||
resetToDefaults()
|
||||
@ -121,8 +128,7 @@
|
||||
)
|
||||
let currentProject: Project | undefined = undefined
|
||||
|
||||
$: thisRef && thisRef.scrollIntoView({ behavior: 'smooth' })
|
||||
$: canSave = getTitle(newIssue.title ?? '').length > 0
|
||||
$: thisRef !== undefined && thisRef.scrollIntoView({ behavior: 'smooth' })
|
||||
|
||||
$: labelRefs = labels.map((it) => ({ ...(it as unknown as TagReference), _id: generateId(), tag: it._id }))
|
||||
</script>
|
||||
@ -200,6 +206,9 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-2 buttons-group small-gap">
|
||||
{#if childIssue !== undefined}
|
||||
<Button label={presentation.string.Delete} size="small" kind="dangerous" on:click={onDelete} />
|
||||
{/if}
|
||||
<Button label={presentation.string.Cancel} size="small" kind="ghost" on:click={close} />
|
||||
<Button
|
||||
disabled={!canSave}
|
||||
|
@ -37,7 +37,7 @@
|
||||
let dragIndex: number | null = null
|
||||
let hoveringIndex: number | null = null
|
||||
|
||||
function openIssue (evt: MouseEvent, target: IssueTemplateChild) {
|
||||
function openIssue (evt: MouseEvent, target: IssueTemplateChild): void {
|
||||
showPopup(
|
||||
IssueTemplateChildEditor,
|
||||
{
|
||||
@ -48,26 +48,32 @@
|
||||
childIssue: target
|
||||
},
|
||||
eventToHTMLElement(evt),
|
||||
(evt: IssueTemplateChild | undefined | null) => {
|
||||
(evt: ['update' | 'delete', IssueTemplateChild] | undefined | null) => {
|
||||
if (evt != null) {
|
||||
const pos = issues.findIndex((it) => it.id === evt.id)
|
||||
const pos = issues.findIndex((it) => it.id === target.id)
|
||||
if (pos !== -1) {
|
||||
issues[pos] = evt
|
||||
dispatch('update-issue', evt)
|
||||
if (evt[0] === 'delete') {
|
||||
issues.splice(pos, 1)
|
||||
issues = issues
|
||||
dispatch('update-issues', issues)
|
||||
} else {
|
||||
issues[pos] = evt[1]
|
||||
dispatch('update-issue', evt[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function resetDrag () {
|
||||
function resetDrag (): void {
|
||||
dragId = null
|
||||
dragIndex = null
|
||||
hoveringIndex = null
|
||||
}
|
||||
|
||||
function handleDragStart (ev: DragEvent, index: number, item: IssueTemplateChild) {
|
||||
if (ev.dataTransfer) {
|
||||
function handleDragStart (ev: DragEvent, index: number, item: IssueTemplateChild): void {
|
||||
if (ev.dataTransfer != null) {
|
||||
ev.dataTransfer.effectAllowed = 'move'
|
||||
ev.dataTransfer.dropEffect = 'move'
|
||||
dragIndex = index
|
||||
@ -75,8 +81,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
function handleDrop (ev: DragEvent, toIndex: number) {
|
||||
if (ev.dataTransfer && dragIndex !== null && toIndex !== dragIndex) {
|
||||
function handleDrop (ev: DragEvent, toIndex: number): void {
|
||||
if (ev.dataTransfer != null && dragIndex !== null && toIndex !== dragIndex) {
|
||||
ev.dataTransfer.dropEffect = 'move'
|
||||
|
||||
dispatch('move', { id: dragId, toIndex })
|
||||
@ -98,7 +104,7 @@
|
||||
let currentProject: Project | undefined = undefined
|
||||
|
||||
function getIssueTemplateId (currentProject: Project | undefined, issue: IssueTemplateChild): string {
|
||||
return currentProject
|
||||
return currentProject !== undefined
|
||||
? `${currentProject.identifier}-${issues.findIndex((it) => it.id === issue.id)}`
|
||||
: `${issues.findIndex((it) => it.id === issue.id)}}`
|
||||
}
|
||||
@ -175,6 +181,10 @@
|
||||
kind={'link'}
|
||||
bind:value={issue.kind}
|
||||
baseClass={tracker.class.Issue}
|
||||
on:change={(evt) => {
|
||||
dispatch('update-issue', { id: issue.id, kind: evt.detail })
|
||||
issue.kind = evt.detail
|
||||
}}
|
||||
/>
|
||||
<EstimationEditor
|
||||
kind={'link'}
|
||||
|
@ -35,8 +35,7 @@
|
||||
let isCollapsed = false
|
||||
let isCreating = false
|
||||
|
||||
function handleIssueSwap (ev: CustomEvent<{ id: Ref<Issue>, toIndex: number }>) {
|
||||
if (children) {
|
||||
function handleIssueSwap (ev: CustomEvent<{ id: Ref<Issue>, toIndex: number }>): void {
|
||||
const { id, toIndex } = ev.detail
|
||||
const index = children.findIndex((p) => p.id === id)
|
||||
if (index !== -1 && index !== toIndex) {
|
||||
@ -47,7 +46,6 @@
|
||||
dispatch('update-issues', children)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
afterUpdate(() => dispatch('changeContent'))
|
||||
|
||||
@ -96,6 +94,7 @@
|
||||
{project}
|
||||
on:move={handleIssueSwap}
|
||||
on:update-issue
|
||||
on:update-issues
|
||||
/>
|
||||
</Scroller>
|
||||
</div>
|
||||
|
@ -14,8 +14,10 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { generateId, Ref, WithLookup } from '@hcengineering/core'
|
||||
import { AttributeBarEditor, getClient, KeyedAttribute } from '@hcengineering/presentation'
|
||||
import { AttributeBarEditor, createQuery, getClient, KeyedAttribute } from '@hcengineering/presentation'
|
||||
import tags, { TagElement, TagReference } from '@hcengineering/tags'
|
||||
import task, { Project } from '@hcengineering/task'
|
||||
import { TaskKindSelector } from '@hcengineering/task-resources'
|
||||
import type { IssueTemplate } from '@hcengineering/tracker'
|
||||
import { Component, Label } from '@hcengineering/ui'
|
||||
import { getFiltredKeys, isCollectionAttr } from '@hcengineering/view-resources'
|
||||
@ -37,7 +39,7 @@
|
||||
keys = filtredKeys.filter((key) => !isCollectionAttr(hierarchy, key))
|
||||
}
|
||||
|
||||
$: updateKeys(['title', 'description', 'priority', 'number', 'assignee', 'component', 'milestone'])
|
||||
$: updateKeys(['title', 'description', 'priority', 'number', 'assignee', 'component', 'milestone', 'kind'])
|
||||
|
||||
const key: KeyedAttribute = {
|
||||
key: 'labels',
|
||||
@ -62,13 +64,37 @@
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let currentProject: Project | undefined
|
||||
const spaceQuery = createQuery()
|
||||
spaceQuery.query(tracker.class.Project, { _id: issue.space }, (res) => {
|
||||
currentProject = res[0]
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="popupPanel-body__aside-grid">
|
||||
<span class="labelOnPanel">
|
||||
<Label label={task.string.TaskType} />
|
||||
</span>
|
||||
<TaskKindSelector
|
||||
projectType={currentProject?.type}
|
||||
value={issue.kind}
|
||||
baseClass={tracker.class.Issue}
|
||||
justify={'left'}
|
||||
width={'100%'}
|
||||
size={'medium'}
|
||||
kind={'link'}
|
||||
showAlways
|
||||
on:change={async (evt) => {
|
||||
if (evt.detail !== undefined) {
|
||||
await client.update(issue, { kind: evt.detail })
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span class="labelOnPanel">
|
||||
<Label label={tracker.string.Priority} />
|
||||
</span>
|
||||
<PriorityEditor value={issue} size={'medium'} shouldShowLabel />
|
||||
<PriorityEditor value={issue} size={'medium'} justify={'left'} width={'100%'} shouldShowLabel />
|
||||
|
||||
<span class="labelOnPanel">
|
||||
<Label label={tracker.string.Assignee} />
|
||||
|
@ -6,13 +6,13 @@ import { convertEstimation } from '../../tracker/tracker.utils'
|
||||
export class TemplateDetailsPage extends CommonTrackerPage {
|
||||
inputTitle = (): Locator => this.page.locator('div.popupPanel-body input[type="text"]')
|
||||
inputDescription = (): Locator => this.page.locator('div.popupPanel-body div.textInput p')
|
||||
buttonPriority = (): Locator => this.page.locator('//span[text()="Priority"]/../button[1]//span')
|
||||
buttonAssignee = (): Locator => this.page.locator('(//span[text()="Assignee"]/../div/button)[1]')
|
||||
buttonPriority = (): Locator => this.page.locator('//span[text()="Priority"]/following-sibling::button[1]//span')
|
||||
buttonAssignee = (): Locator => this.page.locator('//span[text()="Assignee"]/following-sibling::div[1]/button/span')
|
||||
textLabels = (dataLabels: string): Locator => this.page.locator('div.menu-group span', { hasText: dataLabels })
|
||||
buttonAddLabel = (): Locator => this.page.locator('//span[text()="Labels"]/../button[2]//span')
|
||||
buttonComponent = (): Locator => this.page.locator('//span[text()="Component"]/../div/div/button')
|
||||
buttonEstimation = (): Locator => this.page.locator('(//span[text()="Estimation"]/../div/button)[3]')
|
||||
buttonDueDate = (): Locator => this.page.locator('(//span[text()="Due date"]/../div/button)[2]')
|
||||
buttonAddLabel = (): Locator => this.page.locator('//span[text()="Labels"]/following-sibling::button[1]//span')
|
||||
buttonComponent = (): Locator => this.page.locator('//span[text()="Component"]/following-sibling::div[1]/div/button')
|
||||
buttonEstimation = (): Locator => this.page.locator('//span[text()="Estimation"]/following-sibling::div[1]/button')
|
||||
buttonDueDate = (): Locator => this.page.locator('//span[text()="Due date"]/following-sibling::div[1]/button')
|
||||
buttonSaveDueDate = (): Locator => this.page.locator('div.footer > button')
|
||||
activityContent = (): Locator => this.page.locator('div.grid div.content')
|
||||
buttonDelete = (): Locator => this.page.locator('button[class*="menuItem"] > span', { hasText: 'Delete' })
|
||||
|
Loading…
Reference in New Issue
Block a user