diff --git a/models/process/src/index.ts b/models/process/src/index.ts index f6aa1ec51e..a3c64390a6 100644 --- a/models/process/src/index.ts +++ b/models/process/src/index.ts @@ -12,7 +12,7 @@ // limitations under the License. import card, { type Card, type MasterTag, type Tag } from '@hcengineering/card' -import contact, { type Employee } from '@hcengineering/contact' +import contact from '@hcengineering/contact' import core, { AccountRole, type Class, @@ -25,34 +25,29 @@ import core, { type Tx, type Type } from '@hcengineering/core' -import { - ArrOf, - type Builder, - Model, - Prop, - ReadOnly, - TypeAny, - TypeBoolean, - TypeRecord, - TypeRef, - TypeString -} from '@hcengineering/model' +import { type Builder, Model, Prop, ReadOnly, TypeAny, TypeBoolean, TypeRef, TypeString } from '@hcengineering/model' import { TDoc } from '@hcengineering/model-core' import presentation from '@hcengineering/model-presentation' import { TToDo } from '@hcengineering/model-time' import view, { createAction } from '@hcengineering/model-view' import workbench from '@hcengineering/model-workbench' -import { type IntlString } from '@hcengineering/platform' +import { type Asset, type IntlString, type Resource } from '@hcengineering/platform' import { + type CheckFunc, + type ContextId, type Execution, + type ExecutionContext, type ExecutionError, + type ExecutionStatus, type Method, type Process, + type ProcessContext, type ProcessFunction, type ProcessToDo, - type Results, type State, type Step, + type Transition, + type Trigger, processId } from '@hcengineering/process' import time from '@hcengineering/time' @@ -73,41 +68,62 @@ export class TProcess extends TDoc implements Process { @Prop(TypeRef(card.class.MasterTag), card.string.MasterTag) masterTag!: Ref - @Prop(ArrOf(TypeRef(process.class.State)), process.string.States) - states!: Ref[] + @Prop(TypeRef(process.class.State), process.string.NewState) + initState!: Ref @Prop(TypeBoolean(), process.string.ParallelExecutionForbidden) parallelExecutionForbidden?: boolean @Prop(TypeBoolean(), process.string.StartAutomatically) autoStart: boolean | undefined + + context!: Record +} + +@Model(process.class.Trigger, core.class.Doc, DOMAIN_MODEL) +export class TTrigger extends TDoc implements Trigger { + label!: IntlString + + editor?: AnyComponent + + icon!: Asset + + requiredParams!: string[] + + checkFunction?: Resource +} + +@Model(process.class.Transition, core.class.Doc, DOMAIN_MODEL) +export class TTransition extends TDoc implements Transition { + @Prop(TypeRef(process.class.Process), process.string.Process) + process!: Ref + + @Prop(TypeRef(process.class.State), process.string.From) + from!: Ref + + @Prop(TypeRef(process.class.State), process.string.To) + to!: Ref | null + + @Prop(TypeAny(process.component.ActionsPresenter, process.string.Actions), process.string.Actions) + actions!: Step[] + + @Prop(TypeRef(process.class.Trigger), process.string.Trigger) + trigger!: Ref + + triggerParams!: Record } @Model(process.class.Execution, core.class.Doc, DOMAIN_PROCESS) export class TExecution extends TDoc implements Execution { - @Prop(TypeRef(contact.mixin.Employee), contact.string.Employee) - @ReadOnly() - assignee!: Ref - @Prop(TypeRef(process.class.Process), process.string.Process) @ReadOnly() - process!: Ref - - @Prop(TypeRef(process.class.ProcessToDo), time.string.ToDo) - @ReadOnly() - currentToDo!: Ref | null + process!: Ref @Prop(TypeRef(process.class.State), process.string.Step) @ReadOnly() currentState!: Ref - @Prop(TypeBoolean(), process.string.Done) - @ReadOnly() - done!: boolean - - @Prop(TypeRecord(), process.string.Rollback) - @ReadOnly() - rollback!: Record, Tx[]> + rollback!: Tx[][] @Prop(TypeRef(card.class.Card), card.string.Card) @ReadOnly() @@ -119,9 +135,11 @@ export class TExecution extends TDoc implements Execution { parentId?: Ref - context?: Record + context!: ExecutionContext - results?: Results + result?: any + + status!: ExecutionStatus } @Model(process.class.ProcessToDo, time.class.ToDo) @@ -141,7 +159,7 @@ export class TMethod extends TDoc implements Method { editor!: AnyComponent - systemOnly!: boolean + contextClass!: Ref> | null presenter?: AnyComponent @@ -153,7 +171,6 @@ export class TState extends TDoc implements State { process!: Ref title!: string actions!: Step[] - endAction?: Step | null resultType?: Type | null } @@ -170,7 +187,19 @@ export class TProcessFunction extends TDoc implements ProcessFunction { export * from './migration' export function createModel (builder: Builder): void { - builder.createModel(TProcess, TExecution, TProcessToDo, TMethod, TState, TProcessFunction) + builder.createModel(TProcess, TExecution, TProcessToDo, TMethod, TState, TProcessFunction, TTransition, TTrigger) + + createAction(builder, { + action: view.actionImpl.Delete, + label: view.string.Delete, + icon: view.icon.Delete, + keyBinding: ['Meta + Backspace'], + category: view.category.General, + input: 'any', + target: process.class.Transition, + context: { mode: ['context', 'browser'], group: 'remove' }, + visibilityTester: view.function.CanDeleteObject + }) createAction( builder, @@ -376,6 +405,14 @@ export function createModel (builder: Builder): void { presenter: process.component.ProcessPresenter }) + builder.mixin(process.class.Trigger, core.class.Class, view.mixin.AttributePresenter, { + presenter: process.component.TriggerPresenter + }) + + builder.mixin(process.class.State, core.class.Class, view.mixin.AttributePresenter, { + presenter: process.component.StatePresenter + }) + builder.createDoc( view.class.Viewlet, core.space.Model, @@ -387,7 +424,7 @@ export function createModel (builder: Builder): void { baseMenuClass: process.class.Execution }, viewOptions: { - groupBy: ['process', 'assignee', 'done'], + groupBy: ['process', 'done'], orderBy: [ ['modifiedOn', SortingOrder.Descending], ['createdOn', SortingOrder.Descending] @@ -421,11 +458,6 @@ export function createModel (builder: Builder): void { displayProps: { key: 'state', fixed: 'left' } }, { key: '', presenter: process.component.ExecutonPresenter, displayProps: { grow: true } }, - { - key: 'assignee', - displayProps: { key: 'assignee', fixed: 'right' }, - props: { kind: 'list', shouldShowName: false, avatarSize: 'x-small' } - }, { key: 'modifiedOn', displayProps: { fixed: 'right' } @@ -449,7 +481,7 @@ export function createModel (builder: Builder): void { baseMenuClass: process.class.Execution }, viewOptions: { - groupBy: ['process', 'assignee', 'done'], + groupBy: ['process', 'done'], orderBy: [ ['modifiedOn', SortingOrder.Descending], ['createdOn', SortingOrder.Descending] @@ -487,11 +519,6 @@ export function createModel (builder: Builder): void { displayProps: { key: 'state', fixed: 'left' } }, { key: '', presenter: process.component.ExecutonPresenter, displayProps: { grow: true } }, - { - key: 'assignee', - displayProps: { key: 'assignee', fixed: 'right' }, - props: { kind: 'list', shouldShowName: false, avatarSize: 'x-small' } - }, { key: 'modifiedOn', displayProps: { fixed: 'right' } @@ -511,6 +538,18 @@ export function createModel (builder: Builder): void { props: {} }) + builder.createDoc( + card.class.CardSection, + core.space.Model, + { + label: process.string.Processes, + component: process.component.ProcessesExtension, + order: 350, + navigation: [] + }, + process.section.CardProcesses + ) + builder.createDoc( process.class.Method, core.space.Model, @@ -519,7 +558,7 @@ export function createModel (builder: Builder): void { objectClass: process.class.Process, editor: process.component.SubProcessEditor, presenter: process.component.SubProcessPresenter, - systemOnly: false, + contextClass: process.class.Execution, requiredParams: ['_id'] }, process.method.RunSubProcess @@ -533,7 +572,7 @@ export function createModel (builder: Builder): void { editor: process.component.ToDoEditor, objectClass: process.class.ProcessToDo, presenter: process.component.ToDoPresenter, - systemOnly: true, + contextClass: process.class.ProcessToDo, requiredParams: ['state', 'title', 'user'] }, process.method.CreateToDo @@ -546,23 +585,48 @@ export function createModel (builder: Builder): void { label: process.string.UpdateCard, editor: process.component.UpdateCardEditor, objectClass: card.class.Card, + contextClass: null, presenter: process.component.UpdateCardPresenter, - systemOnly: false, requiredParams: [] }, process.method.UpdateCard ) builder.createDoc( - process.class.Method, + process.class.Trigger, core.space.Model, { label: process.string.OnSubProcessesDone, - objectClass: process.class.Execution, - systemOnly: true, + icon: process.icon.WaitSubprocesses, requiredParams: [] }, - process.method.WaitSubProcess + process.trigger.OnSubProcessesDone + ) + + builder.createDoc( + process.class.Trigger, + core.space.Model, + { + label: process.string.OnToDoClose, + icon: process.icon.ToDo, + editor: process.component.ToDoCloseEditor, + requiredParams: ['_id'], + checkFunction: process.triggerCheck.ToDo + }, + process.trigger.OnToDoClose + ) + + builder.createDoc( + process.class.Trigger, + core.space.Model, + { + label: process.string.OnToDoRemove, + icon: process.icon.ToDoRemove, + editor: process.component.ToDoRemoveEditor, + requiredParams: ['_id'], + checkFunction: process.triggerCheck.ToDo + }, + process.trigger.OnToDoRemove ) builder.createDoc(card.class.MasterTagEditorSection, core.space.Model, { diff --git a/models/process/src/migration.ts b/models/process/src/migration.ts index b97cd840f1..d26c1a93c7 100644 --- a/models/process/src/migration.ts +++ b/models/process/src/migration.ts @@ -75,19 +75,8 @@ async function migrateStateFuncs (client: MigrationUpgradeClient): Promise } } } - const endAction = state.endAction - if (endAction?.params != null) { - for (const key of Object.keys(endAction.params)) { - const value = (endAction.params as any)[key] - const context = getContext(value) - if (context !== undefined) { - ;(endAction.params as any)[key] = context - changed = true - } - } - } if (changed) { - await txOp.updateDoc(state._class, state.space, state._id, { actions, endAction }) + await txOp.updateDoc(state._class, state.space, state._id, { actions }) } } } diff --git a/models/process/src/plugin.ts b/models/process/src/plugin.ts index 5cdce460b3..595b4410e0 100644 --- a/models/process/src/plugin.ts +++ b/models/process/src/plugin.ts @@ -17,11 +17,15 @@ import { processId } from '@hcengineering/process' import { type PresentationMiddlewareFactory } from '@hcengineering/presentation/src/pipeline' import process from '@hcengineering/process-resources/src/plugin' import { type ViewAction, type Action } from '@hcengineering/view' +import { type CardSection } from '@hcengineering/card' export default mergeIds(processId, process, { app: { Process: '' as Ref }, + section: { + CardProcesses: '' as Ref + }, pipeline: { ProcessMiddleware: '' as Ref }, diff --git a/models/server-process/src/index.ts b/models/server-process/src/index.ts index 22123a2a6c..a62e751684 100644 --- a/models/server-process/src/index.ts +++ b/models/server-process/src/index.ts @@ -13,15 +13,16 @@ import core, { type Doc } from '@hcengineering/core' import { Mixin, type Builder } from '@hcengineering/model' -import { TMethod, TProcessFunction } from '@hcengineering/model-process' +import { TMethod, TProcessFunction, TTrigger } from '@hcengineering/model-process' import type { Resource } from '@hcengineering/platform' -import process from '@hcengineering/process' +import process, { type CheckFunc, ExecutionStatus } from '@hcengineering/process' import serverCore from '@hcengineering/server-core' import serverProcess, { type ExecuteFunc, type FuncImpl, type MethodImpl, - type TransformFunc + type TransformFunc, + type TriggerImpl } from '@hcengineering/server-process' export { serverProcessId } from '@hcengineering/server-process' @@ -36,8 +37,21 @@ export class TFuncImpl extends TProcessFunction implements FuncImpl { func!: Resource } +@Mixin(serverProcess.mixin.TriggerImpl, process.class.Trigger) +export class TTriggerImpl extends TTrigger implements TriggerImpl { + serverCheckFunc?: Resource +} + export function createModel (builder: Builder): void { - builder.createModel(TMethodImpl, TFuncImpl) + builder.createModel(TMethodImpl, TFuncImpl, TTriggerImpl) + + builder.mixin(process.trigger.OnToDoClose, process.class.Trigger, serverProcess.mixin.TriggerImpl, { + serverCheckFunc: serverProcess.func.CheckToDo + }) + + builder.mixin(process.trigger.OnToDoRemove, process.class.Trigger, serverProcess.mixin.TriggerImpl, { + serverCheckFunc: serverProcess.func.CheckToDo + }) builder.mixin(process.method.RunSubProcess, process.class.Method, serverProcess.mixin.MethodImpl, { func: serverProcess.func.RunSubProcess @@ -51,10 +65,6 @@ export function createModel (builder: Builder): void { func: serverProcess.func.UpdateCard }) - builder.mixin(process.method.WaitSubProcess, process.class.Method, serverProcess.mixin.MethodImpl, { - func: serverProcess.func.WaitSubProcess - }) - builder.mixin(process.function.FirstValue, process.class.ProcessFunction, serverProcess.mixin.FuncImpl, { func: serverProcess.transform.FirstValue }) @@ -104,20 +114,11 @@ export function createModel (builder: Builder): void { txMatch: { _class: core.class.TxUpdateDoc, objectClass: process.class.Execution, - 'operations.error': null + 'operations.status': ExecutionStatus.Active, + space: core.space.Tx }, isAsync: true }) - - builder.createDoc(serverCore.class.Trigger, core.space.Model, { - trigger: serverProcess.trigger.OnProcessRemove, - txMatch: { - _class: core.class.TxRemoveDoc, - objectClass: process.class.Process - }, - isAsync: true - }) - builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverProcess.trigger.OnExecutionCreate, txMatch: { @@ -145,12 +146,34 @@ export function createModel (builder: Builder): void { isAsync: true }) + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverProcess.trigger.OnProcessRemove, + txMatch: { + _class: core.class.TxRemoveDoc, + objectClass: process.class.Process + } + }) + builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverProcess.trigger.OnStateRemove, txMatch: { _class: core.class.TxRemoveDoc, objectClass: process.class.State - }, - isAsync: true + } + }) + + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverProcess.trigger.OnStateActionsUpdate, + txMatch: { + _class: core.class.TxUpdateDoc, + objectClass: process.class.State + } + }) + + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverProcess.trigger.OnTransition, + txMatch: { + objectClass: process.class.Transition + } }) } diff --git a/packages/ui/src/components/DropdownPopup.svelte b/packages/ui/src/components/DropdownPopup.svelte index ccba32aaa5..1289dee211 100644 --- a/packages/ui/src/components/DropdownPopup.svelte +++ b/packages/ui/src/components/DropdownPopup.svelte @@ -17,11 +17,12 @@ import { translateCB } from '@hcengineering/platform' import { themeStore } from '@hcengineering/theme' import { createEventDispatcher, onMount } from 'svelte' - import { deviceOptionsStore, resizeObserver } from '..' + import { deviceOptionsStore, IconSearch, resizeObserver } from '..' import plugin from '../plugin' import type { AnySvelteComponent, ListItem } from '../types' import Icon from './Icon.svelte' import ListView from './ListView.svelte' + import EditWithIcon from './EditWithIcon.svelte' export let icon: Asset | AnySvelteComponent export let placeholder: IntlString = plugin.string.SearchDots @@ -36,7 +37,7 @@ }) } const dispatch = createEventDispatcher() - let searchInput: HTMLInputElement + let searchInput: EditWithIcon onMount(() => { if (searchInput && !$deviceOptionsStore.isMobile) searchInput.focus() @@ -78,13 +79,14 @@