@nocobase/plugin-workflow-manual 0.17.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +9 -0
  3. package/README.zh-CN.md +9 -0
  4. package/client.d.ts +2 -0
  5. package/client.js +1 -0
  6. package/dist/client/WorkflowTodo.d.ts +5 -0
  7. package/dist/client/WorkflowTodoBlockInitializer.d.ts +2 -0
  8. package/dist/client/index.d.ts +6 -0
  9. package/dist/client/index.js +13 -0
  10. package/dist/client/instruction/AssigneesSelect.d.ts +6 -0
  11. package/dist/client/instruction/DetailsBlockProvider.d.ts +2 -0
  12. package/dist/client/instruction/FormBlockInitializer.d.ts +2 -0
  13. package/dist/client/instruction/FormBlockProvider.d.ts +2 -0
  14. package/dist/client/instruction/ModeConfig.d.ts +5 -0
  15. package/dist/client/instruction/SchemaConfig.d.ts +49 -0
  16. package/dist/client/instruction/forms/create.d.ts +3 -0
  17. package/dist/client/instruction/forms/custom.d.ts +5 -0
  18. package/dist/client/instruction/forms/update.d.ts +3 -0
  19. package/dist/client/instruction/index.d.ts +83 -0
  20. package/dist/client/instruction/utils.d.ts +1 -0
  21. package/dist/externalVersion.js +18 -0
  22. package/dist/index.d.ts +2 -0
  23. package/dist/index.js +39 -0
  24. package/dist/locale/en-US.json +30 -0
  25. package/dist/locale/index.d.ts +3 -0
  26. package/dist/locale/index.js +39 -0
  27. package/dist/locale/zh-CN.json +30 -0
  28. package/dist/server/ManualInstruction.d.ts +28 -0
  29. package/dist/server/ManualInstruction.js +150 -0
  30. package/dist/server/Plugin.d.ts +6 -0
  31. package/dist/server/Plugin.js +73 -0
  32. package/dist/server/actions.d.ts +2 -0
  33. package/dist/server/actions.js +94 -0
  34. package/dist/server/collecions/jobs.d.ts +19 -0
  35. package/dist/server/collecions/jobs.js +39 -0
  36. package/dist/server/collecions/users.d.ts +15 -0
  37. package/dist/server/collecions/users.js +37 -0
  38. package/dist/server/collecions/users_jobs.d.ts +3 -0
  39. package/dist/server/collecions/users_jobs.js +70 -0
  40. package/dist/server/forms/create.d.ts +5 -0
  41. package/dist/server/forms/create.js +41 -0
  42. package/dist/server/forms/index.d.ts +6 -0
  43. package/dist/server/forms/index.js +38 -0
  44. package/dist/server/forms/update.d.ts +6 -0
  45. package/dist/server/forms/update.js +41 -0
  46. package/dist/server/index.d.ts +1 -0
  47. package/dist/server/index.js +33 -0
  48. package/package.json +33 -0
  49. package/server.d.ts +2 -0
  50. package/server.js +1 -0
  51. package/src/client/WorkflowTodo.tsx +618 -0
  52. package/src/client/WorkflowTodoBlockInitializer.tsx +30 -0
  53. package/src/client/index.ts +44 -0
  54. package/src/client/instruction/AssigneesSelect.tsx +39 -0
  55. package/src/client/instruction/DetailsBlockProvider.tsx +85 -0
  56. package/src/client/instruction/FormBlockInitializer.tsx +72 -0
  57. package/src/client/instruction/FormBlockProvider.tsx +84 -0
  58. package/src/client/instruction/ModeConfig.tsx +85 -0
  59. package/src/client/instruction/SchemaConfig.tsx +538 -0
  60. package/src/client/instruction/forms/create.tsx +112 -0
  61. package/src/client/instruction/forms/custom.tsx +403 -0
  62. package/src/client/instruction/forms/update.tsx +150 -0
  63. package/src/client/instruction/index.tsx +157 -0
  64. package/src/client/instruction/utils.ts +19 -0
  65. package/src/index.ts +2 -0
  66. package/src/locale/en-US.json +30 -0
  67. package/src/locale/index.ts +12 -0
  68. package/src/locale/zh-CN.json +30 -0
  69. package/src/server/ManualInstruction.ts +151 -0
  70. package/src/server/Plugin.ts +48 -0
  71. package/src/server/__tests__/collections/categories.ts +15 -0
  72. package/src/server/__tests__/collections/comments.ts +24 -0
  73. package/src/server/__tests__/collections/posts.ts +40 -0
  74. package/src/server/__tests__/collections/replies.ts +9 -0
  75. package/src/server/__tests__/collections/tags.ts +15 -0
  76. package/src/server/__tests__/instruction.test.ts +1154 -0
  77. package/src/server/actions.ts +100 -0
  78. package/src/server/collecions/jobs.ts +17 -0
  79. package/src/server/collecions/users.ts +15 -0
  80. package/src/server/collecions/users_jobs.ts +50 -0
  81. package/src/server/forms/create.ts +23 -0
  82. package/src/server/forms/index.ts +13 -0
  83. package/src/server/forms/update.ts +23 -0
  84. package/src/server/index.ts +1 -0
