@rh-support/troubleshoot 2.6.7 → 2.6.9

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 (21) hide show
  1. package/lib/esm/components/CaseEditView/ActiveCustomerEscalation/ActiveCustomerEscalation.d.ts +1 -0
  2. package/lib/esm/components/CaseEditView/ActiveCustomerEscalation/ActiveCustomerEscalation.d.ts.map +1 -1
  3. package/lib/esm/components/CaseEditView/ActiveCustomerEscalation/ActiveCustomerEscalation.js +47 -19
  4. package/lib/esm/components/CaseEditView/Case.d.ts.map +1 -1
  5. package/lib/esm/components/CaseEditView/Case.js +50 -14
  6. package/lib/esm/components/CaseEditView/CaseDetailsAside.d.ts.map +1 -1
  7. package/lib/esm/components/CaseEditView/CaseDetailsAside.js +32 -10
  8. package/lib/esm/components/CaseEditView/RequestRemoteSession/ESSRemoteSession.d.ts.map +1 -1
  9. package/lib/esm/components/CaseEditView/RequestRemoteSession/ESSRemoteSession.js +25 -9
  10. package/lib/esm/components/CaseEditView/RequestRemoteSession/RemoteSessionAgreement.d.ts.map +1 -1
  11. package/lib/esm/components/CaseEditView/RequestRemoteSession/RemoteSessionAgreement.js +25 -9
  12. package/lib/esm/components/CaseEditView/Tabs/CaseDetails/useCustomEmails.d.ts +1 -2
  13. package/lib/esm/components/CaseEditView/Tabs/CaseDetails/useCustomEmails.d.ts.map +1 -1
  14. package/lib/esm/components/CaseEditView/Tabs/CaseDetails/useCustomEmails.js +2 -3
  15. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseComments.d.ts.map +1 -1
  16. package/lib/esm/components/CaseEditView/Tabs/CaseDiscussion/CaseComments.js +14 -1
  17. package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.d.ts.map +1 -1
  18. package/lib/esm/components/CaseEditView/Tabs/CaseHistory/Timeline.js +19 -5
  19. package/lib/esm/components/CaseManagement/RHAssociatesSelector.js +1 -1
  20. package/lib/esm/components/CaseManagement/SendNotifications/CaseContactSelector.js +1 -1
  21. package/package.json +6 -6
@@ -3,6 +3,7 @@ interface IProps {
3
3
  caseNumber: string;
4
4
  caseSeverity: string;
5
5
  caseStatus: string;
6
+ canViewACESection: boolean;
6
7
  }
7
8
  export declare const ActiveCustomerEscalation: React.ForwardRefExoticComponent<IProps & React.RefAttributes<HTMLDivElement>>;
8
9
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"ActiveCustomerEscalation.d.ts","sourceRoot":"","sources":["../../../../../src/components/CaseEditView/ActiveCustomerEscalation/ActiveCustomerEscalation.tsx"],"names":[],"mappings":"AAIA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAM/D,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,wBAAwB,+EA6HnC,CAAC"}
1
+ {"version":3,"file":"ActiveCustomerEscalation.d.ts","sourceRoot":"","sources":["../../../../../src/components/CaseEditView/ActiveCustomerEscalation/ActiveCustomerEscalation.tsx"],"names":[],"mappings":"AAKA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAM/D,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,OAAO,CAAC;CAC9B;AAED,eAAO,MAAM,wBAAwB,+EA8KnC,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { Button, ButtonVariant, List, ListItem, Popover } from '@patternfly/react-core';
2
2
  import InfoCircleIcon from '@patternfly/react-icons/dist/js/icons/info-circle-icon';
3
- import { useCanEditCase } from '@rh-support/react-context';
3
+ import LockIcon from '@patternfly/react-icons/dist/js/icons/lock-icon';
4
+ import { useCanEditCase, useGlobalStateContext } from '@rh-support/react-context';
4
5
  import { Can, resourceActions, resources } from '@rh-support/user-permissions';
5
6
  import React, { forwardRef, useState } from 'react';
6
7
  import { Trans, useTranslation } from 'react-i18next';
@@ -9,6 +10,9 @@ import { RequestEscalationModal } from './RequestEscalationModal';
9
10
  export const ActiveCustomerEscalation = forwardRef((props, ref) => {
10
11
  const { t } = useTranslation();
11
12
  const { caseDetailsPageState: { caseEscalations }, } = useCaseDetailsPageStateContext();
13
+ const { globalMetadataState: { loggedInUserRights }, } = useGlobalStateContext();
14
+ const isExternal = loggedInUserRights.data.isExternal();
15
+ const isInternal = loggedInUserRights.data.isInternal();
12
16
  const canEditCase = useCanEditCase();
13
17
  const isCreateRMEVisible = (caseEscalations.data || []).length === 0 || caseEscalations.data.every((item) => item.status === 'Closed');
14
18
  const commonElements = (React.createElement(React.Fragment, null,
@@ -20,15 +24,27 @@ export const ActiveCustomerEscalation = forwardRef((props, ref) => {
20
24
  React.createElement("a", { href: "/support/escalation", target: "_blank", "aria-label": t('Learn more about Red Hat support case escalation') },
21
25
  React.createElement(InfoCircleIcon, null))),
22
26
  React.createElement("div", { className: "card-body" },
23
- React.createElement("p", null,
24
- React.createElement("strong", null, t('Red Hat associates can open an ACE escalation when')),
25
- ":"),
26
- React.createElement(List, { className: "pf-v5-u-pl-lg" },
27
- React.createElement(ListItem, null, t('The customer wants an update')),
28
- React.createElement(ListItem, null, t('The customer thinks the case is not moving appropriately')),
29
- React.createElement(ListItem, null, t('The customer wants a new resource')),
30
- React.createElement(ListItem, null, t('The issue is more severe than originally thought'))),
31
- React.createElement("a", { href: `/watchlist/internal/aces/new?caseNumber=${props.caseNumber}`, target: "_blank", rel: "noopener noreferrer", className: "pf-v5-c-button pf-m-tertiary", "data-tracking-id": "request-ace-trigger", "aria-label": t('Request an escalation') }, t('Request an escalation')))));
27
+ isInternal && (React.createElement(React.Fragment, null,
28
+ React.createElement("p", null,
29
+ React.createElement(LockIcon, null),
30
+ " ",
31
+ React.createElement("strong", null, `${t('Available to customers only')}`)),
32
+ React.createElement("p", null,
33
+ React.createElement(Trans, null,
34
+ "Available to customers only. When a customer submits an escalation request, it is routed to a support manager for review. Support agents can refer to the",
35
+ ' ',
36
+ React.createElement("a", { href: "https://access.redhat.com/support/escalation", target: "_blank", rel: "noreferrer" }, "Escalation guidelines"),
37
+ "for details.")))),
38
+ isExternal && props.canViewACESection && (React.createElement(React.Fragment, null,
39
+ React.createElement("p", null,
40
+ React.createElement("strong", null, t('Red Hat associates can open an ACE escalation when')),
41
+ ":"),
42
+ React.createElement(List, { className: "pf-v5-u-pl-lg" },
43
+ React.createElement(ListItem, null, t('The customer wants an update')),
44
+ React.createElement(ListItem, null, t('The customer thinks the case is not moving appropriately')),
45
+ React.createElement(ListItem, null, t('The customer wants a new resource')),
46
+ React.createElement(ListItem, null, t('The issue is more severe than originally thought'))),
47
+ React.createElement("a", { href: `/watchlist/internal/aces/new?caseNumber=${props.caseNumber}`, target: "_blank", rel: "noopener noreferrer", className: "pf-v5-c-button pf-m-tertiary", "data-tracking-id": "request-ace-trigger", "aria-label": t('Request an escalation') }, t('Request an escalation')))))));
32
48
  const [openRequestEscalationModal, setOpenRequestEscalationModal] = useState(false);
33
49
  const toggleModal = () => {
34
50
  if (canEditCase.alert())
@@ -43,15 +59,27 @@ export const ActiveCustomerEscalation = forwardRef((props, ref) => {
43
59
  React.createElement(Trans, null, "Learn more about escalation cases."))) },
44
60
  React.createElement(InfoCircleIcon, { title: t('Learn more about escalation cases popover') })))),
