mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-23 10:05:01 +02:00
List extract (#2505)
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import presentation, { createQuery, getClient, MessageViewer } from '@hcengineering/presentation'
|
||||
import setting, { settingId } from '@hcengineering/setting'
|
||||
import type { Issue, IssuesGrouping, IssuesOrdering, IssueStatus, Team } from '@hcengineering/tracker'
|
||||
import type { Issue, IssueStatus, Team } from '@hcengineering/tracker'
|
||||
import {
|
||||
Button,
|
||||
EditBox,
|
||||
@@ -33,14 +33,7 @@
|
||||
showPopup,
|
||||
Spinner
|
||||
} from '@hcengineering/ui'
|
||||
import {
|
||||
ContextMenu,
|
||||
focusStore,
|
||||
ListSelectionProvider,
|
||||
SelectDirection,
|
||||
UpDownNavigator,
|
||||
viewOptionsStore
|
||||
} from '@hcengineering/view-resources'
|
||||
import { ContextMenu, UpDownNavigator } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
|
||||
import { generateIssueShortLink, getIssueId } from '../../../issues'
|
||||
import tracker from '../../../plugin'
|
||||
@@ -49,8 +42,6 @@
|
||||
import CopyToClipboard from './CopyToClipboard.svelte'
|
||||
import SubIssues from './SubIssues.svelte'
|
||||
import SubIssueSelector from './SubIssueSelector.svelte'
|
||||
import { groupBy as groupByFunc, issuesOrderKeyMap, issuesSortOrderMap } from '../../../utils'
|
||||
import contact from '@hcengineering/contact'
|
||||
|
||||
export let _id: Ref<Issue>
|
||||
export let _class: Ref<Class<Issue>>
|
||||
@@ -71,14 +62,6 @@
|
||||
let isEditing = false
|
||||
let descriptionBox: AttachmentStyledBox
|
||||
|
||||
let groupBy: IssuesGrouping
|
||||
let orderBy: IssuesOrdering
|
||||
let shouldShowSubIssues: boolean
|
||||
$: ({ groupBy, orderBy, shouldShowSubIssues } = $viewOptionsStore)
|
||||
$: orderByKey = issuesOrderKeyMap[orderBy]
|
||||
$: subIssuesQuery = shouldShowSubIssues ? {} : { attachedTo: tracker.ids.NoParent }
|
||||
$: query = { space: issue?.space }
|
||||
|
||||
const notificationClient = getResource(notification.function.GetNotificationClient).then((res) => res())
|
||||
|
||||
$: read(_id)
|
||||
@@ -126,72 +109,6 @@
|
||||
$: isDescriptionEmpty = !new DOMParser().parseFromString(description, 'text/html').documentElement.innerText?.trim()
|
||||
$: parentIssue = issue?.$lookup?.attachedTo
|
||||
|
||||
let issues: WithLookup<Issue>[] = []
|
||||
let neighbourIssues: Issue[] = []
|
||||
const issuesQuery = createQuery()
|
||||
const subIssuesQueryClient = createQuery()
|
||||
|
||||
$: if (parentIssue) {
|
||||
subIssuesQueryClient.query(
|
||||
tracker.class.Issue,
|
||||
{ attachedTo: parentIssue?._id },
|
||||
async (result) => (neighbourIssues = result ?? []),
|
||||
{
|
||||
sort: { rank: SortingOrder.Descending }
|
||||
}
|
||||
)
|
||||
} else {
|
||||
issuesQuery.query(
|
||||
tracker.class.Issue,
|
||||
{ ...subIssuesQuery, ...query },
|
||||
(result) => {
|
||||
issues = result
|
||||
},
|
||||
{
|
||||
sort: { [orderByKey]: issuesSortOrderMap[orderByKey] },
|
||||
lookup: {
|
||||
assignee: contact.class.Employee,
|
||||
status: tracker.class.IssueStatus,
|
||||
space: tracker.class.Team,
|
||||
sprint: tracker.class.Sprint,
|
||||
_id: {
|
||||
subIssues: tracker.class.Issue
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
$: groupedIssues = groupByFunc(issues, groupBy)
|
||||
$: flatGroupedIssues = Object.values(groupedIssues ?? {}).flat(1)
|
||||
$: issuesToNavigate = parentIssue ? neighbourIssues : flatGroupedIssues
|
||||
|
||||
const listProvider = new ListSelectionProvider((offset: 1 | -1 | 0, of?: Doc, dir?: SelectDirection) => {
|
||||
if (dir === 'vertical') {
|
||||
if (groupedIssues) {
|
||||
const selectedRowIndex = listProvider.current($focusStore)
|
||||
let position =
|
||||
(of !== undefined ? issuesToNavigate.findIndex((x) => x._id === of?._id) : selectedRowIndex) ?? -1
|
||||
|
||||
position -= offset
|
||||
|
||||
if (position < 0) {
|
||||
position = 0
|
||||
}
|
||||
|
||||
if (position >= issuesToNavigate.length) {
|
||||
position = issuesToNavigate.length - 1
|
||||
}
|
||||
|
||||
listProvider.updateFocus(issuesToNavigate[position])
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$: if (issue) listProvider.updateFocus(issue)
|
||||
|
||||
$: listProvider.update(issuesToNavigate)
|
||||
|
||||
function edit (ev: MouseEvent) {
|
||||
ev.preventDefault()
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
import { flip } from 'svelte/animate'
|
||||
import { getIssueId } from '../../../issues'
|
||||
import tracker from '../../../plugin'
|
||||
import { subIssueListProvider } from '../../../utils'
|
||||
import Circles from '../../icons/Circles.svelte'
|
||||
import AssigneeEditor from '../AssigneeEditor.svelte'
|
||||
import DueDateEditor from '../DueDateEditor.svelte'
|
||||
@@ -41,6 +42,7 @@
|
||||
|
||||
function openIssue (target: Issue) {
|
||||
dispatch('issue-focus', target)
|
||||
subIssueListProvider(issues, target._id)
|
||||
showPanel(tracker.component.EditIssue, target._id, target._class, 'content')
|
||||
}
|
||||
|
||||
@@ -120,7 +122,7 @@
|
||||
{issue.title}
|
||||
</span>
|
||||
{#if issue.subIssues > 0}
|
||||
<SubIssuesSelector value={issue} {currentTeam} statuses={issueStatuses.get(issue.space)} />
|
||||
<SubIssuesSelector value={issue} {currentTeam} />
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex-center flex-no-shrink">
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
import tracker from '../../../plugin'
|
||||
import { getIssueId } from '../../../issues'
|
||||
import IssueStatusIcon from '../IssueStatusIcon.svelte'
|
||||
import { ListSelectionProvider } from '@hcengineering/view-resources'
|
||||
|
||||
export let issue: WithLookup<Issue>
|
||||
|
||||
@@ -48,6 +49,7 @@
|
||||
function openParentIssue () {
|
||||
if (parentIssue) {
|
||||
closeTooltip()
|
||||
ListSelectionProvider.Pop()
|
||||
openIssue(parentIssue._id)
|
||||
}
|
||||
}
|
||||
@@ -138,7 +140,7 @@
|
||||
bind:this={subIssuesElement}
|
||||
class="flex-center sub-issues cursor-pointer"
|
||||
use:tooltip={{ label: tracker.string.OpenSubIssues, direction: 'bottom' }}
|
||||
on:click|preventDefault={areSubIssuesLoading ? undefined : showSubIssues}
|
||||
on:click|preventDefault={showSubIssues}
|
||||
>
|
||||
<span class="overflow-label">{subIssues?.length}</span>
|
||||
<div class="ml-2">
|
||||
|
||||
@@ -13,17 +13,26 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Doc, Ref, WithLookup } from '@hcengineering/core'
|
||||
import { Ref, SortingOrder, WithLookup } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { Issue, IssueStatus, Team } from '@hcengineering/tracker'
|
||||
import { ButtonKind, ButtonSize, getPlatformColor } from '@hcengineering/ui'
|
||||
import { Button, closeTooltip, ProgressCircle, SelectPopup, showPanel, showPopup } from '@hcengineering/ui'
|
||||
import { updateFocus } from '@hcengineering/view-resources'
|
||||
import tracker from '../../../plugin'
|
||||
import {
|
||||
Button,
|
||||
ButtonKind,
|
||||
ButtonSize,
|
||||
closeTooltip,
|
||||
getPlatformColor,
|
||||
ProgressCircle,
|
||||
SelectPopup,
|
||||
showPanel,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import { getIssueId } from '../../../issues'
|
||||
import tracker from '../../../plugin'
|
||||
import { subIssueListProvider } from '../../../utils'
|
||||
|
||||
export let value: WithLookup<Issue>
|
||||
export let currentTeam: Team | undefined
|
||||
export let statuses: WithLookup<IssueStatus>[] | undefined
|
||||
|
||||
export let kind: ButtonKind = 'link-bordered'
|
||||
export let size: ButtonSize = 'inline'
|
||||
@@ -32,21 +41,35 @@
|
||||
|
||||
let btn: HTMLElement
|
||||
|
||||
let subIssues: Issue[] | undefined
|
||||
let doneStatus: Ref<Doc> | undefined
|
||||
let subIssues: Issue[] = []
|
||||
let countComplate: number = 0
|
||||
|
||||
const query = createQuery()
|
||||
const statusesQuery = createQuery()
|
||||
|
||||
let statuses: WithLookup<IssueStatus>[] = []
|
||||
|
||||
$: if (value.$lookup?.subIssues !== undefined) {
|
||||
query.unsubscribe()
|
||||
subIssues = value.$lookup.subIssues as Issue[]
|
||||
subIssues.sort((a, b) => (a.rank ?? '').localeCompare(b.rank ?? ''))
|
||||
} else {
|
||||
query.query(tracker.class.Issue, { attachedTo: value._id }, (res) => (subIssues = res), {
|
||||
sort: { rank: SortingOrder.Ascending }
|
||||
})
|
||||
}
|
||||
|
||||
statusesQuery.query(tracker.class.IssueStatus, {}, (res) => (statuses = res), {
|
||||
lookup: { category: tracker.class.IssueStatusCategory }
|
||||
})
|
||||
|
||||
$: if (statuses && subIssues) {
|
||||
doneStatus = statuses.find((s) => s.category === tracker.issueStatusCategory.Completed)?._id ?? undefined
|
||||
if (doneStatus) countComplate = subIssues.filter((si) => si.status === doneStatus).length
|
||||
const doneStatuses = statuses.filter((s) => s.category === tracker.issueStatusCategory.Completed).map((p) => p._id)
|
||||
countComplate = subIssues.filter((si) => doneStatuses.includes(si.status)).length
|
||||
}
|
||||
$: hasSubIssues = (subIssues?.length ?? 0) > 0
|
||||
|
||||
function getIssueStatusIcon (issue: Issue) {
|
||||
function getIssueStatusIcon (issue: Issue, statuses: WithLookup<IssueStatus>[] | undefined) {
|
||||
const status = statuses?.find((s) => issue.status === s._id)
|
||||
const category = status?.$lookup?.category
|
||||
const color = status?.color ?? category?.color
|
||||
@@ -59,9 +82,11 @@
|
||||
|
||||
function openIssue (target: Ref<Issue>) {
|
||||
if (target !== value._id) {
|
||||
subIssueListProvider(subIssues, target)
|
||||
showPanel(tracker.component.EditIssue, target, value._class, 'content')
|
||||
}
|
||||
}
|
||||
|
||||
function showSubIssues () {
|
||||
if (subIssues) {
|
||||
closeTooltip()
|
||||
@@ -71,7 +96,7 @@
|
||||
value: subIssues.map((iss) => {
|
||||
const text = currentTeam ? `${getIssueId(currentTeam, iss)} ${iss.title}` : iss.title
|
||||
|
||||
return { id: iss._id, text, isSelected: iss._id === value._id, ...getIssueStatusIcon(iss) }
|
||||
return { id: iss._id, text, isSelected: iss._id === value._id, ...getIssueStatusIcon(iss, statuses) }
|
||||
}),
|
||||
width: 'large'
|
||||
},
|
||||
@@ -86,12 +111,6 @@
|
||||
},
|
||||
(selectedIssue) => {
|
||||
selectedIssue !== undefined && openIssue(selectedIssue)
|
||||
},
|
||||
(selectedIssue) => {
|
||||
const focus = subIssues?.find((it) => it._id === selectedIssue.id)
|
||||
if (focus !== undefined) {
|
||||
updateFocus({ focus })
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user