mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-23 11:31:57 +03:00
Updatable categories (#2606)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
d0353ba142
commit
8108f2e59b
@ -218,7 +218,9 @@ export class TSortFuncs extends TClass implements ClassSortFuncs {
|
||||
|
||||
@Mixin(view.mixin.AllValuesFunc, core.class.Class)
|
||||
export class TAllValuesFunc extends TClass implements AllValuesFunc {
|
||||
func!: Resource<(space: Ref<Space> | undefined) => Promise<any[]>>
|
||||
func!: Resource<
|
||||
(space: Ref<Space> | undefined, onUpdate: () => void, queryId: Ref<Doc>) => Promise<any[] | undefined>
|
||||
>
|
||||
}
|
||||
|
||||
@Model(view.class.ViewletPreference, preference.class.Preference)
|
||||
|
@ -14,7 +14,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact, { Employee } from '@hcengineering/contact'
|
||||
import { Class, Doc, DocumentQuery, IdMap, Lookup, Ref, toIdMap, WithLookup } from '@hcengineering/core'
|
||||
import { Class, Doc, DocumentQuery, generateId, IdMap, Lookup, Ref, toIdMap, WithLookup } from '@hcengineering/core'
|
||||
import { Kanban, TypeState } from '@hcengineering/kanban'
|
||||
import notification from '@hcengineering/notification'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
@ -193,6 +193,8 @@
|
||||
|
||||
let states: TypeState[]
|
||||
|
||||
const queryId = generateId()
|
||||
|
||||
$: updateCategories(
|
||||
tracker.class.Issue,
|
||||
issues,
|
||||
@ -205,6 +207,20 @@
|
||||
assignee
|
||||
)
|
||||
|
||||
function update () {
|
||||
updateCategories(
|
||||
tracker.class.Issue,
|
||||
issues,
|
||||
groupBy,
|
||||
viewOptions,
|
||||
viewOptionsConfig,
|
||||
statuses,
|
||||
projects,
|
||||
sprints,
|
||||
assignee
|
||||
)
|
||||
}
|
||||
|
||||
async function updateCategories (
|
||||
_class: Ref<Class<Doc>>,
|
||||
docs: Doc[],
|
||||
@ -222,7 +238,7 @@
|
||||
const categoryFunc = viewOption as CategoryOption
|
||||
if (viewOptions[viewOption.key] ?? viewOption.defaultValue) {
|
||||
const f = await getResource(categoryFunc.action)
|
||||
const res = await f(_class, space, groupByKey)
|
||||
const res = await f(_class, space, groupByKey, update, queryId)
|
||||
if (res !== undefined) {
|
||||
for (const category of categories) {
|
||||
if (!res.includes(category)) {
|
||||
|
@ -21,7 +21,7 @@
|
||||
import SprintPopup from './SprintPopup.svelte'
|
||||
import { Sprint } from '@hcengineering/tracker'
|
||||
|
||||
export let sprint: Sprint
|
||||
export let sprints: Sprint[]
|
||||
export let moveAndDeleteSprint: (selectedSprint?: Sprint) => Promise<void>
|
||||
|
||||
let selectedSprint: Sprint | undefined
|
||||
@ -34,14 +34,14 @@
|
||||
<Card
|
||||
canSave
|
||||
label={tracker.string.MoveAndDeleteSprint}
|
||||
labelProps={{ newSprint: selectedSprintLabel, deleteSprint: sprint.label }}
|
||||
labelProps={{ newSprint: selectedSprintLabel, deleteSprint: sprints.map((p) => p.label) }}
|
||||
okLabel={tracker.string.Delete}
|
||||
okAction={() => moveAndDeleteSprint(selectedSprint)}
|
||||
on:close
|
||||
>
|
||||
<SprintPopup
|
||||
_class={tracker.class.Sprint}
|
||||
ignoreSprints={[sprint]}
|
||||
ignoreSprints={sprints}
|
||||
allowDeselect
|
||||
closeAfterSelect={false}
|
||||
shadows={false}
|
||||
|
@ -201,7 +201,7 @@ async function editTeam (team: Team | undefined): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
async function moveAndDeleteSprint (client: TxOperations, oldSprint: Sprint, newSprint?: Sprint): Promise<void> {
|
||||
async function moveAndDeleteSprints (client: TxOperations, oldSprints: Sprint[], newSprint?: Sprint): Promise<void> {
|
||||
const noSprintLabel = await translate(tracker.string.NoSprint, {})
|
||||
|
||||
showPopup(
|
||||
@ -209,37 +209,39 @@ async function moveAndDeleteSprint (client: TxOperations, oldSprint: Sprint, new
|
||||
{
|
||||
label: tracker.string.MoveAndDeleteSprint,
|
||||
message: tracker.string.MoveAndDeleteSprintConfirm,
|
||||
labelProps: { newSprint: newSprint?.label ?? noSprintLabel, deleteSprint: oldSprint.label }
|
||||
labelProps: { newSprint: newSprint?.label ?? noSprintLabel, deleteSprint: oldSprints.map((p) => p.label) }
|
||||
},
|
||||
undefined,
|
||||
(result?: boolean) => {
|
||||
if (result === true) {
|
||||
void moveIssuesToAnotherSprint(client, oldSprint, newSprint).then((succes) => {
|
||||
if (succes) {
|
||||
void deleteObject(client, oldSprint)
|
||||
}
|
||||
})
|
||||
for (const oldSprint of oldSprints) {
|
||||
void moveIssuesToAnotherSprint(client, oldSprint, newSprint).then((succes) => {
|
||||
if (succes) {
|
||||
void deleteObject(client, oldSprint)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function deleteSprint (sprint: Sprint): Promise<void> {
|
||||
async function deleteSprint (sprints: Sprint[]): Promise<void> {
|
||||
const client = getClient()
|
||||
// Check if available to move issues to another sprint
|
||||
const firstSearchedSprint = await client.findOne(tracker.class.Sprint, { _id: { $nin: [sprint._id] } })
|
||||
const firstSearchedSprint = await client.findOne(tracker.class.Sprint, { _id: { $nin: sprints.map((p) => p._id) } })
|
||||
if (firstSearchedSprint !== undefined) {
|
||||
showPopup(
|
||||
MoveAndDeleteSprintPopup,
|
||||
{
|
||||
sprint,
|
||||
sprints,
|
||||
moveAndDeleteSprint: async (selectedSprint?: Sprint) =>
|
||||
await moveAndDeleteSprint(client, sprint, selectedSprint)
|
||||
await moveAndDeleteSprints(client, sprints, selectedSprint)
|
||||
},
|
||||
'top'
|
||||
)
|
||||
} else {
|
||||
await moveAndDeleteSprint(client, sprint)
|
||||
await moveAndDeleteSprints(client, sprints)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
import { Client, Doc, Ref, Space } from '@hcengineering/core'
|
||||
import type { IntlString, Metadata, Resource } from '@hcengineering/platform'
|
||||
import { mergeIds } from '@hcengineering/platform'
|
||||
import { IssueDraft, IssuePriority, IssueStatus, Project, Sprint } from '@hcengineering/tracker'
|
||||
import { IssueDraft } from '@hcengineering/tracker'
|
||||
import { AnyComponent } from '@hcengineering/ui'
|
||||
import { SortFunc, Viewlet, ViewQueryAction } from '@hcengineering/view'
|
||||
import tracker, { trackerId } from '../../tracker/lib'
|
||||
@ -380,9 +380,17 @@ export default mergeIds(trackerId, tracker, {
|
||||
IssuePrioritySort: '' as SortFunc,
|
||||
SprintSort: '' as SortFunc,
|
||||
SubIssueQuery: '' as ViewQueryAction,
|
||||
GetAllStatuses: '' as Resource<(space: Ref<Space> | undefined) => Promise<Array<Ref<IssueStatus>>>>,
|
||||
GetAllPriority: '' as Resource<(space: Ref<Space> | undefined) => Promise<IssuePriority[]>>,
|
||||
GetAllProjects: '' as Resource<(space: Ref<Space> | undefined) => Promise<Array<Ref<Project>>>>,
|
||||
GetAllSprints: '' as Resource<(space: Ref<Space> | undefined) => Promise<Array<Ref<Sprint>>>>
|
||||
GetAllStatuses: '' as Resource<
|
||||
(space: Ref<Space> | undefined, onUpdate: () => void, queryId: Ref<Doc>) => Promise<any[] | undefined>
|
||||
>,
|
||||
GetAllPriority: '' as Resource<
|
||||
(space: Ref<Space> | undefined, onUpdate: () => void, queryId: Ref<Doc>) => Promise<any[] | undefined>
|
||||
>,
|
||||
GetAllProjects: '' as Resource<
|
||||
(space: Ref<Space> | undefined, onUpdate: () => void, queryId: Ref<Doc>) => Promise<any[] | undefined>
|
||||
>,
|
||||
GetAllSprints: '' as Resource<
|
||||
(space: Ref<Space> | undefined, onUpdate: () => void, queryId: Ref<Doc>) => Promise<any[] | undefined>
|
||||
>
|
||||
}
|
||||
})
|
||||
|
@ -16,6 +16,7 @@
|
||||
import { Employee, formatName } from '@hcengineering/contact'
|
||||
import core, {
|
||||
AttachedData,
|
||||
Class,
|
||||
Doc,
|
||||
DocumentQuery,
|
||||
Ref,
|
||||
@ -53,7 +54,7 @@ import {
|
||||
isWeekend,
|
||||
MILLISECONDS_IN_WEEK
|
||||
} from '@hcengineering/ui'
|
||||
import { ListSelectionProvider, SelectDirection } from '@hcengineering/view-resources'
|
||||
import { CategoryQuery, ListSelectionProvider, SelectDirection } from '@hcengineering/view-resources'
|
||||
import tracker from './plugin'
|
||||
import { defaultPriorities, defaultProjectStatuses, defaultSprintStatuses, issuePriorities } from './types'
|
||||
|
||||
@ -499,7 +500,7 @@ export async function moveIssuesToAnotherSprint (
|
||||
// Update Issues by new Sprint
|
||||
const awaitedUpdates = []
|
||||
for (const issue of movedIssues) {
|
||||
awaitedUpdates.push(client.update(issue, { sprint: newSprint?._id ?? undefined }))
|
||||
awaitedUpdates.push(client.update(issue, { sprint: newSprint?._id ?? null }))
|
||||
}
|
||||
await Promise.all(awaitedUpdates)
|
||||
|
||||
@ -546,41 +547,65 @@ export function subIssueQuery (value: boolean, query: DocumentQuery<Issue>): Doc
|
||||
return value ? query : { ...query, attachedTo: tracker.ids.NoParent }
|
||||
}
|
||||
|
||||
export async function getAllStatuses (space: Ref<Space> | undefined): Promise<Array<Ref<IssueStatus>> | undefined> {
|
||||
if (space === undefined) return
|
||||
return await new Promise((resolve) => {
|
||||
const query = createQuery(true)
|
||||
query.query(tracker.class.IssueStatus, { space }, (res) => {
|
||||
resolve(res.map((p) => p._id))
|
||||
query.unsubscribe()
|
||||
})
|
||||
async function getAllSomething (
|
||||
_class: Ref<Class<Doc>>,
|
||||
space: Ref<Space> | undefined,
|
||||
onUpdate: () => void,
|
||||
queryId: Ref<Doc>
|
||||
): Promise<any[] | undefined> {
|
||||
const promise = new Promise<Array<Ref<Doc>>>((resolve, reject) => {
|
||||
let refresh: boolean = false
|
||||
const lq = CategoryQuery.getLiveQuery(queryId)
|
||||
refresh = lq.query(
|
||||
_class,
|
||||
{
|
||||
space
|
||||
},
|
||||
(res) => {
|
||||
const result = res.map((p) => p._id)
|
||||
CategoryQuery.results.set(queryId, result)
|
||||
resolve(result)
|
||||
onUpdate()
|
||||
}
|
||||
)
|
||||
|
||||
if (!refresh) {
|
||||
resolve(CategoryQuery.results.get(queryId) ?? [])
|
||||
}
|
||||
})
|
||||
return await promise
|
||||
}
|
||||
|
||||
export async function getAllPriority (space: Ref<Space> | undefined): Promise<IssuePriority[] | undefined> {
|
||||
export async function getAllStatuses (
|
||||
space: Ref<Space> | undefined,
|
||||
onUpdate: () => void,
|
||||
queryId: Ref<Doc>
|
||||
): Promise<any[] | undefined> {
|
||||
return await getAllSomething(tracker.class.IssueStatus, space, onUpdate, queryId)
|
||||
}
|
||||
|
||||
export async function getAllPriority (
|
||||
space: Ref<Space> | undefined,
|
||||
onUpdate: () => void,
|
||||
queryId: Ref<Doc>
|
||||
): Promise<any[] | undefined> {
|
||||
return defaultPriorities
|
||||
}
|
||||
|
||||
export async function getAllProjects (space: Ref<Team> | undefined): Promise<Array<Ref<Project>> | undefined> {
|
||||
if (space === undefined) return
|
||||
return await new Promise((resolve) => {
|
||||
const query = createQuery(true)
|
||||
query.query(tracker.class.Project, { space }, (res) => {
|
||||
resolve(res.map((p) => p._id))
|
||||
query.unsubscribe()
|
||||
})
|
||||
})
|
||||
export async function getAllProjects (
|
||||
space: Ref<Team> | undefined,
|
||||
onUpdate: () => void,
|
||||
queryId: Ref<Doc>
|
||||
): Promise<any[] | undefined> {
|
||||
return await getAllSomething(tracker.class.Project, space, onUpdate, queryId)
|
||||
}
|
||||
|
||||
export async function getAllSprints (space: Ref<Team> | undefined): Promise<Array<Ref<Sprint>> | undefined> {
|
||||
if (space === undefined) return
|
||||
return await new Promise((resolve) => {
|
||||
const query = createQuery(true)
|
||||
query.query(tracker.class.Sprint, { space }, (res) => {
|
||||
resolve(res.map((p) => p._id))
|
||||
query.unsubscribe()
|
||||
})
|
||||
})
|
||||
export async function getAllSprints (
|
||||
space: Ref<Team> | undefined,
|
||||
onUpdate: () => void,
|
||||
queryId: Ref<Doc>
|
||||
): Promise<any[] | undefined> {
|
||||
return await getAllSomething(tracker.class.Sprint, space, onUpdate, queryId)
|
||||
}
|
||||
|
||||
export function subIssueListProvider (subIssues: Issue[], target: Ref<Issue>): void {
|
||||
|
@ -13,13 +13,14 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Doc, Lookup, Ref, Space } from '@hcengineering/core'
|
||||
import { Class, Doc, generateId, Lookup, Ref, Space } from '@hcengineering/core'
|
||||
import { getResource, IntlString } from '@hcengineering/platform'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { AnyComponent } from '@hcengineering/ui'
|
||||
import { AttributeModel, BuildModelKey, CategoryOption, ViewOptionModel, ViewOptions } from '@hcengineering/view'
|
||||
import { onDestroy } from 'svelte'
|
||||
import { buildModel, getAdditionalHeader, getCategories, getPresenter, groupBy } from '../../utils'
|
||||
import { noCategory } from '../../viewOptions'
|
||||
import { CategoryQuery, noCategory } from '../../viewOptions'
|
||||
import ListCategory from './ListCategory.svelte'
|
||||
|
||||
export let elementByIndex: Map<number, HTMLDivElement>
|
||||
@ -49,6 +50,15 @@
|
||||
let categories: any[] = []
|
||||
$: updateCategories(_class, docs, groupByKey, viewOptions, viewOptionsConfig)
|
||||
|
||||
const queryId = generateId()
|
||||
onDestroy(() => {
|
||||
CategoryQuery.remove(queryId)
|
||||
})
|
||||
|
||||
function update () {
|
||||
updateCategories(_class, docs, groupByKey, viewOptions, viewOptionsConfig)
|
||||
}
|
||||
|
||||
async function updateCategories (
|
||||
_class: Ref<Class<Doc>>,
|
||||
docs: Doc[],
|
||||
@ -63,7 +73,7 @@
|
||||
const categoryFunc = viewOption as CategoryOption
|
||||
if (viewOptions[viewOption.key] ?? viewOption.defaultValue) {
|
||||
const f = await getResource(categoryFunc.action)
|
||||
const res = await f(_class, space, groupByKey)
|
||||
const res = await f(_class, space, groupByKey, update, queryId)
|
||||
if (res !== undefined) {
|
||||
for (const category of categories) {
|
||||
if (!res.includes(category)) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Class, Doc, Ref, SortingOrder, Space } from '@hcengineering/core'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { getAttributePresenterClass, getClient } from '@hcengineering/presentation'
|
||||
import { createQuery, getAttributePresenterClass, getClient, LiveQuery } from '@hcengineering/presentation'
|
||||
import { getCurrentLocation, locationToUrl } from '@hcengineering/ui'
|
||||
import {
|
||||
DropdownViewOption,
|
||||
@ -89,7 +89,9 @@ export function migrateViewOpttions (): void {
|
||||
export async function showEmptyGroups (
|
||||
_class: Ref<Class<Doc>>,
|
||||
space: Ref<Space> | undefined,
|
||||
key: string
|
||||
key: string,
|
||||
onUpdate: () => void,
|
||||
queryId: Ref<Doc>
|
||||
): Promise<any[] | undefined> {
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
@ -101,7 +103,7 @@ export async function showEmptyGroups (
|
||||
const mixin = hierarchy.as(attributeClass, view.mixin.AllValuesFunc)
|
||||
if (mixin.func !== undefined) {
|
||||
const f = await getResource(mixin.func)
|
||||
const res = await f(space)
|
||||
const res = await f(space, onUpdate, queryId)
|
||||
if (res !== undefined) {
|
||||
const sortFunc = hierarchy.as(attributeClass, view.mixin.SortFuncs)
|
||||
if (sortFunc?.func === undefined) return res
|
||||
@ -111,3 +113,22 @@ export async function showEmptyGroups (
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const CategoryQuery = {
|
||||
queries: new Map<string, LiveQuery>(),
|
||||
results: new Map<string, any[]>(),
|
||||
|
||||
getLiveQuery (index: string): LiveQuery {
|
||||
const current = CategoryQuery.queries.get(index)
|
||||
if (current !== undefined) return current
|
||||
const query = createQuery(true)
|
||||
this.queries.set(index, query)
|
||||
return query
|
||||
},
|
||||
remove (index: string): void {
|
||||
const lq = this.queries.get(index)
|
||||
lq?.unsubscribe()
|
||||
this.queries.delete(index)
|
||||
this.results.delete(index)
|
||||
}
|
||||
}
|
||||
|
@ -247,7 +247,7 @@ export interface ClassSortFuncs extends Class<Doc> {
|
||||
* @public
|
||||
*/
|
||||
export interface AllValuesFunc extends Class<Doc> {
|
||||
func: Resource<(space: Ref<Space> | undefined) => Promise<any[]>>
|
||||
func: Resource<(space: Ref<Space> | undefined, onUpdate: () => void, queryId: Ref<Doc>) => Promise<any[] | undefined>>
|
||||
}
|
||||
|
||||
/**
|
||||
@ -488,7 +488,13 @@ export interface ViewOption {
|
||||
* @public
|
||||
*/
|
||||
export type ViewCategoryAction = Resource<
|
||||
(_class: Ref<Class<Doc>>, space: Ref<Space> | undefined, key: string) => Promise<any[] | undefined>
|
||||
(
|
||||
_class: Ref<Class<Doc>>,
|
||||
space: Ref<Space> | undefined,
|
||||
key: string,
|
||||
onUpdate: () => void,
|
||||
queryId: Ref<Doc>
|
||||
) => Promise<any[] | undefined>
|
||||
>
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user