goblin-desktop 2.1.2 → 2.1.3

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