@@ -0,0 +1,403 @@
1
+ import React, { useContext, useMemo, useState } from 'react';
2
+
3
+ import lodash from 'lodash';
4
+ import { ArrayTable } from '@formily/antd-v5';
5
+ import { Field, createForm } from '@formily/core';
6
+ import { useField, useFieldSchema, useForm } from '@formily/react';
7
+
8
+ import {
9
+ ActionContextProvider,
10
+ CollectionContext,
11
+ CollectionProvider,
12
+ FormBlockContext,
13
+ RecordProvider,
14
+ SchemaComponent,
15
+ SchemaInitializer,
16
+ SchemaInitializerItem,
17
+ SchemaInitializerItemType,
18
+ SchemaInitializerItems,
19
+ gridRowColWrap,
20
+ useCollectionManager,
21
+ useRecord,
22
+ useSchemaInitializer,
23
+ useSchemaInitializerItem,
24
+ } from '@nocobase/client';
25
+ import { merge, uid } from '@nocobase/utils/client';
26
+ import { JOB_STATUS } from '@nocobase/plugin-workflow/client';
27
+
28
+ import { ManualFormType } from '../SchemaConfig';
29
+ import { findSchema } from '../utils';
30
+ import { NAMESPACE, useLang } from '../../../locale';
31
+
32
+ function CustomFormBlockProvider(props) {
33
+ const [fields, setCollectionFields] = useState(props.collection?.fields ?? []);
34
+ const userJob = useRecord();
35
+ const field = useField();
36
+ const fieldSchema = useFieldSchema();
37
+ const [formKey] = Object.keys(fieldSchema.toJSON().properties ?? {});
38
+ const values = userJob?.result?.[formKey];
39
+
40
+ const form = useMemo(
41
+ () =>
42
+ createForm({
43
+ initialValues: values,
44
+ }),
45
+ [values],
46
+ );
47
+
48
+ return !userJob.status || values ? (
49
+ <CollectionProvider
50
+ collection={{
51
+ ...props.collection,
52
+ fields,
53
+ }}
54
+ >
55
+ <RecordProvider record={values} parent={false}>
56
+ <FormBlockContext.Provider
57
+ value={{
58
+ form,
59
+ field,
60
+ setCollectionFields,
61
+ }}
62
+ >
63
+ {props.children}
64
+ </FormBlockContext.Provider>
65
+ </RecordProvider>
66
+ </CollectionProvider>
67
+ ) : null;
68
+ }
69
+
70
+ function CustomFormBlockInitializer() {
71
+ const { insert } = useSchemaInitializer();
72
+ const itemConfig = useSchemaInitializerItem();
73
+ return (
74
+ <SchemaInitializerItem
75
+ {...itemConfig}
76
+ onClick={() => {
77
+ insert({
78
+ type: 'void',
79
+ 'x-decorator': 'CustomFormBlockProvider',
80
+ 'x-decorator-props': {
81
+ collection: {
82
+ name: uid(),
83
+ fields: [],
84
+ },
85
+ },
86
+ 'x-component': 'CardItem',
87
+ 'x-component-props': {
88
+ title: '{{t("Form")}}',
89
+ },
90
+ 'x-designer': 'SimpleDesigner',
91
+ 'x-designer-props': {
92
+ type: 'customForm',
93
+ },
94
+ properties: {
95
+ [uid()]: {
96
+ type: 'void',
97
+ 'x-component': 'FormV2',
98
+ 'x-component-props': {
99
+ // disabled / read-pretty / initialValues
100
+ useProps: '{{ useFormBlockProps }}',
101
+ },
102
+ properties: {
103
+ grid: {
104
+ type: 'void',
105
+ 'x-component': 'Grid',
106
+ 'x-initializer': 'AddCustomFormField',
107
+ },
108
+ actions: {
109
+ type: 'void',
110
+ 'x-decorator': 'ActionBarProvider',
111
+ 'x-component': 'ActionBar',
112
+ 'x-component-props': {
113
+ layout: 'one-column',
114
+ style: {
115
+ marginTop: '1.5em',
116
+ flexWrap: 'wrap',
117
+ },
118
+ },
119
+ 'x-initializer': 'AddActionButton',
120
+ properties: {
121
+ resolve: {
122
+ type: 'void',
123
+ title: `{{t("Continue the process", { ns: "${NAMESPACE}" })}}`,
124
+ 'x-decorator': 'ManualActionStatusProvider',
125
+ 'x-decorator-props': {
126
+ value: JOB_STATUS.RESOLVED,
127
+ },
128
+ 'x-component': 'Action',
129
+ 'x-component-props': {
130
+ type: 'primary',
131
+ useAction: '{{ useSubmit }}',
132
+ },
133
+ 'x-designer': 'ManualActionDesigner',
134
+ },
135
+ },
136
+ },
137
+ },
138
+ },
139
+ },
140
+ });
141
+ }}
142
+ />
143
+ );
144
+ }
145
+
146
+ const GroupLabels = {
147
+ basic: '{{t("Basic")}}',
148
+ choices: '{{t("Choices")}}',
149
+ media: '{{t("Media")}}',
150
+ datetime: '{{t("Date & Time")}}',
151
+ relation: '{{t("Relation")}}',
152
+ advanced: '{{t("Advanced type")}}',
153
+ systemInfo: '{{t("System info")}}',
154
+ others: '{{t("Others")}}',
155
+ };
156
+
157
+ function getOptions(interfaces) {
158
+ const fields = {};
159
+
160
+ Object.keys(interfaces).forEach((type) => {
161
+ const schema = interfaces[type];
162
+ const { group = 'others' } = schema;
163
+ fields[group] = fields[group] || {};
164
+ lodash.set(fields, [group, type], schema);
165
+ });
166
+
167
+ return Object.keys(GroupLabels)
168
+ .filter((groupName) => ['basic', 'choices', 'datetime', 'media'].includes(groupName))
169
+ .map((groupName) => ({
170
+ title: GroupLabels[groupName],
171
+ children: Object.keys(fields[groupName] || {})
172
+ .map((type) => {
173
+ const field = fields[groupName][type];
174
+ return {
175
+ value: type,
176
+ title: field.title,
177
+ name: type,
178
+ ...fields[groupName][type],
179
+ };
180
+ })
181
+ .sort((a, b) => a.order - b.order),
182
+ }));
183
+ }
184
+
185
+ function useCommonInterfaceInitializers(): SchemaInitializerItemType[] {
186
+ const { interfaces } = useCollectionManager();
187
+ const options = getOptions(interfaces);
188
+
189
+ return options.map((group) => ({
190
+ name: group.title,
191
+ type: 'itemGroup',
192
+ title: group.title,
193
+ children: group.children.map((item) => ({
194
+ name: item.name,
195
+ type: 'item',
196
+ title: item.title,
197
+ Component: CustomFormFieldInitializer,
198
+ fieldInterface: item.name,
199
+ })),
200
+ }));
201
+ }
202
+
203
+ const AddCustomFormFieldButtonContext = React.createContext<any>({});
204
+
205
+ const CustomItemsComponent = (props) => {
206
+ const [interfaceOptions, setInterface] = useState<any>(null);
207
+ const [insert, setCallback] = useState<any>();
208
+ const items = useCommonInterfaceInitializers();
209
+ const collection = useContext(CollectionContext);
210
+ const { setCollectionFields } = useContext(FormBlockContext);
211
+
212
+ return (
213
+ <AddCustomFormFieldButtonContext.Provider
214
+ value={{
215
+ onAddField(item) {
216
+ const {
217
+ properties: { unique, type, ...properties },
218
+ ...options
219
+ } = lodash.cloneDeep(item);
220
+ delete properties.name['x-disabled'];
221
+ setInterface({
222
+ ...options,
223
+ properties,
224
+ });
225
+ },
226
+ setCallback,
227
+ }}
228
+ >
229
+ <SchemaInitializerItems {...props} items={items} />
230
+ <ActionContextProvider value={{ visible: Boolean(interfaceOptions) }}>
231
+ {interfaceOptions ? (
232
+ <SchemaComponent
233
+ schema={{
234
+ type: 'void',
235
+ name: 'drawer',
236
+ title: '{{t("Configure field")}}',
237
+ 'x-decorator': 'Form',
238
+ 'x-component': 'Action.Drawer',
239
+ properties: {
240
+ ...interfaceOptions.properties,
241
+ footer: {
242
+ type: 'void',
243
+ 'x-component': 'Action.Drawer.Footer',
244
+ properties: {
245
+ cancel: {
246
+ type: 'void',
247
+ title: '{{t("Cancel")}}',
248
+ 'x-component': 'Action',
249
+ 'x-component-props': {
250
+ useAction() {
251
+ const form = useForm();
252
+ return {
253
+ async run() {
254
+ setCallback(null);
255
+ setInterface(null);
256
+ form.reset();
257
+ },
258
+ };
259
+ },
260
+ },
261
+ },
262
+ submit: {
263
+ type: 'void',
264
+ title: '{{t("Submit")}}',
265
+ 'x-component': 'Action',
266
+ 'x-component-props': {
267
+ type: 'primary',
268
+ useAction() {
269
+ const { values, query } = useForm();
270
+ const messages = [useLang('Field name existed in form')];
271
+ return {
272
+ async run() {
273
+ const { default: options } = interfaceOptions;
274
+ const defaultName = uid();
275
+ options.name = values.name ?? defaultName;
276
+ options.uiSchema.title = values.uiSchema?.title ?? defaultName;
277
+ options.interface = interfaceOptions.name;
278
+ const existed = collection.fields?.find((item) => item.name === options.name);
279
+ if (existed) {
280
+ const field = query('name').take() as Field;
281
+ field.setFeedback({
282
+ type: 'error',
283
+ // code: 'FormulaError',
284
+ messages,
285
+ });
286
+ return;
287
+ }
288
+ const newField = merge(options, values) as any;
289
+ setCollectionFields([...collection.fields, newField]);
290
+
291
+ insert({
292
+ name: options.name,
293
+ type: options.uiSchema.type,
294
+ 'x-decorator': 'FormItem',
295
+ 'x-component': 'CollectionField',
296
+ 'x-component-props': {
297
+ field: newField,
298
+ },
299
+ 'x-collection-field': `${collection.name}.${options.name}`,
300
+ 'x-designer': 'FormItem.Designer',
301
+ });
302
+ setCallback(null);
303
+ setInterface(null);
304
+ },
305
+ };
306
+ },
307
+ },
308
+ },
309
+ },
310
+ },
311
+ },
312
+ }}
313
+ components={{
314
+ ArrayTable,
315
+ }}
316
+ />
317
+ ) : null}
318
+ </ActionContextProvider>
319
+ </AddCustomFormFieldButtonContext.Provider>
320
+ );
321
+ };
322
+
323
+ export const addCustomFormField = new SchemaInitializer({
324
+ name: 'AddCustomFormField',
325
+ wrap: gridRowColWrap,
326
+ insertPosition: 'beforeEnd',
327
+ title: "{{t('Configure fields')}}",
328
+ ItemsComponent: CustomItemsComponent,
329
+ });
330
+
331
+ function CustomFormFieldInitializer() {
332
+ const itemConfig = useSchemaInitializerItem();
333
+ const { insert, setVisible } = useSchemaInitializer();
334
+ const { onAddField, setCallback } = useContext(AddCustomFormFieldButtonContext);
335
+ const { getInterface } = useCollectionManager();
336
+
337
+ const interfaceOptions = getInterface(itemConfig.fieldInterface);
338
+
339
+ return (
340
+ <SchemaInitializerItem
341
+ key={itemConfig.fieldInterface}
342
+ onClick={() => {
343
+ setCallback(() => insert);
344
+ onAddField(interfaceOptions);
345
+ setVisible(false);
346
+ }}
347
+ {...itemConfig}
348
+ />
349
+ );
350
+ }
351
+
352
+ export default {
353
+ title: `{{t("Custom form", { ns: "${NAMESPACE}" })}}`,
354
+ config: {
355
+ useInitializer() {
356
+ return {
357
+ name: 'customForm',
358
+ type: 'item',
359
+ title: `{{t("Custom form", { ns: "${NAMESPACE}" })}}`,
360
+ Component: CustomFormBlockInitializer,
361
+ };
362
+ },
363
+ initializers: {},
364
+ components: {
365
+ CustomFormBlockProvider,
366
+ },
367
+ parseFormOptions(root) {
368
+ const forms = {};
369
+ const formBlocks: any[] = findSchema(root, (item) => item['x-decorator'] === 'CustomFormBlockProvider');
370
+ formBlocks.forEach((formBlock) => {
371
+ const [formKey] = Object.keys(formBlock.properties);
372
+ const formSchema = formBlock.properties[formKey];
373
+ const fields = findSchema(
374
+ formSchema.properties.grid,
375
+ (item) => item['x-component'] === 'CollectionField',
376
+ true,
377
+ );
378
+ formBlock['x-decorator-props'].collection.fields = fields.map(
379
+ (field) => field['x-component-props']?.field ?? field['x-interface-options'],
380
+ );
381
+ forms[formKey] = {
382
+ type: 'custom',
383
+ title: formBlock['x-component-props']?.title || formKey,
384
+ actions: findSchema(formSchema.properties.actions, (item) => item['x-component'] === 'Action').map(
385
+ (item) => ({
386
+ status: item['x-decorator-props'].value,
387
+ values: item['x-action-settings']?.assignedValues?.values,
388
+ key: item.name,
389
+ }),
390
+ ),
391
+ collection: formBlock['x-decorator-props'].collection,
392
+ };
393
+ });
394
+ return forms;
395
+ },
396
+ },
397
+ block: {
398
+ scope: {},
399
+ components: {
400
+ CustomFormBlockProvider,
401
+ },
402
+ },
403
+ } as ManualFormType;
@@ -0,0 +1,150 @@
1
+ import { useFieldSchema } from '@formily/react';
2
+ import React, { useMemo, useState } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import _ from 'lodash';
5
+
6
+ import {
7
+ GeneralSchemaDesigner,
8
+ SchemaSettingsActionModalItem,
9
+ SchemaSettingsBlockTitleItem,
10
+ SchemaSettingsDivider,
11
+ SchemaSettingsLinkageRules,
12
+ SchemaSettingsRemove,
13
+ useCollection,
14
+ useCollectionFilterOptions,
15
+ useDesignable,
16
+ useMenuSearch,
17
+ } from '@nocobase/client';
18
+ import { FilterDynamicComponent } from '@nocobase/plugin-workflow/client';
19
+
20
+ import { NAMESPACE } from '../../../locale';
21
+ import { FormBlockInitializer } from '../FormBlockInitializer';
22
+ import { ManualFormType } from '../SchemaConfig';
23
+ import { findSchema } from '../utils';
24
+
25
+ function UpdateFormDesigner() {
26
+ const { name, title } = useCollection();
27
+ const fieldSchema = useFieldSchema();
28
+ const { t } = useTranslation();
29
+ const { dn } = useDesignable();
30
+
31
+ return (
32
+ <GeneralSchemaDesigner title={title || name}>
33
+ <SchemaSettingsBlockTitleItem />
34
+ <SchemaSettingsActionModalItem
35
+ title={t('Filter settings', { ns: NAMESPACE })}
36
+ schema={{
37
+ name: 'filter',
38
+ type: 'object',
39
+ title: `{{t("Filter")}}`,
40
+ // 'x-decorator': 'FormItem',
41
+ 'x-component': 'Filter',
42
+ 'x-component-props': {
43
+ useProps() {
44
+ const options = useCollectionFilterOptions(fieldSchema?.['x-decorator-props']?.collection);
45
+ return {
46
+ options,
47
+ };
48
+ },
49
+ dynamicComponent: 'FilterDynamicComponent',
50
+ },
51
+ }}
52
+ initialValues={fieldSchema?.['x-decorator-props']}
53
+ onSubmit={({ filter }) => {
54
+ fieldSchema['x-decorator-props'].filter = filter;
55
+ dn.emit('patch', {
56
+ schema: {
57
+ // ['x-uid']: fieldSchema['x-uid'],
58
+ 'x-decorator-props': fieldSchema['x-decorator-props'],
59
+ },
60
+ });
61
+ dn.refresh();
62
+ }}
63
+ />
64
+ <SchemaSettingsLinkageRules collectionName={name} />
65
+ <SchemaSettingsDivider />
66
+ <SchemaSettingsRemove
67
+ removeParentsIfNoChildren
68
+ breakRemoveOn={{
69
+ 'x-component': 'Grid',
70
+ }}
71
+ />
72
+ </GeneralSchemaDesigner>
73
+ );
74
+ }
75
+
76
+ export default {
77
+ title: `{{t("Update record form", { ns: "${NAMESPACE}" })}}`,
78
+ config: {
79
+ useInitializer({ collections }) {
80
+ const childItems = useMemo(
81
+ () =>
82
+ collections.map((item) => ({
83
+ name: _.camelCase(`updateRecordForm-child-${item.name}`),
84
+ type: 'item',
85
+ title: item.title,
86
+ label: item.label,
87
+ schema: {
88
+ collection: item.name,
89
+ title: `{{t("Update record", { ns: "${NAMESPACE}" })}}`,
90
+ formType: 'update',
91
+ 'x-designer': 'UpdateFormDesigner',
92
+ },
93
+ Component: FormBlockInitializer,
94
+ })),
95
+ [collections],
96
+ );
97
+ const [isOpenSubMenu, setIsOpenSubMenu] = useState(false);
98
+ const searchedChildren = useMenuSearch(childItems, isOpenSubMenu, true);
99
+ return {
100
+ name: 'updateRecordForm',
101
+ key: 'updateRecordForm',
102
+ type: 'subMenu',
103
+ title: `{{t("Update record form", { ns: "${NAMESPACE}" })}}`,
104
+ componentProps: {
105
+ onOpenChange(keys) {
106
+ setIsOpenSubMenu(keys.length > 0);
107
+ },
108
+ },
109
+ children: searchedChildren,
110
+ } as any;
111
+ },
112
+ initializers: {
113
+ // AddCustomFormField
114
+ },
115
+ components: {
116
+ FilterDynamicComponent,
117
+ UpdateFormDesigner,
118
+ },
119
+ parseFormOptions(root) {
120
+ const forms = {};
121
+ const formBlocks: any[] = findSchema(
122
+ root,
123
+ (item) => item['x-decorator'] === 'FormBlockProvider' && item['x-decorator-props'].formType === 'update',
124
+ );
125
+ formBlocks.forEach((formBlock) => {
126
+ const [formKey] = Object.keys(formBlock.properties);
127
+ const formSchema = formBlock.properties[formKey];
128
+ forms[formKey] = {
129
+ ...formBlock['x-decorator-props'],
130
+ type: 'update',
131
+ title: formBlock['x-component-props']?.title || formKey,
132
+ actions: findSchema(formSchema.properties.actions, (item) => item['x-component'] === 'Action').map(
133
+ (item) => ({
134
+ status: item['x-decorator-props'].value,
135
+ values: item['x-action-settings']?.assignedValues?.values,
136
+ key: item.name,
137
+ }),
138
+ ),
139
+ };
140
+ });
141
+ return forms;
142
+ },
143
+ },
144
+ block: {
145
+ scope: {
146
+ // useFormBlockProps
147
+ },
148
+ components: {},
149
+ },
150
+ } as ManualFormType;
@@ -0,0 +1,157 @@
1
+ import { SchemaInitializerItemType, useCollectionManager, useCompile } from '@nocobase/client';
2
+
3
+ import {
4
+ defaultFieldNames,
5
+ getCollectionFieldOptions,
6
+ CollectionBlockInitializer,
7
+ Instruction,
8
+ } from '@nocobase/plugin-workflow/client';
9
+
10
+ import { SchemaConfig, SchemaConfigButton } from './SchemaConfig';
11
+ import { ModeConfig } from './ModeConfig';
12
+ import { AssigneesSelect } from './AssigneesSelect';
13
+ import { NAMESPACE } from '../../locale';
14
+
15
+ const MULTIPLE_ASSIGNED_MODE = {
16
+ SINGLE: Symbol('single'),
17
+ ALL: Symbol('all'),
18
+ ANY: Symbol('any'),
19
+ ALL_PERCENTAGE: Symbol('all percentage'),
20
+ ANY_PERCENTAGE: Symbol('any percentage'),
21
+ };
22
+
23
+ export default class extends Instruction {
24
+ title = `{{t("Manual", { ns: "${NAMESPACE}" })}}`;
25
+ type = 'manual';
26
+ group = 'manual';
27
+ description = `{{t("Could be used for manually submitting data, and determine whether to continue or exit. Workflow will generate a todo item for assigned user when it reaches a manual node, and continue processing after user submits the form.", { ns: "${NAMESPACE}" })}}`;
28
+ fieldset = {
29
+ assignees: {
30
+ type: 'array',
31
+ title: `{{t("Assignees", { ns: "${NAMESPACE}" })}}`,
32
+ 'x-decorator': 'FormItem',
33
+ 'x-component': 'AssigneesSelect',
34
+ 'x-component-props': {
35
+ // multiple: true,
36
+ },
37
+ required: true,
38
+ default: [],
39
+ },
40
+ mode: {
41
+ type: 'number',
42
+ title: `{{t("Mode", { ns: "${NAMESPACE}" })}}`,
43
+ 'x-decorator': 'FormItem',
44
+ 'x-component': 'ModeConfig',
45
+ default: 1,
46
+ 'x-reactions': {
47
+ dependencies: ['assignees'],
48
+ fulfill: {
49
+ state: {
50
+ visible: '{{$deps[0].length > 1}}',
51
+ },
52
+ },
53
+ },
54
+ },
55
+ schema: {
56
+ type: 'void',
57
+ title: `{{t("User interface", { ns: "${NAMESPACE}" })}}`,
58
+ 'x-decorator': 'FormItem',
59
+ 'x-component': 'SchemaConfigButton',
60
+ properties: {
61
+ schema: {
62
+ type: 'object',
63
+ 'x-component': 'SchemaConfig',
64
+ default: null,
65
+ },
66
+ },
67
+ },
68
+ forms: {
69
+ type: 'object',
70
+ default: {},
71
+ },
72
+ };
73
+ components = {
74
+ SchemaConfigButton,
75
+ SchemaConfig,
76
+ ModeConfig,
77
+ AssigneesSelect,
78
+ };
79
+ useVariables({ key, title, config }, { types, fieldNames = defaultFieldNames }) {
80
+ // eslint-disable-next-line react-hooks/rules-of-hooks
81
+ const compile = useCompile();
82
+ // eslint-disable-next-line react-hooks/rules-of-hooks
83
+ const { getCollectionFields } = useCollectionManager();
84
+ const formKeys = Object.keys(config.forms ?? {});
85
+ if (!formKeys.length) {
86
+ return null;
87
+ }
88
+
89
+ const options = formKeys
90
+ .map((formKey) => {
91
+ const form = config.forms[formKey];
92
+
93
+ const fieldsOptions = getCollectionFieldOptions({
94
+ fields: form.collection?.fields,
95
+ collection: form.collection,
96
+ types,
97
+ compile,
98
+ getCollectionFields,
99
+ });
100
+ const label = compile(form.title) || formKey;
101
+ return fieldsOptions.length
102
+ ? {
103
+ key: formKey,
104
+ value: formKey,
105
+ label,
106
+ title: label,
107
+ children: fieldsOptions,
108
+ }
109
+ : null;
110
+ })
111
+ .filter(Boolean);
112
+
113
+ return options.length
114
+ ? {
115
+ [fieldNames.value]: key,
116
+ [fieldNames.label]: title,
117
+ [fieldNames.children]: options,
118
+ }
119
+ : null;
120
+ }
121
+ useInitializers(node): SchemaInitializerItemType | null {
122
+ // eslint-disable-next-line react-hooks/rules-of-hooks
123
+ const { getCollection } = useCollectionManager();
124
+ const formKeys = Object.keys(node.config.forms ?? {});
125
+ if (!formKeys.length || node.config.mode) {
126
+ return null;
127
+ }
128
+
129
+ const forms = formKeys
130
+ .map((formKey) => {
131
+ const form = node.config.forms[formKey];
132
+ const { fields = [] } = getCollection(form.collection);
133
+
134
+ return fields.length
135
+ ? ({
136
+ name: form.title ?? formKey,
137
+ type: 'item',
138
+ title: form.title ?? formKey,
139
+ Component: CollectionBlockInitializer,
140
+ collection: form.collection,
141
+ dataSource: `{{$jobsMapByNodeKey.${node.key}.${formKey}}}`,
142
+ } as SchemaInitializerItemType)
143
+ : null;
144
+ })
145
+ .filter(Boolean);
146
+
147
+ return forms.length
148
+ ? {
149
+ name: `#${node.id}`,
150
+ key: 'forms',
151
+ type: 'subMenu',
152
+ title: node.title,
153
+ children: forms,
154
+ }
155
+ : null;
156
+ }
157
+ }