From 0641da8a15e6669edc18c75ca9107debaa227efd Mon Sep 17 00:00:00 2001 From: Denis Bykhov Date: Wed, 26 Mar 2025 15:13:09 +0500 Subject: [PATCH] processes UI (#8349) --- models/process/package.json | 1 + models/process/src/index.ts | 140 ++++++++++++++++-- models/process/src/plugin.ts | 3 + .../src/components/CardEditor.svelte | 71 +++------ .../src/components/CardSelector.svelte | 101 +++++++++++++ .../src/components/EditCard.svelte | 14 +- plugins/card-resources/src/index.ts | 2 + plugins/process-assets/lang/cs.json | 5 +- plugins/process-assets/lang/de.json | 5 +- plugins/process-assets/lang/en.json | 5 +- plugins/process-assets/lang/es.json | 5 +- plugins/process-assets/lang/fr.json | 5 +- plugins/process-assets/lang/it.json | 5 +- plugins/process-assets/lang/pt.json | 5 +- plugins/process-assets/lang/ru.json | 5 +- plugins/process-assets/lang/zh.json | 5 +- plugins/process-resources/package.json | 3 + .../src/components/Main.svelte | 89 +++++++++++ .../src/components/Navigator.svelte | 73 +++++++++ .../src/components/ProcessesExtension.svelte | 1 - .../src/components/RunProcessCardPopup.svelte | 79 ++++++++++ .../src/components/RunProcessPopup.svelte | 33 ++--- .../src/components/icons/IconBacklog.svelte | 2 +- .../src/components/icons/IconCompleted.svelte | 2 +- .../src/components/icons/IconProgress.svelte | 2 +- plugins/process-resources/src/index.ts | 10 +- plugins/process-resources/src/plugin.ts | 15 +- plugins/process-resources/src/types.ts | 22 +++ plugins/process-resources/src/utils.ts | 10 +- .../src/components/Table.svelte | 1 + .../src/components/list/ListItem.svelte | 28 +--- .../src/components/list/ListPresenter.svelte | 21 ++- 32 files changed, 628 insertions(+), 140 deletions(-) create mode 100644 plugins/card-resources/src/components/CardSelector.svelte create mode 100644 plugins/process-resources/src/components/Main.svelte create mode 100644 plugins/process-resources/src/components/Navigator.svelte create mode 100644 plugins/process-resources/src/components/RunProcessCardPopup.svelte create mode 100644 plugins/process-resources/src/types.ts diff --git a/models/process/package.json b/models/process/package.json index fbf028f743..d3a18afc48 100644 --- a/models/process/package.json +++ b/models/process/package.json @@ -42,6 +42,7 @@ "@hcengineering/process-resources": "^0.6.0", "@hcengineering/model-presentation": "^0.6.0", "@hcengineering/view": "^0.6.13", + "@hcengineering/model-workbench": "^0.6.1", "@hcengineering/card": "^0.6.0" } } diff --git a/models/process/src/index.ts b/models/process/src/index.ts index 2df91198a5..a150fee908 100644 --- a/models/process/src/index.ts +++ b/models/process/src/index.ts @@ -12,7 +12,9 @@ // limitations under the License. import card, { type Card, type MasterTag } from '@hcengineering/card' +import contact, { type Employee } from '@hcengineering/contact' import core, { + AccountRole, type Class, DOMAIN_MODEL, type Doc, @@ -22,27 +24,37 @@ import core, { type Space, type Tx } from '@hcengineering/core' -import { ArrOf, Model, Prop, TypeBoolean, TypeRecord, TypeRef, TypeString, type Builder } from '@hcengineering/model' -import presentation from '@hcengineering/model-presentation' +import { + ArrOf, + type Builder, + Model, + Prop, + ReadOnly, + TypeBoolean, + TypeRecord, + 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 ProcessFunction, type Execution, type Method, type Process, + type ProcessFunction, type ProcessToDo, type State, - type Step + type Step, + processId } from '@hcengineering/process' import time from '@hcengineering/time' - import { type AnyComponent } from '@hcengineering/ui' -import process from './plugin' -import contact, { type Employee } from '@hcengineering/contact' import { type AttributeCategory } from '@hcengineering/view' +import process from './plugin' const DOMAIN_PROCESS = 'process' as Domain @@ -64,24 +76,31 @@ export class TProcess extends TDoc implements Process { @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 @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[]> @Prop(TypeRef(card.class.Card), card.string.Card) + @ReadOnly() card!: Ref } @@ -147,6 +166,20 @@ export function createModel (builder: Builder): void { process.action.RunProcess ) + builder.createDoc( + workbench.class.Application, + core.space.Model, + { + label: process.string.Processes, + icon: process.icon.Process, + accessLevel: AccountRole.User, + alias: processId, + hidden: false, + component: process.component.Main + }, + process.app.Process + ) + builder.createDoc( process.class.ProcessFunction, core.space.Model, @@ -218,15 +251,25 @@ export function createModel (builder: Builder): void { view.class.Viewlet, core.space.Model, { + variant: 'cardExecutions', attachTo: process.class.Execution, descriptor: view.viewlet.List, viewOptions: { - groupBy: ['process', 'assignee'], + groupBy: ['process', 'assignee', 'done'], orderBy: [ ['modifiedOn', SortingOrder.Descending], ['createdOn', SortingOrder.Descending] ], - other: [] + other: [ + { + key: 'showDone', + type: 'toggle', + defaultValue: true, + actionTarget: 'query', + action: process.function.ShowDoneQuery, + label: process.string.ShowDone + } + ] }, configOptions: { strict: true, @@ -237,27 +280,96 @@ export function createModel (builder: Builder): void { key: '', label: process.string.Process, presenter: process.component.ExecutonPresenter, - displayProps: { key: 'process' } + displayProps: { key: 'process', fixed: 'left' } }, { key: '', label: process.string.Step, presenter: process.component.ExecutonProgressPresenter, - displayProps: { key: 'state' } + 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', onChange: undefined } + props: { kind: 'list', shouldShowName: false, avatarSize: 'x-small' } }, - 'modifiedOn', - 'createdOn' + { + key: 'modifiedOn', + displayProps: { fixed: 'right' } + }, + { + key: 'createdOn', + displayProps: { fixed: 'right' } + } ] }, process.viewlet.CardExecutions ) + builder.createDoc( + view.class.Viewlet, + core.space.Model, + { + attachTo: process.class.Execution, + descriptor: view.viewlet.List, + viewOptions: { + groupBy: ['process', 'assignee', 'done'], + orderBy: [ + ['modifiedOn', SortingOrder.Descending], + ['createdOn', SortingOrder.Descending] + ], + other: [ + { + key: 'showDone', + type: 'toggle', + defaultValue: true, + actionTarget: 'query', + action: process.function.ShowDoneQuery, + label: process.string.ShowDone + } + ] + }, + configOptions: { + strict: true, + hiddenKeys: ['process', 'currentState'] + }, + config: [ + { + key: 'card', + displayProps: { key: 'card', fixed: 'left' } + }, + { + key: '', + label: process.string.Process, + presenter: process.component.ExecutonPresenter, + displayProps: { key: 'process', fixed: 'left' } + }, + { + key: '', + label: process.string.Step, + presenter: process.component.ExecutonProgressPresenter, + 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' } + }, + { + key: 'createdOn', + displayProps: { fixed: 'right' } + } + ] + }, + process.viewlet.ExecutionsList + ) + builder.createDoc(presentation.class.ComponentPointExtension, core.space.Model, { extension: card.extensions.EditCardExtension, component: process.component.ProcessesExtension, diff --git a/models/process/src/plugin.ts b/models/process/src/plugin.ts index 7af642f44e..98bd825f30 100644 --- a/models/process/src/plugin.ts +++ b/models/process/src/plugin.ts @@ -18,6 +18,9 @@ import process from '@hcengineering/process-resources/src/plugin' import { type Action } from '@hcengineering/view' export default mergeIds(processId, process, { + app: { + Process: '' as Ref + }, ids: { ProcessSettings: '' as Ref }, diff --git a/plugins/card-resources/src/components/CardEditor.svelte b/plugins/card-resources/src/components/CardEditor.svelte index 3f80552654..57e213da62 100644 --- a/plugins/card-resources/src/components/CardEditor.svelte +++ b/plugins/card-resources/src/components/CardEditor.svelte @@ -30,76 +30,41 @@ import { createEventDispatcher } from 'svelte' import card from '../plugin' import CardsPopup from './CardsPopup.svelte' + import CardSelector from './CardSelector.svelte' - export let value: Ref + export let value: Ref | undefined export let readonly: boolean = false export let label: IntlString = card.string.Card export let onChange: (value: any) => void export let attribute: AnyAttribute export let focusIndex: number | undefined = undefined - export let shouldShowLabel: boolean = true export let kind: ButtonKind = 'no-border' export let size: ButtonSize = 'small' export let justify: 'left' | 'center' = 'left' export let width: string | undefined = 'min-content' const dispatch = createEventDispatcher() - const client = getClient() - const hierarchy = client.getHierarchy() - const handleOpen = (event: MouseEvent): void => { - event.stopPropagation() - const _class = (attribute.type as RefTo).to - - if (readonly) { - return - } - - showPopup(CardsPopup, { selected: value, _class }, eventToHTMLElement(event), change) + const change = (val: Ref | undefined): void => { + dispatch('change', val) + onChange(val) } - const change = (val: Card | undefined): void => { - if (readonly || val == null || value === val._id) { - return - } - - value = val._id - dispatch('change', value) - onChange(value) - } - - let doc: Card | undefined - - const query = createQuery() - $: query.query(card.class.Card, { _id: value }, (res) => { - doc = res[0] - }) - - $: _classRef = doc?._class ?? (attribute?.type as RefTo)?.to - $: _class = _classRef !== undefined ? (hierarchy.findClass(_classRef) as MasterTag) : undefined - - $: icon = _class?.icon === view.ids.IconWithEmoji ? IconWithEmoji : _class?.icon - $: iconProps = _class?.icon === view.ids.IconWithEmoji ? { icon: _class?.color } : {} + const _class = (attribute.type as RefTo).to - + {size} + {justify} + {width} + on:change={(e) => { + change(e.detail) + }} +/> diff --git a/plugins/card-resources/src/components/CardSelector.svelte b/plugins/card-resources/src/components/CardSelector.svelte new file mode 100644 index 0000000000..e358cfd6bc --- /dev/null +++ b/plugins/card-resources/src/components/CardSelector.svelte @@ -0,0 +1,101 @@ + + + + diff --git a/plugins/card-resources/src/components/EditCard.svelte b/plugins/card-resources/src/components/EditCard.svelte index 8ff82b6af1..4b09c5f279 100644 --- a/plugins/card-resources/src/components/EditCard.svelte +++ b/plugins/card-resources/src/components/EditCard.svelte @@ -160,16 +160,16 @@ - - + + diff --git a/plugins/card-resources/src/index.ts b/plugins/card-resources/src/index.ts index deed0e6ae6..8454921066 100644 --- a/plugins/card-resources/src/index.ts +++ b/plugins/card-resources/src/index.ts @@ -42,6 +42,8 @@ import CardEditor from './components/CardEditor.svelte' import CardRefPresenter from './components/CardRefPresenter.svelte' import ChangeType from './components/ChangeType.svelte' +export { default as CardSelector } from './components/CardSelector.svelte' + export default async (): Promise => ({ component: { MasterTags, diff --git a/plugins/process-assets/lang/cs.json b/plugins/process-assets/lang/cs.json index 9a08e66948..b57d5941f9 100644 --- a/plugins/process-assets/lang/cs.json +++ b/plugins/process-assets/lang/cs.json @@ -33,6 +33,9 @@ "Trim": "Oříznout", "FirstValue": "První hodnota", "LastValue": "Poslední hodnota", - "Random": "Náhodné" + "Random": "Náhodné", + "AllProcesses": "Všechny procesy", + "MyProcesses": "Moje procesy", + "ShowDone": "Zobrazit hotovo" } } diff --git a/plugins/process-assets/lang/de.json b/plugins/process-assets/lang/de.json index a25ce514a6..5190b0c323 100644 --- a/plugins/process-assets/lang/de.json +++ b/plugins/process-assets/lang/de.json @@ -33,6 +33,9 @@ "Trim": "Entfernen", "FirstValue": "Erstes Wert", "LastValue": "Letztes Wert", - "Random": "Zufällig" + "Random": "Zufällig", + "AllProcesses": "Alle Prozesse", + "MyProcesses": "Meine Prozesse", + "ShowDone": "Erledigt anzeigen" } } diff --git a/plugins/process-assets/lang/en.json b/plugins/process-assets/lang/en.json index 65a770b9bd..acdf1d99c6 100644 --- a/plugins/process-assets/lang/en.json +++ b/plugins/process-assets/lang/en.json @@ -33,6 +33,9 @@ "Trim": "Trim", "FirstValue": "First value", "LastValue": "Last value", - "Random": "Random" + "Random": "Random", + "AllProcesses": "All processes", + "MyProcesses": "My processes", + "ShowDone": "Show done" } } diff --git a/plugins/process-assets/lang/es.json b/plugins/process-assets/lang/es.json index 28ffad7e60..316a234fdd 100644 --- a/plugins/process-assets/lang/es.json +++ b/plugins/process-assets/lang/es.json @@ -33,6 +33,9 @@ "Trim": "Cortar", "FirstValue": "Primer valor", "LastValue": "Último valor", - "Random": "Aleatorio" + "Random": "Aleatorio", + "AllProcesses": "Todos los procesos", + "MyProcesses": "Mis procesos", + "ShowDone": "Mostrar hecho" } } diff --git a/plugins/process-assets/lang/fr.json b/plugins/process-assets/lang/fr.json index aa81b63288..2140f9e2ea 100644 --- a/plugins/process-assets/lang/fr.json +++ b/plugins/process-assets/lang/fr.json @@ -33,6 +33,9 @@ "Trim": "Couper", "FirstValue": "Premier valeur", "LastValue": "Dernière valeur", - "Random": "Aléatoire" + "Random": "Aléatoire", + "AllProcesses": "Tous les processus", + "MyProcesses": "Mes processus", + "ShowDone": "Afficher terminé" } } \ No newline at end of file diff --git a/plugins/process-assets/lang/it.json b/plugins/process-assets/lang/it.json index 64e3519d65..f71908f3d9 100644 --- a/plugins/process-assets/lang/it.json +++ b/plugins/process-assets/lang/it.json @@ -33,6 +33,9 @@ "Trim": "Taglia", "FirstValue": "Primo valore", "LastValue": "Ultimo valore", - "Random": "Casuale" + "Random": "Casuale", + "AllProcesses": "Tutti i processi", + "MyProcesses": "I miei processi", + "ShowDone": "Mostra completato" } } diff --git a/plugins/process-assets/lang/pt.json b/plugins/process-assets/lang/pt.json index d9ddc858cc..90b48b737a 100644 --- a/plugins/process-assets/lang/pt.json +++ b/plugins/process-assets/lang/pt.json @@ -33,6 +33,9 @@ "Trim": "Apagar", "FirstValue": "Primeiro Valor", "LastValue": "Último Valor", - "Random": "Aleatório" + "Random": "Aleatório", + "AllProcesses": "Todos os Processos", + "MyProcesses": "Meus Processos", + "ShowDone": "Mostrar Concluído" } } diff --git a/plugins/process-assets/lang/ru.json b/plugins/process-assets/lang/ru.json index 9ca5879084..f13391f34e 100644 --- a/plugins/process-assets/lang/ru.json +++ b/plugins/process-assets/lang/ru.json @@ -34,6 +34,9 @@ "Trim": "Обрезать", "FirstValue": "Первое значение", "LastValue": "Последнее значение", - "Random": "Случайное" + "Random": "Случайное", + "AllProcesses": "Все процессы", + "MyProcesses": "Мои процессы", + "ShowDone": "Показать выполненные" } } diff --git a/plugins/process-assets/lang/zh.json b/plugins/process-assets/lang/zh.json index a5c9cfb7a3..d550395977 100644 --- a/plugins/process-assets/lang/zh.json +++ b/plugins/process-assets/lang/zh.json @@ -33,6 +33,9 @@ "Trim": "截断", "FirstValue": "第一个值", "LastValue": "最后一个值", - "Random": "随机" + "Random": "随机", + "AllProcesses": "所有过程", + "MyProcesses": "我的过程", + "ShowDone": "显示已完成" } } diff --git a/plugins/process-resources/package.json b/plugins/process-resources/package.json index 84a8fb54fe..bddbaf4ad9 100644 --- a/plugins/process-resources/package.json +++ b/plugins/process-resources/package.json @@ -44,7 +44,10 @@ "@hcengineering/setting-resources": "^0.6.0", "@hcengineering/view": "^0.6.13", "@hcengineering/view-resources": "^0.6.0", + "@hcengineering/card-resources": "^0.6.0", + "@hcengineering/contact": "^0.6.24", "@hcengineering/card": "^0.6.0", + "@hcengineering/workbench-resources": "^0.6.1", "@hcengineering/core": "^0.6.32", "@hcengineering/ui": "^0.6.15", "@hcengineering/rank": "^0.6.4", diff --git a/plugins/process-resources/src/components/Main.svelte b/plugins/process-resources/src/components/Main.svelte new file mode 100644 index 0000000000..570f84900f --- /dev/null +++ b/plugins/process-resources/src/components/Main.svelte @@ -0,0 +1,89 @@ + + + +
+ {#if $deviceInfo.navigator.visible} + + + {/if} + +
+ +
+
diff --git a/plugins/process-resources/src/components/Navigator.svelte b/plugins/process-resources/src/components/Navigator.svelte new file mode 100644 index 0000000000..1ce0040355 --- /dev/null +++ b/plugins/process-resources/src/components/Navigator.svelte @@ -0,0 +1,73 @@ + + + +
+
+ + {#each specials as special} + + + + {/each} + + (menuSelection = res.detail)} /> + + {#if processes.length > 0} +
+ + {#each processes as process} + + + + {/each} + + {/if} + +
+ + +
+
diff --git a/plugins/process-resources/src/components/ProcessesExtension.svelte b/plugins/process-resources/src/components/ProcessesExtension.svelte index e3fef6ac07..10e1c6dc14 100644 --- a/plugins/process-resources/src/components/ProcessesExtension.svelte +++ b/plugins/process-resources/src/components/ProcessesExtension.svelte @@ -29,7 +29,6 @@ } from '@hcengineering/ui' import view, { Viewlet, ViewletPreference, ViewOptions } from '@hcengineering/view' import { List, ListSelectionProvider, SelectDirection, ViewletsSettingButton } from '@hcengineering/view-resources' - import { createEventDispatcher } from 'svelte' import process from '../plugin' import RunProcessPopup from './RunProcessPopup.svelte' diff --git a/plugins/process-resources/src/components/RunProcessCardPopup.svelte b/plugins/process-resources/src/components/RunProcessCardPopup.svelte new file mode 100644 index 0000000000..703859779a --- /dev/null +++ b/plugins/process-resources/src/components/RunProcessCardPopup.svelte @@ -0,0 +1,79 @@ + + + + +
+ i._id === process)} + on:selected={(e) => { + process = e.detail._id + card = undefined + }} + /> +
+ {#if selectedProcess !== undefined} + + {/if} +
diff --git a/plugins/process-resources/src/components/RunProcessPopup.svelte b/plugins/process-resources/src/components/RunProcessPopup.svelte index 82447dc149..93f6dd2374 100644 --- a/plugins/process-resources/src/components/RunProcessPopup.svelte +++ b/plugins/process-resources/src/components/RunProcessPopup.svelte @@ -14,12 +14,12 @@ -->