@rh-support/manage 2.5.237 → 2.5.239

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 (44) hide show
  1. package/lib/esm/ManageTabs.d.ts.map +1 -1
  2. package/lib/esm/ManageTabs.js +17 -2
  3. package/lib/esm/Routes.d.ts +1 -0
  4. package/lib/esm/Routes.d.ts.map +1 -1
  5. package/lib/esm/Routes.js +6 -0
  6. package/lib/esm/components/Groups/AddMembersModal/AddMembersModal.d.ts +12 -0
  7. package/lib/esm/components/Groups/AddMembersModal/AddMembersModal.d.ts.map +1 -0
  8. package/lib/esm/components/Groups/AddMembersModal/AddMembersModal.js +81 -0
  9. package/lib/esm/components/Groups/AddMembersModal/index.d.ts +2 -0
  10. package/lib/esm/components/Groups/AddMembersModal/index.d.ts.map +1 -0
  11. package/lib/esm/components/Groups/AddMembersModal/index.js +1 -0
  12. package/lib/esm/components/Groups/GroupSelect/GroupSelect.d.ts +1 -0
  13. package/lib/esm/components/Groups/GroupSelect/GroupSelect.d.ts.map +1 -1
  14. package/lib/esm/components/Groups/GroupSelect/GroupSelect.js +20 -5
  15. package/lib/esm/components/Groups/ManageGroupUsers/ManageGroupUsers.d.ts.map +1 -1
  16. package/lib/esm/components/Groups/ManageGroupUsers/ManageGroupUsers.js +17 -8
  17. package/lib/esm/components/ManageCaseLabels/AddEditCaseLabelModal.d.ts +19 -0
  18. package/lib/esm/components/ManageCaseLabels/AddEditCaseLabelModal.d.ts.map +1 -0
  19. package/lib/esm/components/ManageCaseLabels/AddEditCaseLabelModal.js +83 -0
  20. package/lib/esm/components/ManageCaseLabels/ManageCaseLabelsTab.d.ts +3 -0
  21. package/lib/esm/components/ManageCaseLabels/ManageCaseLabelsTab.d.ts.map +1 -0
  22. package/lib/esm/components/ManageCaseLabels/ManageCaseLabelsTab.js +208 -0
  23. package/lib/esm/components/ManageCaseLabels/index.d.ts +2 -0
  24. package/lib/esm/components/ManageCaseLabels/index.d.ts.map +1 -0
  25. package/lib/esm/components/ManageCaseLabels/index.js +1 -0
  26. package/lib/esm/components/ManageCaseLabels/types.d.ts +6 -0
  27. package/lib/esm/components/ManageCaseLabels/types.d.ts.map +1 -0
  28. package/lib/esm/components/ManageCaseLabels/types.js +1 -0
  29. package/lib/esm/components/ManagePreferences/sections/PreferencesCaseCreate.d.ts.map +1 -1
  30. package/lib/esm/components/ManagePreferences/sections/PreferencesCaseCreate.js +20 -41
  31. package/lib/esm/constants/GroupSelectActions.d.ts +2 -1
  32. package/lib/esm/constants/GroupSelectActions.d.ts.map +1 -1
  33. package/lib/esm/constants/GroupSelectActions.js +1 -0
  34. package/lib/esm/hooks/useCaseGroupGraphQL.d.ts +6 -1
  35. package/lib/esm/hooks/useCaseGroupGraphQL.d.ts.map +1 -1
  36. package/lib/esm/hooks/useCaseGroupGraphQL.js +42 -37
  37. package/lib/esm/hooks/useCaseLabelGraphQL.d.ts +26 -0
  38. package/lib/esm/hooks/useCaseLabelGraphQL.d.ts.map +1 -0
  39. package/lib/esm/hooks/useCaseLabelGraphQL.js +99 -0
  40. package/lib/esm/hooks/useCurrentUserContact.d.ts +8 -0
  41. package/lib/esm/hooks/useCurrentUserContact.d.ts.map +1 -0
  42. package/lib/esm/hooks/useCurrentUserContact.js +74 -0
  43. package/lib/esm/scss/_main.scss +72 -0
  44. package/package.json +6 -6
