diff --git a/models/board/src/index.ts b/models/board/src/index.ts index 70b40261ed..73633adec9 100644 --- a/models/board/src/index.ts +++ b/models/board/src/index.ts @@ -35,12 +35,13 @@ import contact from '@hcengineering/model-contact' import core, { TDoc, TType } from '@hcengineering/model-core' import preference, { TPreference } from '@hcengineering/model-preference' import tags from '@hcengineering/model-tags' -import task, { TSpaceWithStates, TTask } from '@hcengineering/model-task' +import task, { TSpaceWithStates, TTask, actionTemplates as taskActionTemplates } from '@hcengineering/model-task' import view, { actionTemplates, createAction, actionTemplates as viewTemplates } from '@hcengineering/model-view' import workbench, { Application } from '@hcengineering/model-workbench' import { IntlString } from '@hcengineering/platform' import type { AnyComponent } from '@hcengineering/ui' import board from './plugin' +import { State } from '@hcengineering/task' export { boardId } from '@hcengineering/board' export { boardOperation } from './migration' @@ -99,6 +100,9 @@ export class TCard extends TTask implements Card { @Prop(TypeDate(), task.string.StartDate) startDate!: Timestamp | null + + @Prop(TypeRef(task.class.State), task.string.TaskState, { _id: board.attribute.State }) + declare status: Ref } @Model(board.class.MenuPage, core.class.Doc, DOMAIN_MODEL) @@ -167,6 +171,27 @@ export function createModel (builder: Builder): void { board.app.Board ) + createAction( + builder, + { + ...taskActionTemplates.editStatus, + target: board.class.Board, + actionProps: { + ofAttribute: board.attribute.State, + doneOfAttribute: board.attribute.DoneState + }, + query: { + archived: false + }, + context: { + mode: ['context', 'browser'], + group: 'edit' + }, + override: [task.action.EditStatuses] + }, + board.action.EditStatuses + ) + // const leadLookup: Lookup = // { // state: task.class.State, diff --git a/models/board/src/migration.ts b/models/board/src/migration.ts index bd66670296..893d45378d 100644 --- a/models/board/src/migration.ts +++ b/models/board/src/migration.ts @@ -22,7 +22,7 @@ import task, { KanbanTemplate, createStates } from '@hcengineering/task' import board from './plugin' async function createSpace (tx: TxOperations): Promise { - const currentTemplate = await tx.findOne(core.class.Space, { + const currentTemplate = await tx.findOne(task.class.KanbanTemplateSpace, { _id: board.space.BoardTemplates }) if (currentTemplate === undefined) { @@ -36,10 +36,17 @@ async function createSpace (tx: TxOperations): Promise { private: false, archived: false, members: [], - attachedToClass: board.class.Board + attachedToClass: board.class.Board, + ofAttribute: board.attribute.State, + doneAttribute: board.attribute.DoneState }, board.space.BoardTemplates ) + } else if (currentTemplate.ofAttribute === undefined) { + await tx.update(currentTemplate, { + ofAttribute: board.attribute.State, + doneAttribute: board.attribute.DoneState + }) } const current = await tx.findOne(core.class.Space, { @@ -47,7 +54,7 @@ async function createSpace (tx: TxOperations): Promise { }) if (current === undefined) { const defaultTmpl = await createDefaultKanbanTemplate(tx) - const [states, doneStates] = await createStates(tx, defaultTmpl) + const [states, doneStates] = await createStates(tx, board.attribute.State, board.attribute.DoneState, defaultTmpl) await tx.createDoc( board.class.Board, core.space.Space, @@ -77,13 +84,18 @@ async function createDefaultKanbanTemplate (tx: TxOperations): Promise { await createSpace(tx) diff --git a/models/board/src/plugin.ts b/models/board/src/plugin.ts index 31acbe36d9..6dbcb82901 100644 --- a/models/board/src/plugin.ts +++ b/models/board/src/plugin.ts @@ -62,6 +62,7 @@ export default mergeIds(boardId, board, { ConfigDescription: '' as IntlString }, action: { + EditStatuses: '' as Ref, ConvertToCard: '' as Ref }, actionImpl: { diff --git a/models/lead/src/index.ts b/models/lead/src/index.ts index e7d2763578..f0dc5818bf 100644 --- a/models/lead/src/index.ts +++ b/models/lead/src/index.ts @@ -81,7 +81,7 @@ export class TLead extends TTask implements Lead { @Prop(TypeRef(contact.mixin.Employee), lead.string.Assignee) declare assignee: Ref | null - @Prop(TypeRef(task.class.State), task.string.TaskState, { _id: task.attribute.State }) + @Prop(TypeRef(task.class.State), task.string.TaskState, { _id: lead.attribute.State }) declare status: Ref declare space: Ref @@ -365,6 +365,27 @@ export function createModel (builder: Builder): void { } } + createAction( + builder, + { + ...actionTemplates.editStatus, + target: lead.class.Funnel, + actionProps: { + ofAttribute: lead.attribute.State, + doneOfAttribute: lead.attribute.DoneState + }, + query: { + archived: false + }, + context: { + mode: ['context', 'browser'], + group: 'edit' + }, + override: [task.action.EditStatuses] + }, + lead.action.EditStatuses + ) + builder.createDoc( notification.class.NotificationGroup, core.space.Model, diff --git a/models/lead/src/migration.ts b/models/lead/src/migration.ts index 060c3849f3..bc47ee5770 100644 --- a/models/lead/src/migration.ts +++ b/models/lead/src/migration.ts @@ -22,7 +22,7 @@ import { PaletteColorIndexes } from '@hcengineering/ui/src/colors' import lead from './plugin' async function createSpace (tx: TxOperations): Promise { - const currentTemplate = await tx.findOne(core.class.Space, { + const currentTemplate = await tx.findOne(task.class.KanbanTemplateSpace, { _id: lead.space.FunnelTemplates }) if (currentTemplate === undefined) { @@ -36,10 +36,17 @@ async function createSpace (tx: TxOperations): Promise { private: false, members: [], archived: false, - attachedToClass: lead.class.Funnel + attachedToClass: lead.class.Funnel, + ofAttribute: lead.attribute.State, + doneAttribute: lead.attribute.DoneState }, lead.space.FunnelTemplates ) + } else if (currentTemplate.ofAttribute === undefined) { + await tx.update(currentTemplate, { + ofAttribute: lead.attribute.State, + doneAttribute: lead.attribute.DoneState + }) } const current = await tx.findOne(core.class.Space, { @@ -47,7 +54,7 @@ async function createSpace (tx: TxOperations): Promise { }) if (current === undefined) { const defaultTmpl = await createDefaultKanbanTemplate(tx) - const [states, doneStates] = await createStates(tx, defaultTmpl) + const [states, doneStates] = await createStates(tx, lead.attribute.State, lead.attribute.DoneState, defaultTmpl) await tx.createDoc( lead.class.Funnel, core.space.Space, @@ -81,13 +88,18 @@ async function createDefaultKanbanTemplate (tx: TxOperations): Promise { diff --git a/models/lead/src/plugin.ts b/models/lead/src/plugin.ts index 7d95cb3b20..b409b0b879 100644 --- a/models/lead/src/plugin.ts +++ b/models/lead/src/plugin.ts @@ -60,6 +60,7 @@ export default mergeIds(leadId, lead, { Lead: '' as Ref }, action: { + EditStatuses: '' as Ref, CreateGlobalLead: '' as Ref }, ids: { diff --git a/models/recruit/src/index.ts b/models/recruit/src/index.ts index 9c9d4b37fe..191ce6009b 100644 --- a/models/recruit/src/index.ts +++ b/models/recruit/src/index.ts @@ -165,7 +165,7 @@ export class TApplicant extends TTask implements Applicant { @Prop(TypeRef(contact.mixin.Employee), recruit.string.AssignedRecruiter) declare assignee: Ref | null - @Prop(TypeRef(task.class.State), task.string.TaskState, { _id: task.attribute.State }) + @Prop(TypeRef(task.class.State), task.string.TaskState, { _id: recruit.attribute.State }) declare status: Ref } @@ -368,6 +368,27 @@ export function createModel (builder: Builder): void { recruit.app.Recruit ) + createAction( + builder, + { + ...actionTemplates.editStatus, + target: recruit.class.Vacancy, + actionProps: { + ofAttribute: recruit.attribute.State, + doneOfAttribute: recruit.attribute.DoneState + }, + query: { + archived: false + }, + context: { + mode: ['context', 'browser'], + group: 'edit' + }, + override: [task.action.EditStatuses] + }, + recruit.action.EditStatuses + ) + builder.createDoc( view.class.Viewlet, core.space.Model, diff --git a/models/recruit/src/migration.ts b/models/recruit/src/migration.ts index 331a7f449d..8c920a442f 100644 --- a/models/recruit/src/migration.ts +++ b/models/recruit/src/migration.ts @@ -92,15 +92,20 @@ async function createDefaultKanbanTemplate (tx: TxOperations): Promise as Ref, - title: 'Default vacancy', - description: '', - shortDescription: '', - states: defaultKanban.states, - doneStates: defaultKanban.doneStates - }) + return await createKanbanTemplate( + tx, + { + kanbanId: recruit.template.DefaultVacancy, + space: recruit.space.VacancyTemplates as Ref as Ref, + title: 'Default vacancy', + description: '', + shortDescription: '', + states: defaultKanban.states, + doneStates: defaultKanban.doneStates + }, + recruit.attribute.State, + recruit.attribute.DoneState + ) } async function createSpaces (tx: TxOperations): Promise { @@ -142,7 +147,7 @@ async function createSpaces (tx: TxOperations): Promise { await tx.update(currentReviews, { private: false }) } - const currentTemplate = await tx.findOne(core.class.Space, { + const currentTemplate = await tx.findOne(task.class.KanbanTemplateSpace, { _id: recruit.space.VacancyTemplates }) if (currentTemplate === undefined) { @@ -157,9 +162,16 @@ async function createSpaces (tx: TxOperations): Promise { private: false, members: [], archived: false, - attachedToClass: recruit.class.Vacancy + attachedToClass: recruit.class.Vacancy, + ofAttribute: recruit.attribute.State, + doneAttribute: recruit.attribute.DoneState }, recruit.space.VacancyTemplates ) + } else if (currentTemplate.ofAttribute === undefined) { + await tx.update(currentTemplate, { + ofAttribute: recruit.attribute.State, + doneAttribute: recruit.attribute.DoneState + }) } } diff --git a/models/recruit/src/plugin.ts b/models/recruit/src/plugin.ts index 621b36fa47..3265e492dc 100644 --- a/models/recruit/src/plugin.ts +++ b/models/recruit/src/plugin.ts @@ -31,7 +31,8 @@ export default mergeIds(recruitId, recruit, { CopyApplicationLink: '' as Ref, CopyCandidateLink: '' as Ref, MoveApplicant: '' as Ref, - GetTalentIds: '' as Ref + GetTalentIds: '' as Ref, + EditStatuses: '' as Ref }, actionImpl: { CreateOpinion: '' as ViewAction, diff --git a/models/task/src/index.ts b/models/task/src/index.ts index e5b1aa1b3d..200e098884 100644 --- a/models/task/src/index.ts +++ b/models/task/src/index.ts @@ -158,6 +158,8 @@ export class TKanbanTemplateSpace extends TSpace implements KanbanTemplateSpace description!: IntlString icon!: AnyComponent editor!: AnyComponent + ofAttribute!: Ref> + doneAttribute!: Ref> attachedToClass!: Ref> } @@ -337,6 +339,10 @@ export function createModel (builder: Builder): void { { ...actionTemplates.editStatus, target: task.class.SpaceWithStates, + actionProps: { + ofAttribute: task.attribute.State, + doneOfAttribute: task.attribute.DoneState + }, query: { archived: false }, diff --git a/models/task/src/migration.ts b/models/task/src/migration.ts index 6b6ba82abd..81fd89df43 100644 --- a/models/task/src/migration.ts +++ b/models/task/src/migration.ts @@ -14,6 +14,7 @@ // import { + Attribute, Class, DOMAIN_STATUS, DOMAIN_TX, @@ -25,13 +26,14 @@ import { TxCollectionCUD, TxCreateDoc, TxOperations, - TxUpdateDoc + TxUpdateDoc, + toIdMap } from '@hcengineering/core' import { MigrateOperation, MigrationClient, MigrationUpgradeClient, createOrUpdate } from '@hcengineering/model' import core, { DOMAIN_SPACE } from '@hcengineering/model-core' import tags from '@hcengineering/model-tags' import { DOMAIN_VIEW } from '@hcengineering/model-view' -import { DoneStateTemplate, KanbanTemplate, StateTemplate, Task, genRanks } from '@hcengineering/task' +import { DoneState, DoneStateTemplate, KanbanTemplate, State, StateTemplate, Task, genRanks } from '@hcengineering/task' import view, { Filter, FilteredView } from '@hcengineering/view' import { DOMAIN_TASK } from '.' import task from './plugin' @@ -73,7 +75,9 @@ export async function createSequence (tx: TxOperations, _class: Ref>) */ export async function createKanbanTemplate ( client: TxOperations, - data: KanbanTemplateData + data: KanbanTemplateData, + ofAttribute: Ref>, + doneAtrtribute?: Ref> ): Promise> { const current = await client.findOne(task.class.KanbanTemplate, { _id: data.kanbanId }) if (current !== undefined) { @@ -96,7 +100,7 @@ export async function createKanbanTemplate ( data.doneStates.map((st, i) => client.createDoc(st.isWon ? task.class.WonStateTemplate : task.class.LostStateTemplate, data.space, { rank: doneStateRanks[i], - ofAttribute: task.attribute.DoneState, + ofAttribute: doneAtrtribute ?? ofAttribute, name: st.name, attachedTo: data.kanbanId }) @@ -108,7 +112,7 @@ export async function createKanbanTemplate ( data.states.map((st, i) => client.createDoc(task.class.StateTemplate, data.space, { attachedTo: data.kanbanId, - ofAttribute: task.attribute.State, + ofAttribute, rank: stateRanks[i], name: st.name, color: st.color @@ -226,7 +230,46 @@ async function renameStatePrefs (client: MigrationUpgradeClient): Promise } } +async function fixStatusAttributes (client: MigrationClient): Promise { + const spaces = await client.find(DOMAIN_SPACE, {}) + const map = toIdMap(spaces) + const oldStatuses = await client.find(DOMAIN_STATUS, { space: { $ne: task.space.Statuses } }) + for (const oldStatus of oldStatuses) { + const space = map.get(oldStatus.space) + if (space !== undefined) { + try { + const isDone = oldStatus._class === task.class.DoneState + let ofAttribute = task.attribute.State + if (space._class === ('recruit:class:Vacancy' as Ref>)) { + ofAttribute = isDone + ? ('recruit.attribute.DoneState' as Ref>) + : ('recruit:attribute:State' as Ref>) + } + if (space._class === ('lead:class:Funnel' as Ref>)) { + ofAttribute = isDone + ? ('lead.attribute.DoneState' as Ref>) + : ('lead:attribute:State' as Ref>) + } + if (space._class === ('board:class:Board' as Ref>)) { + ofAttribute = isDone + ? ('board.attribute.DoneState' as Ref>) + : ('board:attribute:State' as Ref>) + } + if (space._class === ('tracker:class:Project' as Ref>)) { + ofAttribute = 'tracker:attribute:IssueStatus' as Ref> + } + if (ofAttribute !== oldStatus.ofAttribute) { + await client.update(DOMAIN_STATUS, { _id: oldStatus._id }, { ofAttribute }) + } + } catch (err) { + console.log(err) + } + } + } +} + async function migrateStatuses (client: MigrationClient): Promise { + await fixStatusAttributes(client) const oldStatuses = await client.find(DOMAIN_STATUS, { space: { $ne: task.space.Statuses } }) const newStatuses: Map = new Map() const oldStatusesMap = new Map, Ref>() diff --git a/plugins/bitrix/src/hr.ts b/plugins/bitrix/src/hr.ts index 93123f85db..48c45bf86c 100644 --- a/plugins/bitrix/src/hr.ts +++ b/plugins/bitrix/src/hr.ts @@ -23,7 +23,12 @@ export async function createVacancy ( const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true) - const [states, doneStates] = await createStates(client, templateId) + const [states, doneStates] = await createStates( + client, + recruit.attribute.State, + recruit.attribute.DoneState, + templateId + ) const id = await client.createDoc(recruit.class.Vacancy, core.space.Space, { name, diff --git a/plugins/board-resources/src/utils/BoardUtils.ts b/plugins/board-resources/src/utils/BoardUtils.ts index 58ac57696f..920f6cd966 100644 --- a/plugins/board-resources/src/utils/BoardUtils.ts +++ b/plugins/board-resources/src/utils/BoardUtils.ts @@ -25,7 +25,7 @@ export async function createBoard ( description: string, templateId?: Ref ): Promise> { - const [states, doneStates] = await createStates(client, templateId) + const [states, doneStates] = await createStates(client, board.attribute.State, board.attribute.DoneState, templateId) const boardRef = await client.createDoc(board.class.Board, core.space.Space, { name, diff --git a/plugins/board/src/index.ts b/plugins/board/src/index.ts index d9e4aff09d..d50bf199ed 100644 --- a/plugins/board/src/index.ts +++ b/plugins/board/src/index.ts @@ -15,7 +15,7 @@ // import { Employee } from '@hcengineering/contact' -import type { Class, Doc, Markup, Ref, Timestamp, Type } from '@hcengineering/core' +import type { Attribute, Class, Doc, Markup, Ref, Timestamp, Type } from '@hcengineering/core' import type { Asset, IntlString, Plugin } from '@hcengineering/platform' import { plugin } from '@hcengineering/platform' import type { Preference } from '@hcengineering/preference' @@ -120,6 +120,10 @@ const boards = plugin(boardId, { string: { ConfigLabel: '' as IntlString }, + attribute: { + State: '' as Ref>, + DoneState: '' as Ref> + }, icon: { Board: '' as Asset, Card: '' as Asset diff --git a/plugins/lead/src/index.ts b/plugins/lead/src/index.ts index 180138b87f..089868b1fe 100644 --- a/plugins/lead/src/index.ts +++ b/plugins/lead/src/index.ts @@ -15,11 +15,11 @@ // import type { Contact } from '@hcengineering/contact' -import type { Class, Doc, Ref, Timestamp } from '@hcengineering/core' +import type { Attribute, Class, Doc, Ref, Timestamp } from '@hcengineering/core' import { Mixin } from '@hcengineering/core' import type { Asset, IntlString, Plugin } from '@hcengineering/platform' import { plugin } from '@hcengineering/platform' -import type { KanbanTemplateSpace, SpaceWithStates, State, Task } from '@hcengineering/task' +import type { DoneState, KanbanTemplateSpace, SpaceWithStates, State, Task } from '@hcengineering/task' /** * @public @@ -73,6 +73,10 @@ const lead = plugin(leadId, { Lead: '' as IntlString, ConfigLabel: '' as IntlString }, + attribute: { + State: '' as Ref>, + DoneState: '' as Ref> + }, icon: { Funnel: '' as Asset, Lead: '' as Asset, diff --git a/plugins/recruit-resources/src/components/CreateVacancy.svelte b/plugins/recruit-resources/src/components/CreateVacancy.svelte index af8c7e77fd..12564c658c 100644 --- a/plugins/recruit-resources/src/components/CreateVacancy.svelte +++ b/plugins/recruit-resources/src/components/CreateVacancy.svelte @@ -174,7 +174,12 @@ const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true) - const [states, doneStates] = await createStates(client, templateId) + const [states, doneStates] = await createStates( + client, + recruit.attribute.State, + recruit.attribute.DoneState, + templateId + ) const id = await client.createDoc( recruit.class.Vacancy, diff --git a/plugins/recruit/src/index.ts b/plugins/recruit/src/index.ts index fdb615b22c..e8f7a55519 100644 --- a/plugins/recruit/src/index.ts +++ b/plugins/recruit/src/index.ts @@ -15,11 +15,21 @@ import { Event } from '@hcengineering/calendar' import type { Channel, Organization, Person } from '@hcengineering/contact' -import type { AttachedData, AttachedDoc, Class, Doc, Mixin, Ref, Space, Timestamp } from '@hcengineering/core' +import type { + AttachedData, + AttachedDoc, + Attribute, + Class, + Doc, + Mixin, + Ref, + Space, + Timestamp +} from '@hcengineering/core' import type { Asset, IntlString, Plugin, Resource } from '@hcengineering/platform' import { plugin } from '@hcengineering/platform' import { TagReference } from '@hcengineering/tags' -import type { KanbanTemplateSpace, SpaceWithStates, State, Task } from '@hcengineering/task' +import type { DoneState, KanbanTemplateSpace, SpaceWithStates, State, Task } from '@hcengineering/task' import { AnyComponent, ResolvedLocation } from '@hcengineering/ui' /** @@ -155,6 +165,10 @@ const recruit = plugin(recruitId, { Candidate: '' as Ref>, VacancyList: '' as Ref> }, + attribute: { + State: '' as Ref>, + DoneState: '' as Ref> + }, component: { EditVacancy: '' as AnyComponent }, diff --git a/plugins/setting-resources/src/components/statuses/Templates.svelte b/plugins/setting-resources/src/components/statuses/Templates.svelte index ab2c545c7b..e8f979bf87 100644 --- a/plugins/setting-resources/src/components/statuses/Templates.svelte +++ b/plugins/setting-resources/src/components/statuses/Templates.svelte @@ -28,7 +28,7 @@ let templateMap = new Map, KanbanTemplate>() const templatesQ = createQuery() $: if (folder !== undefined) { - templatesQ.query(task.class.KanbanTemplate, { space: folder._id as Ref as Ref }, (result) => { + templatesQ.query(task.class.KanbanTemplate, { space: folder._id }, (result) => { templates = result }) } else { @@ -81,7 +81,7 @@ await Promise.all( doneStates.map(async (ds) => { - await client.createDoc(ds.class, space as Ref as Ref, { + await client.createDoc(ds.class, space, { attachedTo: template, ofAttribute: task.attribute.DoneState, name: ds.name, diff --git a/plugins/task-resources/src/components/CreateStatePopup.svelte b/plugins/task-resources/src/components/CreateStatePopup.svelte index 419c5dea38..c87703e96f 100644 --- a/plugins/task-resources/src/components/CreateStatePopup.svelte +++ b/plugins/task-resources/src/components/CreateStatePopup.svelte @@ -13,7 +13,7 @@ // limitations under the License. --> -{#if spaceEditor} - -{/if}
-
+
{#each states as state, i} {@const color = getPlatformColorDef(state.color ?? getColorNumberByText(state.name), $themeStore.dark)} {#if state} @@ -155,7 +151,7 @@ onDelete: () => dispatch('delete', { state }), showDelete: states.length > 1, onUpdate: () => { - showPopup(task.component.CreateStatePopup, { status: state, template }, undefined) + showPopup(task.component.CreateStatePopup, { status: state, space, ofAttribute }, undefined) } }, eventToHTMLElement(ev), @@ -169,7 +165,7 @@ {/if} {/each}
-
+
-
+