UBER-963: Related issues (#3773)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev
2023-10-03 01:20:37 +07:00
committed by GitHub
parent 152c858c1d
commit b2435326fd
31 changed files with 564 additions and 301 deletions
@@ -13,8 +13,8 @@
// limitations under the License.
-->
<script lang="ts">
import core, { SortingOrder, WithLookup } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation'
import core, { IdMap, SortingOrder, StatusCategory, WithLookup, toIdMap } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { Issue, IssueStatus } from '@hcengineering/tracker'
import {
Icon,
@@ -25,7 +25,6 @@
closeTooltip,
getPlatformColorDef,
navigate,
showPopup,
themeStore,
tooltip
} from '@hcengineering/ui'
@@ -33,6 +32,7 @@
import { getIssueId, issueLinkFragmentProvider } from '../../../issues'
import tracker from '../../../plugin'
import IssueStatusIcon from '../IssueStatusIcon.svelte'
import { listIssueStatusOrder } from '../../../utils'
export let issue: WithLookup<Issue>
@@ -56,49 +56,6 @@
}
}
function showSubIssues () {
if (subIssues) {
closeTooltip()
showPopup(
SelectPopup,
{
value: subIssues.map((iss) => {
const project = iss.$lookup?.space
const status = iss.$lookup?.status as WithLookup<IssueStatus>
const icon = status.$lookup?.category?.icon
const color = status.color ?? status.$lookup?.category?.color
return {
id: iss._id,
icon,
isSelected: iss._id === issue._id,
...(project !== undefined ? { text: `${getIssueId(project, iss)} ${iss.title}` } : undefined),
...(color !== undefined ? { iconColor: getPlatformColorDef(color, $themeStore.dark).icon } : undefined)
}
}),
width: 'large'
},
{
getBoundingClientRect: () => {
const rect = subIssuesElement.getBoundingClientRect()
const offsetX = 5
const offsetY = -1
return DOMRect.fromRect({ width: 1, height: 1, x: rect.right + offsetX, y: rect.top + offsetY })
}
},
(selectedIssue) => {
if (selectedIssue !== undefined) {
const issue = subIssues?.find((p) => p._id === selectedIssue)
if (issue) {
openIssue(issue)
}
}
}
)
}
}
$: areSubIssuesLoading = !subIssues
$: parentIssue = issue.$lookup?.attachedTo ? (issue.$lookup?.attachedTo as Issue) : null
$: if (parentIssue && parentIssue.subIssues > 0) {
@@ -119,6 +76,52 @@
}
$: parentStatus = parentIssue ? $statusStore.get(parentIssue.status) : undefined
let categories: IdMap<StatusCategory> = new Map()
getClient()
.findAll(core.class.StatusCategory, {})
.then((res) => {
categories = toIdMap(res)
})
let sortedSubIssues: WithLookup<Issue>[] = []
$: {
if (subIssues !== undefined) {
subIssues.sort(
(a, b) =>
listIssueStatusOrder.indexOf($statusStore.get(a.status)?.category ?? tracker.issueStatusCategory.Backlog) -
listIssueStatusOrder.indexOf($statusStore.get(b.status)?.category ?? tracker.issueStatusCategory.Backlog)
)
sortedSubIssues = subIssues ?? []
}
}
$: subIssueValue = sortedSubIssues.map((iss) => {
const project = iss.$lookup?.space
const status = iss.$lookup?.status as WithLookup<IssueStatus>
const icon = status.$lookup?.category?.icon
const color = status.color ?? status.$lookup?.category?.color
const c = $statusStore.get(iss.status)?.category
const category = c !== undefined ? categories.get(c) : undefined
return {
id: iss._id,
icon,
isSelected: iss._id === issue._id,
...(project !== undefined ? { text: `${getIssueId(project, iss)} ${iss.title}` } : undefined),
...(color !== undefined ? { iconColor: getPlatformColorDef(color, $themeStore.dark).icon } : undefined),
category:
category !== undefined
? {
label: category.label,
icon: category.icon
}
: undefined
}
})
</script>
{#if parentIssue}
@@ -152,8 +155,15 @@
<div
bind:this={subIssuesElement}
class="flex-center sub-issues cursor-pointer"
use:tooltip={{ label: tracker.string.OpenSubIssues, direction: 'bottom' }}
on:click|preventDefault={showSubIssues}
use:tooltip={{
component: SelectPopup,
props: {
value: subIssueValue,
onSelect: openIssue,
showShadow: false,
width: 'large'
}
}}
>
<span class="overflow-label">{subIssues?.length}</span>
<div class="ml-2">
@@ -13,23 +13,14 @@
// limitations under the License.
-->
<script lang="ts">
import { Ref, SortingOrder, Status, WithLookup } from '@hcengineering/core'
import { createQuery } from '@hcengineering/presentation'
import core, { IdMap, Ref, SortingOrder, Status, StatusCategory, WithLookup, toIdMap } from '@hcengineering/core'
import { createQuery, getClient } from '@hcengineering/presentation'
import { Issue, Project } from '@hcengineering/tracker'
import {
Button,
ButtonKind,
ButtonSize,
ProgressCircle,
SelectPopup,
closeTooltip,
showPanel,
showPopup
} from '@hcengineering/ui'
import { Button, ButtonKind, ButtonSize, ProgressCircle, SelectPopup, showPanel } from '@hcengineering/ui'
import { statusStore } from '@hcengineering/view-resources'
import { getIssueId } from '../../../issues'
import tracker from '../../../plugin'
import { subIssueListProvider } from '../../../utils'
import { listIssueStatusOrder, subIssueListProvider } from '../../../utils'
import IssueStatusIcon from '../IssueStatusIcon.svelte'
export let value: WithLookup<Issue>
@@ -46,6 +37,7 @@
$: project = currentProject
let subIssues: Issue[] = []
let _subIssues: Issue[] = []
let countComplete: number = 0
const projectQuery = createQuery()
@@ -64,11 +56,18 @@
$: update(value)
let categories: IdMap<StatusCategory> = new Map()
getClient()
.findAll(core.class.StatusCategory, {})
.then((res) => {
categories = toIdMap(res)
})
function update (value: WithLookup<Issue>): void {
if (value.$lookup?.subIssues !== undefined) {
query.unsubscribe()
subIssues = value.$lookup.subIssues as Issue[]
subIssues.sort((a, b) => (a.rank ?? '').localeCompare(b.rank ?? ''))
} else if (value.subIssues > 0) {
query.query(tracker.class.Issue, { attachedTo: value._id }, (res) => (subIssues = res), {
sort: { rank: SortingOrder.Ascending }
@@ -101,43 +100,38 @@
}
}
function showSubIssues () {
if (subIssues) {
closeTooltip()
showPopup(
SelectPopup,
{
value: subIssues.map((iss) => {
const text = project ? `${getIssueId(project, iss)} ${iss.title}` : iss.title
return {
id: iss._id,
text,
isSelected: iss._id === value._id,
icon: IssueStatusIcon,
iconProps: {
value: $statusStore.get(iss.status),
size: 'small',
fill: undefined
}
}
}),
width: 'large'
},
{
getBoundingClientRect: () => {
const rect = btn.getBoundingClientRect()
const offsetX = 0
const offsetY = 0
return DOMRect.fromRect({ width: 1, height: 1, x: rect.left + offsetX, y: rect.bottom + offsetY })
}
},
(selectedIssue) => {
selectedIssue !== undefined && openIssue(selectedIssue)
}
)
}
$: {
subIssues.sort(
(a, b) =>
listIssueStatusOrder.indexOf($statusStore.get(a.status)?.category ?? tracker.issueStatusCategory.Backlog) -
listIssueStatusOrder.indexOf($statusStore.get(b.status)?.category ?? tracker.issueStatusCategory.Backlog)
)
_subIssues = subIssues
}
$: subIssuesValue = _subIssues.map((iss) => {
const text = project ? `${getIssueId(project, iss)} ${iss.title}` : iss.title
const c = $statusStore.get(iss.status)?.category
const category = c !== undefined ? categories.get(c) : undefined
return {
id: iss._id,
text,
isSelected: iss._id === value._id,
icon: IssueStatusIcon,
iconProps: {
value: $statusStore.get(iss.status),
size: 'small',
fill: undefined
},
category:
category !== undefined
? {
label: category.label,
icon: category.icon
}
: undefined
}
})
</script>
{#if hasSubIssues}
@@ -147,9 +141,14 @@
{kind}
{size}
{justify}
on:click={(ev) => {
ev.stopPropagation()
if (subIssues) showSubIssues()
showTooltip={{
component: SelectPopup,
props: {
value: subIssuesValue,
onSelect: openIssue,
showShadow: false,
width: 'large'
}
}}
>
<svelte:fragment slot="content">