design-comuni-plone-theme 11.23.2 → 11.24.1

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 (35) hide show
  1. package/.github/workflows/main.yml +1 -1
  2. package/.github/workflows/npm.yml +1 -1
  3. package/.github/workflows/prs.yml +1 -1
  4. package/.github/workflows/release.yml +1 -1
  5. package/.yarn/cache/{volto-form-block-npm-3.9.2-cb78fb6cd0-a47c5241be.zip → volto-form-block-npm-3.10.0-8cd1c7a976-8ddce2c624.zip} +0 -0
  6. package/.yarn/install-state.gz +0 -0
  7. package/CHANGELOG.md +39 -0
  8. package/Makefile +1 -1
  9. package/RELEASE.md +15 -0
  10. package/locales/de/LC_MESSAGES/volto.po +5 -0
  11. package/locales/en/LC_MESSAGES/volto.po +5 -0
  12. package/locales/es/LC_MESSAGES/volto.po +5 -0
  13. package/locales/fr/LC_MESSAGES/volto.po +5 -0
  14. package/locales/it/LC_MESSAGES/volto.po +5 -0
  15. package/locales/volto.pot +6 -1
  16. package/package.json +3 -3
  17. package/publiccode.yml +2 -2
  18. package/src/components/ItaliaTheme/Blocks/Listing/SimpleCard/Card/SimpleCardDefault.jsx +5 -1
  19. package/src/components/ItaliaTheme/View/PersonaView/PersonaRuolo.jsx +11 -5
  20. package/src/config/italiaConfig.js +1 -0
  21. package/src/customizations/volto/components/manage/Controlpanels/Groups/GroupsControlpanel.jsx +709 -0
  22. package/src/customizations/volto/components/manage/Controlpanels/Groups/RenderGroups.jsx +122 -0
  23. package/src/customizations/volto/components/manage/Diff/DiffField.jsx +2 -3
  24. package/src/customizations/volto/components/manage/Widgets/RecurrenceWidget/EndField.jsx +18 -11
  25. package/src/customizations/volto/components/manage/Widgets/RecurrenceWidget/RecurrenceWidget.jsx +233 -116
  26. package/src/customizations/volto-form-block/components/FormView.jsx +44 -40
  27. package/src/customizations/volto-form-block/components/Sidebar.jsx +4 -1
  28. package/src/customizations/volto-form-block/components/View.jsx +9 -2
  29. package/src/customizations/volto-form-block/formSchema.js +18 -0
  30. package/src/theme/ItaliaTheme/Blocks/common/_searchBlockFilters.scss +10 -4
  31. package/src/theme/ItaliaTheme/Widgets/_reactSelect.scss +5 -2
  32. package/src/theme/ItaliaTheme/_common.scss +5 -3
  33. package/src/theme/_site-variables.scss +1 -1
  34. package/src/theme/bootstrap-override/bootstrap-italia/_focus.scss +5 -2
  35. package/src/theme/bootstrap-override/bootstrap-italia/_headercenter.scss +5 -2
