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