mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-13 19:58:09 +00:00
Tracker: Fix kanban query (#2204)
Signed-off-by: Dvinyanin Alexandr <dvinyanin.alexandr@gmail.com>
This commit is contained in:
parent
800e364ae5
commit
171921a46c
@ -287,7 +287,7 @@
|
|||||||
<div class="kanban-container top-divider">
|
<div class="kanban-container top-divider">
|
||||||
<ScrollBox>
|
<ScrollBox>
|
||||||
<div class="kanban-content">
|
<div class="kanban-content">
|
||||||
{#each states as state, si}
|
{#each states as state, si (state._id)}
|
||||||
{@const stateObjects = getStateObjects(objects, state, dragCard)}
|
{@const stateObjects = getStateObjects(objects, state, dragCard)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -15,17 +15,23 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import contact from '@anticrm/contact'
|
import contact from '@anticrm/contact'
|
||||||
import { Class, Doc, DocumentQuery, Lookup, Ref, SortingOrder, WithLookup } from '@anticrm/core'
|
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 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 { 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 { focusStore, ListSelectionProvider, SelectDirection, selectionStore } from '@anticrm/view-resources'
|
||||||
import ActionContext from '@anticrm/view-resources/src/components/ActionContext.svelte'
|
import ActionContext from '@anticrm/view-resources/src/components/ActionContext.svelte'
|
||||||
import Menu from '@anticrm/view-resources/src/components/Menu.svelte'
|
import Menu from '@anticrm/view-resources/src/components/Menu.svelte'
|
||||||
import { onMount } from 'svelte'
|
import { onMount } from 'svelte'
|
||||||
import tracker from '../../plugin'
|
import tracker from '../../plugin'
|
||||||
import { getKanbanStatuses, issuesSortOrderMap } from '../../utils'
|
import {
|
||||||
|
getIssueStatusStates,
|
||||||
|
getKanbanStatuses,
|
||||||
|
getPriorityStates,
|
||||||
|
issuesGroupBySorting,
|
||||||
|
issuesSortOrderMap
|
||||||
|
} from '../../utils'
|
||||||
import CreateIssue from '../CreateIssue.svelte'
|
import CreateIssue from '../CreateIssue.svelte'
|
||||||
import ProjectEditor from '../projects/ProjectEditor.svelte'
|
import ProjectEditor from '../projects/ProjectEditor.svelte'
|
||||||
import AssigneePresenter from './AssigneePresenter.svelte'
|
import AssigneePresenter from './AssigneePresenter.svelte'
|
||||||
@ -53,13 +59,13 @@
|
|||||||
const spaceQuery = createQuery()
|
const spaceQuery = createQuery()
|
||||||
const statusesQuery = createQuery()
|
const statusesQuery = createQuery()
|
||||||
|
|
||||||
const client = getClient()
|
|
||||||
let currentTeam: Team | undefined
|
let currentTeam: Team | undefined
|
||||||
$: spaceQuery.query(tracker.class.Team, { _id: currentSpace }, (res) => {
|
$: spaceQuery.query(tracker.class.Team, { _id: currentSpace }, (res) => {
|
||||||
currentTeam = res.shift()
|
currentTeam = res.shift()
|
||||||
})
|
})
|
||||||
|
|
||||||
let issueStatuses: WithLookup<IssueStatus>[] | undefined
|
let issueStatuses: WithLookup<IssueStatus>[] | undefined
|
||||||
|
$: issueStatusStates = getIssueStatusStates(issueStatuses)
|
||||||
$: statusesQuery.query(
|
$: statusesQuery.query(
|
||||||
tracker.class.IssueStatus,
|
tracker.class.IssueStatus,
|
||||||
{ attachedTo: currentSpace },
|
{ 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>
|
</script>
|
||||||
|
|
||||||
{#await getKanbanStatuses(client, groupBy, resultQuery, shouldShowEmptyGroups) then states}
|
{#if !states?.length}
|
||||||
|
<Loading />
|
||||||
|
{:else}
|
||||||
<ActionContext
|
<ActionContext
|
||||||
context={{
|
context={{
|
||||||
mode: 'browser'
|
mode: 'browser'
|
||||||
@ -209,7 +252,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</Kanban>
|
</Kanban>
|
||||||
{/await}
|
{/if}
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.names {
|
.names {
|
||||||
|
@ -94,7 +94,7 @@
|
|||||||
<div class="value">
|
<div class="value">
|
||||||
<MiniToggle bind:on={shouldShowSubIssues} on:change={updateOptions} />
|
<MiniToggle bind:on={shouldShowSubIssues} on:change={updateOptions} />
|
||||||
</div>
|
</div>
|
||||||
{#if _groupBy === IssuesGrouping.Status}
|
{#if _groupBy === IssuesGrouping.Status || _groupBy === IssuesGrouping.Priority}
|
||||||
<span class="label"><Label label={tracker.string.ShowEmptyGroups} /></span>
|
<span class="label"><Label label={tracker.string.ShowEmptyGroups} /></span>
|
||||||
<div class="value">
|
<div class="value">
|
||||||
<MiniToggle bind:on={_shouldShowEmptyGroups} on:change={updateOptions} />
|
<MiniToggle bind:on={_shouldShowEmptyGroups} on:change={updateOptions} />
|
||||||
|
@ -13,8 +13,10 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import { SortingOrder, SortingQuery } from '@anticrm/core'
|
||||||
import { Asset, IntlString } from '@anticrm/platform'
|
import { Asset, IntlString } from '@anticrm/platform'
|
||||||
import {
|
import {
|
||||||
|
Issue,
|
||||||
IssuePriority,
|
IssuePriority,
|
||||||
IssuesDateModificationPeriod,
|
IssuesDateModificationPeriod,
|
||||||
IssuesGrouping,
|
IssuesGrouping,
|
||||||
@ -76,3 +78,11 @@ export const defaultPriorities = [
|
|||||||
IssuePriority.Medium,
|
IssuePriority.Medium,
|
||||||
IssuePriority.Low
|
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]: {}
|
||||||
|
}
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import contact, { Employee, formatName } from '@anticrm/contact'
|
import { Employee, formatName } from '@anticrm/contact'
|
||||||
import { DocumentQuery, Ref, SortingOrder, TxOperations } from '@anticrm/core'
|
import { DocumentQuery, Ref, SortingOrder, WithLookup } from '@anticrm/core'
|
||||||
import { TypeState } from '@anticrm/kanban'
|
import { TypeState } from '@anticrm/kanban'
|
||||||
import { Asset, IntlString, translate } from '@anticrm/platform'
|
import { Asset, IntlString, translate } from '@anticrm/platform'
|
||||||
import {
|
import {
|
||||||
@ -340,35 +340,13 @@ export function getCategories (
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getKanbanStatuses (
|
export async function getKanbanStatuses (
|
||||||
client: TxOperations,
|
|
||||||
groupBy: IssuesGrouping,
|
groupBy: IssuesGrouping,
|
||||||
issueQuery: DocumentQuery<Issue>,
|
issues: Array<WithLookup<Issue>>
|
||||||
shouldShowEmptyGroups: boolean
|
|
||||||
): Promise<TypeState[]> {
|
): Promise<TypeState[]> {
|
||||||
if (groupBy === IssuesGrouping.NoGrouping) {
|
if (groupBy === IssuesGrouping.NoGrouping) {
|
||||||
return [{ _id: undefined, color: 0, title: await translate(tracker.string.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) {
|
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 states = issues.reduce<TypeState[]>((result, issue) => {
|
||||||
const { priority } = issue
|
const { priority } = issue
|
||||||
if (result.find(({ _id }) => _id === priority) !== undefined) return result
|
if (result.find(({ _id }) => _id === priority) !== undefined) return result
|
||||||
@ -390,10 +368,6 @@ export async function getKanbanStatuses (
|
|||||||
return states
|
return states
|
||||||
}
|
}
|
||||||
if (groupBy === IssuesGrouping.Status) {
|
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) => {
|
return issues.reduce<TypeState[]>((result, issue) => {
|
||||||
const status = issue.$lookup?.status
|
const status = issue.$lookup?.status
|
||||||
if (status === undefined || result.find(({ _id }) => _id === status._id) !== undefined) return result
|
if (status === undefined || result.find(({ _id }) => _id === status._id) !== undefined) return result
|
||||||
@ -410,10 +384,6 @@ export async function getKanbanStatuses (
|
|||||||
}, [])
|
}, [])
|
||||||
}
|
}
|
||||||
if (groupBy === IssuesGrouping.Assignee) {
|
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, {})
|
const noAssignee = await translate(tracker.string.NoAssignee, {})
|
||||||
return issues.reduce<TypeState[]>((result, issue) => {
|
return issues.reduce<TypeState[]>((result, issue) => {
|
||||||
if (result.find(({ _id }) => _id === issue.assignee) !== undefined) return result
|
if (result.find(({ _id }) => _id === issue.assignee) !== undefined) return result
|
||||||
@ -429,10 +399,6 @@ export async function getKanbanStatuses (
|
|||||||
}, [])
|
}, [])
|
||||||
}
|
}
|
||||||
if (groupBy === IssuesGrouping.Project) {
|
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, {})
|
const noProject = await translate(tracker.string.NoProject, {})
|
||||||
return issues.reduce<TypeState[]>((result, issue) => {
|
return issues.reduce<TypeState[]>((result, issue) => {
|
||||||
if (result.find(({ _id }) => _id === issue.project) !== undefined) return result
|
if (result.find(({ _id }) => _id === issue.project) !== undefined) return result
|
||||||
@ -449,3 +415,23 @@ export async function getKanbanStatuses (
|
|||||||
}
|
}
|
||||||
return []
|
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
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user