45
61
  React.createElement("div", { className: "card-body" },
46
- React.createElement("p", null,
47
- React.createElement("strong", null, t('Request a management escalation if your issue')),
48
- ":"),
49
- React.createElement(List, { className: "pf-v5-u-pl-lg" },
50
- React.createElement(ListItem, null, t(`Isn't being resolved appropriately.`)),
51
- React.createElement(ListItem, null, t('Needs a senior resource.')),
52
- React.createElement(ListItem, null, t('Is more severe or should be a higher priority.'))),
53
- commonElements,
54
- React.createElement(Button, { variant: ButtonVariant.secondary, onClick: toggleModal, "data-tracking-id": "request-rme-trigger", "aria-label": t('Request an escalation') }, t('Request an escalation')))));
62
+ isInternal && (React.createElement(React.Fragment, null,
63
+ React.createElement("p", null,
64
+ React.createElement(LockIcon, null),
65
+ " ",
66
+ React.createElement("strong", null, `${t('Available to customers only')}`)),
67
+ React.createElement("p", null,
68
+ React.createElement(Trans, null,
69
+ "Available to customers only. When a customer submits an escalation request, it is routed to a support manager for review. Support agents can refer to the",
70
+ ' ',
71
+ React.createElement("a", { href: "https://access.redhat.com/support/escalation", target: "_blank", rel: "noreferrer" }, "Escalation guidelines"),
72
+ "for details.")))),
73
+ isExternal && (React.createElement(React.Fragment, null,
74
+ React.createElement("p", null,
75
+ React.createElement("strong", null, t('Request a management escalation if your issue')),
76
+ ":"),
77
+ React.createElement(List, { className: "pf-v5-u-pl-lg" },
78
+ React.createElement(ListItem, null, t(`Isn't being resolved appropriately.`)),
79
+ React.createElement(ListItem, null, t('Needs a senior resource.')),
80
+ React.createElement(ListItem, null, t('Is more severe or should be a higher priority.'))),
81
+ commonElements,
82
+ React.createElement(Button, { variant: ButtonVariant.secondary, onClick: toggleModal, "data-tracking-id": "request-rme-trigger", "aria-label": t('Request an escalation') }, t('Request an escalation')))))));
55
83
  return (React.createElement(React.Fragment, null,
56
84
  React.createElement(RequestEscalationModal, { caseNumber: props.caseNumber, severity: props.caseSeverity, caseStatus: props.caseStatus, show: openRequestEscalationModal, onClose: toggleModal }),
57
85
  React.createElement(Can, { do: resourceActions.CREATE, on: resources.ICE_ESCALATION, passThrough: true }, (canCreateICE) => canCreateICE ? isInternalElements : isCreateRMEVisible ? isNotInternalElements : React.createElement(React.Fragment, null))));
