goblin-desktop 4.2.0 → 4.2.2

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 (144) hide show
  1. package/.editorconfig +9 -9
  2. package/.zou-flow +2 -2
  3. package/LICENSE +21 -21
  4. package/README.md +108 -108
  5. package/builders/builders.js +3 -3
  6. package/builders/wizard.js +462 -462
  7. package/contexts.js +13 -13
  8. package/desktop-manager.js +10 -10
  9. package/desktop-window.js +13 -13
  10. package/desktop.js +13 -13
  11. package/eslint.config.js +65 -65
  12. package/lib/service.js +137 -137
  13. package/package.json +48 -48
  14. package/password-wizard.js +24 -24
  15. package/quest-run-wizard.js +24 -24
  16. package/taskbar.js +13 -13
  17. package/widgets/audio/sfx.js +177 -177
  18. package/widgets/board/view.js +37 -37
  19. package/widgets/board/widget.js +65 -65
  20. package/widgets/contexts/logic-handlers.js +36 -36
  21. package/widgets/contexts/service.js +40 -40
  22. package/widgets/contexts/view.js +21 -21
  23. package/widgets/contexts/widget.js +147 -147
  24. package/widgets/datagrid/datagrid-entity.js +82 -82
  25. package/widgets/datagrid/datagrid-headers.js +274 -274
  26. package/widgets/datagrid/styles.js +13 -13
  27. package/widgets/datagrid/widget.js +192 -192
  28. package/widgets/datagrid-cell/styles.js +52 -52
  29. package/widgets/datagrid-cell/widget.js +41 -41
  30. package/widgets/datagrid-item/styles.js +15 -15
  31. package/widgets/datagrid-item/widget.js +74 -74
  32. package/widgets/default/view.js +160 -160
  33. package/widgets/desktop/compensator.js +9 -9
  34. package/widgets/desktop/logic-handlers.js +243 -243
  35. package/widgets/desktop/reducer.js +35 -35
  36. package/widgets/desktop/service.js +858 -858
  37. package/widgets/desktop/styles.js +28 -28
  38. package/widgets/desktop/widget.js +191 -191
  39. package/widgets/desktop-clock/styles.js +69 -69
  40. package/widgets/desktop-clock/widget.js +387 -387
  41. package/widgets/desktop-clock-clock/styles.js +56 -56
  42. package/widgets/desktop-clock-clock/widget.js +96 -96
  43. package/widgets/desktop-clock-menu/styles.js +129 -129
  44. package/widgets/desktop-clock-menu/widget.js +63 -63
  45. package/widgets/desktop-connection-status/reducer.js +15 -15
  46. package/widgets/desktop-connection-status/styles.js +44 -44
  47. package/widgets/desktop-connection-status/widget.js +134 -134
  48. package/widgets/desktop-content/widget.js +68 -68
  49. package/widgets/desktop-footer/reducer.js +31 -31
  50. package/widgets/desktop-footer/styles.js +36 -36
  51. package/widgets/desktop-footer/widget.js +52 -52
  52. package/widgets/desktop-monitors/styles.js +155 -155
  53. package/widgets/desktop-monitors/widget.js +272 -272
  54. package/widgets/desktop-notebook/styles.js +155 -155
  55. package/widgets/desktop-notebook/widget.js +252 -252
  56. package/widgets/desktop-notifications/styles.js +147 -147
  57. package/widgets/desktop-notifications/widget.js +231 -231
  58. package/widgets/desktop-scale/reducer.js +15 -15
  59. package/widgets/desktop-scale/styles.js +49 -49
  60. package/widgets/desktop-scale/widget.js +173 -172
  61. package/widgets/desktop-state-monitor/styles.js +72 -72
  62. package/widgets/desktop-state-monitor/widget.js +123 -123
  63. package/widgets/desktop-taskbar/widget.js +57 -57
  64. package/widgets/desktop-themes-menu/widget.js +121 -121
  65. package/widgets/desktop-topbar/widget.js +201 -201
  66. package/widgets/desktop-window/service.js +56 -56
  67. package/widgets/desktop-window/styles.js +22 -22
  68. package/widgets/desktop-window/widget.js +70 -70
  69. package/widgets/detail/compensator.js +17 -17
  70. package/widgets/detail/view.js +40 -40
  71. package/widgets/detail/widget.js +119 -119
  72. package/widgets/editor/widget.js +82 -82
  73. package/widgets/entity-alerts/styles.js +77 -77
  74. package/widgets/entity-alerts/widget.js +328 -328
  75. package/widgets/entity-list/styles.js +66 -66
  76. package/widgets/entity-list/view.js +36 -36
  77. package/widgets/entity-list/widget.js +209 -209
  78. package/widgets/entity-list-item/widget.js +68 -68
  79. package/widgets/entity-row/styles.js +105 -105
  80. package/widgets/entity-row/widget.js +524 -524
  81. package/widgets/entity-row-button/styles.js +46 -46
  82. package/widgets/entity-row-button/widget.js +57 -57
  83. package/widgets/entity-view/reducer.js +20 -20
  84. package/widgets/entity-view/styles.js +90 -90
  85. package/widgets/entity-view/widget.js +601 -601
  86. package/widgets/facet-checkbox/styles.js +17 -17
  87. package/widgets/facet-checkbox/widget.js +43 -43
  88. package/widgets/facet-filter/widget.js +94 -94
  89. package/widgets/facet-filter-add/styles.js +30 -30
  90. package/widgets/facet-filter-add/widget.js +105 -105
  91. package/widgets/facet-filter-button/styles.js +74 -74
  92. package/widgets/facet-filter-button/widget.js +214 -214
  93. package/widgets/facet-filter-list-dialog/styles.js +59 -59
  94. package/widgets/facet-filter-list-dialog/widget.js +273 -273
  95. package/widgets/facet-filter-list-dialog-footer/styles.js +22 -22
  96. package/widgets/facet-filter-list-dialog-footer/widget.js +105 -105
  97. package/widgets/facet-filter-range-dialog/styles.js +82 -82
  98. package/widgets/facet-filter-range-dialog/widget.js +399 -399
  99. package/widgets/facet-filter-range-dialog-footer/styles.js +22 -22
  100. package/widgets/facet-filter-range-dialog-footer/widget.js +182 -182
  101. package/widgets/gamepad/widget.js +75 -75
  102. package/widgets/helpers/facet-helpers.js +105 -105
  103. package/widgets/hinter/reducer.js +35 -35
  104. package/widgets/hinter/styles.js +79 -79
  105. package/widgets/hinter/view.js +32 -32
  106. package/widgets/hinter/widget.js +289 -291
  107. package/widgets/junction/styles.js +22 -22
  108. package/widgets/junction/widget.js +50 -50
  109. package/widgets/main-tab-menu/styles.js +17 -17
  110. package/widgets/main-tab-menu/widget.js +130 -130
  111. package/widgets/map/view.js +49 -49
  112. package/widgets/map/widget.js +64 -64
  113. package/widgets/monitor/reducer.js +15 -15
  114. package/widgets/monitor/widget.js +60 -60
  115. package/widgets/navigating-layer/widget.js +25 -25
  116. package/widgets/notifications-button/widget.js +44 -44
  117. package/widgets/password-wizard/service.js +53 -53
  118. package/widgets/password-wizard/ui.js +66 -66
  119. package/widgets/plugin/reducer.js +19 -19
  120. package/widgets/plugin/styles.js +294 -294
  121. package/widgets/plugin/widget.js +622 -622
  122. package/widgets/quest-run-wizard/service.js +49 -49
  123. package/widgets/quest-run-wizard/ui.js +25 -25
  124. package/widgets/search/styles.js +80 -80
  125. package/widgets/search/widget.js +283 -285
  126. package/widgets/simple/view.js +28 -28
  127. package/widgets/status-filters/widget.js +121 -121
  128. package/widgets/tab/styles.js +16 -16
  129. package/widgets/tab/widget.js +89 -89
  130. package/widgets/tab-content/widget.js +35 -35
  131. package/widgets/tabs/widget.js +48 -48
  132. package/widgets/taskbar/service.js +99 -99
  133. package/widgets/taskbar/view.js +27 -27
  134. package/widgets/taskbar/widget.js +167 -167
  135. package/widgets/widget-doc-caller/reducer.js +15 -15
  136. package/widgets/widget-doc-caller/styles.js +20 -20
  137. package/widgets/widget-doc-caller/widget.js +55 -55
  138. package/widgets/wizard/widget.js +298 -298
  139. package/widgets/wizard-buttons/widget.js +111 -111
  140. package/widgets/workitem/styles.js +286 -286
  141. package/widgets/workitem/view.js +62 -62
  142. package/widgets/workitem/widget.js +988 -988
  143. package/widgets/workitem-dialog/widget.js +86 -86
  144. package/widgets/workitem-fields/widget.js +64 -64
