diff --git a/models/tracker/src/plugin.ts b/models/tracker/src/plugin.ts index a0e324c192..9ef276915b 100644 --- a/models/tracker/src/plugin.ts +++ b/models/tracker/src/plugin.ts @@ -19,7 +19,7 @@ import { type Doc, type Ref } from '@hcengineering/core' import { type ObjectSearchCategory, type ObjectSearchFactory } from '@hcengineering/model-presentation' import { type NotificationGroup, type NotificationType } from '@hcengineering/notification' import { mergeIds, type IntlString, type Resource } from '@hcengineering/platform' -import { type ProjectType, type TaskTypeDescriptor } from '@hcengineering/task' +import { type ProjectType } from '@hcengineering/task' import { trackerId } from '@hcengineering/tracker' import tracker from '@hcengineering/tracker-resources/src/plugin' import type { AnyComponent } from '@hcengineering/ui/src/types' @@ -109,8 +109,5 @@ export default mergeIds(trackerId, tracker, { DeleteProject: '' as Ref>>, DeleteProjectClean: '' as Ref>>, DeleteIssue: '' as Ref>> - }, - descriptors: { - Issue: '' as Ref } }) diff --git a/packages/presentation/src/components/DocPopup.svelte b/packages/presentation/src/components/DocPopup.svelte index 031be9b54a..5d22f84781 100644 --- a/packages/presentation/src/components/DocPopup.svelte +++ b/packages/presentation/src/components/DocPopup.svelte @@ -13,7 +13,7 @@ // limitations under the License. --> diff --git a/packages/presentation/src/components/ObjectPopup.svelte b/packages/presentation/src/components/ObjectPopup.svelte index a038ce4eac..f1654f6900 100644 --- a/packages/presentation/src/components/ObjectPopup.svelte +++ b/packages/presentation/src/components/ObjectPopup.svelte @@ -13,7 +13,14 @@ // limitations under the License. --> diff --git a/plugins/task/src/utils.ts b/plugins/task/src/utils.ts index 92dc12a5cf..41c18d9e11 100644 --- a/plugins/task/src/utils.ts +++ b/plugins/task/src/utils.ts @@ -15,7 +15,9 @@ import core, { Class, + ClassifierKind, Data, + Doc, DocumentQuery, Hierarchy, IdMap, @@ -23,16 +25,14 @@ import core, { Status, StatusCategory, TxOperations, - type AnyAttribute, - type RefTo, - Doc, generateId, - ClassifierKind + type AnyAttribute, + type RefTo } from '@hcengineering/core' +import { PlatformError, getEmbeddedLabel, unknownStatus } from '@hcengineering/platform' import { LexoDecimal, LexoNumeralSystem36, LexoRank } from 'lexorank' import LexoRankBucket from 'lexorank/lib/lexoRank/lexoRankBucket' import task, { Project, ProjectStatus, ProjectType, Task, TaskType } from '.' -import { getEmbeddedLabel } from '@hcengineering/platform' /** * @public @@ -196,6 +196,18 @@ export type TaskTypeWithFactory = Omit, 'statuses' | 'parent' | ' type ProjectData = Omit, 'statuses' | 'private' | 'members' | 'archived' | 'targetClass'> +async function createStates ( + client: TxOperations, + states: Data[], + stateClass: Ref> +): Promise[]> { + const statuses: Ref[] = [] + for (const st of states) { + statuses.push(await createState(client, stateClass, st)) + } + return statuses +} + /** * @public */ @@ -210,65 +222,17 @@ export async function createProjectType ( return current._id } - async function createStates (states: Data[], stateClass: Ref>): Promise[]> { - const statuses: Ref[] = [] - for (const st of states) { - statuses.push(await createState(client, stateClass, st)) - } - return statuses - } - const _tasks: Ref[] = [] const tasksData = new Map, Data>() const _statues = new Set>() - for (const it of tasks) { - const { factory, _id: taskId, ...data } = it - const statuses = await createStates(factory, data.statusClass) - for (const st of statuses) { - _statues.add(st) - } - const tdata = { - ...data, - parent: _id, - statuses - } - - const ofClassClass = client.getHierarchy().getClass(data.ofClass) - - tdata.icon = ofClassClass.icon - - if (tdata.targetClass === undefined) { - // Create target class for custom field. - const targetClassId: Ref> = generateId() - tdata.targetClass = targetClassId - - await client.createDoc( - core.class.Mixin, - core.space.Model, - { - extends: data.ofClass, - kind: ClassifierKind.MIXIN, - label: ofClassClass.label, - icon: ofClassClass.icon - }, - targetClassId - ) - - await client.createMixin(targetClassId, core.class.Mixin, core.space.Model, task.mixin.TaskTypeClass, { - taskType: taskId, - projectType: _id - }) - } - await client.createDoc(task.class.TaskType, core.space.Model, tdata as Data, taskId) - tasksData.set(taskId, tdata as Data) - _tasks.push(taskId) - } const categoryObj = client.getModel().findObject(data.descriptor) if (categoryObj === undefined) { throw new Error('category is not found in model') } + await createTaskTypes(tasks, _id, client, _statues, tasksData, _tasks, false) + const baseClassClass = client.getHierarchy().getClass(categoryObj.baseClass) const targetProjectClassId: Ref> = generateId() @@ -311,3 +275,106 @@ export async function createProjectType ( return tmpl } + +/** + * @public + */ +export async function updateProjectType ( + client: TxOperations, + projectType: Ref, + tasks: TaskTypeWithFactory[] +): Promise { + const current = await client.findOne(task.class.ProjectType, { _id: projectType }) + if (current === undefined) { + throw new PlatformError(unknownStatus('No project type found')) + } + + const _tasks: Ref[] = [...current.tasks] + const tasksData = new Map, Data>() + const _statues = new Set>() + + const hasUpdates = await createTaskTypes(tasks, projectType, client, _statues, tasksData, _tasks, true) + + if (hasUpdates) { + const ttypes = await client.findAll(task.class.TaskType, { _id: { $in: _tasks } }) + const newStatuses = calculateStatuses( + { + statuses: current.statuses, + tasks: _tasks + }, + new Map(ttypes.map((it) => [it._id, it])), + [] + ) + await client.update(current, { + tasks: _tasks, + statuses: newStatuses + }) + } +} + +async function createTaskTypes ( + tasks: TaskTypeWithFactory[], + _id: Ref, + client: TxOperations, + _statues: Set>, + tasksData: Map, Data>, + _tasks: Ref[], + skipExisting: boolean +): Promise { + const existingTaskTypes = await client.findAll(task.class.TaskType, { parent: _id }) + + let hasUpdates = false + for (const it of tasks) { + const { factory, _id: taskId, ...data } = it + + if (skipExisting) { + const existingOne = existingTaskTypes.find((tt) => tt.ofClass === data.ofClass) + if (existingOne !== undefined) { + // We have similar one, let's check categories + continue + } + } + hasUpdates = true + + const statuses = await createStates(client, factory, data.statusClass) + for (const st of statuses) { + _statues.add(st) + } + const tdata = { + ...data, + parent: _id, + statuses + } + + const ofClassClass = client.getHierarchy().getClass(data.ofClass) + + tdata.icon = ofClassClass.icon + + if (tdata.targetClass === undefined) { + // Create target class for custom field. + const targetClassId: Ref> = generateId() + tdata.targetClass = targetClassId + + await client.createDoc( + core.class.Mixin, + core.space.Model, + { + extends: data.ofClass, + kind: ClassifierKind.MIXIN, + label: ofClassClass.label, + icon: ofClassClass.icon + }, + targetClassId + ) + + await client.createMixin(targetClassId, core.class.Mixin, core.space.Model, task.mixin.TaskTypeClass, { + taskType: taskId, + projectType: _id + }) + } + await client.createDoc(task.class.TaskType, core.space.Model, tdata as Data, taskId) + tasksData.set(taskId, tdata as Data) + _tasks.push(taskId) + } + return hasUpdates +} diff --git a/plugins/tracker-resources/src/components/issues/StatusSelector.svelte b/plugins/tracker-resources/src/components/issues/StatusSelector.svelte index 25fd526ce2..598935b105 100644 --- a/plugins/tracker-resources/src/components/issues/StatusSelector.svelte +++ b/plugins/tracker-resources/src/components/issues/StatusSelector.svelte @@ -14,7 +14,7 @@ -->