@@ -1 +1 @@
1
- {"version":3,"file":"Case.d.ts","sourceRoot":"","sources":["../../../../src/components/CaseEditView/Case.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAwC,MAAM,OAAO,CAAC;AAE7D,OAAO,EAAS,mBAAmB,EAAiD,MAAM,kBAAkB,CAAC;AAkB7G,UAAU,MAAM;IACZ,UAAU,EAAE,mBAAmB,CAAC;CACnC;AAED,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,KAAK,EAAE,MAAM,qBA6LzC"}
1
+ {"version":3,"file":"Case.d.ts","sourceRoot":"","sources":["../../../../src/components/CaseEditView/Case.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAwC,MAAM,OAAO,CAAC;AAE7D,OAAO,EAAS,mBAAmB,EAAiD,MAAM,kBAAkB,CAAC;AAmB7G,UAAU,MAAM;IACZ,UAAU,EAAE,mBAAmB,CAAC;CACnC;AAED,MAAM,CAAC,OAAO,UAAU,IAAI,CAAC,KAAK,EAAE,MAAM,qBA4OzC"}
@@ -1,3 +1,12 @@
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
+ };
1
10
  import { CoverSpinner, ErrorBoundary, OverviewContentLoader, ToastNotification, useDocumentTitle, usePrevious, } from '@rh-support/components';
2
11
  import { GlobalMetadataStateContext, HostnameAwarenessModal } from '@rh-support/react-context';
3
12
  import { AbilityContext, CaseDetailsFields, resourceActions, resources } from '@rh-support/user-permissions';
@@ -9,6 +18,7 @@ import { useTranslation } from 'react-i18next';
9
18
  import { Route, Switch, useLocation, useParams, useRouteMatch } from 'react-router-dom';
10
19
  import { useCaseDispatch, useCaseSelector } from '../../context/CaseContext';
11
20
  import { useCaseDetailsPageDispatchContext } from '../../context/CaseDetailsPageContext';
21
+ import { CaseReducerConstants } from '../../reducers/CaseConstNTypes';
12
22
  import { fetchAttachments, fetchCaseEscalations, fetchExternalTrackers, fetchFeedbacks, } from '../../reducers/CaseDetailsPageReducer';
13
23
  import { fetchCaseDetails, setCaseAccountNumber } from '../../reducers/CaseReducer';
14
24
  import { CaseDetailsAside } from './CaseDetailsAside';
@@ -29,6 +39,7 @@ export default function Case(props) {
29
39
  const queryParams = getUrlParsedParams(useLocation().search);
30
40
  const ability = useContext(AbilityContext);
31
41
  const caseDetailsTabsRef = useRef(null);
42
+ const currentCaseRef = useRef();
32
43
  const canSeeFeedbackButtons = ability.can(resourceActions.PATCH, resources.CASE_DETAILS, CaseDetailsFields.CASE_DETAILS_FEEDBACK_BUTTONS);
33
44
  const canReadCase = ability.can(resourceActions.READ, resources.CASE_DETAILS);
34
45
  const canSeeExternalTracker = ability.can(resourceActions.PATCH, resources.CASE_DETAILS, CaseDetailsFields.CASE_DETAILS_EXTERNAL_TRACKER);
@@ -80,20 +91,45 @@ export default function Case(props) {
80
91
  props.routeProps.history.replace('/case/list');
81
92
  return;
82
93
  }
83
- if (caseNumber) {
84
- fetchCaseDetails(caseDispatch, caseNumber, loggedInUserRights.data, loggedInUser.data);
85
- canSeeRMEs &&
86
- fetchCaseEscalations(caseDetailsPageDispatch, caseNumber, loggedInUserRights.data.getAccountNumber());
87
- // fetch data in advance
88
- canSeeAttachments &&
89
- fetchAttachments(caseDetailsPageDispatch, caseNumber, loggedInUsersAccount.data.secureSupport);
90
- canSeeExternalTracker && fetchExternalTrackers(caseDetailsPageDispatch, caseNumber);
91
- checkRemoteSessionFlag();
92
- }
93
- if (canSeeFeedbackButtons && caseNumber) {
94
- // fetch data in advance
95
- fetchFeedbacks(caseDetailsPageDispatch, caseNumber);
96
- }
94
+ const targetCaseNumber = caseNumber;
95
+ caseDispatch({ type: CaseReducerConstants.resetCaseState });
96
+ // Update reference after reset
97
+ currentCaseRef.current = targetCaseNumber;
98
+ const safeFetchCaseDetails = () => __awaiter(this, void 0, void 0, function* () {
99
+ try {
100
+ // Check that we're still on the intended case before fetching
101
+ if (currentCaseRef.current !== targetCaseNumber)
102
+ return;
103
+ const safeDispatch = (action) => {
104
+ // Only dispatch if we're still working with the target case number
105
+ if (currentCaseRef.current === targetCaseNumber) {
106
+ caseDispatch(action);
107
+ }
108
+ };
109
+ yield fetchCaseDetails(safeDispatch, targetCaseNumber, loggedInUserRights.data, loggedInUser.data);
110
+ if (currentCaseRef.current !== targetCaseNumber)
111
+ return;
112
+ if (canSeeRMEs) {
113
+ fetchCaseEscalations((action) => currentCaseRef.current === targetCaseNumber && caseDetailsPageDispatch(action), targetCaseNumber, loggedInUserRights.data.getAccountNumber());
114
+ }
115
+ if (canSeeAttachments) {
116
+ fetchAttachments((action) => currentCaseRef.current === targetCaseNumber && caseDetailsPageDispatch(action), targetCaseNumber, loggedInUsersAccount.data.secureSupport);
117
+ }
118
+ if (canSeeExternalTracker) {
119
+ fetchExternalTrackers((action) => currentCaseRef.current === targetCaseNumber && caseDetailsPageDispatch(action), targetCaseNumber);
120
+ }
121
+ if (canSeeFeedbackButtons) {
122
+ fetchFeedbacks((action) => currentCaseRef.current === targetCaseNumber && caseDetailsPageDispatch(action), targetCaseNumber);
123
+ }
124
+ if (currentCaseRef.current === targetCaseNumber) {
125
+ checkRemoteSessionFlag();
126
+ }
127
+ }
128
+ catch (error) {
129
+ console.error('Error fetching case details:', error);
130
+ }
131
+ });
132
+ safeFetchCaseDetails();
97
133
  // eslint-disable-next-line react-hooks/exhaustive-deps
98
134
  }, [
99
135
  caseNumber,
@@ -1 +1 @@
1
- {"version":3,"file":"CaseDetailsAside.d.ts","sourceRoot":"","sources":["../../../../src/components/CaseEditView/CaseDetailsAside.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAkD,MAAM,OAAO,CAAC;AAYvE,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;CACtB;AAMD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,qBAqX7C"}
1
+ {"version":3,"file":"CaseDetailsAside.d.ts","sourceRoot":"","sources":["../../../../src/components/CaseEditView/CaseDetailsAside.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAA2D,MAAM,OAAO,CAAC;AAYhF,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;CACtB;AAMD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,qBA+Y7C"}
@@ -7,6 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
+ import { publicApi } from '@cee-eng/hydrajs';
10
11
  import { Button, Checkbox, Icon, Popover, PopoverPosition, Switch, Tooltip } from '@patternfly/react-core';
11
12
  import AngleDoubleLeftIcon from '@patternfly/react-icons/dist/js/icons/angle-double-left-icon';
12
13
  import AngleDoubleRightIcon from '@patternfly/react-icons/dist/js/icons/angle-double-right-icon';
@@ -14,13 +15,13 @@ import InfoCircleIcon from '@patternfly/react-icons/dist/js/icons/info-circle-ic
14
15
  import ListIcon from '@patternfly/react-icons/dist/js/icons/list-icon';
15
16
  import LockIcon from '@patternfly/react-icons/dist/js/icons/lock-icon';
16
17
  import TrendUpIcon from '@patternfly/react-icons/dist/js/icons/trend-up-icon';
17
- import { ErrorBoundary, ToastNotification, useBreakpoint, useForceUpdate } from '@rh-support/components';
18
+ import { ErrorBoundary, ToastNotification, useBreakpoint, useFetch, useForceUpdate } from '@rh-support/components';
18
19
  import { GlobalMetadataDispatchContext, toggleViewAsCustomerFlag, useGlobalStateContext, useUserPreferences, } from '@rh-support/react-context';
19
20
  import { ability, CaseDetailsFields, resourceActions, resources } from '@rh-support/user-permissions';
20
21
  import { isSpecialSupportOfferingEnabled, scrollIntoView } from '@rh-support/utils';
21
22
  import isEmpty from 'lodash/isEmpty';
22
23
  import isEqual from 'lodash/isEqual';
23
- import React, { useContext, useEffect, useRef, useState } from 'react';
24
+ import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
24
25
  import { Trans, useTranslation } from 'react-i18next';
25
26
  import { Link } from 'react-router-dom';
26
27
  import { useCaseSelector } from '../../context/CaseContext';
@@ -33,7 +34,7 @@ import { ESSRemoteSession, RemoteSessionAgreement } from './RequestRemoteSession
33
34
  export function CaseDetailsAside(props) {
34
35
  var _a, _b;
35
36
  const { t } = useTranslation();
36
- const { severity, status, isFetchingCaseDetails, isFetchingCaseDetailsError, acceptedRemoteSessionTerms, product, waitingOnCallback, } = useCaseSelector((state) => ({
37
+ const { severity, status, isFetchingCaseDetails, isFetchingCaseDetailsError, acceptedRemoteSessionTerms, product, waitingOnCallback, groupNumber, } = useCaseSelector((state) => ({
37
38
  severity: state.caseDetails.severity,
38
39
  status: state.caseDetails.status,
39
40
  isFetchingCaseDetails: state.isFetchingCaseDetails,
@@ -41,6 +42,7 @@ export function CaseDetailsAside(props) {
41
42
  acceptedRemoteSessionTerms: state.caseDetails.remoteSessionTermsAcked,
42
43
  waitingOnCallback: state.caseDetails.waitingOnCallback,
43
44
  product: state.caseDetails.product,
45
+ groupNumber: state.caseDetails.groupNumber,
44
46
  }), isEqual);
45
47
  const selectedProduct = product;
46
48
  const { caseDetailsPageState: { caseEscalations }, } = useCaseDetailsPageStateContext();
@@ -61,10 +63,8 @@ export function CaseDetailsAside(props) {
61
63
  const isXLScreen = breakPoint.xl;
62
64
  const toggleCustomerViewRef = useRef(null);
63
65
  const isFirstMountRef = useRef(true);
64
- const { globalMetadataState: { navBarRef, viewAsCustomer, loggedInUsersAccount, loggedInUserRights, allProducts }, } = useGlobalStateContext();
66
+ const { globalMetadataState: { navBarRef, viewAsCustomer, loggedInUsersAccount, allProducts }, } = useGlobalStateContext();
65
67
  const isSecureSupportAccount = loggedInUsersAccount.data.secureSupport;
66
- // To enable RSA Section for external users
67
- const isExternal = loggedInUserRights.data.isExternal();
68
68
  // To check if user has read only access
69
69
  const canReadCase = ability.can(resourceActions.READ, resources.CASE_DETAILS);
70
70
  const onToggleAside = (ev) => {
@@ -78,7 +78,28 @@ export function CaseDetailsAside(props) {
78
78
  const { getOriginalCaseView, updateOriginalCaseView } = useUserPreferences();
79
79
  const [viewAsInternalPref, setViewAsInternalPref] = useState();
80
80
  const [isDefaultBoxChecked, setIsDefaultBoxChecked] = useState(true);
81
+ const [groups, setGroups] = useState([]);
81
82
  const { isExportingPDF } = useContext(PDFContext);
83
+ //getCaseGroupsForInternalUser returns casegroups with write permission for both internal and external users
84
+ const getCasegroupsWithWritePermissionFetch = useFetch(publicApi.caseGroups.getCaseGroupsForInternalUser, {
85
+ propgateErrors: true,
86
+ });
87
+ const fetchGroupsWithWritePermission = () => __awaiter(this, void 0, void 0, function* () {
88
+ try {
89
+ const userAccountNumber = loggedInUsersAccount.data.accountNumber;
90
+ const fetchedGroups = yield getCasegroupsWithWritePermissionFetch.request(userAccountNumber);
91
+ setGroups(fetchedGroups);
92
+ }
93
+ catch (error) {
94
+ console.error('Failed to fetch case groups with write permission:', error);
95
+ ToastNotification.addDangerMessage(t('Failed to fetch case groups'));
96
+ setGroups([]);
97
+ }
98
+ });
99
+ // Memoized permission check that updates when groupNumber or groups change
100
+ const isWritePermissionforCase = useMemo(() => {
101
+ return groups.some((group) => group.groupNum === groupNumber) || groupNumber === '-1';
102
+ }, [groupNumber, groups]);
82
103
  useEffect(() => {
83
104
  const userPreferredCaseView = () => __awaiter(this, void 0, void 0, function* () {
84
105
  try {
@@ -102,6 +123,7 @@ export function CaseDetailsAside(props) {
102
123
  }
103
124
  });
104
125
  userPreferredCaseView();
126
+ fetchGroupsWithWritePermission();
105
127
  // eslint-disable-next-line react-hooks/exhaustive-deps
106
128
  }, []);
107
129
  const setSectionToScollRef = (sectionRef) => {
@@ -210,12 +232,12 @@ export function CaseDetailsAside(props) {
210
232
  React.createElement(Checkbox, { label: t('Set to default'), isChecked: isDefaultBoxChecked, onChange: onDefaultCheckboxChange, id: "set-default-view-checkbox", name: "default-view-checkbox", isDisabled: isDefaultBoxChecked, className: 'defaultViewCheckbox' })))),
211
233
  React.createElement(ErrorBoundary, { errorMsgInfo: { message: t('There was an error loading top solutions') } },
212
234
  React.createElement(CaseSolutions, { caseNumber: caseNumber, ref: topSolutionsRef, isSecureSupportAccount: isSecureSupportAccount })),
213
- isExternal && isESSCustomer && (React.createElement(ErrorBoundary, { errorMsgInfo: {
235
+ isESSCustomer && (React.createElement(ErrorBoundary, { errorMsgInfo: {
214
236
  message: t('There was an error loading requesting remote session section'),
215
237
  } },
216
238
  React.createElement(ESSRemoteSession, { waitingOnCallback: waitingOnCallback, remoteSessionTermsAcked: acceptedRemoteSessionTerms, caseNumber: caseNumber, caseSeverity: severity, caseStatus: status, ref: createEscalationRef, readOnly: canReadCase }))),
217
- isExternal && !isESSCustomer && (React.createElement(ErrorBoundary, { errorMsgInfo: { message: t('There was an error loading remote session agreement section') } },
239
+ !isESSCustomer && (React.createElement(ErrorBoundary, { errorMsgInfo: { message: t('There was an error loading remote session agreement section') } },
218
240
  React.createElement(RemoteSessionAgreement, { caseNumber: caseNumber, caseSeverity: severity, caseStatus: status, ref: createEscalationRef, waitingOnCallback: waitingOnCallback, acceptedRemoteSessionTerms: acceptedRemoteSessionTerms && waitingOnCallback, readOnly: canReadCase }))),
219
- !caseEscalations.isFetching && canViewACESection && (React.createElement(ErrorBoundary, { errorMsgInfo: { message: t('There was an error loading customer escalation section') } },
220
- React.createElement(ActiveCustomerEscalation, { caseNumber: caseNumber, caseSeverity: severity, caseStatus: status, ref: createEscalationRef })))))));
241
+ !caseEscalations.isFetching && isWritePermissionforCase && (React.createElement(ErrorBoundary, { errorMsgInfo: { message: t('There was an error loading customer escalation section') } },
242
+ React.createElement(ActiveCustomerEscalation, { canViewACESection: canViewACESection, caseNumber: caseNumber, caseSeverity: severity, caseStatus: status, ref: createEscalationRef })))))));
221
243
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ESSRemoteSession.d.ts","sourceRoot":"","sources":["../../../../../src/components/CaseEditView/RequestRemoteSession/ESSRemoteSession.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAKxE,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,eAAO,MAAM,gBAAgB,+EA+F3B,CAAC"}
1
+ {"version":3,"file":"ESSRemoteSession.d.ts","sourceRoot":"","sources":["../../../../../src/components/CaseEditView/RequestRemoteSession/ESSRemoteSession.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAKxE,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,eAAO,MAAM,gBAAgB,+EAuH3B,CAAC"}
@@ -1,9 +1,10 @@
1
1
  import { Button, List, ListItem, Popover, Tooltip } from '@patternfly/react-core';
2
2
  import InfoCircleIcon from '@patternfly/react-icons/dist/js/icons/info-circle-icon';
3
+ import LockIcon from '@patternfly/react-icons/dist/js/icons/lock-icon';
3
4
  import { GlobalMetadataStateContext, useCanEditCase } from '@rh-support/react-context';
4
5
  import { TncConstants } from '@rh-support/utils';
5
6
  import React, { forwardRef, useContext, useRef, useState } from 'react';
6
- import { useTranslation } from 'react-i18next';
7
+ import { Trans, useTranslation } from 'react-i18next';
7
8
  import { NewEssTermsModal } from './NewEssTermsModal';
8
9
  export const ESSRemoteSession = forwardRef((props, ref) => {
9
10
  const tooltipRef = useRef();
@@ -11,6 +12,8 @@ export const ESSRemoteSession = forwardRef((props, ref) => {
11
12
  const canEditCase = useCanEditCase();
12
13
  const { caseNumber, caseStatus, readOnly, waitingOnCallback } = props;
13
14
  const { globalMetadataState: { loggedInUser, loggedInUserRights }, } = useContext(GlobalMetadataStateContext);
15
+ const isExternal = loggedInUserRights.data.isExternal();
16
+ const isInternal = loggedInUserRights.data.isInternal();
14
17
  const [openNewESSRemoteSessionModal, setOpenNewESSRemoteSessionModal] = useState(false);
15
18
  // To toggle New ESS RS Modal
16
19
  const toggleNewESSRemoteSessionModal = () => {
@@ -25,14 +28,27 @@ export const ESSRemoteSession = forwardRef((props, ref) => {
25
28
  React.createElement(Popover, { "aria-label": t('Remote Session Helper Info'), bodyContent: t('A remote session allows support engineers to view or access your computer to simplify collaboration and troubleshooting.'), footerContent: React.createElement("a", { href: "/articles/3566571", target: "_blank", rel: "noopener noreferrer", "aria-label": t('Read more about remote sessions') }, t('Read more about remote sessions')) },
26
29
  React.createElement(InfoCircleIcon, null))),
27
30
  React.createElement("div", { className: "card-body" },
28
- React.createElement("p", null,
29
- React.createElement("strong", null, `${t('Red Hat may agree to remotely access your systems if:')}`)),
30
- React.createElement(List, { className: "pf-v5-u-pl-xl" },
31
- React.createElement(ListItem, null, t('Such access will help diagnose and have greater understanding into the issue.')),
32
- React.createElement(ListItem, null, t('Agreed upon by both Red Hat and you, the end user'))),
33
- React.createElement("p", { className: "rrs-light-grey-text" }, t('Red Hat will add a comment to your case to schedule your remote session.')),
34
- waitingOnCallback && (React.createElement(Tooltip, { content: t('Remote session has already been requested.'), triggerRef: tooltipRef, "aria-live": 'polite' })),
35
- React.createElement(Button, { "aria-label": waitingOnCallback ? t('Remote session requested') : t('Request remote session'), variant: waitingOnCallback ? 'primary' : 'secondary', onClick: toggleNewESSRemoteSessionModal, "data-tracking-id": "accept-remote-session-agreement-trigger", isAriaDisabled: waitingOnCallback, ref: tooltipRef }, waitingOnCallback ? t('Remote session requested') : t('Request remote session')))));
31
+ isInternal && (React.createElement(React.Fragment, null,
32
+ React.createElement("p", null,
33
+ React.createElement(LockIcon, null),
34
+ " ",
35
+ React.createElement("strong", null, `${t('Available to customers only')}`)),
36
+ React.createElement("p", null,
37
+ React.createElement(Trans, null,
38
+ "Available to customers only. If requested and agreed upon, a remote session will be scheduled and confirmed in a case comment. Refer to the",
39
+ ' ',
40
+ React.createElement("a", { href: "https://access.redhat.com/articles/3566571", target: "_blank", rel: "noreferrer" }, "Remote session guide"),
41
+ ' ',
42
+ "for details.")))),
43
+ isExternal && (React.createElement(React.Fragment, null,
44
+ React.createElement("p", null,
45
+ React.createElement("strong", null, `${t('Red Hat may agree to remotely access your systems if:')}`)),
46
+ React.createElement(List, { className: "pf-v5-u-pl-xl" },
47
+ React.createElement(ListItem, null, t('Such access will help diagnose and have greater understanding into the issue.')),
48
+ React.createElement(ListItem, null, t('Agreed upon by both Red Hat and you, the end user'))),
49
+ React.createElement("p", { className: "rrs-light-grey-text" }, t('Red Hat will add a comment to your case to schedule your remote session.')),
50
+ waitingOnCallback && (React.createElement(Tooltip, { content: t('Remote session has already been requested.'), triggerRef: tooltipRef, "aria-live": 'polite' })),
51
+ React.createElement(Button, { "aria-label": waitingOnCallback ? t('Remote session requested') : t('Request remote session'), variant: waitingOnCallback ? 'primary' : 'secondary', onClick: toggleNewESSRemoteSessionModal, "data-tracking-id": "accept-remote-session-agreement-trigger", isAriaDisabled: waitingOnCallback, ref: tooltipRef }, waitingOnCallback ? t('Remote session requested') : t('Request remote session')))))));
36
52
  return (React.createElement(React.Fragment, null,
37
53
  ESSRemoteSessionSectionBody,
38
54
  React.createElement(NewEssTermsModal, { caseNumber: caseNumber, caseStatus: caseStatus, show: openNewESSRemoteSessionModal, onClose: toggleNewESSRemoteSessionModal, siteCode: TncConstants.REMOTE_RIDER_SITE_CODE, eventCode: TncConstants.REMOTER_RIDER_EVENT_CODE, loggedInUser: loggedInUser.data.ssoUsername === undefined ? '' : loggedInUser.data.ssoUsername, loggedInUserRights: loggedInUserRights, readOnly: readOnly })));
@@ -1 +1 @@
1
- {"version":3,"file":"RemoteSessionAgreement.d.ts","sourceRoot":"","sources":["../../../../../src/components/CaseEditView/RequestRemoteSession/RemoteSessionAgreement.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAKxE,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,0BAA0B,EAAE,OAAO,CAAC;IACpC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,sBAAsB,+EA6FjC,CAAC"}
1
+ {"version":3,"file":"RemoteSessionAgreement.d.ts","sourceRoot":"","sources":["../../../../../src/components/CaseEditView/RequestRemoteSession/RemoteSessionAgreement.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAKxE,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,0BAA0B,EAAE,OAAO,CAAC;IACpC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,sBAAsB,+EAqHjC,CAAC"}
@@ -1,9 +1,10 @@
1
1
  import { Button, List, ListItem, Popover, Tooltip } from '@patternfly/react-core';
2
2
  import InfoCircleIcon from '@patternfly/react-icons/dist/js/icons/info-circle-icon';
3
+ import LockIcon from '@patternfly/react-icons/dist/js/icons/lock-icon';
3
4
  import { GlobalMetadataStateContext, useCanEditCase } from '@rh-support/react-context';
4
5
  import { TncConstants } from '@rh-support/utils';
5
6
  import React, { forwardRef, useContext, useRef, useState } from 'react';
6
- import { useTranslation } from 'react-i18next';
7
+ import { Trans, useTranslation } from 'react-i18next';
7
8
  import { RemoteSessionAgreementModal } from './RemoteSessionAgreementModal';
8
9
  export const RemoteSessionAgreement = forwardRef((props, ref) => {
9
10
  const tooltipRef = useRef();
@@ -11,6 +12,8 @@ export const RemoteSessionAgreement = forwardRef((props, ref) => {
11
12
  const canEditCase = useCanEditCase();
12
13
  const { caseNumber, caseStatus, acceptedRemoteSessionTerms, readOnly, waitingOnCallback } = props;
13
14
  const { globalMetadataState: { loggedInUser, loggedInUserRights }, } = useContext(GlobalMetadataStateContext);
15
+ const isExternal = loggedInUserRights.data.isExternal();
16
+ const isInternal = loggedInUserRights.data.isInternal();
14
17
  const [openRequestRemoteSessionModal, setOpenRequestRemoteSessionModal] = useState(false);
15
18
  // To toggle RSA Modal
16
19
  const toggleRemoteSessionAgreementModal = () => {
@@ -25,14 +28,27 @@ export const RemoteSessionAgreement = forwardRef((props, ref) => {
25
28
  React.createElement(Popover, { "aria-label": t('Remote Session Helper Info'), bodyContent: t('Before Red Hat logs into your systems to analyze your support issue during a remote session, Red Hat needs your approval and authorization for such remote access'), footerContent: React.createElement("a", { href: "/articles/3566571", target: "_blank", rel: "noopener noreferrer", "aria-label": t('Read more about remote sessions') }, t('Read more about remote sessions')) },
26
29
  React.createElement(InfoCircleIcon, null))),
27
30
  React.createElement("div", { className: "card-body" },
28
- React.createElement("p", null,
29
- React.createElement("strong", null, `${t('Red Hat may agree to remotely access your systems if:')}`)),
30
- React.createElement(List, { className: "pf-v5-u-pl-lg" },
31
- React.createElement(ListItem, null, t('Such access will help diagnose and have greater understanding into the issue')),
32
- React.createElement(ListItem, null, t('Agreed upon by both Red Hat and you, the end user'))),
33
- React.createElement("p", { className: "rrs-light-grey-text" }, t('Red Hat will add a comment to your case to schedule your remote session.')),
34
- acceptedRemoteSessionTerms && (React.createElement(Tooltip, { content: t('Remote session has already been requested.'), triggerRef: tooltipRef, "aria-live": 'polite' })),
35
- React.createElement(Button, { "aria-label": waitingOnCallback ? t('Remote session requested') : t('Request remote session'), variant: waitingOnCallback ? 'primary' : 'secondary', onClick: toggleRemoteSessionAgreementModal, "data-tracking-id": "accept-remote-session-agreement-trigger", isAriaDisabled: waitingOnCallback, ref: tooltipRef }, waitingOnCallback ? t('Remote session requested') : t('Request remote session')))));
31
+ isInternal && (React.createElement(React.Fragment, null,
32
+ React.createElement("p", null,
33
+ React.createElement(LockIcon, null),
34
+ " ",
35
+ React.createElement("strong", null, `${t('Available to customers only')}`)),
36
+ React.createElement("p", null,
37
+ React.createElement(Trans, null,
38
+ "Available to customers only. If requested and agreed upon, a remote session will be scheduled and confirmed in a case comment. Refer to the",
39
+ ' ',
40
+ React.createElement("a", { href: "https://access.redhat.com/articles/3566571", target: "_blank", rel: "noreferrer" }, "Remote session guide"),
41
+ ' ',
42
+ "for details.")))),
43
+ isExternal && (React.createElement(React.Fragment, null,
44
+ React.createElement("p", null,
45
+ React.createElement("strong", null, `${t('Red Hat may agree to remotely access your systems if:')}`)),
46
+ React.createElement(List, { className: "pf-v5-u-pl-lg" },
47
+ React.createElement(ListItem, null, t('Such access will help diagnose and have greater understanding into the issue')),
48
+ React.createElement(ListItem, null, t('Agreed upon by both Red Hat and you, the end user'))),
49
+ React.createElement("p", { className: "rrs-light-grey-text" }, t('Red Hat will add a comment to your case to schedule your remote session.')),
50
+ acceptedRemoteSessionTerms && (React.createElement(Tooltip, { content: t('Remote session has already been requested.'), triggerRef: tooltipRef, "aria-live": 'polite' })),
51
+ React.createElement(Button, { "aria-label": waitingOnCallback ? t('Remote session requested') : t('Request remote session'), variant: waitingOnCallback ? 'primary' : 'secondary', onClick: toggleRemoteSessionAgreementModal, "data-tracking-id": "accept-remote-session-agreement-trigger", isAriaDisabled: waitingOnCallback, ref: tooltipRef }, waitingOnCallback ? t('Remote session requested') : t('Request remote session')))))));
36
52
  return (React.createElement(React.Fragment, null,
37
53
  RemoteSessionAgreementSectionBody,
38
54
  React.createElement(RemoteSessionAgreementModal, { caseNumber: caseNumber, caseStatus: caseStatus, show: openRequestRemoteSessionModal, onClose: toggleRemoteSessionAgreementModal, siteCode: TncConstants.REMOTE_RIDER_SITE_CODE, eventCode: TncConstants.REMOTER_RIDER_EVENT_CODE, loggedInUser: loggedInUser.data.ssoUsername === undefined ? '' : loggedInUser.data.ssoUsername, loggedInUserRights: loggedInUserRights, readOnly: readOnly })));
@@ -1,4 +1,4 @@
1
- import { isEmailValid, isEmailValidForCaseContactSelector } from '@rh-support/utils';
1
+ import { isEmailValid } from '@rh-support/utils';
2
2
  interface IProps {
3
3
  caseNumber: string;
4
4
  accountNumber: string;
@@ -9,7 +9,6 @@ export declare function useCustomEmails(props: IProps): {
9
9
  canAddCustomEmail: boolean;
10
10
  hideCustomEmails: boolean;
11
11
  isEmailValid: typeof isEmailValid;
12
- isEmailValidForCaseContactSelector: typeof isEmailValidForCaseContactSelector;
13
12
  isUpdatingCustomEmails: boolean;
14
13
  showAddEmailToAccountModal: (emailString: string) => Promise<boolean>;
15
14
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useCustomEmails.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDetails/useCustomEmails.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAwB,YAAY,EAAE,kCAAkC,EAAE,MAAM,mBAAmB,CAAC;AAY3G,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM;+BAyED,MAAM;+BAnCN,MAAM,UAAU,MAAM,IAAI;;;;;;8CAbX,MAAM;EAkGhE"}
1
+ {"version":3,"file":"useCustomEmails.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDetails/useCustomEmails.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAwB,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAYvE,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM;+BAyED,MAAM;+BAnCN,MAAM,UAAU,MAAM,IAAI;;;;;8CAbX,MAAM;EAiGhE"}
@@ -11,7 +11,7 @@ import { kase } from '@cee-eng/hydrajs';
11
11
  import { PromisifyModal, ToastNotification, useFetch } from '@rh-support/components';
12
12
  import { fetchAccountCustomEmails, GlobalMetadataDispatchContext, GlobalMetadataStateContext, } from '@rh-support/react-context';
13
13
  import { ability, CaseDetailsFields, resourceActions, resources } from '@rh-support/user-permissions';
14
- import { haventLoadedMetadata, isEmailValid, isEmailValidForCaseContactSelector } from '@rh-support/utils';
14
+ import { haventLoadedMetadata, isEmailValid } from '@rh-support/utils';
15
15
  import findIndex from 'lodash/findIndex';
16
16
  import isEmpty from 'lodash/isEmpty';
17
17
  import isEqual from 'lodash/isEqual';
@@ -44,7 +44,7 @@ export function useCustomEmails(props) {
44
44
  });
45
45
  const addCustomEmail = (emailVal_1, onAdd_1, ...args_1) => __awaiter(this, [emailVal_1, onAdd_1, ...args_1], void 0, function* (emailVal, onAdd, skipAccountCheck = false) {
46
46
  try {
47
- if (isEmpty(emailVal) || !isEmailValidForCaseContactSelector(emailVal)) {
47
+ if (isEmpty(emailVal) || !isEmailValid(emailVal)) {
48
48
  return;
49
49
  }
50
50
  // lower case the email vals to avoid sfdc error
@@ -112,7 +112,6 @@ export function useCustomEmails(props) {
112
112
  canAddCustomEmail,
113
113
  hideCustomEmails,
114
114
  isEmailValid,
115
- isEmailValidForCaseContactSelector,
116
115
  isUpdatingCustomEmails: deleteEmail.isFetching || addNewEmail.isFetching,
117
116
  showAddEmailToAccountModal,
118
117
  };
@@ -1 +1 @@
1
- {"version":3,"file":"CaseComments.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/CaseComments.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AA+BvE,OAAO,KAAkD,MAAM,OAAO,CAAC;AAQvE,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAClF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,kBAAkB,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAChH;AAED,QAAA,MAAM,YAAY,4EAkVhB,CAAC;AAEH,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"CaseComments.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseDiscussion/CaseComments.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,yCAAyC,CAAC;AA+BvE,OAAO,KAAkD,MAAM,OAAO,CAAC;AAQvE,UAAU,MAAM;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAClF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,kBAAkB,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAChH;AAED,QAAA,MAAM,YAAY,4EAuWhB,CAAC;AAEH,eAAe,YAAY,CAAC"}
@@ -137,10 +137,23 @@ const CaseComments = React.forwardRef((props, ref) => {
137
137
  const commentText = (text) => {
138
138
  return { __html: DOMPurify.sanitize(text) };
139
139
  };
140
+ // Transforms <strong> and <em> tags in HTML string to styled <span> tags to fix incorrect bold/italic rendering in exported PDF
141
+ function formatMarkdownHtmlForPDF(htmlString) {
142
+ return (htmlString
143
+ // Handle nested <strong><em>...</em></strong> or <em><strong>...</strong></em> and apply both bold and italic (skew) styles
144
+ .replace(/<(strong|em)>\s*<(em|strong)>(.*?)<\/\2>\s*<\/\1>/gi, '<span style="font-weight: bold; font-size: 0.95em; transform: skewX(-10deg);">$3</span>')
145
+ // Handle standalone <strong>
146
+ .replace(/<strong>(.*?)<\/strong>/gi, '<span style="font-weight: bold; font-size: 0.95em;">$1</span>')
147
+ // Handle standalone <em>
148
+ .replace(/<em>(.*?)<\/em>/gi, '<span style="transform: skewX(-10deg);">$1</span>'));
149
+ }
140
150
  const commentMarkdown = (markdown) => {
141
- const htmlString = parseCommentMarkdown(markdown, { showButtonForAttachmentLink: true, disableImagePreview: isExportingPDF },
151
+ let htmlString = parseCommentMarkdown(markdown, { showButtonForAttachmentLink: true, disableImagePreview: isExportingPDF },
142
152
  // @ts-ignore
143
153
  { openLinksInNewTab: true, gfm: true, breaks: true });
154
+ if (isExportingPDF) {
155
+ htmlString = formatMarkdownHtmlForPDF(htmlString);
156
+ }
144
157
  return { __html: DOMPurify.sanitize(htmlString) };
145
158
  };
146
159
  const onCommentAreaClick = (e) => {
@@ -1 +1 @@
1
- {"version":3,"file":"Timeline.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseHistory/Timeline.tsx"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,CAAC;AA2BxB,OAAO,KAAsC,MAAM,OAAO,CAAC;AAgT3D,QAAA,MAAM,QAAQ;;uBAmUb,CAAC;AAEF,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"Timeline.d.ts","sourceRoot":"","sources":["../../../../../../src/components/CaseEditView/Tabs/CaseHistory/Timeline.tsx"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,CAAC;AA2BxB,OAAO,KAAsC,MAAM,OAAO,CAAC;AAwT3D,QAAA,MAAM,QAAQ;;uBA4Ub,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -22,7 +22,7 @@ import { Trans, useTranslation } from 'react-i18next';
22
22
  const TimelineEvent = ({ date, text, side, user, useAvatar }) => {
23
23
  if (side === 'left') {
24
24
  if (useAvatar) {
25
- // we only want to render the main date if the user changes, otherwise stay with just the time
25
+ // we render the main date if the user changes or if the date changes
26
26
  return (React.createElement("div", { className: `${side}-node ${user}`, role: "complementary", "aria-label": `${user}'s timeline Event Time` },
27
27
  React.createElement("div", { className: `contentTimeline content-${side}-history content-${side}-${user}` },
28
28
  React.createElement("h2", { className: `content-date-${side}-history` }, date),
@@ -187,18 +187,19 @@ const applyReplacements = (item) => {
187
187
  newText = formatBooleanChange(fieldName, item.newValue);
188
188
  if (typeof newText === 'string')
189
189
  newText = newText.replace(/Problem Statement/g, 'Title');
190
+ if (typeof newText === 'string')
191
+ newText = newText.replace(/Case Type/g, 'Support Type');
190
192
  return newText;
191
193
  };
192
194
  // function to change the apiResponse
193
195
  const transformApiResponseToTimelineData = (apiResponse) => {
194
196
  let previousUser = null;
197
+ let previousDate = null;
195
198
  const timelineEvents = apiResponse.historyItems
196
199
  // Filter out 'Case Group' and 'OwnerId' changes because we don't have decode of value
197
200
  .filter((item) => item.fieldName !== 'FolderId__c' && item.fieldName !== 'OwnerId')
198
201
  .map((item, index) => {
199
202
  const currentUser = item.createdByUserName;
200
- const useAvatar = index === 0 || (!!previousUser && currentUser !== previousUser);
201
- previousUser = currentUser;
202
203
  let eventDate;
203
204
  if (typeof item.createdDate === 'string' && item.createdDate.includes('T')) {
204
205
  eventDate = parse(item.createdDate, "yyyy-MM-dd'T'HH:mm:ss'Z'", new Date());
@@ -206,6 +207,12 @@ const transformApiResponseToTimelineData = (apiResponse) => {
206
207
  else {
207
208
  eventDate = fromUnixTime(item.createdDate / 1000);
208
209
  }
210
+ const currentDate = format(eventDate, 'MMMM d, yyyy');
211
+ const useAvatar = index === 0 ||
212
+ (!!previousUser && currentUser !== previousUser) ||
213
+ (!!previousDate && currentDate !== previousDate);
214
+ previousUser = currentUser;
215
+ previousDate = currentDate;
209
216
  const user = item.createdByCustomer !== undefined
210
217
  ? item.createdByCustomer
211
218
  ? 'customer'
@@ -214,7 +221,7 @@ const transformApiResponseToTimelineData = (apiResponse) => {
214
221
  ? 'internal'
215
222
  : 'customer';
216
223
  const leftEvent = {
217
- date: format(eventDate, 'MMMM d, yyyy'),
224
+ date: currentDate,
218
225
  text: format(eventDate, 'h:mm a zzz'),
219
226
  };
220
227
  let outputText = item.outputText;
@@ -287,10 +294,15 @@ const Timeline = ({ caseNumber }) => {
287
294
  };
288
295
  const applyAvatarLogic = (events) => {
289
296
  let previousUser = null;
297
+ let previousDate = null;
290
298
  return events.map((event, index) => {
291
299
  const currentUser = event.right.date.split(' ')[0];
292
- const useAvatar = index === 0 || (!!previousUser && currentUser !== previousUser);
300
+ const currentDate = event.left.date;
301
+ const useAvatar = index === 0 ||
302
+ (!!previousUser && currentUser !== previousUser) ||
303
+ (!!previousDate && currentDate !== previousDate);
293
304
  previousUser = currentUser;
305
+ previousDate = currentDate;
294
306
  return Object.assign(Object.assign({}, event), { right: Object.assign(Object.assign({}, event.right), { useAvatar: useAvatar }) });
295
307
  });
296
308
  };
@@ -395,6 +407,8 @@ const Timeline = ({ caseNumber }) => {
395
407
  return isSameDay(eventDate, selectedDate);
396
408
  });
397
409
  }
410
+ // Reapply avatar logic to filtered events to ensure dates show properly
411
+ finalFilteredEvents = applyAvatarLogic(finalFilteredEvents);
398
412
  setFilteredEvents(finalFilteredEvents);
399
413
  setPage(1);
400
414
  }, [searchValue, dateValue, timelineEvents]);
@@ -114,7 +114,7 @@ function RHAssociatesSelector(props) {
114
114
  return React.createElement(React.Fragment, null);
115
115
  return (React.createElement(React.Fragment, null, canNotifyRHAssociates && (React.createElement("div", { className: "form-group" },
116
116
  React.createElement("label", { className: "react-typeahead-label-wrapper", htmlFor: "get-support-notifications" },
117
- React.createElement(Trans, null, "Red Hat associates"),
117
+ React.createElement(Trans, null, "Internal Contacts"),
118
118
  React.createElement(ContactSelectorInternal, { className: "push-bottom-narrow", selectedContacts: filter(selectedNotificationContacts, (c) => c.isInternal), showSelectedContacts: true, contactsToExclude: !isEmpty(selectedOwner.data) ? [{ ssoUsername: selectedOwner.data.ssoUsername }] : [], id: "open-case-rh-notifications", name: "open-case-rh-notifications", multiple: true, contactListParams: {
119
119
  internal: false, // to get non-ldap contacts only
120
120
  isInternalContact: true,
@@ -198,7 +198,7 @@ function CaseContactSelector(props) {
198
198
  ...customEmailsList.data,
199
199
  ...filter(selectedNotificationContacts, (c) => !c.isInternal),
200
200
  ];
201
- if (xor(alreadySelected, selectedContacts).length === 0)
201
+ if (alreadySelected.length !== 1 && xor(alreadySelected, selectedContacts).length === 0)
202
202
  return;
203
203
  if (canEditCase.alert())
204
204
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rh-support/troubleshoot",
3
- "version": "2.6.7",
3
+ "version": "2.6.9",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "registry": "https://registry.npmjs.org"
@@ -58,11 +58,11 @@
58
58
  "@progress/kendo-licensing": "1.3.5",
59
59
  "@progress/kendo-react-pdf": "^5.16.0",
60
60
  "@redux-devtools/extension": "^3.3.0",
61
- "@rh-support/components": "2.5.20",
62
- "@rh-support/react-context": "2.5.22",
61
+ "@rh-support/components": "2.5.21",
62
+ "@rh-support/react-context": "2.5.23",
63
63
  "@rh-support/types": "2.0.5",
64
- "@rh-support/user-permissions": "2.5.13",
65
- "@rh-support/utils": "2.5.12",
64
+ "@rh-support/user-permissions": "2.5.14",
65
+ "@rh-support/utils": "2.5.13",
66
66
  "@types/react-redux": "^7.1.33",
67
67
  "@types/redux": "^3.6.0",
68
68
  "date-fns": "3.6.0",
@@ -134,5 +134,5 @@
134
134
  "defaults and supports es6-module",
135
135
  "maintained node versions"
136
136
  ],
137
- "gitHead": "223261f3803b043e58594a1cff1d85383169ff1d"
137
+ "gitHead": "dbb149c331bc0fefa148b6158467e0181e49f0bb"
138
138
  }