mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-22 19:11:33 +03:00
Rank order (#2150)
Signed-off-by: Dvinyanin Alexandr <dvinyanin.alexandr@gmail.com>
This commit is contained in:
parent
cade773b95
commit
1e21c85379
@ -6,6 +6,9 @@ HR:
|
||||
|
||||
- Allow to change assignee in Kanban
|
||||
|
||||
Tracker:
|
||||
- Manual issues ordering
|
||||
|
||||
## 0.6.29
|
||||
|
||||
Platform:
|
||||
|
@ -494,7 +494,7 @@ export function createModel (builder: Builder): void {
|
||||
id: issuesId,
|
||||
label: tracker.string.Issues,
|
||||
icon: tracker.icon.Issues,
|
||||
component: tracker.component.IssuesView,
|
||||
component: tracker.component.Issues,
|
||||
componentProps: {
|
||||
title: tracker.string.Issues
|
||||
}
|
||||
|
@ -27,7 +27,7 @@
|
||||
export let states: TypeState[] = []
|
||||
export let query: DocumentQuery<Item> = {}
|
||||
export let fieldName: string
|
||||
export let rankFieldName: string
|
||||
export let rankFieldName: string | undefined
|
||||
export let selection: number | undefined = undefined
|
||||
export let checked: Doc[] = []
|
||||
|
||||
@ -57,7 +57,10 @@
|
||||
dragItem?: Item // required for svelte to properly recalculate state.
|
||||
): ExtItem[] {
|
||||
const stateCards = objects.filter((it) => (it as any)[fieldName] === state._id)
|
||||
stateCards.sort((a, b) => (a as any)[rankFieldName]?.localeCompare((b as any)[rankFieldName]))
|
||||
if (rankFieldName !== undefined) {
|
||||
const sortField = rankFieldName
|
||||
stateCards.sort((a, b) => (a as any)[sortField]?.localeCompare((b as any)[sortField]))
|
||||
}
|
||||
return stateCards.map((it, idx, arr) => ({
|
||||
it,
|
||||
prev: arr[idx - 1],
|
||||
@ -96,14 +99,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
const dragCardRank = (dragCard as any)[rankFieldName]
|
||||
if (dragCardInitialRank !== (dragCard as any)[rankFieldName]) {
|
||||
if (rankFieldName !== undefined && dragCardInitialRank !== (dragCard as any)[rankFieldName]) {
|
||||
const dragCardRank = (dragCard as any)[rankFieldName]
|
||||
updates = {
|
||||
...updates,
|
||||
[rankFieldName]: dragCardRank
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length > 0) {
|
||||
await updateItem(dragCard, updates)
|
||||
}
|
||||
@ -113,7 +115,7 @@
|
||||
const client = getClient()
|
||||
|
||||
let dragCard: Item | undefined
|
||||
let dragCardInitialRank: string
|
||||
let dragCardInitialRank: string | undefined
|
||||
let dragCardInitialState: StateType
|
||||
|
||||
let isDragging = false
|
||||
@ -144,23 +146,26 @@
|
||||
if (card !== undefined && card[fieldName] !== state._id) {
|
||||
card[fieldName] = state._id
|
||||
const objs = getStateObjects(objects, state)
|
||||
card[rankFieldName] = calcRank(objs[objs.length - 1]?.it, undefined)
|
||||
if (rankFieldName !== undefined) card[rankFieldName] = calcRank(objs[objs.length - 1]?.it, undefined)
|
||||
}
|
||||
}
|
||||
function cardDragOver (evt: CardDragEvent, object: ExtItem): void {
|
||||
if (dragCard !== undefined) {
|
||||
;(dragCard as any)[rankFieldName] = doCalcRank(object, evt)
|
||||
;(dragCard as any)[fieldName] = (dragCard as any)[fieldName]
|
||||
if (rankFieldName !== undefined) {
|
||||
;(dragCard as any)[rankFieldName] = doCalcRank(object, evt)
|
||||
}
|
||||
}
|
||||
}
|
||||
function cardDrop (evt: CardDragEvent, object: ExtItem): void {
|
||||
if (dragCard !== undefined) {
|
||||
if (dragCard !== undefined && rankFieldName !== undefined) {
|
||||
;(dragCard as any)[rankFieldName] = doCalcRank(object, evt)
|
||||
}
|
||||
isDragging = false
|
||||
}
|
||||
function onDragStart (object: ExtItem, state: TypeState): void {
|
||||
dragCardInitialState = state._id
|
||||
dragCardInitialRank = (object.it as any)[rankFieldName]
|
||||
dragCardInitialRank = rankFieldName === undefined ? undefined : (object.it as any)[rankFieldName]
|
||||
dragCard = object.it
|
||||
isDragging = true
|
||||
dispatch('obj-focus', object.it)
|
||||
|
@ -106,6 +106,7 @@
|
||||
"NoAssignee": "No assignee",
|
||||
"LastUpdated": "Last updated",
|
||||
"DueDate": "Due date",
|
||||
"Manual": "Manual",
|
||||
"All": "All",
|
||||
"PastWeek": "Past week",
|
||||
"PastMonth": "Past month",
|
||||
|
@ -106,6 +106,7 @@
|
||||
"NoAssignee": "Нет исполнителя",
|
||||
"LastUpdated": "Последнее обновление",
|
||||
"DueDate": "Срок",
|
||||
"Manual": "Пользовательский",
|
||||
"All": "Все",
|
||||
"PastWeek": "Предыдущая неделя",
|
||||
"PastMonth": "Предыдущий месяц",
|
||||
|
@ -25,7 +25,10 @@
|
||||
let query: DocumentQuery<Issue>
|
||||
$: statusQuery.query(
|
||||
tracker.class.IssueStatus,
|
||||
{ category: { $in: [tracker.issueStatusCategory.Unstarted, tracker.issueStatusCategory.Started] } },
|
||||
{
|
||||
category: { $in: [tracker.issueStatusCategory.Unstarted, tracker.issueStatusCategory.Started] },
|
||||
space: currentSpace
|
||||
},
|
||||
(result) => {
|
||||
query = { status: { $in: result.map(({ _id }) => _id) }, space: currentSpace }
|
||||
}
|
||||
|
@ -23,9 +23,13 @@
|
||||
|
||||
const statusQuery = createQuery()
|
||||
let query: DocumentQuery<Issue> = {}
|
||||
$: statusQuery.query(tracker.class.IssueStatus, { category: tracker.issueStatusCategory.Backlog }, (result) => {
|
||||
query = { status: { $in: result.map(({ _id }) => _id) }, space: currentSpace }
|
||||
})
|
||||
$: statusQuery.query(
|
||||
tracker.class.IssueStatus,
|
||||
{ category: tracker.issueStatusCategory.Backlog, space: currentSpace },
|
||||
(result) => {
|
||||
query = { status: { $in: result.map(({ _id }) => _id) }, space: currentSpace }
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<IssuesView {query} title={tracker.string.BacklogIssues} />
|
||||
|
@ -0,0 +1,26 @@
|
||||
<!--
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref } from '@anticrm/core'
|
||||
import { Team } from '@anticrm/tracker'
|
||||
import tracker from '../../plugin'
|
||||
import IssuesView from './IssuesView.svelte'
|
||||
|
||||
export let currentSpace: Ref<Team>
|
||||
|
||||
$: query = { space: currentSpace }
|
||||
</script>
|
||||
|
||||
<IssuesView {query} title={tracker.string.ActiveIssues} />
|
@ -14,18 +14,18 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact from '@anticrm/contact'
|
||||
import { Class, Doc, DocumentQuery, FindOptions, Ref, SortingOrder, WithLookup } from '@anticrm/core'
|
||||
import { Kanban, TypeState } from '@anticrm/kanban'
|
||||
import { Class, Doc, DocumentQuery, Lookup, Ref, SortingOrder, WithLookup } from '@anticrm/core'
|
||||
import { Kanban } from '@anticrm/kanban'
|
||||
import notification from '@anticrm/notification'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import { Issue, IssuesGrouping, IssueStatus, Team, ViewOptions } from '@anticrm/tracker'
|
||||
import { Issue, IssuesGrouping, IssuesOrdering, IssueStatus, Team, ViewOptions } from '@anticrm/tracker'
|
||||
import { Button, Component, Icon, IconAdd, showPanel, showPopup, Tooltip } 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 } from '../../utils'
|
||||
import { getKanbanStatuses, issuesSortOrderMap } from '../../utils'
|
||||
import CreateIssue from '../CreateIssue.svelte'
|
||||
import ProjectEditor from '../projects/ProjectEditor.svelte'
|
||||
import AssigneePresenter from './AssigneePresenter.svelte'
|
||||
@ -40,7 +40,9 @@
|
||||
export let query: DocumentQuery<Issue> = {}
|
||||
|
||||
$: currentSpace = typeof query.space === 'string' ? query.space : tracker.team.DefaultTeam
|
||||
$: ({ groupBy, shouldShowEmptyGroups, shouldShowSubIssues } = viewOptions)
|
||||
$: ({ groupBy, orderBy, shouldShowEmptyGroups, shouldShowSubIssues } = viewOptions)
|
||||
$: sort = { [orderBy]: issuesSortOrderMap[orderBy] }
|
||||
$: rankFieldName = orderBy === IssuesOrdering.Manual ? orderBy : undefined
|
||||
$: resultQuery = {
|
||||
...(shouldShowSubIssues ? {} : { attachedTo: tracker.ids.NoParent }),
|
||||
space: currentSpace,
|
||||
@ -57,17 +59,10 @@
|
||||
})
|
||||
|
||||
let issueStatuses: WithLookup<IssueStatus>[] | undefined
|
||||
let states: TypeState[] | undefined
|
||||
$: statusesQuery.query(
|
||||
tracker.class.IssueStatus,
|
||||
{ attachedTo: currentSpace },
|
||||
(is) => {
|
||||
states = is.map((status) => ({
|
||||
_id: status._id,
|
||||
title: status.name,
|
||||
color: status.color ?? status.$lookup?.category?.color ?? 0,
|
||||
icon: status.$lookup?.category?.icon ?? undefined
|
||||
}))
|
||||
issueStatuses = is
|
||||
},
|
||||
{
|
||||
@ -80,13 +75,11 @@
|
||||
return object as WithLookup<Issue>
|
||||
}
|
||||
|
||||
const options: FindOptions<Issue> = {
|
||||
lookup: {
|
||||
assignee: contact.class.Employee,
|
||||
space: tracker.class.Team,
|
||||
_id: {
|
||||
subIssues: tracker.class.Issue
|
||||
}
|
||||
const lookup: Lookup<Issue> = {
|
||||
assignee: contact.class.Employee,
|
||||
space: tracker.class.Team,
|
||||
_id: {
|
||||
subIssues: tracker.class.Issue
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,10 +117,10 @@
|
||||
_class={tracker.class.Issue}
|
||||
search=""
|
||||
{states}
|
||||
{options}
|
||||
options={{ sort, lookup }}
|
||||
query={resultQuery}
|
||||
fieldName={groupBy}
|
||||
rankFieldName={'rank'}
|
||||
{rankFieldName}
|
||||
on:content={(evt) => {
|
||||
listProvider.update(evt.detail)
|
||||
}}
|
||||
|
@ -18,6 +18,7 @@ import { Resources } from '@anticrm/platform'
|
||||
import { ObjectSearchResult } from '@anticrm/presentation'
|
||||
import { Issue, Team } from '@anticrm/tracker'
|
||||
import Inbox from './components/inbox/Inbox.svelte'
|
||||
import Issues from './components/issues/Issues.svelte'
|
||||
import Active from './components/issues/Active.svelte'
|
||||
import AssigneePresenter from './components/issues/AssigneePresenter.svelte'
|
||||
import Backlog from './components/issues/Backlog.svelte'
|
||||
@ -111,6 +112,7 @@ export async function queryIssue<D extends Issue> (
|
||||
export default async (): Promise<Resources> => ({
|
||||
component: {
|
||||
NopeComponent,
|
||||
Issues,
|
||||
Active,
|
||||
Backlog,
|
||||
Inbox,
|
||||
|
@ -122,6 +122,7 @@ export default mergeIds(trackerId, tracker, {
|
||||
NoAssignee: '' as IntlString,
|
||||
LastUpdated: '' as IntlString,
|
||||
DueDate: '' as IntlString,
|
||||
Manual: '' as IntlString,
|
||||
All: '' as IntlString,
|
||||
PastWeek: '' as IntlString,
|
||||
PastMonth: '' as IntlString,
|
||||
@ -179,6 +180,7 @@ export default mergeIds(trackerId, tracker, {
|
||||
Inbox: '' as AnyComponent,
|
||||
MyIssues: '' as AnyComponent,
|
||||
Views: '' as AnyComponent,
|
||||
Issues: '' as AnyComponent,
|
||||
Active: '' as AnyComponent,
|
||||
Backlog: '' as AnyComponent,
|
||||
Projects: '' as AnyComponent,
|
||||
|
@ -43,7 +43,8 @@ export const issuesOrderByOptions: Record<IssuesOrdering, IntlString> = {
|
||||
[IssuesOrdering.Status]: tracker.string.Status,
|
||||
[IssuesOrdering.Priority]: tracker.string.Priority,
|
||||
[IssuesOrdering.LastUpdated]: tracker.string.LastUpdated,
|
||||
[IssuesOrdering.DueDate]: tracker.string.DueDate
|
||||
[IssuesOrdering.DueDate]: tracker.string.DueDate,
|
||||
[IssuesOrdering.Manual]: tracker.string.Manual
|
||||
}
|
||||
|
||||
export const issuesDateModificationPeriodOptions: Record<IssuesDateModificationPeriod, IntlString> = {
|
||||
|
@ -54,7 +54,7 @@ export interface Selection {
|
||||
}
|
||||
|
||||
export type IssuesGroupByKeys = keyof Pick<Issue, 'status' | 'priority' | 'assignee' | 'project'>
|
||||
export type IssuesOrderByKeys = keyof Pick<Issue, 'status' | 'priority' | 'modifiedOn' | 'dueDate'>
|
||||
export type IssuesOrderByKeys = keyof Pick<Issue, 'status' | 'priority' | 'modifiedOn' | 'dueDate' | 'rank'>
|
||||
|
||||
export const issuesGroupKeyMap: Record<IssuesGrouping, IssuesGroupByKeys | undefined> = {
|
||||
[IssuesGrouping.Status]: 'status',
|
||||
@ -68,14 +68,16 @@ export const issuesOrderKeyMap: Record<IssuesOrdering, IssuesOrderByKeys> = {
|
||||
[IssuesOrdering.Status]: 'status',
|
||||
[IssuesOrdering.Priority]: 'priority',
|
||||
[IssuesOrdering.LastUpdated]: 'modifiedOn',
|
||||
[IssuesOrdering.DueDate]: 'dueDate'
|
||||
[IssuesOrdering.DueDate]: 'dueDate',
|
||||
[IssuesOrdering.Manual]: 'rank'
|
||||
}
|
||||
|
||||
export const issuesSortOrderMap: Record<IssuesOrderByKeys, SortingOrder> = {
|
||||
status: SortingOrder.Ascending,
|
||||
priority: SortingOrder.Ascending,
|
||||
modifiedOn: SortingOrder.Descending,
|
||||
dueDate: SortingOrder.Descending
|
||||
dueDate: SortingOrder.Descending,
|
||||
rank: SortingOrder.Ascending
|
||||
}
|
||||
|
||||
export const issuesGroupEditorMap: Record<'status' | 'priority' | 'project', AnyComponent | undefined> = {
|
||||
|
@ -82,8 +82,9 @@ export enum IssuesGrouping {
|
||||
export enum IssuesOrdering {
|
||||
Status = 'status',
|
||||
Priority = 'priority',
|
||||
LastUpdated = 'lastUpdated',
|
||||
DueDate = 'dueDate'
|
||||
LastUpdated = 'modifiedOn',
|
||||
DueDate = 'dueDate',
|
||||
Manual = 'rank'
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user