Tracker: Fix kanban query (#2204)

Signed-off-by: Dvinyanin Alexandr <dvinyanin.alexandr@gmail.com>
This commit is contained in:
Alex 2022-07-06 13:11:32 +07:00 committed by GitHub
parent 800e364ae5
commit 171921a46c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 85 additions and 46 deletions

View File

@ -287,7 +287,7 @@
<div class="kanban-container top-divider">
<ScrollBox>
<div class="kanban-content">
{#each states as state, si}
{#each states as state, si (state._id)}
{@const stateObjects = getStateObjects(objects, state, dragCard)}
<div

View File

@ -15,17 +15,23 @@
<script lang="ts">
import contact from '@anticrm/contact'
import { Class, Doc, DocumentQuery, Lookup, Ref, SortingOrder, WithLookup } from '@anticrm/core'
import { Kanban } from '@anticrm/kanban'
import { Kanban, TypeState } from '@anticrm/kanban'
import notification from '@anticrm/notification'
import { createQuery, getClient } from '@anticrm/presentation'
import { createQuery } from '@anticrm/presentation'
import { Issue, IssuesGrouping, IssuesOrdering, IssueStatus, Team, ViewOptions } from '@anticrm/tracker'
import { Button, Component, Icon, IconAdd, showPanel, showPopup, getPlatformColor } from '@anticrm/ui'
import { Button, Component, Icon, IconAdd, showPanel, showPopup, getPlatformColor, Loading } from '@anticrm/ui'
import { focusStore, ListSelectionProvider, SelectDirection, selectionStore } from '@anticrm/view-resources'
import ActionContext from '@anticrm/view-resources/src/components/ActionContext.svelte'
import Menu from '@anticrm/view-resources/src/components/Menu.svelte'
import { onMount } from 'svelte'
import tracker from '../../plugin'
import { getKanbanStatuses, issuesSortOrderMap } from '../../utils'
import {
getIssueStatusStates,
getKanbanStatuses,
getPriorityStates,
issuesGroupBySorting,
issuesSortOrderMap
} from '../../utils'
import CreateIssue from '../CreateIssue.svelte'
import ProjectEditor from '../projects/ProjectEditor.svelte'
import AssigneePresenter from './AssigneePresenter.svelte'
@ -53,13 +59,13 @@
const spaceQuery = createQuery()
const statusesQuery = createQuery()
const client = getClient()
let currentTeam: Team | undefined
$: spaceQuery.query(tracker.class.Team, { _id: currentSpace }, (res) => {
currentTeam = res.shift()
})
let issueStatuses: WithLookup<IssueStatus>[] | undefined
$: issueStatusStates = getIssueStatusStates(issueStatuses)
$: statusesQuery.query(
tracker.class.IssueStatus,
{ attachedTo: currentSpace },
@ -105,9 +111,46 @@
}
)
}
const issuesQuery = createQuery()
let issueStates: TypeState[] = []
$: issuesQuery.query(
tracker.class.Issue,
resultQuery,
async (result) => {
issueStates = await getKanbanStatuses(groupBy, result)
},
{
lookup: {
status: [tracker.class.IssueStatus, { category: tracker.class.IssueStatusCategory }],
project: tracker.class.Project,
assignee: contact.class.Employee
},
sort: issuesGroupBySorting[groupBy]
}
)
let priorityStates: TypeState[] = []
getPriorityStates().then((states) => {
priorityStates = states
})
function getIssueStates (
groupBy: IssuesGrouping,
showEmptyGroups: boolean,
states: TypeState[],
statusStates: TypeState[],
priorityStates: TypeState[]
) {
if (!showEmptyGroups) return states
if (groupBy === IssuesGrouping.Status) return statusStates
if (groupBy === IssuesGrouping.Priority) return priorityStates
return []
}
$: states = getIssueStates(groupBy, shouldShowEmptyGroups, issueStates, issueStatusStates, priorityStates)
</script>
{#await getKanbanStatuses(client, groupBy, resultQuery, shouldShowEmptyGroups) then states}
{#if !states?.length}
<Loading />
{:else}
<ActionContext
context={{
mode: 'browser'
@ -209,7 +252,7 @@
</div>
</svelte:fragment>
</Kanban>
{/await}
{/if}
<style lang="scss">
.names {

View File

@ -94,7 +94,7 @@
<div class="value">
<MiniToggle bind:on={shouldShowSubIssues} on:change={updateOptions} />
</div>
{#if _groupBy === IssuesGrouping.Status}
{#if _groupBy === IssuesGrouping.Status || _groupBy === IssuesGrouping.Priority}
<span class="label"><Label label={tracker.string.ShowEmptyGroups} /></span>
<div class="value">
<MiniToggle bind:on={_shouldShowEmptyGroups} on:change={updateOptions} />

View File

@ -13,8 +13,10 @@
// limitations under the License.
//
import { SortingOrder, SortingQuery } from '@anticrm/core'
import { Asset, IntlString } from '@anticrm/platform'
import {
Issue,
IssuePriority,
IssuesDateModificationPeriod,
IssuesGrouping,
@ -76,3 +78,11 @@ export const defaultPriorities = [
IssuePriority.Medium,
IssuePriority.Low
]
export const issuesGroupBySorting: Record<IssuesGrouping, SortingQuery<Issue>> = {
[IssuesGrouping.Status]: { '$lookup.status.rank': SortingOrder.Ascending },
[IssuesGrouping.Assignee]: { '$lookup.assignee.name': SortingOrder.Ascending },
[IssuesGrouping.Priority]: { priority: SortingOrder.Ascending },
[IssuesGrouping.Project]: { '$lookup.project.label': SortingOrder.Ascending },
[IssuesGrouping.NoGrouping]: {}
}

View File

@ -13,8 +13,8 @@
// limitations under the License.
//
import contact, { Employee, formatName } from '@anticrm/contact'
import { DocumentQuery, Ref, SortingOrder, TxOperations } from '@anticrm/core'
import { Employee, formatName } from '@anticrm/contact'
import { DocumentQuery, Ref, SortingOrder, WithLookup } from '@anticrm/core'
import { TypeState } from '@anticrm/kanban'
import { Asset, IntlString, translate } from '@anticrm/platform'
import {
@ -340,35 +340,13 @@ export function getCategories (
}
export async function getKanbanStatuses (
client: TxOperations,
groupBy: IssuesGrouping,
issueQuery: DocumentQuery<Issue>,
shouldShowEmptyGroups: boolean
issues: Array<WithLookup<Issue>>
): Promise<TypeState[]> {
if (groupBy === IssuesGrouping.NoGrouping) {
return [{ _id: undefined, color: 0, title: await translate(tracker.string.NoGrouping, {}) }]
}
if (groupBy === IssuesGrouping.Status && shouldShowEmptyGroups) {
return (
await client.findAll(
tracker.class.IssueStatus,
{ attachedTo: issueQuery.space },
{
lookup: { category: tracker.class.IssueStatusCategory },
sort: { rank: SortingOrder.Ascending }
}
)
).map((status) => ({
_id: status._id,
title: status.name,
color: status.color ?? status.$lookup?.category?.color ?? 0,
icon: status.$lookup?.category?.icon ?? undefined
}))
}
if (groupBy === IssuesGrouping.Priority) {
const issues = await client.findAll(tracker.class.Issue, issueQuery, {
sort: { priority: SortingOrder.Ascending }
})
const states = issues.reduce<TypeState[]>((result, issue) => {
const { priority } = issue
if (result.find(({ _id }) => _id === priority) !== undefined) return result
@ -390,10 +368,6 @@ export async function getKanbanStatuses (
return states
}
if (groupBy === IssuesGrouping.Status) {
const issues = await client.findAll(tracker.class.Issue, issueQuery, {
lookup: { status: [tracker.class.IssueStatus, { category: tracker.class.IssueStatusCategory }] },
sort: { '$lookup.status.rank': SortingOrder.Ascending }
})
return issues.reduce<TypeState[]>((result, issue) => {
const status = issue.$lookup?.status
if (status === undefined || result.find(({ _id }) => _id === status._id) !== undefined) return result
@ -410,10 +384,6 @@ export async function getKanbanStatuses (
}, [])
}
if (groupBy === IssuesGrouping.Assignee) {
const issues = await client.findAll(tracker.class.Issue, issueQuery, {
lookup: { assignee: contact.class.Employee },
sort: { '$lookup.assignee.name': SortingOrder.Ascending }
})
const noAssignee = await translate(tracker.string.NoAssignee, {})
return issues.reduce<TypeState[]>((result, issue) => {
if (result.find(({ _id }) => _id === issue.assignee) !== undefined) return result
@ -429,10 +399,6 @@ export async function getKanbanStatuses (
}, [])
}
if (groupBy === IssuesGrouping.Project) {
const issues = await client.findAll(tracker.class.Issue, issueQuery, {
lookup: { project: tracker.class.Project },
sort: { '$lookup.project.label': SortingOrder.Ascending }
})
const noProject = await translate(tracker.string.NoProject, {})
return issues.reduce<TypeState[]>((result, issue) => {
if (result.find(({ _id }) => _id === issue.project) !== undefined) return result
@ -449,3 +415,23 @@ export async function getKanbanStatuses (
}
return []
}
export function getIssueStatusStates (issueStatuses: Array<WithLookup<IssueStatus>> = []): TypeState[] {
return issueStatuses.map((status) => ({
_id: status._id,
title: status.name,
color: status.color ?? status.$lookup?.category?.color ?? 0,
icon: status.$lookup?.category?.icon ?? undefined
}))
}
export async function getPriorityStates (): Promise<TypeState[]> {
return await Promise.all(
defaultPriorities.map(async (priority) => ({
_id: priority,
title: await translate(issuePriorities[priority].label, {}),
color: 0,
icon: issuePriorities[priority].icon
}))
)
}