@@ -1 +1 @@
1
- {"version":3,"file":"ManageTabs.d.ts","sourceRoot":"","sources":["../../src/ManageTabs.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,EAAE,mBAAmB,EAAa,MAAM,kBAAkB,CAAC;AAelE,UAAU,MAAM;IACZ,UAAU,EAAE,mBAAmB,CAAC,EAAE,CAAC,CAAC;IACpC,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,qBA6MvC"}
1
+ {"version":3,"file":"ManageTabs.d.ts","sourceRoot":"","sources":["../../src/ManageTabs.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAExC,OAAO,EAAE,mBAAmB,EAAa,MAAM,kBAAkB,CAAC;AAiBlE,UAAU,MAAM;IACZ,UAAU,EAAE,mBAAmB,CAAC,EAAE,CAAC,CAAC;IACpC,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,qBAgOvC"}
@@ -1,5 +1,5 @@
1
1
  import { Tab, TabContent, Tabs, TabsComponent, TabTitleText } from '@patternfly/react-core';
2
- import { ErrorBoundary, useDocumentTitle } from '@rh-support/components';
2
+ import { ErrorBoundary, LoadingIndicator, useDocumentTitle } from '@rh-support/components';
3
3
  import { ability, resourceActions, resources } from '@rh-support/user-permissions';
4
4
  import map from 'lodash/map';
5
5
  import React, { useState } from 'react';
@@ -8,6 +8,7 @@ import { useParams } from 'react-router-dom';
8
8
  import { ConfigsTable } from './components/Configs/ConfigsTable';
9
9
  import { ManageGroupUsers } from './components/Groups/ManageGroupUsers';
10
10
  import { ManageGroupedBookmarkedAccountsTab } from './components/ManageBookmarkedAccountsTab';
11
+ import { ManageCaseLabelsTab } from './components/ManageCaseLabels';
11
12
  import { ExpiredContentManagement } from './components/ManageExpiredContents/ExpiredContentManagement';
12
13
  // TODO: Uncomment when partnerships tab is re-implemented
13
14
  // import { ManagePartnerships } from './components/ManagePartnerships/index';
@@ -16,13 +17,15 @@ import { TagsManagement } from './components/ManageTags';
16
17
  // import { NotificationEmails } from './components/NotificationEmails/index';
17
18
  import { TopContentManagement } from './components/TopContentManagement';
18
19
  import { PageTitle } from './constants/pageTitle';
20
+ import { useCurrentUserContact } from './hooks/useCurrentUserContact';
19
21
  import { Routes } from './Routes';
20
22
  export function ManageTabs(props) {
21
23
  var _a, _b;
22
24
  const [activeTabKey, setActiveTabKey] = useState(0);
23
25
  const { activeTab } = useParams();
24
26
  const { t } = useTranslation();
25
- const { groupsRoute, bookmarkedAccountsRoute, topContentRoute,
27
+ const { loading: contactLoading } = useCurrentUserContact();
28
+ const { groupsRoute, bookmarkedAccountsRoute, caseLabelsRoute, topContentRoute,
26
29
  // notificationEmailsRoute,
27
30
  preferencesRoute, configsRoute,
28
31
  // partnershipsRoute, // TODO: Uncomment when partnerships tab is re-implemented
@@ -41,6 +44,10 @@ export function ManageTabs(props) {
41
44
  // CaseDetailsFields.CASE_DETAILS_SEND_NOTIFICATIONS
42
45
  // );
43
46
  const canViewConfigsTab = ability.can(resourceActions.READ, resources.APP_INTERNAL_CONFIGS);
47
+ if (contactLoading) {
48
+ return (React.createElement("div", { className: "grid-main manage" },
49
+ React.createElement(LoadingIndicator, { show: true, size: "sm" })));
50
+ }
44
51
  const tabsToRender = [];
45
52
  canManageBookmarkAccounts &&
46
53
  tabsToRender.push({
@@ -60,6 +67,14 @@ export function ManageTabs(props) {
60
67
  component: React.createElement(ManageGroupUsers, null),
61
68
  id: 'manage-groups',
62
69
  });
70
+ tabsToRender.push({
71
+ title: 'Case labels',
72
+ key: 'case-labels-tab',
73
+ 'data-tracking-id': 'manage-case-labels-tab',
74
+ routePath: caseLabelsRoute,
75
+ component: React.createElement(ManageCaseLabelsTab, null),
76
+ id: 'manage-case-labels',
77
+ });
63
78
  /* TODO: Re-enable notification emails tab when ready
64
79
  canViewEmailNotificationTab &&
65
80
  canAddCustomNotification &&
@@ -11,6 +11,7 @@ interface ManageRoutes {
11
11
  };
12
12
  getPathsArray: () => string[];
13
13
  groups: Route;
14
+ caseLabels: Route;
14
15
  savedSearches: Route;
15
16
  topContent: Route;
16
17
  expiredContent: Route;
@@ -1 +1 @@
1
- {"version":3,"file":"Routes.d.ts","sourceRoot":"","sources":["../../src/Routes.ts"],"names":[],"mappings":"AAAA,UAAU,KAAK;IACX,QAAQ,CAAC,OAAO,EAAE,MAAM,MAAM,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,YAAY;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,KAAK,CAAC;IAC1B,eAAe,EAAE,KAAK,CAAC;IACvB,QAAQ,EAAE,MAAM;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IAC1C,aAAa,EAAE,MAAM,MAAM,EAAE,CAAC;IAC9B,MAAM,EAAE,KAAK,CAAC;IACd,aAAa,EAAE,KAAK,CAAC;IACrB,UAAU,EAAE,KAAK,CAAC;IAClB,cAAc,EAAE,KAAK,CAAC;IACtB,WAAW,EAAE,KAAK,CAAC;IACnB,WAAW,EAAE,KAAK,CAAC;IACnB,kBAAkB,EAAE,KAAK,CAAC;IAC1B,OAAO,EAAE,KAAK,CAAC;IACf,YAAY,EAAE,KAAK,CAAC;CACvB;AAED,eAAO,MAAM,MAAM,EAAE,YAqFpB,CAAC"}
1
+ {"version":3,"file":"Routes.d.ts","sourceRoot":"","sources":["../../src/Routes.ts"],"names":[],"mappings":"AAAA,UAAU,KAAK;IACX,QAAQ,CAAC,OAAO,EAAE,MAAM,MAAM,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AAED,UAAU,YAAY;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,KAAK,CAAC;IAC1B,eAAe,EAAE,KAAK,CAAC;IACvB,QAAQ,EAAE,MAAM;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IAC1C,aAAa,EAAE,MAAM,MAAM,EAAE,CAAC;IAC9B,MAAM,EAAE,KAAK,CAAC;IACd,UAAU,EAAE,KAAK,CAAC;IAClB,aAAa,EAAE,KAAK,CAAC;IACrB,UAAU,EAAE,KAAK,CAAC;IAClB,cAAc,EAAE,KAAK,CAAC;IACtB,WAAW,EAAE,KAAK,CAAC;IACnB,WAAW,EAAE,KAAK,CAAC;IACnB,kBAAkB,EAAE,KAAK,CAAC;IAC1B,OAAO,EAAE,KAAK,CAAC;IACf,YAAY,EAAE,KAAK,CAAC;CACvB;AAED,eAAO,MAAM,MAAM,EAAE,YA2FpB,CAAC"}
package/lib/esm/Routes.js CHANGED
@@ -12,6 +12,12 @@ export const Routes = {
12
12
  return getPath(this);
13
13
  },
14
14
  },
15
+ caseLabels: {
16
+ path: 'case-labels',
17
+ getPath() {
18
+ return getPath(this);
19
+ },
20
+ },
15
21
  fileDiagnostics: {
16
22
  path: 'file-diagnostics',
17
23
  getPath() {
@@ -0,0 +1,12 @@
1
+ import { ICaseGroup } from '@cee-eng/hydrajs/@types/api/public/caseGroups';
2
+ import React from 'react';
3
+ import type { IGroupUserWithContactId } from '../../../utils/caseGroupMapper';
4
+ interface IProps {
5
+ readonly group: ICaseGroup;
6
+ readonly existingMembers: IGroupUserWithContactId[];
7
+ readonly onSuccess: () => void;
8
+ readonly onClose: () => void;
9
+ }
10
+ export declare function AddMembersModal({ group, existingMembers, onSuccess, onClose }: IProps): React.JSX.Element;
11
+ export {};
12
+ //# sourceMappingURL=AddMembersModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AddMembersModal.d.ts","sourceRoot":"","sources":["../../../../../src/components/Groups/AddMembersModal/AddMembersModal.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,+CAA+C,CAAC;AAM3E,OAAO,KAAgC,MAAM,OAAO,CAAC;AAIrD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAE9E,UAAU,MAAM;IACZ,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,uBAAuB,EAAE,CAAC;IACpD,QAAQ,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;CAChC;AAED,wBAAgB,eAAe,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,MAAM,qBAyGrF"}
@@ -0,0 +1,81 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { Button, Modal, ModalBody, ModalFooter, ModalHeader, ModalVariant } from '@patternfly/react-core';
11
+ import { CaseContactsSelectorExternal, ToastNotification } from '@rh-support/components';
12
+ import { useGlobalStateContext } from '@rh-support/react-context';
13
+ import React, { useCallback, useState } from 'react';
14
+ import { useTranslation } from 'react-i18next';
15
+ import { useCreateCaseGroupMember } from '../../../hooks/useCaseGroupGraphQL';
16
+ export function AddMembersModal({ group, existingMembers, onSuccess, onClose }) {
17
+ var _a, _b;
18
+ const { t } = useTranslation();
19
+ const { globalMetadataState: { loggedInUserRights, loggedInUsersAccount }, } = useGlobalStateContext();
20
+ const { createMember, loading: createLoading } = useCreateCaseGroupMember();
21
+ const [selectedContacts, setSelectedContacts] = useState([]);
22
+ const [isSubmitting, setIsSubmitting] = useState(false);
23
+ const accountNumber = (_b = (_a = loggedInUsersAccount === null || loggedInUsersAccount === void 0 ? void 0 : loggedInUsersAccount.data) === null || _a === void 0 ? void 0 : _a.accountNumber) !== null && _b !== void 0 ? _b : '';
24
+ const contactSsoUsername = '';
25
+ const contactsToExclude = existingMembers.map((m) => ({
26
+ ssoUsername: m.ssoUsername,
27
+ firstName: m.firstName,
28
+ lastName: m.lastName,
29
+ }));
30
+ const contactFilter = useCallback((contact) => !contact.isOrgAdmin, []);
31
+ const onChange = useCallback((contacts) => {
32
+ if (!(contacts === null || contacts === void 0 ? void 0 : contacts.length))
33
+ return;
34
+ const contact = contacts[0];
35
+ if (!('ssoUsername' in contact))
36
+ return;
37
+ setSelectedContacts((prev) => {
38
+ const exists = prev.some((c) => c.ssoUsername === contact.ssoUsername);
39
+ return exists ? prev.filter((c) => c.ssoUsername !== contact.ssoUsername) : [...prev, contact];
40
+ });
41
+ }, []);
42
+ const onSubmit = () => __awaiter(this, void 0, void 0, function* () {
43
+ const validContacts = selectedContacts.filter((contact) => contact.id);
44
+ if (validContacts.length === 0)
45
+ return;
46
+ setIsSubmitting(true);
47
+ try {
48
+ const results = yield Promise.allSettled(validContacts.map((contact) => createMember(contact.id, group.groupNum)));
49
+ const succeeded = results.filter((r) => r.status === 'fulfilled');
50
+ const failed = results.filter((r) => r.status === 'rejected');
51
+ if (succeeded.length > 0) {
52
+ ToastNotification.addSuccessMessage(t('{{count}} people have been successfully added.', { count: succeeded.length }));
53
+ onSuccess();
54
+ }
55
+ if (failed.length > 0) {
56
+ const firstError = failed[0].reason;
57
+ const message = String((firstError === null || firstError === void 0 ? void 0 : firstError.message) || '');
58
+ if (message.includes('Organization Administrator')) {
59
+ ToastNotification.addDangerMessage(t('Organization Administrators cannot be added as case group members'));
60
+ }
61
+ else if (message.includes('insufficient access rights')) {
62
+ ToastNotification.addDangerMessage(t('You do not have permission to add members to this group'));
63
+ }
64
+ else {
65
+ ToastNotification.addDangerMessage(t('There was an error adding members to this group'));
66
+ }
67
+ }
68
+ onClose();
69
+ }
70
+ finally {
71
+ setIsSubmitting(false);
72
+ }
73
+ });
74
+ return (React.createElement(Modal, { variant: ModalVariant.medium, isOpen: true, onClose: onClose, "aria-label": t('Add members to group') },
75
+ React.createElement(ModalHeader, { title: t('Add members to {{groupName}}', { groupName: group.name }) }),
76
+ React.createElement(ModalBody, null,
77
+ React.createElement(CaseContactsSelectorExternal, { id: "add-members-contact-select", selectedAccountNumber: accountNumber, loggedInUserRights: loggedInUserRights.data, contactSsoUsername: contactSsoUsername, contactsToExclude: [...contactsToExclude, ...selectedContacts], contactFilter: contactFilter, selected: selectedContacts, onChange: onChange, onContactEnriched: () => { }, multiple: true, clearButton: true, isDisabled: isSubmitting, placeholder: t('Search by name or SSO username') })),
78
+ React.createElement(ModalFooter, null,
79
+ React.createElement(Button, { key: "confirm", variant: "primary", onClick: onSubmit, isDisabled: selectedContacts.length === 0 || isSubmitting || createLoading, isLoading: isSubmitting, spinnerAriaValueText: isSubmitting ? t('Adding members') : undefined }, t('Add')),
80
+ React.createElement(Button, { key: "cancel", variant: "link", onClick: onClose, isDisabled: isSubmitting }, t('Cancel')))));
81
+ }
@@ -0,0 +1,2 @@
1
+ export * from './AddMembersModal';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/Groups/AddMembersModal/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1 @@
1
+ export * from './AddMembersModal';
@@ -3,6 +3,7 @@ import React from 'react';
3
3
  interface IProps {
4
4
  isDisabled?: boolean;
5
5
  onSelect: (group: ICaseGroup | null) => void;
6
+ onAddMembers?: () => void;
6
7
  }
7
8
  export declare const GroupSelect: (props: IProps) => React.JSX.Element;
8
9
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"GroupSelect.d.ts","sourceRoot":"","sources":["../../../../../src/components/Groups/GroupSelect/GroupSelect.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,+CAA+C,CAAC;AAsC3E,OAAO,KAAmD,MAAM,OAAO,CAAC;AAMxE,UAAU,MAAM;IACZ,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;CAChD;AAKD,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,sBA4kBxC,CAAC"}
1
+ {"version":3,"file":"GroupSelect.d.ts","sourceRoot":"","sources":["../../../../../src/components/Groups/GroupSelect/GroupSelect.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,+CAA+C,CAAC;AAsC3E,OAAO,KAAmD,MAAM,OAAO,CAAC;AAMxE,UAAU,MAAM;IACZ,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAKD,eAAO,MAAM,WAAW,GAAI,OAAO,MAAM,sBAmmBxC,CAAC"}
@@ -45,16 +45,24 @@ export const GroupSelect = (props) => {
45
45
  const { renameGroup: renameGroupMutation, loading: renameLoading } = useRenameCaseGroup();
46
46
  const { deleteGroup: deleteGroupMutation, loading: deleteLoading } = useDeleteCaseGroup();
47
47
  const hasFetchedFromGraphQL = useRef(false);
48
- // Always fetch groups from GraphQL — never rely on potentially stale Hydra data
48
+ const [groupsFetchCompleted, setGroupsFetchCompleted] = useState(false);
49
+ // Always fetch groups from GraphQL — never rely on potentially stale Hydra data.
50
+ // Auto-select only after this settles so members can load against real group data.
49
51
  useEffect(() => {
50
- if (!hasFetchedFromGraphQL.current && apolloClient && accountId) {
51
- hasFetchedFromGraphQL.current = true;
52
- fetchCaseGroupsForSSO(dispatch, loggedInUserJwtToken.preferred_username, apolloClient, accountId);
52
+ if (!apolloClient || !accountId) {
53
+ setGroupsFetchCompleted(true);
54
+ return;
53
55
  }
56
+ if (hasFetchedFromGraphQL.current)
57
+ return;
58
+ hasFetchedFromGraphQL.current = true;
59
+ Promise.resolve(fetchCaseGroupsForSSO(dispatch, loggedInUserJwtToken.preferred_username, apolloClient, accountId)).finally(() => setGroupsFetchCompleted(true));
54
60
  }, [dispatch, loggedInUserJwtToken.preferred_username, apolloClient, accountId]);
55
61
  // Set default group once groups are loaded
56
62
  useEffect(() => {
57
63
  var _a, _b;
64
+ if (!groupsFetchCompleted || caseGroups.isFetching)
65
+ return;
58
66
  if (isEmpty(selectedGroup) && !isEmpty(caseGroups) && ((_a = caseGroups === null || caseGroups === void 0 ? void 0 : caseGroups.data) === null || _a === void 0 ? void 0 : _a.length) > 0) {
59
67
  const defaultCaseGroup = find(caseGroups === null || caseGroups === void 0 ? void 0 : caseGroups.data, (g) => g.isDefault);
60
68
  const newSelectedGroup = (defaultCaseGroup === null || defaultCaseGroup === void 0 ? void 0 : defaultCaseGroup.groupNum) === '-1'
@@ -66,7 +74,7 @@ export const GroupSelect = (props) => {
66
74
  }
67
75
  }
68
76
  // eslint-disable-next-line react-hooks/exhaustive-deps
69
- }, [caseGroups]);
77
+ }, [caseGroups, groupsFetchCompleted]);
70
78
  // handle dropdown open/close toggle
71
79
  const [isOpen, setIsOpen] = useState(false);
72
80
  // handle group selection
@@ -94,6 +102,7 @@ export const GroupSelect = (props) => {
94
102
  // Group actions dropdown state
95
103
  const hasGroupPermissions = ability.can(resourceActions.CREATE, resources.CASE_GROUPS);
96
104
  const [showDropdown, setShowDropdown] = useState(hasGroupPermissions);
105
+ useEffect(() => setShowDropdown(hasGroupPermissions), [hasGroupPermissions]);
97
106
  const [showInput, setShowInput] = useState(false);
98
107
  const [isDropdownOpen, setIsDropdownOpen] = useState(false);
99
108
  const toggleIsDropdownOpen = () => setIsDropdownOpen(!isDropdownOpen);
@@ -310,11 +319,17 @@ export const GroupSelect = (props) => {
310
319
  setAction('');
311
320
  setInput(selectedGroup);
312
321
  };
322
+ const handleAddMembers = () => {
323
+ var _a;
324
+ closeDropdown();
325
+ (_a = props.onAddMembers) === null || _a === void 0 ? void 0 : _a.call(props);
326
+ };
313
327
  const dropdownItems = [
314
328
  React.createElement(MenuItem, { className: "create-group-action", isDisabled: !ability.can(resourceActions.CREATE, resources.CASE_GROUPS), key: "create", onClick: onActionFocus(actions.create), "data-tracking-id": "manage-group-create" }, t('Create')),
315
329
  React.createElement(Divider, { component: "li" }),
316
330
  React.createElement(MenuItem, { className: "create-group-action", isDisabled: !selectedGroup || !ability.can(resourceActions.UPDATE, resources.CASE_GROUPS), key: "rename", onClick: onActionFocus(actions.rename), "data-tracking-id": "manage-group-rename" }, t('Rename')),
317
331
  React.createElement(MenuItem, { className: "create-group-action", isDisabled: !selectedGroup || !ability.can(resourceActions.CREATE, resources.CASE_GROUPS), key: "duplicate", onClick: onActionFocus(actions.duplicate), "data-tracking-id": "manage-group-duplicate" }, t('Duplicate')),
332
+ React.createElement(MenuItem, { className: "create-group-action", isDisabled: !selectedGroup || !ability.can(resourceActions.CREATE, resources.CASE_GROUPS), key: "add-members", onClick: handleAddMembers, "data-tracking-id": "manage-group-add-members" }, t('Add members')),
318
333
  React.createElement(MenuItem, { isDanger: true, className: "create-group-action del-btn", isDisabled: !selectedGroup || !ability.can(resourceActions.DELETE, resources.CASE_GROUPS), key: "delete", onClick: onActionFocus(actions.delete), "data-tracking-id": "manage-group-delete" }, t('Delete')),
319
334
  ];
320
335
  const dropdownToggle = (toggleRef) => (React.createElement(MenuToggle, { isFullHeight: true, className: "pf-v6-u-ml-sm manage-group-actions-toggle-options pf-v6-u-p-md", ref: toggleRef, isDisabled: props.isDisabled || disableOnFetch, onClick: toggleIsDropdownOpen, isExpanded: isDropdownOpen, "data-tracking-id": "manage-group-toggle-options", "aria-label": "Group actions dropdown toggle", icon: React.createElement(CogIcon, null) }));
@@ -1 +1 @@
1
- {"version":3,"file":"ManageGroupUsers.d.ts","sourceRoot":"","sources":["../../../../../src/components/Groups/ManageGroupUsers/ManageGroupUsers.tsx"],"names":[],"mappings":"AAKA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAYnD,eAAO,MAAM,gBAAgB,yBAyL5B,CAAC"}
1
+ {"version":3,"file":"ManageGroupUsers.d.ts","sourceRoot":"","sources":["../../../../../src/components/Groups/ManageGroupUsers/ManageGroupUsers.tsx"],"names":[],"mappings":"AAMA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAanD,eAAO,MAAM,gBAAgB,yBAgN5B,CAAC"}
@@ -10,12 +10,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { SearchInput } from '@patternfly/react-core';
11
11
  import { ErrorBoundary, usePrevious } from '@rh-support/components';
12
12
  import { useGlobalStateContext } from '@rh-support/react-context';
13
+ import { ability, resourceActions, resources } from '@rh-support/user-permissions';
13
14
  import isEmpty from 'lodash/isEmpty';
14
15
  import React, { useEffect, useState } from 'react';
15
16
  import { Trans, useTranslation } from 'react-i18next';
16
17
  import { useLocation } from 'react-router-dom';
17
18
  import { useCaseGroupMembers } from '../../../hooks/useCaseGroupGraphQL';
18
19
  import { ManageTable } from '../../ManageTable/ManageTable';
20
+ import { AddMembersModal } from '../AddMembersModal';
19
21
  import { DefaultGroupSwitch } from '../DefualtGroupSwitch';
20
22
  import { GroupSelect } from '../GroupSelect';
21
23
  // TODO: Uncomment when updateUsers is migrated to GraphQL
@@ -26,7 +28,9 @@ export const ManageGroupUsers = () => {
26
28
  const { globalMetadataState: { loggedInUsersAccount }, } = useGlobalStateContext();
27
29
  const disableGroupSelect = !loggedInUsersAccount || !loggedInUsersAccount.data || !loggedInUsersAccount.data.accountNumber;
28
30
  const [selectedGroup, setSelectedGroup] = useState();
29
- const { data: groupUsers, loading: isFetching, fetchMembers } = useCaseGroupMembers();
31
+ const { data: groupUsers, loading: isFetching, fetchMembers, refetch } = useCaseGroupMembers();
32
+ const canManageCaseGroups = ability.can(resourceActions.CREATE, resources.CASE_GROUPS);
33
+ const [isAddMembersModalOpen, setIsAddMembersModalOpen] = useState(false);
30
34
  // TODO: Uncomment when updateUsers is migrated to GraphQL
31
35
  // function onPermissionError(error: Error, user: IGroupUser) {
32
36
  // setData([...data]);
@@ -111,11 +115,15 @@ export const ManageGroupUsers = () => {
111
115
  // />
112
116
  // ),
113
117
  // },
114
- {
115
- id: 'default',
116
- title: t('Default group'),
117
- cell: (user, index) => (React.createElement(DefaultGroupSwitch, { id: `${index}`, onToggle: onDefaultToggle, group: selectedGroup, user: user })),
118
- },
118
+ ...(canManageCaseGroups
119
+ ? [
120
+ {
121
+ id: 'default',
122
+ title: t('Default group'),
123
+ cell: (user, index) => (React.createElement(DefaultGroupSwitch, { id: `${index}`, onToggle: onDefaultToggle, group: selectedGroup, user: user })),
124
+ },
125
+ ]
126
+ : []),
119
127
  ];
120
128
  // filter out search query.
121
129
  const [userSearch, setUserSearch] = useState('');
@@ -148,11 +156,12 @@ export const ManageGroupUsers = () => {
148
156
  React.createElement("h2", { className: "pf-v6-u-pb-sm" }, t('Groups')),
149
157
  React.createElement("p", null, t('To filter cases while searching, add or edit groups.'))),
150
158
  React.createElement("div", { className: "toolbar pf-v6-u-justify-content-space-between" },
151
- React.createElement(GroupSelect, { isDisabled: isFetching || disableGroupSelect, onSelect: onGroupSelect }),
159
+ React.createElement(GroupSelect, { isDisabled: isFetching || disableGroupSelect, onSelect: onGroupSelect, onAddMembers: () => setIsAddMembersModalOpen(true) }),
152
160
  React.createElement("span", { className: "toolbar-right" },
153
161
  React.createElement("label", { htmlFor: "account-group-filter" },
154
162
  React.createElement(Trans, null, "Filter by")),
155
163
  React.createElement(SearchInput, { className: "pf-v6-u-flex-grow-1 pf-v6-c-search-input pf-v6-u-background-color-100", id: "account-group-filter", value: userSearch, isDisabled: isFetching || !selectedGroup, onChange: (_, value) => setUserSearch(value), placeholder: t('Search for people in this group'), "aria-label": "Group search input", onClear: () => setUserSearch('') }))),
156
164
  React.createElement(ErrorBoundary, { errorMsgInfo: { message: t('There was an error loading group users list') } },
157
- React.createElement(ManageTable, { ariaLabel: t('Table to manage groups'), columns: columns, data: data, sortInfo: { column: 'first-name', direction: 'asc' }, isFetching: isFetching, isError: false, pagination: false }))));
165
+ React.createElement(ManageTable, { ariaLabel: t('Table to manage groups'), columns: columns, data: data, sortInfo: { column: 'first-name', direction: 'asc' }, isFetching: isFetching, isError: false, pagination: false })),
166
+ canManageCaseGroups && isAddMembersModalOpen && selectedGroup && (React.createElement(AddMembersModal, { group: selectedGroup, existingMembers: initialData, onSuccess: refetch, onClose: () => setIsAddMembersModalOpen(false) }))));
158
167
  };
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import { ICaseLabel } from './types';
3
+ export type SaveCaseLabelResult = {
4
+ success: boolean;
5
+ fieldError?: string;
6
+ };
7
+ interface IProps {
8
+ isOpen: boolean;
9
+ addMode: boolean;
10
+ label?: ICaseLabel | null;
11
+ isSaving?: boolean;
12
+ onClose: () => void;
13
+ onSave: (name: string, options?: {
14
+ keepOpen?: boolean;
15
+ }) => Promise<SaveCaseLabelResult> | SaveCaseLabelResult;
16
+ }
17
+ export declare function AddEditCaseLabelModal({ isOpen, addMode, label, isSaving, onClose, onSave }: IProps): React.JSX.Element;
18
+ export {};
19
+ //# sourceMappingURL=AddEditCaseLabelModal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AddEditCaseLabelModal.d.ts","sourceRoot":"","sources":["../../../../src/components/ManageCaseLabels/AddEditCaseLabelModal.tsx"],"names":[],"mappings":"AAeA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAGnD,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,MAAM,MAAM,mBAAmB,GAAG;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,UAAU,MAAM;IACZ,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,mBAAmB,CAAC,GAAG,mBAAmB,CAAC;CAClH;AAED,wBAAgB,qBAAqB,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,QAAgB,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,qBA0H1G"}
@@ -0,0 +1,83 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { Button, ButtonVariant, FormGroup, FormHelperText, HelperText, HelperTextItem, Modal, ModalBody, ModalFooter, ModalHeader, ModalVariant, TextInput, } from '@patternfly/react-core';
11
+ import isEmpty from 'lodash/isEmpty';
12
+ import React, { useEffect, useState } from 'react';
13
+ import { Trans, useTranslation } from 'react-i18next';
14
+ export function AddEditCaseLabelModal({ isOpen, addMode, label, isSaving = false, onClose, onSave }) {
15
+ const { t } = useTranslation();
16
+ const [name, setName] = useState('');
17
+ const [error, setError] = useState('');
18
+ useEffect(() => {
19
+ if (isOpen) {
20
+ setName(addMode ? '' : (label === null || label === void 0 ? void 0 : label.name) || '');
21
+ setError('');
22
+ }
23
+ }, [isOpen, addMode, label]);
24
+ const validate = (value) => {
25
+ if (isEmpty(value.trim())) {
26
+ return t('Case label name cannot be empty.');
27
+ }
28
+ return '';
29
+ };
30
+ const resetForm = () => {
31
+ setName('');
32
+ setError('');
33
+ };
34
+ const handleSave = () => __awaiter(this, void 0, void 0, function* () {
35
+ const validationError = validate(name);
36
+ if (!isEmpty(validationError)) {
37
+ setError(validationError);
38
+ return;
39
+ }
40
+ const result = yield onSave(name.trim(), { keepOpen: false });
41
+ if (!result.success && result.fieldError) {
42
+ setError(result.fieldError);
43
+ }
44
+ });
45
+ const handleSaveAndNew = () => __awaiter(this, void 0, void 0, function* () {
46
+ const validationError = validate(name);
47
+ if (!isEmpty(validationError)) {
48
+ setError(validationError);
49
+ return;
50
+ }
51
+ const result = yield onSave(name.trim(), { keepOpen: true });
52
+ if (result.success) {
53
+ resetForm();
54
+ return;
55
+ }
56
+ if (result.fieldError) {
57
+ setError(result.fieldError);
58
+ }
59
+ });
60
+ const handleClose = () => {
61
+ resetForm();
62
+ onClose();
63
+ };
64
+ return (React.createElement(Modal, { id: "add-edit-case-label-modal", "aria-describedby": "add-edit-case-label-modal", isOpen: isOpen, onClose: handleClose, variant: ModalVariant.small },
65
+ React.createElement(ModalHeader, null,
66
+ React.createElement("h2", null, addMode ? t('Create new case label') : t('Rename case label'))),
67
+ React.createElement(ModalBody, null,
68
+ React.createElement(FormGroup, { label: t('Case label name'), isRequired: true },
69
+ React.createElement(TextInput, { isRequired: true, type: "text", id: "case-label-name-input", value: name, onChange: (_, value) => {
70
+ setName(value);
71
+ setError('');
72
+ }, placeholder: t('Enter a unique case label name'), validated: isEmpty(error) ? 'default' : 'error', isDisabled: isSaving }),
73
+ !isEmpty(error) && (React.createElement(FormHelperText, null,
74
+ React.createElement(HelperText, null,
75
+ React.createElement(HelperTextItem, { variant: "error" }, error)))))),
76
+ React.createElement(ModalFooter, null,
77
+ React.createElement(Button, { key: "save", variant: ButtonVariant.primary, onClick: handleSave, isDisabled: isEmpty(name.trim()) || !isEmpty(error) || isSaving, isLoading: isSaving },
78
+ React.createElement(Trans, null, "Save")),
79
+ addMode && (React.createElement(Button, { key: "save-and-new", variant: ButtonVariant.secondary, onClick: handleSaveAndNew, isDisabled: isEmpty(name.trim()) || !isEmpty(error) || isSaving, isLoading: isSaving },
80
+ React.createElement(Trans, null, "Save & New"))),
81
+ React.createElement(Button, { key: "cancel", variant: ButtonVariant.link, onClick: handleClose, isDisabled: isSaving },
82
+ React.createElement(Trans, null, "Cancel")))));
83
+ }
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ export declare function ManageCaseLabelsTab(): React.JSX.Element;
3
+ //# sourceMappingURL=ManageCaseLabelsTab.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ManageCaseLabelsTab.d.ts","sourceRoot":"","sources":["../../../../src/components/ManageCaseLabels/ManageCaseLabelsTab.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAmC5D,wBAAgB,mBAAmB,sBA+SlC"}