From 4b4b9edbcc6dc9f386ffa8a33241afe40066feb3 Mon Sep 17 00:00:00 2001 From: Denis Bykhov Date: Tue, 5 May 2026 18:37:39 +0500 Subject: [PATCH] add AskSubclass option to CreateCard process and implement ClassUserInput component for flexible subclass selection (#10822) Signed-off-by: Denis Bykhov --- models/process/src/actions.ts | 2 +- .../src/components/TypeSelector.svelte | 3 +- plugins/process-assets/lang/cs.json | 3 +- plugins/process-assets/lang/de.json | 3 +- plugins/process-assets/lang/en.json | 3 +- plugins/process-assets/lang/es.json | 3 +- plugins/process-assets/lang/fr.json | 3 +- plugins/process-assets/lang/it.json | 3 +- plugins/process-assets/lang/ja.json | 3 +- plugins/process-assets/lang/ko.json | 3 +- plugins/process-assets/lang/pt-br.json | 3 +- plugins/process-assets/lang/pt.json | 3 +- plugins/process-assets/lang/ru.json | 3 +- plugins/process-assets/lang/tr.json | 3 +- plugins/process-assets/lang/zh.json | 3 +- .../contextEditors/ClassUserInput.svelte | 53 +++++++++++++ .../contextEditors/RequestUserInput.svelte | 32 +++++--- .../presenters/CreateCardPresenter.svelte | 5 +- .../settings/CreateCardEditor.svelte | 78 ++++++++++++++----- plugins/process-resources/src/plugin.ts | 3 +- 20 files changed, 167 insertions(+), 48 deletions(-) create mode 100644 plugins/process-resources/src/components/contextEditors/ClassUserInput.svelte diff --git a/models/process/src/actions.ts b/models/process/src/actions.ts index 38fc53629a..6ecac2aff8 100644 --- a/models/process/src/actions.ts +++ b/models/process/src/actions.ts @@ -87,7 +87,7 @@ export function defineMethods (builder: Builder): void { editor: process.component.CreateCardEditor, presenter: process.component.CreateCardPresenter, createdContext: { _class: card.class.Card }, - requiredParams: ['title', '_class'] + requiredParams: ['title', 'targetClass'] }, process.method.CreateCard ) diff --git a/plugins/card-resources/src/components/TypeSelector.svelte b/plugins/card-resources/src/components/TypeSelector.svelte index 36bad6f25d..b9736a3e9e 100644 --- a/plugins/card-resources/src/components/TypeSelector.svelte +++ b/plugins/card-resources/src/components/TypeSelector.svelte @@ -25,13 +25,14 @@ export let width: string | undefined = undefined export let kind: ButtonKind | undefined = undefined export let size: ButtonSize | undefined = undefined + export let parent: Ref = card.class.Card export let disabled: boolean = false const client = getClient() const hierarchy = client.getHierarchy() function filterClasses (): [DropdownIntlItem, DropdownIntlItem[]][] { - const descendants = hierarchy.getDescendants(card.class.Card).filter((p) => p !== card.class.Card) + const descendants = hierarchy.getDescendants(parent).filter((p) => p !== card.class.Card) const added = new Set>>() const base = new Map>, Class[]>() for (const _id of descendants) { diff --git a/plugins/process-assets/lang/cs.json b/plugins/process-assets/lang/cs.json index 42bda02948..3203b5b923 100644 --- a/plugins/process-assets/lang/cs.json +++ b/plugins/process-assets/lang/cs.json @@ -170,7 +170,8 @@ "MarkupFromText": "MarkUp z textu", "TextFromMarkup": "Text z MarkUp", "TextFromSelect": "Text z výběru", - "SelectFromText": "Výběr z textu" + "SelectFromText": "Výběr z textu", + "AskSubclass": "Zeptat se na podtřídu" }, "error": { "MethodNotFound": "Metoda nenalezena: {methodId}", diff --git a/plugins/process-assets/lang/de.json b/plugins/process-assets/lang/de.json index 087e57e1e4..873547e928 100644 --- a/plugins/process-assets/lang/de.json +++ b/plugins/process-assets/lang/de.json @@ -170,7 +170,8 @@ "TextFromMarkup": "Text aus Markup", "MarkupFromText": "Markup aus Text", "TextFromSelect": "Text aus Auswahl", - "SelectFromText": "Auswahl aus Text" + "SelectFromText": "Auswahl aus Text", + "AskSubclass": "Nach Unterklasse fragen" }, "error": { "MethodNotFound": "Methode nicht gefunden: {methodId}", diff --git a/plugins/process-assets/lang/en.json b/plugins/process-assets/lang/en.json index 9be999f900..bea2c5b76a 100644 --- a/plugins/process-assets/lang/en.json +++ b/plugins/process-assets/lang/en.json @@ -175,7 +175,8 @@ "TextFromMarkup": "Text from markup", "MarkupFromText": "Markup from text", "TextFromSelect": "Text from select", - "SelectFromText": "Select from text" + "SelectFromText": "Select from text", + "AskSubclass": "Ask subclass" }, "error": { "MethodNotFound": "Method not found: {methodId}", diff --git a/plugins/process-assets/lang/es.json b/plugins/process-assets/lang/es.json index 3f9d12bf64..973a7ad5c9 100644 --- a/plugins/process-assets/lang/es.json +++ b/plugins/process-assets/lang/es.json @@ -175,7 +175,8 @@ "TextFromMarkup": "Texto desde markup", "MarkupFromText": "Markup desde texto", "TextFromSelect": "Texto desde selección", - "SelectFromText": "Selección desde texto" + "SelectFromText": "Selección desde texto", + "AskSubclass": "Preguntar por subclase" }, "error": { "MethodNotFound": "Método no encontrado: {methodId}", diff --git a/plugins/process-assets/lang/fr.json b/plugins/process-assets/lang/fr.json index 47a7a0806d..860b3dfc57 100644 --- a/plugins/process-assets/lang/fr.json +++ b/plugins/process-assets/lang/fr.json @@ -175,7 +175,8 @@ "TextFromMarkup": "Texte depuis markup", "MarkupFromText": "Markup depuis texte", "TextFromSelect": "Texte depuis sélection", - "SelectFromText": "Sélection depuis texte" + "SelectFromText": "Sélection depuis texte", + "AskSubclass": "Demander la sous-classe" }, "error": { "MethodNotFound": "Méthode introuvable : {methodId}", diff --git a/plugins/process-assets/lang/it.json b/plugins/process-assets/lang/it.json index 0abfa19c8c..9d88fbc691 100644 --- a/plugins/process-assets/lang/it.json +++ b/plugins/process-assets/lang/it.json @@ -175,7 +175,8 @@ "TextFromMarkup": "Testo da markup", "MarkupFromText": "Markup da testo", "TextFromSelect": "Testo da selezione", - "SelectFromText": "Selezione da testo" + "SelectFromText": "Selezione da testo", + "AskSubclass": "Chiedi sottoclasse" }, "error": { "MethodNotFound": "Metodo non trovato: {methodId}", diff --git a/plugins/process-assets/lang/ja.json b/plugins/process-assets/lang/ja.json index 29aea93578..d6253f91a2 100644 --- a/plugins/process-assets/lang/ja.json +++ b/plugins/process-assets/lang/ja.json @@ -174,7 +174,8 @@ "TextFromMarkup": "テキストからマークアップ", "MarkupFromText": "マークアップからテキスト", "TextFromSelect": "テキストから選択", - "SelectFromText": "選択からテキスト" + "SelectFromText": "選択からテキスト", + "AskSubclass": "サブクラスを尋ねる" }, "error": { "MethodNotFound": "メソッドが見つかりません: {methodId}", diff --git a/plugins/process-assets/lang/ko.json b/plugins/process-assets/lang/ko.json index 58f452fd1c..042d94f0a1 100644 --- a/plugins/process-assets/lang/ko.json +++ b/plugins/process-assets/lang/ko.json @@ -175,7 +175,8 @@ "TextFromMarkup": "마크업에서 텍스트로", "MarkupFromText": "텍스트에서 마크업으로", "TextFromSelect": "선택에서 텍스트로", - "SelectFromText": "텍스트에서 선택으로" + "SelectFromText": "텍스트에서 선택으로", + "AskSubclass": "하위 클래스 선택" }, "error": { "MethodNotFound": "메서드를 찾을 수 없습니다: {methodId}", diff --git a/plugins/process-assets/lang/pt-br.json b/plugins/process-assets/lang/pt-br.json index 28540b8b8a..1d2665b96e 100644 --- a/plugins/process-assets/lang/pt-br.json +++ b/plugins/process-assets/lang/pt-br.json @@ -163,7 +163,8 @@ "TextFromMarkup": "Texto de marcação", "MarkupFromText": "Marcação de texto", "TextFromSelect": "Texto de seleção", - "SelectFromText": "Seleção de texto" + "SelectFromText": "Seleção de texto", + "AskSubclass": "Perguntar subclasse" }, "error": { "MethodNotFound": "Método não encontrado: {methodId}", diff --git a/plugins/process-assets/lang/pt.json b/plugins/process-assets/lang/pt.json index 546076873f..f94a9a46fb 100644 --- a/plugins/process-assets/lang/pt.json +++ b/plugins/process-assets/lang/pt.json @@ -175,7 +175,8 @@ "TextFromMarkup": "Texto de marcação", "MarkupFromText": "Marcação de texto", "TextFromSelect": "Texto de seleção", - "SelectFromText": "Seleção de texto" + "SelectFromText": "Seleção de texto", + "AskSubclass": "Perguntar subclasse" }, "error": { "MethodNotFound": "Método não encontrado: {methodId}", diff --git a/plugins/process-assets/lang/ru.json b/plugins/process-assets/lang/ru.json index 33caf6ab35..a4918da8fc 100644 --- a/plugins/process-assets/lang/ru.json +++ b/plugins/process-assets/lang/ru.json @@ -175,7 +175,8 @@ "MarkupFromText": "Текст из разметки", "TextFromMarkup": "Разметка из текста", "TextFromSelect": "Текст из выбора", - "SelectFromText": "Выбор из текста" + "SelectFromText": "Выбор из текста", + "AskSubclass": "Спросить подкласс" }, "error": { "MethodNotFound": "Метод не найден: {methodId}", diff --git a/plugins/process-assets/lang/tr.json b/plugins/process-assets/lang/tr.json index fed651ff77..791429d87f 100644 --- a/plugins/process-assets/lang/tr.json +++ b/plugins/process-assets/lang/tr.json @@ -170,7 +170,8 @@ "TextFromMarkup": "Metinden işaretleme", "MarkupFromText": "İşaretlemeden metin", "TextFromSelect": "Seçimden metin", - "SelectFromText": "Metinden seçim" + "SelectFromText": "Metinden seçim", + "AskSubclass": "Alt sınıfı sor" }, "error": { "MethodNotFound": "Metod bulunamadı: {methodId}", diff --git a/plugins/process-assets/lang/zh.json b/plugins/process-assets/lang/zh.json index 94928bf0e9..cd5c7f5f0e 100644 --- a/plugins/process-assets/lang/zh.json +++ b/plugins/process-assets/lang/zh.json @@ -175,7 +175,8 @@ "TextFromMarkup": "标记到文本", "MarkupFromText": "文本到标记", "TextFromSelect": "选择到文本", - "SelectFromText": "文本到选择" + "SelectFromText": "文本到选择", + "AskSubclass": "询问子类" }, "error": { "MethodNotFound": "找不到方法:{methodId}", diff --git a/plugins/process-resources/src/components/contextEditors/ClassUserInput.svelte b/plugins/process-resources/src/components/contextEditors/ClassUserInput.svelte new file mode 100644 index 0000000000..56b005ab8b --- /dev/null +++ b/plugins/process-resources/src/components/contextEditors/ClassUserInput.svelte @@ -0,0 +1,53 @@ + + + +{#if attribute} +
+
+{/if} +
+ { + if (e.detail !== undefined) { + onChange(e.detail) + } + }} + /> +
diff --git a/plugins/process-resources/src/components/contextEditors/RequestUserInput.svelte b/plugins/process-resources/src/components/contextEditors/RequestUserInput.svelte index 50140ea3bc..417e9988f4 100644 --- a/plugins/process-resources/src/components/contextEditors/RequestUserInput.svelte +++ b/plugins/process-resources/src/components/contextEditors/RequestUserInput.svelte @@ -21,6 +21,7 @@ import plugin from '../../plugin' import TransitionPresenter from '../settings/TransitionPresenter.svelte' import RequestUserInputAttribute from './RequestUserInputAttribute.svelte' + import ClassUserInput from './ClassUserInput.svelte' export let processId: Ref export let space: Ref @@ -66,16 +67,27 @@ {/if}
{#each inputs as input} - { - values[input.id] = e.detail - values = values - }} - /> + {#if input.key === '_class'} + { + values[input.id] = e.detail + values = values + }} + /> + {:else} + { + values[input.id] = e.detail + values = values + }} + /> + {/if} {/each}
diff --git a/plugins/process-resources/src/components/presenters/CreateCardPresenter.svelte b/plugins/process-resources/src/components/presenters/CreateCardPresenter.svelte index 22d65f9996..b0c1738ee4 100644 --- a/plugins/process-resources/src/components/presenters/CreateCardPresenter.svelte +++ b/plugins/process-resources/src/components/presenters/CreateCardPresenter.svelte @@ -33,9 +33,8 @@ $: contextValue = params.title !== undefined ? parseContext(params.title) : undefined $: context = getContext(client, process, core.class.TypeString, 'attribute') - $: _class = params?._class - ? (client.getHierarchy().getClass(params._class as Ref) as MasterTag) - : undefined + $: target = params?.targetClass ?? params?._class + $: _class = target !== undefined ? (client.getHierarchy().getClass(target as Ref) as MasterTag) : undefined $: icon = _class?.icon diff --git a/plugins/process-resources/src/components/settings/CreateCardEditor.svelte b/plugins/process-resources/src/components/settings/CreateCardEditor.svelte index c617b4101c..207299d069 100644 --- a/plugins/process-resources/src/components/settings/CreateCardEditor.svelte +++ b/plugins/process-resources/src/components/settings/CreateCardEditor.svelte @@ -18,10 +18,12 @@ import core, { AnyAttribute, Class, Ref } from '@hcengineering/core' import { translateCB } from '@hcengineering/platform' import presentation, { getClient } from '@hcengineering/presentation' - import { Process, Step } from '@hcengineering/process' - import { Button, eventToHTMLElement, Label, SelectPopup, showPopup, tooltip } from '@hcengineering/ui' + import { createContext, Process, Step } from '@hcengineering/process' + import { Button, eventToHTMLElement, Label, SelectPopup, showPopup, Toggle, tooltip } from '@hcengineering/ui' import { createEventDispatcher } from 'svelte' import ParamsEditor from './ParamsEditor.svelte' + import plugin from '../../plugin' + import { generateContextId } from '../../utils' export let process: Process export let step: Step @@ -29,14 +31,16 @@ const dispatch = createEventDispatcher() step.params.title = step.params.title ?? '' + step.params.targetClass = step.params.targetClass ?? step.params._class ?? process.masterTag step.params._class = step.params._class ?? process.masterTag let params = step.params - let _class: Ref> = (params._class as Ref>) ?? process.masterTag + let targetClass: Ref> = + (params.targetClass as Ref>) ?? (params._class as Ref>) ?? process.masterTag const client = getClient() - const hierarchy = client.getHierarchy() + const h = client.getHierarchy() translateCB(cardPlugin.string.Card, {}, undefined, (res) => { setName(res) @@ -59,8 +63,8 @@ } function getKeys (_class: Ref>): AnyAttribute[] { - const ignoreKeys = ['_class', 'parent', 'attachments', 'todos'] - const attributes = hierarchy.getAllAttributes(_class, core.class.Doc) + const ignoreKeys = ['_class', 'targetClass', 'parent', 'attachments', 'todos'] + const attributes = h.getAllAttributes(_class, core.class.Doc) const res: AnyAttribute[] = [] for (const [key, attr] of attributes) { if (attr.hidden === true) continue @@ -71,10 +75,10 @@ } let keys = Object.keys(params).filter((key) => { - return key !== '_class' + return key !== '_class' && key !== 'targetClass' }) - $: allAttrs = getKeys(_class) + $: allAttrs = getKeys(targetClass) $: possibleAttrs = allAttrs.filter((attr) => !keys.includes(attr.name)) function addKey (key: string): void { @@ -112,25 +116,48 @@ function typeChange (_id: Ref>): void { if (_id === undefined) return - allAttrs = getKeys(_id) + const attrKeys = ['_class', 'targetClass', ...getKeys(_id).map((p) => p.name)] const oldParams = { ...params } params = {} - for (const attr of allAttrs) { - const key = attr.name + for (const key of attrKeys) { if (oldParams[key] !== undefined) { ;(params as any)[key] = oldParams[key] } } - keys = Object.keys(params) - params._class = _class + keys = Object.keys(params).filter((key) => { + return key !== '_class' && key !== 'targetClass' + }) + if (params.targetClass !== _id) { + params._class = _id + params.targetClass = _id + } step.params = params if (step.context != null) { - step.context._class = _class + step.context._class = _id } + step = step dispatch('change', step) } - $: typeChange(_class) + $: typeChange(targetClass) + + $: subclasses = h.getDescendants(targetClass).filter((c) => !h.isMixin(c) && c !== targetClass) + + let askSubclass = params._class !== params.targetClass + $: askSubclass = params._class !== params.targetClass + + function changeAskSubclass (e: CustomEvent): void { + const context = createContext({ + type: 'userRequest', + id: generateContextId(), + _class: targetClass, + key: '_class' + }) + if (e.detail !== undefined) { + step.params._class = e.detail ? context : targetClass + dispatch('change', step) + } + }
@@ -143,18 +170,31 @@
-{#key _class} - +{#key targetClass} + {/key} {#if possibleAttrs.length > 0}
diff --git a/plugins/process-resources/src/plugin.ts b/plugins/process-resources/src/plugin.ts index 652df27f95..55d58db6b6 100644 --- a/plugins/process-resources/src/plugin.ts +++ b/plugins/process-resources/src/plugin.ts @@ -281,7 +281,8 @@ export default mergeIds(processId, process, { DayFromDate: '' as IntlString, DateDifference: '' as IntlString, TextFromSelect: '' as IntlString, - SelectFromText: '' as IntlString + SelectFromText: '' as IntlString, + AskSubclass: '' as IntlString }, permission: { RunProcess: '' as Ref,