@@ -1,988 +1,988 @@
1
- import React from 'react';
2
- import MouseTrap from 'mousetrap';
3
- import Widget from 'goblin-laboratory/widgets/widget';
4
- import Form from 'goblin-laboratory/widgets/form';
5
- import WorkitemFields from '../workitem-fields/widget.js';
6
- import PropTypes from 'prop-types';
7
-
8
- import Container from 'goblin-gadgets/widgets/container/widget';
9
- import Label from 'goblin-gadgets/widgets/label/widget';
10
- import Button from 'goblin-gadgets/widgets/button/widget';
11
- import CommandButton from 'goblin-gadgets/widgets/command-button/widget';
12
- import ScrollableContainer from 'goblin-gadgets/widgets/scrollable-container/widget';
13
- import DialogModal from 'goblin-gadgets/widgets/dialog-modal/widget';
14
- import CheckList from 'goblin-gadgets/widgets/check-list/widget';
15
- import EntityAlerts from 'goblin-desktop/widgets/entity-alerts/widget.js';
16
- import T from 't';
17
-
18
- /******************************************************************************/
19
-
20
- const Title = Widget.connect((state, props) => {
21
- const path = `backend.${props.entityId}.meta.summaries.info`;
22
- const text = state.get(path);
23
- return {text};
24
- })(Label);
25
-
26
- class Workitem extends Form {
27
- constructor() {
28
- super(...arguments);
29
-
30
- this.state = {
31
- showDeleteDialog: false,
32
- deleteAction: 'archive',
33
- showStatureSmall: false,
34
- };
35
-
36
- this.doAction = this.doAction.bind(this);
37
- this.doMainAction = this.doMainAction.bind(this);
38
- this.onSubmit = this.onSubmit.bind(this);
39
- this.onRollback = this.onRollback.bind(this);
40
- this.onClose = this.onClose.bind(this);
41
- this.onEdit = this.onEdit.bind(this);
42
- this.onTrash = this.onTrash.bind(this);
43
- this.onDelete = this.onDelete.bind(this);
44
- this.onDoDelete = this.onDoDelete.bind(this);
45
- this.onArchive = this.onArchive.bind(this);
46
- this.onPublish = this.onPublish.bind(this);
47
- this.onCopyInfoToClipboard = this.onCopyInfoToClipboard.bind(this);
48
- this.editSettings = this.editSettings.bind(this);
49
- this.hide = this.hide.bind(this);
50
- }
51
-
52
- //#region get/set
53
- get showDeleteDialog() {
54
- return this.state.showDeleteDialog;
55
- }
56
- set showDeleteDialog(value) {
57
- this.setState({
58
- showDeleteDialog: value,
59
- });
60
- }
61
-
62
- get deleteAction() {
63
- return this.state.deleteAction;
64
- }
65
- set deleteAction(value) {
66
- this.setState({
67
- deleteAction: value,
68
- });
69
- }
70
-
71
- get showStatureSmall() {
72
- return this.state.showStatureSmall;
73
- }
74
- set showStatureSmall(value) {
75
- this.setState({
76
- showStatureSmall: value,
77
- });
78
- }
79
- //#endregion
80
-
81
- componentDidMount() {
82
- // FIXME: stacked workitems not handled properly
83
- // MouseTrap.bind('esc', this.onClose);
84
- MouseTrap.bind('ctrl+enter', this.doMainAction);
85
- }
86
-
87
- componentWillUnmount() {
88
- super.componentWillUnmount();
89
- // MouseTrap.unbind('esc');
90
- MouseTrap.unbind('ctrl+enter', this.doMainAction);
91
- }
92
-
93
- getChildContext() {
94
- return {
95
- readonly: this.props.readonly,
96
- id: this.props.id,
97
- entityId: this.props.entityId,
98
- dragServiceId: this.props.dragServiceId,
99
- };
100
- }
101
-
102
- static get childContextTypes() {
103
- return {
104
- readonly: PropTypes.any,
105
- id: PropTypes.string,
106
- entityId: PropTypes.string,
107
- dragServiceId: PropTypes.string,
108
- };
109
- }
110
-
111
- get service() {
112
- return this.props.id.split('@')[0];
113
- }
114
-
115
- get desktopId() {
116
- return this.context.desktopId;
117
- }
118
-
119
- get contextId() {
120
- return this.context.contextId;
121
- }
122
-
123
- getCommand(button) {
124
- const getQuest = (id) => {
125
- switch (id) {
126
- case 'rollback':
127
- case 'validate':
128
- return `close`;
129
- case 'delete':
130
- return `trash-entity`;
131
- case 'submit':
132
- case 'publish':
133
- case 'trash':
134
- case 'archive':
135
- return `${id}-entity`;
136
- default:
137
- return id;
138
- }
139
- };
140
-
141
- const service = button.questService ?? this.props.id.split('@', 1)[0];
142
- let quest = button.quest ?? getQuest(button.id);
143
- return `${service}.${quest}`;
144
- }
145
-
146
- onSubmit() {
147
- this.doAs(this.service, 'close', {
148
- kind: 'validate',
149
- desktopId: this.desktopId,
150
- });
151
- }
152
-
153
- onRollback() {
154
- this.doAs(this.service, 'close', {
155
- kind: 'rollback',
156
- desktopId: this.desktopId,
157
- });
158
- //TODO-VNEXT: REPAIR ME
159
- //this.hideHinter();
160
- }
161
-
162
- onClose() {
163
- const kind = this.props.kind || 'editor';
164
- switch (kind) {
165
- case 'editor':
166
- this.doAs(this.service, 'close', {
167
- kind: 'terminate',
168
- desktopId: this.desktopId,
169
- });
170
- break;
171
- default:
172
- this.hide();
173
- }
174
- }
175
-
176
- onEdit() {
177
- const entity = this.getBackendState(this.props.entityId);
178
- if (entity) {
179
- this.doAs(this.service, 'edit', {
180
- entity,
181
- desktopId: this.desktopId,
182
- });
183
- }
184
- }
185
-
186
- hide() {
187
- this.doAs(this.service, 'hide');
188
-
189
- //in case of use in a search list,
190
- //dispatch to search list a null rowId
191
- if (
192
- this.props.leftPanelWorkitemId &&
193
- this.props.leftPanelWorkitemId.match(/^.*-search@.*/)
194
- ) {
195
- this.dispatchTo(
196
- this.props.leftPanelWorkitemId,
197
- {type: 'select-row', rowId: null, entityId: null},
198
- 'entity-view'
199
- );
200
- }
201
- }
202
-
203
- doMainAction() {
204
- //TODO: actionate main action
205
- console.log('main action');
206
- }
207
-
208
- doAction(action) {
209
- const kind = this.props.kind || 'editor';
210
- switch (kind) {
211
- case 'editor':
212
- this.doAs(this.service, 'close', {
213
- kind: action,
214
- desktopId: this.desktopId,
215
- });
216
- break;
217
- default:
218
- this.doAs(this.service, `${action}-entity`);
219
- }
220
- }
221
-
222
- onPublish() {
223
- this.doAction('publish');
224
- }
225
-
226
- onDelete() {
227
- this.deleteAction = 'archive';
228
- this.showDeleteDialog = true;
229
- }
230
-
231
- onDoDelete() {
232
- this.showDeleteDialog = false;
233
- if (this.deleteAction === 'trash') {
234
- this.onTrash();
235
- } else {
236
- this.onArchive();
237
- }
238
- }
239
-
240
- onTrash() {
241
- this.doAction('trash');
242
- }
243
-
244
- onArchive() {
245
- this.doAction('archive');
246
- }
247
-
248
- onCopyInfoToClipboard() {
249
- Form.copyTextToClipboard(this.props.entityId);
250
-
251
- this.doAs(this.service, 'add-state-monitor', {
252
- key: this.props.entityId,
253
- });
254
- }
255
-
256
- editSettings() {
257
- this.doAs(this.service, 'open-entity-workitem', {
258
- entityId: `workitem@${this.props.id.split('@')[0]}`,
259
- });
260
- }
261
-
262
- /******************************************************************************/
263
-
264
- handleOnClick(button) {
265
- switch (button.id) {
266
- case 'validate':
267
- this.onSubmit();
268
- break;
269
- case 'publish':
270
- this.onPublish();
271
- break;
272
- case 'edit':
273
- this.onEdit();
274
- break;
275
- case 'rollback':
276
- this.onRollback();
277
- break;
278
- case 'delete':
279
- this.onDelete();
280
- break;
281
- case 'archive':
282
- this.onArchive();
283
- break;
284
- case 'trash':
285
- this.onTrash();
286
- break;
287
- default:
288
- break;
289
- }
290
-
291
- if (button.quest) {
292
- if (button.questService) {
293
- this.doFor(button.questService, button.quest, button.questParams);
294
- } else {
295
- this.doAs(this.service, button.quest, button.questParams);
296
- }
297
- }
298
- }
299
-
300
- renderActionListButton(button, hasStature, index) {
301
- const command = this.getCommand(button);
302
- return (
303
- <div
304
- key={index}
305
- className={
306
- hasStature
307
- ? this.styles.classNames.actionListWithStature
308
- : this.styles.classNames.actionList
309
- }
310
- >
311
- <CommandButton
312
- command={command}
313
- kind="secondary-action"
314
- grow="1"
315
- place="1/1"
316
- onClick={() => this.handleOnClick(button)}
317
- {...button}
318
- />
319
- </div>
320
- );
321
- }
322
-
323
- renderActionListButtonsGroup(list, index) {
324
- const name = list.get(0).get('statureGroupName');
325
-
326
- return (
327
- <div key={index} className={this.styles.classNames.actionsListSmall}>
328
- <div className={this.styles.classNames.actionsListSmallTitle}>
329
- <Label
330
- textTransform="uppercase"
331
- fontSize="75%"
332
- fontWeight="bold"
333
- text={name}
334
- />
335
- </div>
336
- <div
337
- key={index}
338
- className={this.styles.classNames.actionsListSmallContent}
339
- >
340
- {list.map((button, index) =>
341
- this.renderActionListButton(button.toJS(), true, index)
342
- )}
343
- </div>
344
- </div>
345
- );
346
- }
347
-
348
- renderActionListButtonsGroups(listButtons, groups) {
349
- if (!this.showStatureSmall) {
350
- return null;
351
- }
352
-
353
- const result = [];
354
- groups.forEach((group, index) => {
355
- const list = listButtons.filter(
356
- (b) =>
357
- b.get('stature') === 'small' &&
358
- b.get('statureGroup', 'default') === group
359
- );
360
- result.push(this.renderActionListButtonsGroup(list, index));
361
- });
362
- return result;
363
- }
364
-
365
- renderActionListButtons(listButtons) {
366
- if (listButtons.size === 0) {
367
- return null;
368
- }
369
-
370
- const smalls = listButtons.filter((b) => b.get('stature') === 'small');
371
- const hasStatureSmall = smalls.size > 0;
372
-
373
- const groups = new Set();
374
- smalls.forEach((small) => groups.add(small.get('statureGroup', 'default')));
375
-
376
- const hasStatureMajor = !!listButtons.find(
377
- (b) => b.get('stature') === 'major'
378
- );
379
-
380
- if (hasStatureSmall && hasStatureMajor) {
381
- return (
382
- <div className={this.styles.classNames.actionsListWithStature}>
383
- <div className={this.styles.classNames.actionsListStature}>
384
- <Button
385
- shape="rounded"
386
- glyph={
387
- this.showStatureSmall
388
- ? 'solid/chevron-down'
389
- : 'solid/chevron-up'
390
- }
391
- onClick={() => (this.showStatureSmall = !this.showStatureSmall)}
392
- />
393
- </div>
394
- <div className={this.styles.classNames.actionsListMajor}>
395
- {listButtons
396
- .filter((b) => b.get('stature') === 'major')
397
- .map((button, index) =>
398
- this.renderActionListButton(button.toJS(), true, index)
399
- )}
400
- </div>
401
- {this.renderActionListButtonsGroups(listButtons, groups)}
402
- </div>
403
- );
404
- } else {
405
- return (
406
- <div className={this.styles.classNames.actionsList}>
407
- {listButtons.map((button, index) =>
408
- this.renderActionListButton(button.toJS(), false, index)
409
- )}
410
- </div>
411
- );
412
- }
413
- }
414
-
415
- renderActionButton(button, layout, index, count) {
416
- const secondary = layout === 'secondary';
417
- const command = this.getCommand(button);
418
- console.log('renderActionButton', command);
419
- return (
420
- <CommandButton
421
- command={command}
422
- key={index}
423
- kind={secondary ? 'secondary-action' : 'action'}
424
- width={secondary ? null : '0px'}
425
- grow={secondary ? null : '1'}
426
- place={`${index + 1}/${count}`}
427
- onClick={() => this.handleOnClick(button)}
428
- {...button}
429
- />
430
- );
431
- }
432
-
433
- renderActionButtonsList(buttons, layout) {
434
- const count = buttons.size;
435
- return buttons.map((button, index) =>
436
- this.renderActionButton(button.toJS(), layout, index, count)
437
- );
438
- }
439
-
440
- renderActionButtons() {
441
- if (!this.props.buttons) {
442
- return null;
443
- }
444
-
445
- const primaryButtons = this.props.buttons.filter((b) => {
446
- const layout = b.get('layout');
447
- return !layout || layout === 'primary';
448
- });
449
- const secondaryButtons = this.props.buttons.filter(
450
- (b) => b.get('layout') === 'secondary'
451
- );
452
- const thirdButtons = this.props.buttons.filter(
453
- (b) => b.get('layout') === 'third'
454
- );
455
- const fourthButtons = this.props.buttons.filter(
456
- (b) => b.get('layout') === 'fourth'
457
- );
458
- const listButtons = this.props.buttons.filter(
459
- (b) => b.get('layout') === 'list'
460
- );
461
-
462
- if (fourthButtons.size > 0) {
463
- return (
464
- <div className={this.styles.classNames.actionsLines}>
465
- {this.renderActionListButtons(listButtons)}
466
- <div className={this.styles.classNames.actionsLineSecondary}>
467
- {this.renderActionButtonsList(fourthButtons, 'secondary')}
468
- </div>
469
- <div className={this.styles.classNames.actionsLineSecondary}>
470
- {this.renderActionButtonsList(thirdButtons, 'secondary')}
471
- </div>
472
- <div className={this.styles.classNames.actionsLineSecondary}>
473
- {this.renderActionButtonsList(secondaryButtons, 'secondary')}
474
- </div>
475
- <div className={this.styles.classNames.actionsLinePrimary}>
476
- {this.renderActionButtonsList(primaryButtons, 'primary')}
477
- </div>
478
- </div>
479
- );
480
- } else if (thirdButtons.size > 0) {
481
- return (
482
- <div className={this.styles.classNames.actionsLines}>
483
- {this.renderActionListButtons(listButtons)}
484
- <div className={this.styles.classNames.actionsLineSecondary}>
485
- {this.renderActionButtonsList(thirdButtons, 'secondary')}
486
- </div>
487
- <div className={this.styles.classNames.actionsLineSecondary}>
488
- {this.renderActionButtonsList(secondaryButtons, 'secondary')}
489
- </div>
490
- <div className={this.styles.classNames.actionsLinePrimary}>
491
- {this.renderActionButtonsList(primaryButtons, 'primary')}
492
- </div>
493
- </div>
494
- );
495
- } else if (secondaryButtons.size > 0) {
496
- return (
497
- <div className={this.styles.classNames.actionsLines}>
498
- {this.renderActionListButtons(listButtons)}
499
- <div className={this.styles.classNames.actionsLineSecondary}>
500
- {this.renderActionButtonsList(secondaryButtons, 'secondary')}
501
- </div>
502
- <div className={this.styles.classNames.actionsLinePrimary}>
503
- {this.renderActionButtonsList(primaryButtons, 'primary')}
504
- </div>
505
- </div>
506
- );
507
- } else if (listButtons.size > 0) {
508
- return (
509
- <div className={this.styles.classNames.actionsLines}>
510
- {this.renderActionListButtons(listButtons)}
511
- <div className={this.styles.classNames.actionsLinePrimary}>
512
- {this.renderActionButtonsList(primaryButtons, 'primary')}
513
- </div>
514
- </div>
515
- );
516
- } else {
517
- return (
518
- <Container kind="actions">
519
- {this.renderActionButtonsList(primaryButtons, 'primary')}
520
- </Container>
521
- );
522
- }
523
- }
524
-
525
- renderStatusBase() {
526
- if (this.props.status === 'draft') {
527
- return (
528
- <div className={this.styles.classNames.statusDraft}>
529
- <Label kind="pane-warning" text={T('Brouillon')} />
530
- </div>
531
- );
532
- } else if (this.props.status === 'archived') {
533
- return (
534
- <div className={this.styles.classNames.statusArchived}>
535
- <Label kind="pane-warning" text={T('Archivé')} />
536
- </div>
537
- );
538
- } else if (this.props.status === 'trashed') {
539
- return (
540
- <div className={this.styles.classNames.statusTrashed}>
541
- <Label kind="pane-warning" text={T('Détruit')} />
542
- </div>
543
- );
544
- } else if (this.props.status === 'missing') {
545
- return (
546
- <div className={this.styles.classNames.statusTrashed}>
547
- <Label kind="pane-warning" text={T('Manquant')} />
548
- </div>
549
- );
550
- } else {
551
- return null;
552
- }
553
- }
554
-
555
- renderStatusBusiness() {
556
- if (this.props.businessStatus) {
557
- let nabuId = this.getSchema(
558
- `${this.props.entityType}.status.valuesInfo.${this.props.businessStatus}.text.nabuId`
559
- );
560
- if (!nabuId) {
561
- nabuId = this.props.businessStatus;
562
- }
563
- return (
564
- <div className={this.styles.classNames.statusBusiness}>
565
- <Label kind="pane-warning" text={{nabuId}} />
566
- </div>
567
- );
568
- } else {
569
- return null;
570
- }
571
- }
572
-
573
- renderStatusTopButtons() {
574
- return (
575
- <div className={this.styles.classNames.topButtons}>
576
- <Button
577
- kind="pane-warning"
578
- glyph="solid/times"
579
- tooltip={T('Fermer')}
580
- onClick={this.onClose}
581
- />
582
- </div>
583
- );
584
- }
585
-
586
- renderStatusButtons() {
587
- if (document.queryCommandSupported('copy')) {
588
- return (
589
- <div className={this.styles.classNames.statusButtons}>
590
- <Button
591
- kind="pane-warning"
592
- glyph="light/radar"
593
- tooltip={T('Voir dans le State Monitor')}
594
- onClick={this.onCopyInfoToClipboard}
595
- />
596
- <Button
597
- kind="pane-warning"
598
- glyph="solid/edit"
599
- tooltip={T('Editer les champs additionnels')}
600
- onClick={this.editSettings}
601
- />
602
- </div>
603
- );
604
- } else {
605
- return (
606
- <div className={this.styles.classNames.statusButtons}>
607
- <Button
608
- kind="pane-warning"
609
- glyph="solid/edit"
610
- tooltip={T('Editer')}
611
- onClick={this.editSettings}
612
- />
613
- </div>
614
- );
615
- }
616
- }
617
-
618
- renderAlerts() {
619
- return <EntityAlerts entityId={this.props.entityId} />;
620
- }
621
-
622
- renderStatus() {
623
- return (
624
- <React.Fragment>
625
- <div className={this.styles.classNames.status}>
626
- {this.renderStatusTopButtons()}
627
- {this.renderStatusBase()}
628
- {this.renderStatusBusiness()}
629
- {this.renderStatusButtons()}
630
- </div>
631
- {this.renderAlerts()}
632
- </React.Fragment>
633
- );
634
- }
635
-
636
- renderEditor() {
637
- const Form = this.Form;
638
- const {entityId} = this.props;
639
-
640
- const scrollableId = `workitem-edit@${entityId || 'generic'}`;
641
-
642
- return (
643
- <Container
644
- kind="view"
645
- grow="1"
646
- horizontalSpacing="large"
647
- busy={this.props.loading}
648
- width="100%"
649
- >
650
- <Container kind="pane-header">
651
- <Title
652
- entityId={entityId}
653
- kind="pane-header"
654
- singleLine={true}
655
- wrap="no"
656
- />
657
- {this.props.version}
658
- </Container>
659
- {this.renderStatus()}
660
- <ScrollableContainer
661
- kind="panes"
662
- id={scrollableId}
663
- restoreScroll={true}
664
- >
665
- <Form
666
- component={FormComponent}
667
- validateOn="submit"
668
- model={`backend.${entityId}`}
669
- >
670
- {this.props.children}
671
- <WorkitemFields
672
- id={`workitem@${this.props.id.split('@')[0]}`}
673
- workitemId={this.props.id}
674
- readonly={false}
675
- />
676
- </Form>
677
- </ScrollableContainer>
678
- {this.renderActionButtons()}
679
- </Container>
680
- );
681
- }
682
-
683
- renderDetail() {
684
- const Form = this.Form;
685
- const {entityId} = this.props;
686
-
687
- const scrollableId = `workitem-readonly@${entityId || 'generic'}`;
688
-
689
- return (
690
- <Container kind="column-full" addClassName="hinter-container">
691
- <Container kind="pane-header">
692
- <Title
693
- entityId={entityId}
694
- kind="pane-header"
695
- singleLine={true}
696
- wrap="no"
697
- />
698
- </Container>
699
- {this.renderStatus()}
700
- <ScrollableContainer
701
- kind="panes"
702
- id={scrollableId}
703
- restoreScroll={true}
704
- >
705
- <Form
706
- component={FormComponent}
707
- validateOn="submit"
708
- model={`backend.${this.props.entityId}`}
709
- >
710
- <div className={this.styles.classNames.formFields}>
711
- {this.props.children}
712
- <WorkitemFields
713
- id={`workitem@${this.props.id.split('@')[0]}`}
714
- workitemId={this.props.id}
715
- readonly={true}
716
- />
717
- </div>
718
- </Form>
719
- </ScrollableContainer>
720
- {this.renderActionButtons()}
721
- </Container>
722
- );
723
- }
724
-
725
- renderDocument() {
726
- const Form = this.Form;
727
- const {entityId, children} = this.props;
728
- return (
729
- <Container kind="column-full">
730
- <Container kind="pane-header">
731
- <Title
732
- entityId={entityId}
733
- kind="pane-header"
734
- singleLine={true}
735
- wrap="no"
736
- />
737
- </Container>
738
- {this.renderStatus()}
739
- <Form
740
- component={FormFragmentComponent}
741
- validateOn="submit"
742
- model={`backend.${entityId}`}
743
- >
744
- {children}
745
- </Form>
746
-
747
- {this.renderActionButtons()}
748
- </Container>
749
- );
750
- }
751
-
752
- renderForm() {
753
- const Form = this.Form;
754
- const formClass = this.styles.classNames.form;
755
- return (
756
- <Form
757
- component={FormComponent}
758
- formClass={formClass}
759
- validateOn="submit"
760
- model={`backend.${this.props.entityId}`}
761
- >
762
- {this.props.children}
763
- </Form>
764
- );
765
- }
766
-
767
- renderBoard() {
768
- const Form = this.Form;
769
- const boardClass = this.styles.classNames.board;
770
- return (
771
- <Form
772
- component={FormComponent}
773
- formClass={boardClass}
774
- validateOn="submit"
775
- model={`backend.${this.props.entityId}`}
776
- >
777
- {this.props.children}
778
- </Form>
779
- );
780
- }
781
-
782
- renderRoadbook() {
783
- const Form = this.Form;
784
- const roadbookClass = this.styles.classNames.roadbook;
785
- return (
786
- <Form
787
- component={FormComponent}
788
- formClass={roadbookClass}
789
- validateOn="submit"
790
- model={`backend.${this.props.entityId}`}
791
- >
792
- {this.props.children}
793
- </Form>
794
- );
795
- }
796
-
797
- renderDesk() {
798
- const Form = this.Form;
799
- const deskClass = this.styles.classNames.desk;
800
- return (
801
- <Form
802
- component={FormComponent}
803
- formClass={deskClass}
804
- validateOn="submit"
805
- model={`backend.${this.props.entityId}`}
806
- >
807
- {this.props.children}
808
- </Form>
809
- );
810
- }
811
-
812
- renderWorkitem() {
813
- const kind = this.props.kind || 'editor';
814
- switch (kind) {
815
- case 'editor':
816
- return this.renderEditor();
817
- case 'detail':
818
- return this.renderDetail();
819
- case 'form':
820
- return this.renderForm();
821
- case 'map':
822
- case 'board':
823
- return this.renderBoard();
824
- case 'roadbook':
825
- return this.renderRoadbook();
826
- case 'desk':
827
- return this.renderDesk();
828
- case 'document':
829
- return this.renderDocument();
830
- default:
831
- console.error(`Workitem does not support kind='${kind}'`);
832
- return null;
833
- }
834
- }
835
-
836
- renderDeleteDialog() {
837
- if (!this.showDeleteDialog) {
838
- return null;
839
- }
840
-
841
- const archived = this.props.status === 'archived';
842
-
843
- let deleteAction = this.deleteAction;
844
- if (archived && deleteAction === 'archive') {
845
- deleteAction = 'unknown';
846
- }
847
-
848
- const list = [];
849
- if (!archived) {
850
- list.push({name: 'archive', description: T('Archiver')});
851
- }
852
- list.push({name: 'trash', description: T('Détruire')});
853
-
854
- const title = archived
855
- ? T('Voulez-vous détruire la fiche archivée ?')
856
- : T('Comment voulez-vous supprimer la fiche ?');
857
-
858
- let glyph, text, description, disabled, style;
859
-
860
- switch (deleteAction) {
861
- case 'archive':
862
- glyph = 'solid/archive';
863
- text = T('Archiver');
864
- description =
865
- '```Le __statut fiche__ deviendra __archivé__. Son apparition dans les recherches sera déterminé par les filtres. Il pourra en tout temps reprendre le statut __publié__.```';
866
- disabled = false;
867
- style = this.styles.classNames.deleteInfArchive;
868
- break;
869
-
870
- case 'trash':
871
- glyph = 'solid/tombstone';
872
- text = T('Détruire');
873
- description =
874
- "```La fiche ne sera plus indexée. En conséquence, elle n'apparaîtra plus dans les recherches. Elle reste à disposition du gestionnaire de données jusqu'à la prochaine opération de nettoyage.```";
875
- disabled = false;
876
- style = this.styles.classNames.deleteInfTrash;
877
- break;
878
-
879
- default:
880
- glyph = null;
881
- text = '';
882
- description = '';
883
- disabled = true;
884
- style = this.styles.classNames.deleteInfUnknown;
885
- break;
886
- }
887
-
888
- return (
889
- <DialogModal subkind="full">
890
- <div className={this.styles.classNames.deleteSup}>
891
- <Label text={title} />
892
- <div className={this.styles.classNames.deleteRadio}>
893
- <CheckList
894
- kind="radio"
895
- direction="column"
896
- selectionMode="single"
897
- list={list}
898
- value={deleteAction}
899
- selectionChanged={(a) => (this.deleteAction = a)}
900
- />
901
- <Label
902
- width="100px"
903
- height="100px"
904
- glyphSize="400%"
905
- glyph={glyph}
906
- glyphPosition="center"
907
- justify="center"
908
- />
909
- </div>
910
- </div>
911
- <div className={style}>
912
- <div className={this.styles.classNames.deleteDescription}>
913
- <Label text={description} width="500px" />
914
- </div>
915
- <div className={this.styles.classNames.deleteButtons}>
916
- {disabled ? null : (
917
- <Button
918
- kind="action"
919
- place="1/2"
920
- grow="1"
921
- glyph={glyph}
922
- text={text}
923
- onClick={this.onDoDelete}
924
- />
925
- )}
926
- <Button
927
- kind="action"
928
- place={disabled ? '1/1' : '2/2'}
929
- grow="1"
930
- glyph="solid/times"
931
- text={T('Annuler')}
932
- onClick={() => (this.showDeleteDialog = false)}
933
- />
934
- </div>
935
- </div>
936
- </DialogModal>
937
- );
938
- }
939
-
940
- render() {
941
- if (!this.props.id) {
942
- return <div>Missing id props on Workitem component</div>;
943
- }
944
- if (!this.props.entityId) {
945
- return <div>Missing entityId props on Workitem component</div>;
946
- }
947
-
948
- return (
949
- <>
950
- {this.renderWorkitem()}
951
- {this.renderDeleteDialog()}
952
- </>
953
- );
954
- }
955
- }
956
-
957
- /******************************************************************************/
958
- export default Widget.connect((state, props) => {
959
- const loading = state.get(`backend.${props.id}.loading`, true);
960
- if (props.entityId) {
961
- return {
962
- status: state.get(`backend.${props.entityId}.meta.status`),
963
- businessStatus: state.get(`backend.${props.entityId}.status`),
964
- entityType: state.get(`backend.${props.entityId}.meta.type`),
965
- loading,
966
- };
967
- } else {
968
- return {
969
- status: null,
970
- businessStatus: null,
971
- entityType: null,
972
- loading,
973
- };
974
- }
975
- })(Workitem);
976
-
977
- class FormComponent extends React.PureComponent {
978
- render() {
979
- const {formClass} = this.props;
980
- return <div className={formClass}>{this.props.children}</div>;
981
- }
982
- }
983
-
984
- class FormFragmentComponent extends React.PureComponent {
985
- render() {
986
- return <>{this.props.children}</>;
987
- }
988
- }
1
+ import React from 'react';
2
+ import MouseTrap from 'mousetrap';
3
+ import Widget from 'goblin-laboratory/widgets/widget';
4
+ import Form from 'goblin-laboratory/widgets/form';
5
+ import WorkitemFields from '../workitem-fields/widget.js';
6
+ import PropTypes from 'prop-types';
7
+
8
+ import Container from 'goblin-gadgets/widgets/container/widget';
9
+ import Label from 'goblin-gadgets/widgets/label/widget';
10
+ import Button from 'goblin-gadgets/widgets/button/widget';
11
+ import CommandButton from 'goblin-gadgets/widgets/command-button/widget';
12
+ import ScrollableContainer from 'goblin-gadgets/widgets/scrollable-container/widget';
13
+ import DialogModal from 'goblin-gadgets/widgets/dialog-modal/widget';
14
+ import CheckList from 'goblin-gadgets/widgets/check-list/widget';
15
+ import EntityAlerts from 'goblin-desktop/widgets/entity-alerts/widget.js';
16
+ import T from 't';
17
+
18
+ /******************************************************************************/
19
+
20
+ const Title = Widget.connect((state, props) => {
21
+ const path = `backend.${props.entityId}.meta.summaries.info`;
22
+ const text = state.get(path);
23
+ return {text};
24
+ })(Label);
25
+
26
+ class Workitem extends Form {
27
+ constructor() {
28
+ super(...arguments);
29
+
30
+ this.state = {
31
+ showDeleteDialog: false,
32
+ deleteAction: 'archive',
33
+ showStatureSmall: false,
34
+ };
35
+
36
+ this.doAction = this.doAction.bind(this);
37
+ this.doMainAction = this.doMainAction.bind(this);
38
+ this.onSubmit = this.onSubmit.bind(this);
39
+ this.onRollback = this.onRollback.bind(this);
40
+ this.onClose = this.onClose.bind(this);
41
+ this.onEdit = this.onEdit.bind(this);
42
+ this.onTrash = this.onTrash.bind(this);
43
+ this.onDelete = this.onDelete.bind(this);
44
+ this.onDoDelete = this.onDoDelete.bind(this);
45
+ this.onArchive = this.onArchive.bind(this);
46
+ this.onPublish = this.onPublish.bind(this);
47
+ this.onCopyInfoToClipboard = this.onCopyInfoToClipboard.bind(this);
48
+ this.editSettings = this.editSettings.bind(this);
49
+ this.hide = this.hide.bind(this);
50
+ }
51
+
52
+ //#region get/set
53
+ get showDeleteDialog() {
54
+ return this.state.showDeleteDialog;
55
+ }
56
+ set showDeleteDialog(value) {
57
+ this.setState({
58
+ showDeleteDialog: value,
59
+ });
60
+ }
61
+
62
+ get deleteAction() {
63
+ return this.state.deleteAction;
64
+ }
65
+ set deleteAction(value) {
66
+ this.setState({
67
+ deleteAction: value,
68
+ });
69
+ }
70
+
71
+ get showStatureSmall() {
72
+ return this.state.showStatureSmall;
73
+ }
74
+ set showStatureSmall(value) {
75
+ this.setState({
76
+ showStatureSmall: value,
77
+ });
78
+ }
79
+ //#endregion
80
+
81
+ componentDidMount() {
82
+ // FIXME: stacked workitems not handled properly
83
+ // MouseTrap.bind('esc', this.onClose);
84
+ MouseTrap.bind('ctrl+enter', this.doMainAction);
85
+ }
86
+
87
+ componentWillUnmount() {
88
+ super.componentWillUnmount();
89
+ // MouseTrap.unbind('esc');
90
+ MouseTrap.unbind('ctrl+enter', this.doMainAction);
91
+ }
92
+
93
+ getChildContext() {
94
+ return {
95
+ readonly: this.props.readonly,
96
+ id: this.props.id,
97
+ entityId: this.props.entityId,
98
+ dragServiceId: this.props.dragServiceId,
99
+ };
100
+ }
101
+
102
+ static get childContextTypes() {
103
+ return {
104
+ readonly: PropTypes.any,
105
+ id: PropTypes.string,
106
+ entityId: PropTypes.string,
107
+ dragServiceId: PropTypes.string,
108
+ };
109
+ }
110
+
111
+ get service() {
112
+ return this.props.id.split('@')[0];
113
+ }
114
+
115
+ get desktopId() {
116
+ return this.context.desktopId;
117
+ }
118
+
119
+ get contextId() {
120
+ return this.context.contextId;
121
+ }
122
+
123
+ getCommand(button) {
124
+ const getQuest = (id) => {
125
+ switch (id) {
126
+ case 'rollback':
127
+ case 'validate':
128
+ return `close`;
129
+ case 'delete':
130
+ return `trash-entity`;
131
+ case 'submit':
132
+ case 'publish':
133
+ case 'trash':
134
+ case 'archive':
135
+ return `${id}-entity`;
136
+ default:
137
+ return id;
138
+ }
139
+ };
140
+
141
+ const service = button.questService ?? this.props.id.split('@', 1)[0];
142
+ let quest = button.quest ?? getQuest(button.id);
143
+ return `${service}.${quest}`;
144
+ }
145
+
146
+ onSubmit() {
147
+ this.doAs(this.service, 'close', {
148
+ kind: 'validate',
149
+ desktopId: this.desktopId,
150
+ });
151
+ }
152
+
153
+ onRollback() {
154
+ this.doAs(this.service, 'close', {
155
+ kind: 'rollback',
156
+ desktopId: this.desktopId,
157
+ });
158
+ //TODO-VNEXT: REPAIR ME
159
+ //this.hideHinter();
160
+ }
161
+
162
+ onClose() {
163
+ const kind = this.props.kind || 'editor';
164
+ switch (kind) {
165
+ case 'editor':
166
+ this.doAs(this.service, 'close', {
167
+ kind: 'terminate',
168
+ desktopId: this.desktopId,
169
+ });
170
+ break;
171
+ default:
172
+ this.hide();
173
+ }
174
+ }
175
+
176
+ onEdit() {
177
+ const entity = this.getBackendState(this.props.entityId);
178
+ if (entity) {
179
+ this.doAs(this.service, 'edit', {
180
+ entity,
181
+ desktopId: this.desktopId,
182
+ });
183
+ }
184
+ }
185
+
186
+ hide() {
187
+ this.doAs(this.service, 'hide');
188
+
189
+ //in case of use in a search list,
190
+ //dispatch to search list a null rowId
191
+ if (
192
+ this.props.leftPanelWorkitemId &&
193
+ this.props.leftPanelWorkitemId.match(/^.*-search@.*/)
194
+ ) {
195
+ this.dispatchTo(
196
+ this.props.leftPanelWorkitemId,
197
+ {type: 'select-row', rowId: null, entityId: null},
198
+ 'entity-view'
199
+ );
200
+ }
201
+ }
202
+
203
+ doMainAction() {
204
+ //TODO: actionate main action
205
+ console.log('main action');
206
+ }
207
+
208
+ doAction(action) {
209
+ const kind = this.props.kind || 'editor';
210
+ switch (kind) {
211
+ case 'editor':
212
+ this.doAs(this.service, 'close', {
213
+ kind: action,
214
+ desktopId: this.desktopId,
215
+ });
216
+ break;
217
+ default:
218
+ this.doAs(this.service, `${action}-entity`);
219
+ }
220
+ }
221
+
222
+ onPublish() {
223
+ this.doAction('publish');
224
+ }
225
+
226
+ onDelete() {
227
+ this.deleteAction = 'archive';
228
+ this.showDeleteDialog = true;
229
+ }
230
+
231
+ onDoDelete() {
232
+ this.showDeleteDialog = false;
233
+ if (this.deleteAction === 'trash') {
234
+ this.onTrash();
235
+ } else {
236
+ this.onArchive();
237
+ }
238
+ }
239
+
240
+ onTrash() {
241
+ this.doAction('trash');
242
+ }
243
+
244
+ onArchive() {
245
+ this.doAction('archive');
246
+ }
247
+
248
+ onCopyInfoToClipboard() {
249
+ Form.copyTextToClipboard(this.props.entityId);
250
+
251
+ this.doAs(this.service, 'add-state-monitor', {
252
+ key: this.props.entityId,
253
+ });
254
+ }
255
+
256
+ editSettings() {
257
+ this.doAs(this.service, 'open-entity-workitem', {
258
+ entityId: `workitem@${this.props.id.split('@')[0]}`,
259
+ });
260
+ }
261
+
262
+ /******************************************************************************/
263
+
264
+ handleOnClick(button) {
265
+ switch (button.id) {
266
+ case 'validate':
267
+ this.onSubmit();
268
+ break;
269
+ case 'publish':
270
+ this.onPublish();
271
+ break;
272
+ case 'edit':
273
+ this.onEdit();
274
+ break;
275
+ case 'rollback':
276
+ this.onRollback();
277
+ break;
278
+ case 'delete':
279
+ this.onDelete();
280
+ break;
281
+ case 'archive':
282
+ this.onArchive();
283
+ break;
284
+ case 'trash':
285
+ this.onTrash();
286
+ break;
287
+ default:
288
+ break;
289
+ }
290
+
291
+ if (button.quest) {
292
+ if (button.questService) {
293
+ this.doFor(button.questService, button.quest, button.questParams);
294
+ } else {
295
+ this.doAs(this.service, button.quest, button.questParams);
296
+ }
297
+ }
298
+ }
299
+
300
+ renderActionListButton(button, hasStature, index) {
301
+ const command = this.getCommand(button);
302
+ return (
303
+ <div
304
+ key={index}
305
+ className={
306
+ hasStature
307
+ ? this.styles.classNames.actionListWithStature
308
+ : this.styles.classNames.actionList
309
+ }
310
+ >
311
+ <CommandButton
312
+ command={command}
313
+ kind="secondary-action"
314
+ grow="1"
315
+ place="1/1"
316
+ onClick={() => this.handleOnClick(button)}
317
+ {...button}
318
+ />
319
+ </div>
320
+ );
321
+ }
322
+
323
+ renderActionListButtonsGroup(list, index) {
324
+ const name = list.get(0).get('statureGroupName');
325
+
326
+ return (
327
+ <div key={index} className={this.styles.classNames.actionsListSmall}>
328
+ <div className={this.styles.classNames.actionsListSmallTitle}>
329
+ <Label
330
+ textTransform="uppercase"
331
+ fontSize="75%"
332
+ fontWeight="bold"
333
+ text={name}
334
+ />
335
+ </div>
336
+ <div
337
+ key={index}
338
+ className={this.styles.classNames.actionsListSmallContent}
339
+ >
340
+ {list.map((button, index) =>
341
+ this.renderActionListButton(button.toJS(), true, index)
342
+ )}
343
+ </div>
344
+ </div>
345
+ );
346
+ }
347
+
348
+ renderActionListButtonsGroups(listButtons, groups) {
349
+ if (!this.showStatureSmall) {
350
+ return null;
351
+ }
352
+
353
+ const result = [];
354
+ groups.forEach((group, index) => {
355
+ const list = listButtons.filter(
356
+ (b) =>
357
+ b.get('stature') === 'small' &&
358
+ b.get('statureGroup', 'default') === group
359
+ );
360
+ result.push(this.renderActionListButtonsGroup(list, index));
361
+ });
362
+ return result;
363
+ }
364
+
365
+ renderActionListButtons(listButtons) {
366
+ if (listButtons.size === 0) {
367
+ return null;
368
+ }
369
+
370
+ const smalls = listButtons.filter((b) => b.get('stature') === 'small');
371
+ const hasStatureSmall = smalls.size > 0;
372
+
373
+ const groups = new Set();
374
+ smalls.forEach((small) => groups.add(small.get('statureGroup', 'default')));
375
+
376
+ const hasStatureMajor = !!listButtons.find(
377
+ (b) => b.get('stature') === 'major'
378
+ );
379
+
380
+ if (hasStatureSmall && hasStatureMajor) {
381
+ return (
382
+ <div className={this.styles.classNames.actionsListWithStature}>
383
+ <div className={this.styles.classNames.actionsListStature}>
384
+ <Button
385
+ shape="rounded"
386
+ glyph={
387
+ this.showStatureSmall
388
+ ? 'solid/chevron-down'
389
+ : 'solid/chevron-up'
390
+ }
391
+ onClick={() => (this.showStatureSmall = !this.showStatureSmall)}
392
+ />
393
+ </div>
394
+ <div className={this.styles.classNames.actionsListMajor}>
395
+ {listButtons
396
+ .filter((b) => b.get('stature') === 'major')
397
+ .map((button, index) =>
398
+ this.renderActionListButton(button.toJS(), true, index)
399
+ )}
400
+ </div>
401
+ {this.renderActionListButtonsGroups(listButtons, groups)}
402
+ </div>
403
+ );
404
+ } else {
405
+ return (
406
+ <div className={this.styles.classNames.actionsList}>
407
+ {listButtons.map((button, index) =>
408
+ this.renderActionListButton(button.toJS(), false, index)
409
+ )}
410
+ </div>
411
+ );
412
+ }
413
+ }
414
+
415
+ renderActionButton(button, layout, index, count) {
416
+ const secondary = layout === 'secondary';
417
+ const command = this.getCommand(button);
418
+ console.log('renderActionButton', command);
419
+ return (
420
+ <CommandButton
421
+ command={command}
422
+ key={index}
423
+ kind={secondary ? 'secondary-action' : 'action'}
424
+ width={secondary ? null : '0px'}
425
+ grow={secondary ? null : '1'}
426
+ place={`${index + 1}/${count}`}
427
+ onClick={() => this.handleOnClick(button)}
428
+ {...button}
429
+ />
430
+ );
431
+ }
432
+
433
+ renderActionButtonsList(buttons, layout) {
434
+ const count = buttons.size;
435
+ return buttons.map((button, index) =>
436
+ this.renderActionButton(button.toJS(), layout, index, count)
437
+ );
438
+ }
439
+
440
+ renderActionButtons() {
441
+ if (!this.props.buttons) {
442
+ return null;
443
+ }
444
+
445
+ const primaryButtons = this.props.buttons.filter((b) => {
446
+ const layout = b.get('layout');
447
+ return !layout || layout === 'primary';
448
+ });
449
+ const secondaryButtons = this.props.buttons.filter(
450
+ (b) => b.get('layout') === 'secondary'
451
+ );
452
+ const thirdButtons = this.props.buttons.filter(
453
+ (b) => b.get('layout') === 'third'
454
+ );
455
+ const fourthButtons = this.props.buttons.filter(
456
+ (b) => b.get('layout') === 'fourth'
457
+ );
458
+ const listButtons = this.props.buttons.filter(
459
+ (b) => b.get('layout') === 'list'
460
+ );
461
+
462
+ if (fourthButtons.size > 0) {
463
+ return (
464
+ <div className={this.styles.classNames.actionsLines}>
465
+ {this.renderActionListButtons(listButtons)}
466
+ <div className={this.styles.classNames.actionsLineSecondary}>
467
+ {this.renderActionButtonsList(fourthButtons, 'secondary')}
468
+ </div>
469
+ <div className={this.styles.classNames.actionsLineSecondary}>
470
+ {this.renderActionButtonsList(thirdButtons, 'secondary')}
471
+ </div>
472
+ <div className={this.styles.classNames.actionsLineSecondary}>
473
+ {this.renderActionButtonsList(secondaryButtons, 'secondary')}
474
+ </div>
475
+ <div className={this.styles.classNames.actionsLinePrimary}>
476
+ {this.renderActionButtonsList(primaryButtons, 'primary')}
477
+ </div>
478
+ </div>
479
+ );
480
+ } else if (thirdButtons.size > 0) {
481
+ return (
482
+ <div className={this.styles.classNames.actionsLines}>
483
+ {this.renderActionListButtons(listButtons)}
484
+ <div className={this.styles.classNames.actionsLineSecondary}>
485
+ {this.renderActionButtonsList(thirdButtons, 'secondary')}
486
+ </div>
487
+ <div className={this.styles.classNames.actionsLineSecondary}>
488
+ {this.renderActionButtonsList(secondaryButtons, 'secondary')}
489
+ </div>
490
+ <div className={this.styles.classNames.actionsLinePrimary}>
491
+ {this.renderActionButtonsList(primaryButtons, 'primary')}
492
+ </div>
493
+ </div>
494
+ );
495
+ } else if (secondaryButtons.size > 0) {
496
+ return (
497
+ <div className={this.styles.classNames.actionsLines}>
498
+ {this.renderActionListButtons(listButtons)}
499
+ <div className={this.styles.classNames.actionsLineSecondary}>
500
+ {this.renderActionButtonsList(secondaryButtons, 'secondary')}
501
+ </div>
502
+ <div className={this.styles.classNames.actionsLinePrimary}>
503
+ {this.renderActionButtonsList(primaryButtons, 'primary')}
504
+ </div>
505
+ </div>
506
+ );
507
+ } else if (listButtons.size > 0) {
508
+ return (
509
+ <div className={this.styles.classNames.actionsLines}>
510
+ {this.renderActionListButtons(listButtons)}
511
+ <div className={this.styles.classNames.actionsLinePrimary}>
512
+ {this.renderActionButtonsList(primaryButtons, 'primary')}
513
+ </div>
514
+ </div>
515
+ );
516
+ } else {
517
+ return (
518
+ <Container kind="actions">
519
+ {this.renderActionButtonsList(primaryButtons, 'primary')}
520
+ </Container>
521
+ );
522
+ }
523
+ }
524
+
525
+ renderStatusBase() {
526
+ if (this.props.status === 'draft') {
527
+ return (
528
+ <div className={this.styles.classNames.statusDraft}>
529
+ <Label kind="pane-warning" text={T('Brouillon')} />
530
+ </div>
531
+ );
532
+ } else if (this.props.status === 'archived') {
533
+ return (
534
+ <div className={this.styles.classNames.statusArchived}>
535
+ <Label kind="pane-warning" text={T('Archivé')} />
536
+ </div>
537
+ );
538
+ } else if (this.props.status === 'trashed') {
539
+ return (
540
+ <div className={this.styles.classNames.statusTrashed}>
541
+ <Label kind="pane-warning" text={T('Détruit')} />
542
+ </div>
543
+ );
544
+ } else if (this.props.status === 'missing') {
545
+ return (
546
+ <div className={this.styles.classNames.statusTrashed}>
547
+ <Label kind="pane-warning" text={T('Manquant')} />
548
+ </div>
549
+ );
550
+ } else {
551
+ return null;
552
+ }
553
+ }
554
+
555
+ renderStatusBusiness() {
556
+ if (this.props.businessStatus) {
557
+ let nabuId = this.getSchema(
558
+ `${this.props.entityType}.status.valuesInfo.${this.props.businessStatus}.text.nabuId`
559
+ );
560
+ if (!nabuId) {
561
+ nabuId = this.props.businessStatus;
562
+ }
563
+ return (
564
+ <div className={this.styles.classNames.statusBusiness}>
565
+ <Label kind="pane-warning" text={{nabuId}} />
566
+ </div>
567
+ );
568
+ } else {
569
+ return null;
570
+ }
571
+ }
572
+
573
+ renderStatusTopButtons() {
574
+ return (
575
+ <div className={this.styles.classNames.topButtons}>
576
+ <Button
577
+ kind="pane-warning"
578
+ glyph="solid/times"
579
+ tooltip={T('Fermer')}
580
+ onClick={this.onClose}
581
+ />
582
+ </div>
583
+ );
584
+ }
585
+
586
+ renderStatusButtons() {
587
+ if (document.queryCommandSupported('copy')) {
588
+ return (
589
+ <div className={this.styles.classNames.statusButtons}>
590
+ <Button
591
+ kind="pane-warning"
592
+ glyph="light/radar"
593
+ tooltip={T('Voir dans le State Monitor')}
594
+ onClick={this.onCopyInfoToClipboard}
595
+ />
596
+ <Button
597
+ kind="pane-warning"
598
+ glyph="solid/edit"
599
+ tooltip={T('Editer les champs additionnels')}
600
+ onClick={this.editSettings}
601
+ />
602
+ </div>
603
+ );
604
+ } else {
605
+ return (
606
+ <div className={this.styles.classNames.statusButtons}>
607
+ <Button
608
+ kind="pane-warning"
609
+ glyph="solid/edit"
610
+ tooltip={T('Editer')}
611
+ onClick={this.editSettings}
612
+ />
613
+ </div>
614
+ );
615
+ }
616
+ }
617
+
618
+ renderAlerts() {
619
+ return <EntityAlerts entityId={this.props.entityId} />;
620
+ }
621
+
622
+ renderStatus() {
623
+ return (
624
+ <React.Fragment>
625
+ <div className={this.styles.classNames.status}>
626
+ {this.renderStatusTopButtons()}
627
+ {this.renderStatusBase()}
628
+ {this.renderStatusBusiness()}
629
+ {this.renderStatusButtons()}
630
+ </div>
631
+ {this.renderAlerts()}
632
+ </React.Fragment>
633
+ );
634
+ }
635
+
636
+ renderEditor() {
637
+ const Form = this.Form;
638
+ const {entityId} = this.props;
639
+
640
+ const scrollableId = `workitem-edit@${entityId || 'generic'}`;
641
+
642
+ return (
643
+ <Container
644
+ kind="view"
645
+ grow="1"
646
+ horizontalSpacing="large"
647
+ busy={this.props.loading}
648
+ width="100%"
649
+ >
650
+ <Container kind="pane-header">
651
+ <Title
652
+ entityId={entityId}
653
+ kind="pane-header"
654
+ singleLine={true}
655
+ wrap="no"
656
+ />
657
+ {this.props.version}
658
+ </Container>
659
+ {this.renderStatus()}
660
+ <ScrollableContainer
661
+ kind="panes"
662
+ id={scrollableId}
663
+ restoreScroll={true}
664
+ >
665
+ <Form
666
+ component={FormComponent}
667
+ validateOn="submit"
668
+ model={`backend.${entityId}`}
669
+ >
670
+ {this.props.children}
671
+ <WorkitemFields
672
+ id={`workitem@${this.props.id.split('@')[0]}`}
673
+ workitemId={this.props.id}
674
+ readonly={false}
675
+ />
676
+ </Form>
677
+ </ScrollableContainer>
678
+ {this.renderActionButtons()}
679
+ </Container>
680
+ );
681
+ }
682
+
683
+ renderDetail() {
684
+ const Form = this.Form;
685
+ const {entityId} = this.props;
686
+
687
+ const scrollableId = `workitem-readonly@${entityId || 'generic'}`;
688
+
689
+ return (
690
+ <Container kind="column-full" addClassName="hinter-container">
691
+ <Container kind="pane-header">
692
+ <Title
693
+ entityId={entityId}
694
+ kind="pane-header"
695
+ singleLine={true}
696
+ wrap="no"
697
+ />
698
+ </Container>
699
+ {this.renderStatus()}
700
+ <ScrollableContainer
701
+ kind="panes"
702
+ id={scrollableId}
703
+ restoreScroll={true}
704
+ >
705
+ <Form
706
+ component={FormComponent}
707
+ validateOn="submit"
708
+ model={`backend.${this.props.entityId}`}
709
+ >
710
+ <div className={this.styles.classNames.formFields}>
711
+ {this.props.children}
712
+ <WorkitemFields
713
+ id={`workitem@${this.props.id.split('@')[0]}`}
714
+ workitemId={this.props.id}
715
+ readonly={true}
716
+ />
717
+ </div>
718
+ </Form>
719
+ </ScrollableContainer>
720
+ {this.renderActionButtons()}
721
+ </Container>
722
+ );
723
+ }
724
+
725
+ renderDocument() {
726
+ const Form = this.Form;
727
+ const {entityId, children} = this.props;
728
+ return (
729
+ <Container kind="column-full">
730
+ <Container kind="pane-header">
731
+ <Title
732
+ entityId={entityId}
733
+ kind="pane-header"
734
+ singleLine={true}
735
+ wrap="no"
736
+ />
737
+ </Container>
738
+ {this.renderStatus()}
739
+ <Form
740
+ component={FormFragmentComponent}
741
+ validateOn="submit"
742
+ model={`backend.${entityId}`}
743
+ >
744
+ {children}
745
+ </Form>
746
+
747
+ {this.renderActionButtons()}
748
+ </Container>
749
+ );
750
+ }
751
+
752
+ renderForm() {
753
+ const Form = this.Form;
754
+ const formClass = this.styles.classNames.form;
755
+ return (
756
+ <Form
757
+ component={FormComponent}
758
+ formClass={formClass}
759
+ validateOn="submit"
760
+ model={`backend.${this.props.entityId}`}
761
+ >
762
+ {this.props.children}
763
+ </Form>
764
+ );
765
+ }
766
+
767
+ renderBoard() {
768
+ const Form = this.Form;
769
+ const boardClass = this.styles.classNames.board;
770
+ return (
771
+ <Form
772
+ component={FormComponent}
773
+ formClass={boardClass}
774
+ validateOn="submit"
775
+ model={`backend.${this.props.entityId}`}
776
+ >
777
+ {this.props.children}
778
+ </Form>
779
+ );
780
+ }
781
+
782
+ renderRoadbook() {
783
+ const Form = this.Form;
784
+ const roadbookClass = this.styles.classNames.roadbook;
785
+ return (
786
+ <Form
787
+ component={FormComponent}
788
+ formClass={roadbookClass}
789
+ validateOn="submit"
790
+ model={`backend.${this.props.entityId}`}
791
+ >
792
+ {this.props.children}
793
+ </Form>
794
+ );
795
+ }
796
+
797
+ renderDesk() {
798
+ const Form = this.Form;
799
+ const deskClass = this.styles.classNames.desk;
800
+ return (
801
+ <Form
802
+ component={FormComponent}
803
+ formClass={deskClass}
804
+ validateOn="submit"
805
+ model={`backend.${this.props.entityId}`}
806
+ >
807
+ {this.props.children}
808
+ </Form>
809
+ );
810
+ }
811
+
812
+ renderWorkitem() {
813
+ const kind = this.props.kind || 'editor';
814
+ switch (kind) {
815
+ case 'editor':
816
+ return this.renderEditor();
817
+ case 'detail':
818
+ return this.renderDetail();
819
+ case 'form':
820
+ return this.renderForm();
821
+ case 'map':
822
+ case 'board':
823
+ return this.renderBoard();
824
+ case 'roadbook':
825
+ return this.renderRoadbook();
826
+ case 'desk':
827
+ return this.renderDesk();
828
+ case 'document':
829
+ return this.renderDocument();
830
+ default:
831
+ console.error(`Workitem does not support kind='${kind}'`);
832
+ return null;
833
+ }
834
+ }
835
+
836
+ renderDeleteDialog() {
837
+ if (!this.showDeleteDialog) {
838
+ return null;
839
+ }
840
+
841
+ const archived = this.props.status === 'archived';
842
+
843
+ let deleteAction = this.deleteAction;
844
+ if (archived && deleteAction === 'archive') {
845
+ deleteAction = 'unknown';
846
+ }
847
+
848
+ const list = [];
849
+ if (!archived) {
850
+ list.push({name: 'archive', description: T('Archiver')});
851
+ }
852
+ list.push({name: 'trash', description: T('Détruire')});
853
+
854
+ const title = archived
855
+ ? T('Voulez-vous détruire la fiche archivée ?')
856
+ : T('Comment voulez-vous supprimer la fiche ?');
857
+
858
+ let glyph, text, description, disabled, style;
859
+
860
+ switch (deleteAction) {
861
+ case 'archive':
862
+ glyph = 'solid/archive';
863
+ text = T('Archiver');
864
+ description =
865
+ '```Le __statut fiche__ deviendra __archivé__. Son apparition dans les recherches sera déterminé par les filtres. Il pourra en tout temps reprendre le statut __publié__.```';
866
+ disabled = false;
867
+ style = this.styles.classNames.deleteInfArchive;
868
+ break;
869
+
870
+ case 'trash':
871
+ glyph = 'solid/tombstone';
872
+ text = T('Détruire');
873
+ description =
874
+ "```La fiche ne sera plus indexée. En conséquence, elle n'apparaîtra plus dans les recherches. Elle reste à disposition du gestionnaire de données jusqu'à la prochaine opération de nettoyage.```";
875
+ disabled = false;
876
+ style = this.styles.classNames.deleteInfTrash;
877
+ break;
878
+
879
+ default:
880
+ glyph = null;
881
+ text = '';
882
+ description = '';
883
+ disabled = true;
884
+ style = this.styles.classNames.deleteInfUnknown;
885
+ break;
886
+ }
887
+
888
+ return (
889
+ <DialogModal subkind="full">
890
+ <div className={this.styles.classNames.deleteSup}>
891
+ <Label text={title} />
892
+ <div className={this.styles.classNames.deleteRadio}>
893
+ <CheckList
894
+ kind="radio"
895
+ direction="column"
896
+ selectionMode="single"
897
+ list={list}
898
+ value={deleteAction}
899
+ selectionChanged={(a) => (this.deleteAction = a)}
900
+ />
901
+ <Label
902
+ width="100px"
903
+ height="100px"
904
+ glyphSize="400%"
905
+ glyph={glyph}
906
+ glyphPosition="center"
907
+ justify="center"
908
+ />
909
+ </div>
910
+ </div>
911
+ <div className={style}>
912
+ <div className={this.styles.classNames.deleteDescription}>
913
+ <Label text={description} width="500px" />
914
+ </div>
915
+ <div className={this.styles.classNames.deleteButtons}>
916
+ {disabled ? null : (
917
+ <Button
918
+ kind="action"
919
+ place="1/2"
920
+ grow="1"
921
+ glyph={glyph}
922
+ text={text}
923
+ onClick={this.onDoDelete}
924
+ />
925
+ )}
926
+ <Button
927
+ kind="action"
928
+ place={disabled ? '1/1' : '2/2'}
929
+ grow="1"
930
+ glyph="solid/times"
931
+ text={T('Annuler')}
932
+ onClick={() => (this.showDeleteDialog = false)}
933
+ />
934
+ </div>
935
+ </div>
936
+ </DialogModal>
937
+ );
938
+ }
939
+
940
+ render() {
941
+ if (!this.props.id) {
942
+ return <div>Missing id props on Workitem component</div>;
943
+ }
944
+ if (!this.props.entityId) {
945
+ return <div>Missing entityId props on Workitem component</div>;
946
+ }
947
+
948
+ return (
949
+ <>
950
+ {this.renderWorkitem()}
951
+ {this.renderDeleteDialog()}
952
+ </>
953
+ );
954
+ }
955
+ }
956
+
957
+ /******************************************************************************/
958
+ export default Widget.connect((state, props) => {
959
+ const loading = state.get(`backend.${props.id}.loading`, true);
960
+ if (props.entityId) {
961
+ return {
962
+ status: state.get(`backend.${props.entityId}.meta.status`),
963
+ businessStatus: state.get(`backend.${props.entityId}.status`),
964
+ entityType: state.get(`backend.${props.entityId}.meta.type`),
965
+ loading,
966
+ };
967
+ } else {
968
+ return {
969
+ status: null,
970
+ businessStatus: null,
971
+ entityType: null,
972
+ loading,
973
+ };
974
+ }
975
+ })(Workitem);
976
+
977
+ class FormComponent extends React.PureComponent {
978
+ render() {
979
+ const {formClass} = this.props;
980
+ return <div className={formClass}>{this.props.children}</div>;
981
+ }
982
+ }
983
+
984
+ class FormFragmentComponent extends React.PureComponent {
985
+ render() {
986
+ return <>{this.props.children}</>;
987
+ }
988
+ }