@@ -0,0 +1,709 @@
1
+ // CUSTOMIZATION:
2
+ // - 124: Set AuthenticatedUsers roles as authenticatedRole for all groups
3
+ // - 263: Changed updateGroupRole function to update all checkboxes when changing the AuthenticatedUsers checkboxes
4
+
5
+ /**
6
+ * Users controlpanel container.
7
+ * @module components/manage/Controlpanels/UsersControlpanel
8
+ */
9
+ import {
10
+ createGroup,
11
+ deleteGroup,
12
+ listGroups,
13
+ getControlpanel,
14
+ listRoles,
15
+ updateGroup,
16
+ authenticatedRole,
17
+ } from '@plone/volto/actions';
18
+ import {
19
+ Icon,
20
+ ModalForm,
21
+ Toast,
22
+ Toolbar,
23
+ RenderGroups,
24
+ Pagination,
25
+ Error,
26
+ } from '@plone/volto/components';
27
+ import { Link } from 'react-router-dom';
28
+ import { Helmet, messages } from '@plone/volto/helpers';
29
+ import clearSVG from '@plone/volto/icons/clear.svg';
30
+ import addUserSvg from '@plone/volto/icons/add-user.svg';
31
+ import saveSVG from '@plone/volto/icons/save.svg';
32
+ import ploneSVG from '@plone/volto/icons/plone.svg';
33
+ import { find, map } from 'lodash';
34
+ import PropTypes from 'prop-types';
35
+ import React, { Component } from 'react';
36
+ import { FormattedMessage, injectIntl } from 'react-intl';
37
+ import { Portal } from 'react-portal';
38
+ import { connect } from 'react-redux';
39
+
40
+ import { toast } from 'react-toastify';
41
+ import { bindActionCreators, compose } from 'redux';
42
+ import {
43
+ Confirm,
44
+ Container,
45
+ Button,
46
+ Form,
47
+ Input,
48
+ Segment,
49
+ Table,
50
+ } from 'semantic-ui-react';
51
+
52
+ /**
53
+ * GroupsControlpanel class.
54
+ * @class GroupsControlpanel
55
+ * @extends Component
56
+ */
57
+ class GroupsControlpanel extends Component {
58
+ /**
59
+ * Property types.
60
+ * @property {Object} propTypes Property types.
61
+ * @static
62
+ */
63
+ static propTypes = {
64
+ listRoles: PropTypes.func.isRequired,
65
+ listGroups: PropTypes.func.isRequired,
66
+ pathname: PropTypes.string.isRequired,
67
+ roles: PropTypes.arrayOf(
68
+ PropTypes.shape({
69
+ '@id': PropTypes.string,
70
+ '@type': PropTypes.string,
71
+ id: PropTypes.string,
72
+ }),
73
+ ).isRequired,
74
+ groups: PropTypes.arrayOf(
75
+ PropTypes.shape({
76
+ Title: PropTypes.string,
77
+ Description: PropTypes.string,
78
+ roles: PropTypes.arrayOf(PropTypes.string),
79
+ groupname: PropTypes.string,
80
+ }),
81
+ ).isRequired,
82
+ };
83
+
84
+ /**
85
+ * Constructor
86
+ * @method constructor
87
+ * @param {Object} props Component properties
88
+ * @constructs Sharing
89
+ */
90
+ constructor(props) {
91
+ super(props);
92
+ this.onChangeSearch = this.onChangeSearch.bind(this);
93
+ this.onSearchGroups = this.onSearchGroups.bind(this);
94
+ this.deleteGroup = this.deleteGroup.bind(this);
95
+ this.onDeleteOk = this.onDeleteOk.bind(this);
96
+ this.onDeleteCancel = this.onDeleteCancel.bind(this);
97
+ this.onAddGroupSubmit = this.onAddGroupSubmit.bind(this);
98
+ this.onAddGroupError = this.onAddGroupError.bind(this);
99
+ this.onAddGroupSuccess = this.onAddGroupSuccess.bind(this);
100
+ this.updateGroupRole = this.updateGroupRole.bind(this);
101
+ this.state = {
102
+ search: '',
103
+ isLoading: false,
104
+ addGroupError: '',
105
+ showDelete: false,
106
+ groupToDelete: undefined,
107
+ showAddGroup: false,
108
+ groupEntries: [],
109
+ isClient: false,
110
+ authenticatedRole: props.inheritedRole || [],
111
+ currentPage: 0,
112
+ pageSize: 10,
113
+ };
114
+ }
115
+
116
+ fetchData = async () => {
117
+ await this.props.getControlpanel('usergroup');
118
+ await this.props.listRoles();
119
+ if (!this.props.many_groups) {
120
+ await this.props.listGroups();
121
+ const inheritedRoles = this.props.groups?.find(
122
+ (el) => el.id === 'AuthenticatedUsers',
123
+ )?.roles;
124
+ this.setState({
125
+ groupEntries: this.props.groups,
126
+ authenticatedRole: inheritedRoles,
127
+ });
128
+ }
129
+ };
130
+ /**
131
+ * Component did mount
132
+ * @method componentDidMount
133
+ * @returns {undefined}
134
+ */
135
+ componentDidMount() {
136
+ this.setState({
137
+ isClient: true,
138
+ });
139
+ this.fetchData();
140
+ }
141
+
142
+ UNSAFE_componentWillReceiveProps(nextProps) {
143
+ if (
144
+ (this.props.deleteGroupRequest.loading &&
145
+ nextProps.deleteGroupRequest.loaded) ||
146
+ (this.props.createGroupRequest.loading &&
147
+ nextProps.createGroupRequest.loaded)
148
+ ) {
149
+ this.props.listGroups(this.state.search);
150
+ }
151
+ if (
152
+ this.props.createGroupRequest.loading &&
153
+ nextProps.createGroupRequest.loaded
154
+ ) {
155
+ this.onAddGroupSuccess();
156
+ }
157
+ if (
158
+ this.props.createGroupRequest.loading &&
159
+ nextProps.createGroupRequest.error
160
+ ) {
161
+ this.onAddGroupError(nextProps.createGroupRequest.error);
162
+ }
163
+ if (
164
+ this.props.loadRolesRequest.loading &&
165
+ nextProps.loadRolesRequest.error
166
+ ) {
167
+ this.setState({
168
+ error: nextProps.loadRolesRequest.error,
169
+ });
170
+ }
171
+ }
172
+
173
+ getGroupFromProps(value) {
174
+ return find(this.props.groups, ['@id', value]);
175
+ }
176
+
177
+ /**
178
+ *
179
+ *
180
+ * @param {*} event Event object
181
+ * @memberof GroupsControlpanel
182
+ * @returns {undefined}
183
+ */
184
+ onSearchGroups(event) {
185
+ this.setState({ isLoading: true });
186
+ event.preventDefault();
187
+ this.props
188
+ .listGroups(this.state.search)
189
+ .then(() => {
190
+ this.setState({ isLoading: false });
191
+ })
192
+ .catch((error) => {
193
+ this.setState({ isLoading: false });
194
+ // eslint-disable-next-line no-console
195
+ console.error('Error searching group', error);
196
+ });
197
+ }
198
+
199
+ /**
200
+ * On change search handler
201
+ * @method onChangeSearch
202
+ * @param {object} event Event object.
203
+ * @returns {undefined}
204
+ */
205
+ onChangeSearch(event) {
206
+ this.setState({
207
+ search: event.target.value,
208
+ });
209
+ }
210
+
211
+ /**
212
+ *
213
+ *
214
+ * @param {*} event Event object.
215
+ * @param {*} { value } id (groupname)
216
+ * @memberof GroupsControlpanel
217
+ * @returns {undefined}
218
+ */
219
+ deleteGroup(event, { value }) {
220
+ if (value) {
221
+ this.setState({
222
+ showDelete: true,
223
+ groupToDelete: this.getGroupFromProps(value),
224
+ });
225
+ }
226
+ }
227
+
228
+ /**
229
+ * On delete ok
230
+ * @method onDeleteOk
231
+ * @returns {undefined}
232
+ */
233
+ onDeleteOk() {
234
+ if (this.state.groupToDelete) {
235
+ this.props.deleteGroup(this.state.groupToDelete.id);
236
+ this.setState({
237
+ showDelete: false,
238
+ groupToDelete: undefined,
239
+ });
240
+ }
241
+ }
242
+
243
+ /**
244
+ * On delete cancel
245
+ * @method onDeleteCancel
246
+ * @returns {undefined}
247
+ */
248
+ onDeleteCancel() {
249
+ this.setState({
250
+ showDelete: false,
251
+ itemsToDelete: [],
252
+ });
253
+ }
254
+
255
+ /**
256
+ *
257
+ * @param {*} name
258
+ * @param {*} value
259
+ * @memberof GroupsControlpanel
260
+ */
261
+
262
+ updateGroupRole(name, value) {
263
+ this.setState((prevState) => {
264
+ let updatedAuthenticatedRole;
265
+ // check if checkbox is AuthenticatedUsers
266
+ if (name === 'AuthenticatedUsers') {
267
+ // check if that role is already set
268
+ updatedAuthenticatedRole = prevState.authenticatedRole.includes(value)
269
+ ? // Remove role if unchecked
270
+ prevState.authenticatedRole.filter((role) => role !== value)
271
+ : // Add role if checked
272
+ [...prevState.authenticatedRole, value];
273
+ } else {
274
+ // if not AuthenticatedUser, do not change the authenticated roles
275
+ updatedAuthenticatedRole = prevState.authenticatedRole;
276
+ }
277
+
278
+ // Handle the groupEntries role update
279
+ const updatedGroupEntries = map(prevState.groupEntries, (entry) => {
280
+ if (entry.id === name) {
281
+ if (entry.roles.includes(value)) {
282
+ // If the role is unchecked, remove it from roles
283
+ return {
284
+ ...entry,
285
+ roles: entry.roles.filter((role) => role !== value),
286
+ };
287
+ } else {
288
+ // If the role is checked, add it to roles
289
+ return {
290
+ ...entry,
291
+ roles: [...entry.roles, value],
292
+ };
293
+ }
294
+ } else if (
295
+ name === 'AuthenticatedUsers' &&
296
+ !prevState.authenticatedRole.includes(value)
297
+ ) {
298
+ // If 'AuthenticatedUsers' is unchecked, reset the other group roles
299
+ return {
300
+ ...entry,
301
+ roles: entry.roles.filter((role) => role !== value), // Remove the role from all other groups
302
+ };
303
+ }
304
+
305
+ return entry; // For other entries, return unchanged
306
+ });
307
+
308
+ return {
309
+ groupEntries: updatedGroupEntries,
310
+ authenticatedRole: updatedAuthenticatedRole,
311
+ };
312
+ });
313
+ }
314
+ /**
315
+ * @param {*} event
316
+ * @memberof GroupsControlpanel
317
+ */
318
+ updateGroupRoleSubmit = (e) => {
319
+ e.stopPropagation();
320
+ this.state.groupEntries.forEach((item) => {
321
+ this.props.updateGroup(item.id, item);
322
+ });
323
+ this.props.authenticatedRole(this.state.authenticatedRole);
324
+ toast.success(
325
+ <Toast
326
+ success
327
+ title={this.props.intl.formatMessage(messages.success)}
328
+ content={this.props.intl.formatMessage(messages.updateGroups)}
329
+ />,
330
+ );
331
+ };
332
+ /**
333
+ *
334
+ *
335
+ * @param {object} data Form data from the ModalForm.
336
+ * @param {func} callback to set new form data in the ModalForm
337
+ * @memberof GroupsControlpanel
338
+ * @returns {undefined}
339
+ */
340
+ onAddGroupSubmit(data, callback) {
341
+ this.props.createGroup(data);
342
+ this.setState({
343
+ addGroupSetFormDataCallback: callback,
344
+ });
345
+ }
346
+
347
+ /**
348
+ * Handle Errors after createGroup()
349
+ *
350
+ * @param {*} error object. Requires the property .message
351
+ * @memberof GroupsControlpanel
352
+ * @returns {undefined}
353
+ */
354
+ onAddGroupError(error) {
355
+ this.setState({
356
+ addGroupError: error.response.body.message,
357
+ });
358
+ }
359
+
360
+ componentDidUpdate(prevProps, prevState) {
361
+ if (this.props.groups !== prevProps.groups) {
362
+ this.setState({
363
+ groupEntries: this.props.groups,
364
+ });
365
+ }
366
+ }
367
+
368
+ /**
369
+ * Handle Success after createGroup()
370
+ *
371
+ * @memberof GroupsControlpanel
372
+ * @returns {undefined}
373
+ */
374
+ onAddGroupSuccess() {
375
+ this.state.addGroupSetFormDataCallback({});
376
+ this.setState({
377
+ showAddGroup: false,
378
+ addGroupError: undefined,
379
+ addGroupSetFormDataCallback: undefined,
380
+ });
381
+ toast.success(
382
+ <Toast
383
+ success
384
+ title={this.props.intl.formatMessage(messages.success)}
385
+ content={this.props.intl.formatMessage(messages.groupCreated)}
386
+ />,
387
+ );
388
+ }
389
+
390
+ /**
391
+ * On change page
392
+ * @method onChangePage
393
+ * @param {object} event Event object.
394
+ * @param {string} value Page value.
395
+ * @returns {undefined}
396
+ */
397
+ onChangePage = (event, { value }) => {
398
+ this.setState({
399
+ currentPage: value,
400
+ });
401
+ };
402
+
403
+ /**
404
+ * Render method.
405
+ * @method render
406
+ * @returns {string} Markup for the component.
407
+ */
408
+ render() {
409
+ if (this.state.error) {
410
+ return <Error error={this.state.error} />;
411
+ }
412
+ /*let fullnameToDelete = this.state.groupToDelete
413
+ ? this.state.groupToDelete.fullname
414
+ : '';*/
415
+ let groupNameToDelete = this.state.groupToDelete
416
+ ? this.state.groupToDelete.id
417
+ : '';
418
+
419
+ return (
420
+ <Container className="users-control-panel">
421
+ <Helmet title={this.props.intl.formatMessage(messages.groups)} />
422
+ <div className="container">
423
+ <Confirm
424
+ open={this.state.showDelete}
425
+ header={this.props.intl.formatMessage(
426
+ messages.deleteGroupConfirmTitle,
427
+ )}
428
+ content={
429
+ <div className="content">
430
+ <ul className="content">
431
+ <FormattedMessage
432
+ id="Do you really want to delete the group {groupname}?"
433
+ defaultMessage="Do you really want to delete the group {groupname}?"
434
+ values={{
435
+ groupname: <b>{groupNameToDelete}</b>,
436
+ }}
437
+ />
438
+ </ul>
439
+ </div>
440
+ }
441
+ onCancel={this.onDeleteCancel}
442
+ onConfirm={this.onDeleteOk}
443
+ size={null}
444
+ />
445
+ {this.state.showAddGroup ? (
446
+ <ModalForm
447
+ open={this.state.showAddGroup}
448
+ className="modal"
449
+ onSubmit={this.onAddGroupSubmit}
450
+ submitError={this.state.addGroupError}
451
+ onCancel={() => this.setState({ showAddGroup: false })}
452
+ title={this.props.intl.formatMessage(messages.addGroupsFormTitle)}
453
+ loading={this.props.createGroupRequest.loading}
454
+ schema={{
455
+ fieldsets: [
456
+ {
457
+ id: 'default',
458
+ title: 'FIXME: Group Data',
459
+ fields: [
460
+ 'title',
461
+ 'description',
462
+ 'groupname',
463
+ 'email',
464
+ 'roles',
465
+ ],
466
+ },
467
+ ],
468
+ properties: {
469
+ title: {
470
+ title: this.props.intl.formatMessage(
471
+ messages.addGroupsFormTitleTitle,
472
+ ),
473
+ type: 'string',
474
+ description: '',
475
+ },
476
+ description: {
477
+ title: this.props.intl.formatMessage(
478
+ messages.addGroupsFormDescriptionTitle,
479
+ ),
480
+ type: 'string',
481
+ description: '',
482
+ },
483
+ groupname: {
484
+ title: this.props.intl.formatMessage(
485
+ messages.addGroupsFormGroupNameTitle,
486
+ ),
487
+ type: 'string',
488
+ description:
489
+ 'A unique identifier for the group. Can not be changed after creation.',
490
+ },
491
+ email: {
492
+ title: this.props.intl.formatMessage(
493
+ messages.addGroupsFormEmailTitle,
494
+ ),
495
+ type: 'string',
496
+ description: '',
497
+ widget: 'email',
498
+ },
499
+ roles: {
500
+ title: this.props.intl.formatMessage(
501
+ messages.addGroupsFormRolesTitle,
502
+ ),
503
+ type: 'array',
504
+ choices: this.props.roles.map((role) => [
505
+ role.id,
506
+ role.title,
507
+ ]),
508
+ noValueOption: false,
509
+ description: '',
510
+ },
511
+ },
512
+ required: ['groupname'],
513
+ }}
514
+ />
515
+ ) : null}
516
+ </div>
517
+ <Segment.Group raised>
518
+ <Segment className="primary">
519
+ <FormattedMessage id="Groups" defaultMessage="Groups" />
520
+ </Segment>
521
+ <Segment secondary>
522
+ <FormattedMessage
523
+ id="Groups are logical collections of users, such as departments and business units. Groups are not directly related to permissions on a global level, you normally use Roles for that - and let certain Groups have a particular role. The symbol{plone_svg}indicates a role inherited from membership in another group."
524
+ defaultMessage="Groups are logical collections of users, such as departments and business units. Groups are not directly related to permissions on a global level, you normally use Roles for that - and let certain Groups have a particular role. The symbol{plone_svg}indicates a role inherited from membership in another group."
525
+ values={{
526
+ plone_svg: (
527
+ <Icon
528
+ name={ploneSVG}
529
+ size="20px"
530
+ color="#007EB1"
531
+ title={'plone-svg'}
532
+ />
533
+ ),
534
+ }}
535
+ />
536
+ </Segment>
537
+ <Segment>
538
+ <Form onSubmit={this.onSearchGroups}>
539
+ <Form.Field>
540
+ <Input
541
+ name="SearchableText"
542
+ action={{
543
+ icon: 'search',
544
+ loading: this.state.isLoading,
545
+ disabled: this.state.isLoading,
546
+ }}
547
+ placeholder={this.props.intl.formatMessage(
548
+ messages.searchGroups,
549
+ )}
550
+ onChange={this.onChangeSearch}
551
+ id="group-search-input"
552
+ />
553
+ </Form.Field>
554
+ </Form>
555
+ </Segment>
556
+ <Form>
557
+ <div className="table">
558
+ {((this.props.many_groups &&
559
+ this.state.groupEntries.length > 0) ||
560
+ !this.props.many_groups) && (
561
+ <Table padded striped attached unstackable>
562
+ <Table.Header>
563
+ <Table.Row>
564
+ <Table.HeaderCell>
565
+ <FormattedMessage
566
+ id="Groupname"
567
+ defaultMessage="Groupname"
568
+ />
569
+ </Table.HeaderCell>
570
+ {this.props.roles.map((role) => (
571
+ <Table.HeaderCell key={role.id}>
572
+ {role.title}
573
+ </Table.HeaderCell>
574
+ ))}
575
+ <Table.HeaderCell>
576
+ <FormattedMessage
577
+ id="Actions"
578
+ defaultMessage="Actions"
579
+ />
580
+ </Table.HeaderCell>
581
+ </Table.Row>
582
+ </Table.Header>
583
+ <Table.Body data-group="groups">
584
+ {this.state.groupEntries
585
+ .slice(
586
+ this.state.currentPage * 10,
587
+ this.state.pageSize * (this.state.currentPage + 1),
588
+ )
589
+ .map((group) => (
590
+ <RenderGroups
591
+ key={group.id}
592
+ onDelete={this.deleteGroup}
593
+ roles={this.props.roles}
594
+ group={group}
595
+ updateGroups={this.updateGroupRole}
596
+ inheritedRole={this.state.authenticatedRole}
597
+ />
598
+ ))}
599
+ </Table.Body>
600
+ </Table>
601
+ )}
602
+ {this.state.groupEntries.length === 0 && this.state.search && (
603
+ <Segment>
604
+ {this.props.intl.formatMessage(messages.groupSearchNoResults)}
605
+ </Segment>
606
+ )}
607
+ </div>
608
+ <div className="contents-pagination">
609
+ <Pagination
610
+ current={this.state.currentPage}
611
+ total={Math.ceil(
612
+ this.state.groupEntries?.length / this.state.pageSize,
613
+ )}
614
+ onChangePage={this.onChangePage}
615
+ />
616
+ </div>
617
+ </Form>
618
+ </Segment.Group>
619
+ {this.state.isClient && (
620
+ <Portal node={document.getElementById('toolbar')}>
621
+ <Toolbar
622
+ pathname={this.props.pathname}
623
+ hideDefaultViewButtons
624
+ inner={
625
+ <>
626
+ <Button
627
+ id="toolbar-save"
628
+ className="save"
629
+ aria-label={this.props.intl.formatMessage(messages.save)}
630
+ onClick={this.updateGroupRoleSubmit}
631
+ loading={this.props.createGroupRequest.loading}
632
+ >
633
+ <Icon
634
+ name={saveSVG}
635
+ className="circled"
636
+ size="30px"
637
+ title={this.props.intl.formatMessage(messages.save)}
638
+ />
639
+ </Button>
640
+ <Link to="/controlpanel" className="cancel">
641
+ <Icon
642
+ name={clearSVG}
643
+ className="circled"
644
+ aria-label={this.props.intl.formatMessage(
645
+ messages.cancel,
646
+ )}
647
+ size="30px"
648
+ title={this.props.intl.formatMessage(messages.cancel)}
649
+ />
650
+ </Link>
651
+ <Button
652
+ id="toolbar-add"
653
+ aria-label={this.props.intl.formatMessage(
654
+ messages.addGroupsButtonTitle,
655
+ )}
656
+ onClick={() => {
657
+ this.setState({ showAddGroup: true });
658
+ }}
659
+ loading={this.props.createGroupRequest.loading}
660
+ >
661
+ <Icon
662
+ name={addUserSvg}
663
+ size="45px"
664
+ color="#826A6A"
665
+ title={this.props.intl.formatMessage(
666
+ messages.addGroupsButtonTitle,
667
+ )}
668
+ />
669
+ </Button>
670
+ </>
671
+ }
672
+ />
673
+ </Portal>
674
+ )}
675
+ </Container>
676
+ );
677
+ }
678
+ }
679
+
680
+ export default compose(
681
+ injectIntl,
682
+ connect(
683
+ (state, props) => ({
684
+ roles: state.roles.roles,
685
+ groups: state.groups.groups,
686
+ description: state.description,
687
+ many_users: state.controlpanels?.controlpanel?.data?.many_users,
688
+ many_groups: state.controlpanels?.controlpanel?.data?.many_groups,
689
+ pathname: props.location.pathname,
690
+ deleteGroupRequest: state.groups.delete,
691
+ createGroupRequest: state.groups.create,
692
+ loadRolesRequest: state.roles,
693
+ inheritedRole: state.authRole.authenticatedRole,
694
+ }),
695
+ (dispatch) =>
696
+ bindActionCreators(
697
+ {
698
+ listRoles,
699
+ listGroups,
700
+ deleteGroup,
701
+ getControlpanel,
702
+ createGroup,
703
+ updateGroup,
704
+ authenticatedRole,
705
+ },
706
+ dispatch,
707
+ ),
708
+ ),
709
+ )(GroupsControlpanel);