@pega/react-sdk-overrides 25.1.10 → 25.1.11

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 (28) hide show
  1. package/lib/designSystemExtension/AlertBanner/AlertBanner.css +46 -0
  2. package/lib/designSystemExtension/AlertBanner/AlertBanner.tsx +37 -20
  3. package/lib/designSystemExtension/FieldGroupList/FieldGroupList.tsx +1 -1
  4. package/lib/field/SelectableCard/SelectableCard.tsx +17 -3
  5. package/lib/infra/Assignment/Assignment.tsx +10 -1
  6. package/lib/infra/Assignment/useValidationBanner.ts +29 -0
  7. package/lib/infra/Containers/FlowContainer/FlowContainer.tsx +5 -6
  8. package/lib/infra/Containers/ModalViewContainer/ModalViewContainer.tsx +6 -1
  9. package/lib/infra/Containers/ViewContainer/ViewContainer.tsx +4 -8
  10. package/lib/infra/DeferLoad/DeferLoad.tsx +24 -9
  11. package/lib/infra/NavBar/NavBar.tsx +6 -2
  12. package/lib/infra/Reference/Reference.tsx +5 -0
  13. package/lib/infra/RootContainer/RootContainer.tsx +4 -5
  14. package/lib/infra/View/View.tsx +5 -6
  15. package/lib/template/AppShell/AppShell.css +0 -4
  16. package/lib/template/CaseSummary/CaseSummary.tsx +2 -7
  17. package/lib/template/FieldGroupTemplate/FieldGroupTemplate.tsx +10 -3
  18. package/lib/template/HierarchicalForm/HierarchicalForm.tsx +58 -0
  19. package/lib/template/HierarchicalForm/hooks.ts +224 -0
  20. package/lib/template/HierarchicalForm/index.tsx +1 -0
  21. package/lib/template/ListView/ListView.tsx +73 -15
  22. package/lib/template/MultiReferenceReadOnly/MultiReferenceReadOnly.tsx +16 -1
  23. package/lib/template/ObjectPage/index.tsx +1 -0
  24. package/lib/template/SelfServiceCaseView/SelfServiceCaseView.tsx +51 -43
  25. package/lib/template/SimpleTable/SimpleTableManual/SimpleTableManual.tsx +14 -6
  26. package/lib/template/SimpleTable/SimpleTableSelectReadonly/SimpleTableSelectReadonly.tsx +179 -0
  27. package/lib/template/SimpleTable/SimpleTableSelectReadonly/index.tsx +1 -0
  28. package/package.json +1 -1
@@ -0,0 +1,46 @@
1
+ .alert-banner-title {
2
+ font-weight: 700;
3
+ font-size: 1rem;
4
+ display: flex;
5
+ align-items: center;
6
+ gap: 0.25rem;
7
+ }
8
+
9
+ .alert-banner-badge {
10
+ display: inline-flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+ min-width: 1rem;
14
+ height: 1rem;
15
+ padding: 0 0.2rem;
16
+ border-radius: 999px;
17
+ font-size: 0.6rem;
18
+ font-weight: 700;
19
+ line-height: 1;
20
+ vertical-align: middle;
21
+ }
22
+
23
+ .alert-banner-badge--urgent {
24
+ background: var(--app-error-dark-color);
25
+ color: #fff;
26
+ }
27
+
28
+ .alert-banner-badge--warning {
29
+ background: var(--app-warning-color-dark);
30
+ color: #fff;
31
+ }
32
+
33
+ .alert-banner-badge--success {
34
+ background: var(--stepper-completed-bg-color);
35
+ color: #fff;
36
+ }
37
+
38
+ .alert-banner-badge--info {
39
+ background: var(--link-button-color);
40
+ color: #fff;
41
+ }
42
+
43
+ .alert-banner-list {
44
+ margin: 0;
45
+ padding-left: 1.25rem;
46
+ }
@@ -1,39 +1,56 @@
1
- import { Alert } from '@mui/material';
1
+ import { Alert, AlertTitle } from '@mui/material';
2
+ import './AlertBanner.css';
2
3
 
3
4
  // AlertBanner is one of the few components that does NOT have getPConnect.
4
5
  // So, no need to extend PConnProps
5
6
  interface AlertBannerProps {
6
- // If any, enter additional props that only exist on Date here
7
7
  id: string;
8
8
  variant: string;
9
9
  messages: string[];
10
10
  onDismiss?: any;
11
11
  }
12
12
 
13
- const SEVERITY_MAP = {
14
- urgent: 'error',
15
- warning: 'warning',
16
- success: 'success',
17
- info: 'info'
13
+ const VARIANT_MAP: Record<string, { severity: string; label: string }> = {
14
+ urgent: { severity: 'error', label: 'Error' },
15
+ warning: { severity: 'warning', label: 'Warning' },
16
+ success: { severity: 'success', label: 'Success' },
17
+ info: { severity: 'info', label: 'Info' }
18
18
  };
19
19
 
20
- export default function AlertBanner(props: AlertBannerProps) {
21
- const { id, variant, messages, onDismiss } = props;
22
- let additionalProps = {};
20
+ function renderMessage(message: string) {
21
+ const colonIndex = message.indexOf(':');
22
+ if (colonIndex === -1) return message;
23
+ return (
24
+ <>
25
+ <strong>{message.slice(0, colonIndex + 1)}</strong>
26
+ {message.slice(colonIndex + 1)}
27
+ </>
28
+ );
29
+ }
23
30
 
24
- if (onDismiss) {
25
- additionalProps = {
26
- onClose: onDismiss
27
- };
28
- }
31
+ export default function AlertBanner({ id, variant, messages, onDismiss }: AlertBannerProps) {
32
+ const { severity, label } = VARIANT_MAP[variant] ?? VARIANT_MAP.info;
33
+ const isMultiple = messages.length > 1;
29
34
 
30
35
  return (
31
36
  <div id={id}>
32
- {messages.map(message => (
33
- <Alert key={message} variant='outlined' severity={SEVERITY_MAP[variant]} {...additionalProps}>
34
- {message}
35
- </Alert>
36
- ))}
37
+ <Alert variant='outlined' severity={severity as any} {...(onDismiss && { onClose: onDismiss })}>
38
+ {isMultiple && (
39
+ <AlertTitle className='alert-banner-title'>
40
+ {label}
41
+ <span className={`alert-banner-badge alert-banner-badge--${variant}`}>{messages.length}</span>
42
+ </AlertTitle>
43
+ )}
44
+ {isMultiple ? (
45
+ <ul className='alert-banner-list'>
46
+ {messages.map(message => (
47
+ <li key={message}>{renderMessage(message)}</li>
48
+ ))}
49
+ </ul>
50
+ ) : (
51
+ renderMessage(messages[0])
52
+ )}
53
+ </Alert>
37
54
  </div>
38
55
  );
39
56
  }
@@ -24,7 +24,7 @@ export default function FieldGroupList(props: FieldGroupListProps) {
24
24
  <Grid2 style={{ width: '100%' }}>
25
25
  <Grid2 container spacing={1}>
26
26
  {props.items.map(item => (
27
- <Grid2 style={{ width: '100%' }}>
27
+ <Grid2 key={item.name} style={{ width: '100%' }}>
28
28
  <b>{item.name}</b>
29
29
  {props.onDelete && (
30
30
  <button
@@ -33,6 +33,15 @@ export default function SelectableCard(props) {
33
33
  const cardDataSource = readOnly || displayMode === 'DISPLAY_ONLY' ? readOnlyList || [] : dataSource?.source;
34
34
  const imageDescriptionKey = showImageDescription ? imageDescription : undefined;
35
35
 
36
+ const handleCardClick = event => {
37
+ if (disabled || readOnly) return;
38
+ // If the click landed on or inside a label/input, native behavior already handles it.
39
+ if (event.target.closest?.('label, input')) return;
40
+ // Find the radio/checkbox input inside this card and click it programmatically.
41
+ const input = event.currentTarget.querySelector('input[type="radio"], input[type="checkbox"]');
42
+ if (input) input.click();
43
+ };
44
+
36
45
  let radioItemSelected = false;
37
46
 
38
47
  return (
@@ -75,7 +84,12 @@ export default function SelectableCard(props) {
75
84
 
76
85
  const component = (
77
86
  <div key={item[recordKey]} style={{ paddingTop: '15px' }}>
78
- <Card className={className} style={{ display: 'flex', flexDirection: 'column', height: '100%' }} data-testid={testId}>
87
+ <Card
88
+ className={className}
89
+ style={{ display: 'flex', flexDirection: 'column', height: '100%', cursor: disabled || readOnly ? 'default' : 'pointer' }}
90
+ data-testid={testId}
91
+ onClick={handleCardClick}
92
+ >
79
93
  <CardContent
80
94
  style={{
81
95
  ...((imagePosition === 'inline-start' || imagePosition === 'inline-end') && { display: 'flex', height: '100%' }),
@@ -118,7 +132,7 @@ export default function SelectableCard(props) {
118
132
  onBlur={onBlur}
119
133
  onClick={onClick}
120
134
  onKeyDown={onKeyDown}
121
- disabled={disabled}
135
+ disabled={disabled || readOnly}
122
136
  {...additionalProps}
123
137
  />
124
138
  }
@@ -135,7 +149,7 @@ export default function SelectableCard(props) {
135
149
  onBlur={onBlur}
136
150
  onClick={onClick}
137
151
  onKeyDown={onKeyDown}
138
- disabled={disabled}
152
+ disabled={disabled || readOnly}
139
153
  {...additionalProps}
140
154
  />
141
155
  }
@@ -5,6 +5,8 @@ import CloseIcon from '@mui/icons-material/Close';
5
5
 
6
6
  import { getComponentFromMap } from '@pega/react-sdk-components/lib/bridge/helpers/sdk_component_map';
7
7
  import { useFocusFirstField, useScrolltoTop } from '@pega/react-sdk-components/lib/hooks';
8
+ import AlertBanner from '@pega/react-sdk-components/lib/components/designSystemExtension/AlertBanner/AlertBanner';
9
+ import { useValidationBanner } from './useValidationBanner';
8
10
 
9
11
  import type { PConnProps } from '@pega/react-sdk-components/lib/types/PConnProps';
10
12
 
@@ -23,6 +25,7 @@ export default function Assignment(props: PropsWithChildren<AssignmentProps>) {
23
25
  const MultiStep = getComponentFromMap('MultiStep');
24
26
 
25
27
  const { getPConnect, children, itemKey = '', isInModal = false, banners = [] } = props;
28
+ const validationMessages = useValidationBanner(itemKey);
26
29
  const thePConn = getPConnect();
27
30
 
28
31
  const [bHasNavigation, setHasNavigation] = useState(false);
@@ -88,8 +91,9 @@ export default function Assignment(props: PropsWithChildren<AssignmentProps>) {
88
91
  }
89
92
 
90
93
  const scrollId = window.location.href.includes('embedded') ? '#pega-part-of-page' : '#portal';
94
+ const currentAssignmentViewName = getPConnect().getCaseInfo().getCurrentAssignmentViewName();
91
95
  useScrolltoTop(scrollId, children);
92
- useFocusFirstField('Assignment', children);
96
+ useFocusFirstField('Assignment', currentAssignmentViewName);
93
97
 
94
98
  useEffect(() => {
95
99
  if (children) {
@@ -310,6 +314,11 @@ export default function Assignment(props: PropsWithChildren<AssignmentProps>) {
310
314
 
311
315
  return (
312
316
  <div id='Assignment'>
317
+ {validationMessages.length > 0 && (
318
+ <div style={{ marginBottom: '1rem' }}>
319
+ <AlertBanner id={`validation-banner-${itemKey}`} variant='urgent' messages={validationMessages} />
320
+ </div>
321
+ )}
313
322
  {banners}
314
323
  {bHasNavigation ? (
315
324
  <>
@@ -0,0 +1,29 @@
1
+ import { useState, useEffect, useCallback } from 'react';
2
+
3
+ function formatError(error: any, localizedVal: Function): string {
4
+ if (typeof error === 'string') return localizedVal(error, 'Messages');
5
+ const label = error.label?.endsWith(':') ? error.label : `${error.label}:`;
6
+ return localizedVal(`${label} ${error.description}`, 'Messages');
7
+ }
8
+
9
+ /**
10
+ * Subscribes to the PCore store and returns formatted validation error messages
11
+ * for the given itemKey. Reacts to any ADD_MESSAGES / CLEAR_MESSAGES dispatch
12
+ * (blur, tab, submit, etc.) so the banner appears and clears in real time.
13
+ */
14
+ export function useValidationBanner(itemKey: string): string[] {
15
+ const [messages, setMessages] = useState<string[]>([]);
16
+
17
+ const readMessages = useCallback(() => {
18
+ const localizedVal = PCore.getLocaleUtils().getLocaleValue;
19
+ const errors: any[] = PCore.getMessageManager().getValidationErrorMessages(itemKey) || [];
20
+ setMessages(errors.map(error => formatError(error, localizedVal)));
21
+ }, [itemKey]);
22
+
23
+ useEffect(() => {
24
+ readMessages();
25
+ return PCore.getStore().subscribe(readMessages);
26
+ }, [readMessages]);
27
+
28
+ return messages;
29
+ }
@@ -22,11 +22,10 @@ interface FlowContainerProps extends PConnProps {
22
22
  activeContainerItemID: string;
23
23
  }
24
24
 
25
- //
26
- // WARNING: It is not expected that this file should be modified. It is part of infrastructure code that works with
27
- // Redux and creation/update of Redux containers and PConnect. Modifying this code could have undesireable results and
28
- // is totally at your own risk.
29
- //
25
+ /**
26
+ * WARNING: This file is part of the infrastructure component responsible for working with Redux and managing the creation and update of Redux containers and PConnect.
27
+ * You may override Material components within this component if needed, but do not modify any container-related logic. Changing this logic can lead to unexpected behavior.
28
+ */
30
29
 
31
30
  const useStyles = makeStyles(theme => ({
32
31
  root: {
@@ -199,7 +198,7 @@ export const FlowContainer = (props: FlowContainerProps) => {
199
198
  }, [props]);
200
199
 
201
200
  const caseId = thePConn.getCaseSummary().content.pyID;
202
- const urgency = getPConnect().getCaseSummary().assignments ? getPConnect().getCaseSummary().assignments?.[0].urgency : '';
201
+ const urgency = getPConnect().getCaseSummary().assignments ? getPConnect().getCaseSummary().assignments?.[0]?.urgency : '';
203
202
  const operatorInitials = Utils.getInitials(PCore.getEnvironmentInfo().getOperatorName() || '');
204
203
 
205
204
  const displayPageMessages = () => {
@@ -13,6 +13,11 @@ import { getComponentFromMap } from '@pega/react-sdk-components/lib/bridge/helpe
13
13
  import { getBanners } from '@pega/react-sdk-components/lib/components/helpers/case-utils';
14
14
  import type { PConnProps } from '@pega/react-sdk-components/lib/types/PConnProps';
15
15
 
16
+ /**
17
+ * WARNING: This file is part of the infrastructure component responsible for working with Redux and managing the creation and update of Redux containers and PConnect.
18
+ * You may override Material components within this component if needed, but do not modify any container-related logic. Changing this logic can lead to unexpected behavior.
19
+ */
20
+
16
21
  interface ModalViewContainerProps extends PConnProps {
17
22
  // If any, enter additional props that only exist on this component
18
23
  loadingInfo?: string;
@@ -310,7 +315,7 @@ export default function ModalViewContainer(props: ModalViewContainerProps) {
310
315
 
311
316
  return (
312
317
  <>
313
- <Dialog open={bShowModal} aria-labelledby='form-dialog-title' maxWidth={false}>
318
+ <Dialog open={bShowModal} aria-labelledby='form-dialog-title' maxWidth='sm' fullWidth>
314
319
  <DialogTitle id='form-dialog-title' className={`${classes.dlgTitle} psdk-dialog-title`}>
315
320
  {title}
316
321
  </DialogTitle>
@@ -17,14 +17,10 @@ interface ViewContainerProps extends PConnProps {
17
17
  limit?: number;
18
18
  }
19
19
 
20
- // ViewContainer can emit View
21
- // import View from '../View';
22
-
23
- //
24
- // WARNING: It is not expected that this file should be modified. It is part of infrastructure code that works with
25
- // Redux and creation/update of Redux containers and PConnect. Modifying this code could have undesireable results and
26
- // is totally at your own risk.
27
- //
20
+ /**
21
+ * WARNING: This file is part of the infrastructure component responsible for working with Redux and managing the creation and update of Redux containers and PConnect.
22
+ * You may override Material components within this component if needed, but do not modify any container-related logic. Changing this logic can lead to unexpected behavior.
23
+ */
28
24
 
29
25
  export default function ViewContainer(props: ViewContainerProps) {
30
26
  // const { getPConnect, children, routingInfo, name } = props;
@@ -13,13 +13,13 @@ interface DeferLoadProps extends PConnProps {
13
13
  isTab: boolean;
14
14
  deferLoadId: string;
15
15
  lastUpdateCaseTime: any;
16
+ template?: string;
16
17
  }
17
18
 
18
- //
19
- // WARNING: It is not expected that this file should be modified. It is part of infrastructure code that works with
20
- // Redux and creation/update of Redux containers and PConnect. Modifying this code could have undesireable results and
21
- // is totally at your own risk.
22
- //
19
+ /**
20
+ * WARNING: This file is part of the infrastructure component responsible for working with Redux and managing the creation and update of Redux containers and PConnect.
21
+ * You may override Material components within this component if needed, but do not modify any container-related logic. Changing this logic can lead to unexpected behavior.
22
+ */
23
23
 
24
24
  const useStyles = makeStyles(theme => ({
25
25
  root: {
@@ -35,7 +35,7 @@ const useStyles = makeStyles(theme => ({
35
35
  }));
36
36
 
37
37
  export default function DeferLoad(props: DeferLoadProps) {
38
- const { getPConnect, name, deferLoadId, isTab, lastUpdateCaseTime } = props;
38
+ const { getPConnect, name, deferLoadId, isTab, lastUpdateCaseTime, template } = props;
39
39
  const [content, setContent] = useState<any>(null);
40
40
  const [isLoading, setLoading] = useState(true);
41
41
  const [currentLoadedAssignment, setCurrentLoadedAssignment] = useState('');
@@ -75,7 +75,7 @@ export default function DeferLoad(props: DeferLoadProps) {
75
75
  updateData: isContainerPreview
76
76
  });
77
77
 
78
- const onResponse = data => {
78
+ const onResponse = (data: any, isEditable = false) => {
79
79
  setLoading(false);
80
80
  if (deferLoadId) {
81
81
  PCore.getDeferLoadManager().start(
@@ -92,11 +92,16 @@ export default function DeferLoad(props: DeferLoadProps) {
92
92
  meta: data,
93
93
  options: {
94
94
  context: pConnect.getContextName(),
95
- pageReference: pConnect.getPageReference()
95
+ pageReference: pConnect.getPageReference(),
96
+ target: pConnect.getTarget(),
97
+ hasForm: true,
98
+ viewName: (pConnect as any).viewName
96
99
  }
97
100
  };
98
101
  const configObject = PCore.createPConnect(config);
99
- configObject.getPConnect().setInheritedProp('displayMode', 'DISPLAY_ONLY');
102
+ if (!isEditable) {
103
+ configObject.getPConnect().setInheritedProp('displayMode', 'DISPLAY_ONLY');
104
+ }
100
105
  setContent(createElement(createPConnectComponent(), configObject));
101
106
  if (deferLoadId) {
102
107
  PCore.getDeferLoadManager().stop(deferLoadId, getPConnect().getContextName());
@@ -132,6 +137,16 @@ export default function DeferLoad(props: DeferLoadProps) {
132
137
  .then(data => {
133
138
  onResponse(data);
134
139
  });
140
+ } else if (template === 'HierarchicalForm') {
141
+ const root = {
142
+ config: {
143
+ context: pConnect.getPageReference(),
144
+ name,
145
+ type: 'view'
146
+ },
147
+ type: 'reference'
148
+ };
149
+ onResponse(root, true);
135
150
  } else {
136
151
  getPConnect()
137
152
  .getActionsApi()
@@ -150,12 +150,14 @@ export default function NavBar(props: NavBarProps) {
150
150
 
151
151
  function navPanelButtonClick(oPageData: any) {
152
152
  const { pyClassName, pyRuleName } = oPageData;
153
-
154
153
  pConn
155
154
  .getActionsApi()
156
155
  .showPage(pyRuleName, pyClassName)
157
156
  .then(() => {
158
157
  console.log(`${localizedVal('showPage completed', localeCategory)}`);
158
+ })
159
+ .catch(error => {
160
+ console.error('Failed to navigate to page from NavBar', error);
159
161
  });
160
162
  }
161
163
 
@@ -165,12 +167,14 @@ export default function NavBar(props: NavBarProps) {
165
167
  containerName: 'primary',
166
168
  flowType: sFlowType || 'pyStartCase'
167
169
  };
168
-
169
170
  pConn
170
171
  .getActionsApi()
171
172
  .createWork(sCaseType, actionInfo)
172
173
  .then(() => {
173
174
  console.log(`${localizedVal('createWork completed', localeCategory)}`);
175
+ })
176
+ .catch(error => {
177
+ console.error('Failed to create case from NavBar', error);
174
178
  });
175
179
  }
176
180
 
@@ -38,6 +38,11 @@ export default function Reference(props: ReferenceProps) {
38
38
  pageReference: context && context.startsWith('@CLASS') ? '' : context
39
39
  });
40
40
 
41
+ if (referenceConfig.inheritedProps && referenceConfig.inheritedProps.length > 0) {
42
+ const inheritedProps = pConnect.getInheritedProps();
43
+ referenceConfig.inheritedProps = Object.keys(inheritedProps).map(prop => ({ prop, value: inheritedProps[prop] }));
44
+ }
45
+
41
46
  viewComponent.props.getPConnect().setInheritedConfig({
42
47
  ...referenceConfig,
43
48
  readOnly,
@@ -15,11 +15,10 @@ interface RootContainerProps extends PConnProps {
15
15
  httpMessages: any[];
16
16
  }
17
17
 
18
- //
19
- // WARNING: It is not expected that this file should be modified. It is part of infrastructure code that works with
20
- // Redux and creation/update of Redux containers and PConnect. Modifying this code could have undesireable results and
21
- // is totally at your own risk.
22
- //
18
+ /**
19
+ * WARNING: This file is part of the infrastructure component responsible for working with Redux and managing the creation and update of Redux containers and PConnect.
20
+ * You may override Material components within this component if needed, but do not modify any container-related logic. Changing this logic can lead to unexpected behavior.
21
+ */
23
22
 
24
23
  const renderingModes = ['portal', 'view'];
25
24
 
@@ -21,13 +21,12 @@ interface ViewProps extends PConnProps {
21
21
  type?: any;
22
22
  }
23
23
 
24
- //
25
- // WARNING: It is not expected that this file should be modified. It is part of infrastructure code that works with
26
- // Redux and creation/update of Redux containers and PConnect. Modifying this code could have undesireable results and
27
- // is totally at your own risk.
28
- //
24
+ /**
25
+ * WARNING: This file is part of the infrastructure component responsible for working with Redux and managing the creation and update of Redux containers and PConnect.
26
+ * You may override Material components within this component if needed, but do not modify any container-related logic. Changing this logic can lead to unexpected behavior.
27
+ */
29
28
 
30
- const FORMTEMPLATES = ['OneColumn', 'TwoColumn', 'DefaultForm', 'WideNarrow', 'NarrowWide'];
29
+ const FORMTEMPLATES = ['OneColumn', 'TwoColumn', 'DefaultForm', 'WideNarrow', 'NarrowWide', 'HierarchicalForm'];
31
30
  const NO_HEADER_TEMPLATES = [
32
31
  'SubTabs',
33
32
  'SimpleTable',
@@ -33,7 +33,3 @@
33
33
  .progress-spinner {
34
34
  text-align: center;
35
35
  }
36
-
37
- ::ng-deep snack-bar-container.snackbar-newline {
38
- white-space: pre-line;
39
- }
@@ -12,14 +12,9 @@ export default function CaseSummary(props: PropsWithChildren<CaseSummaryProps>)
12
12
  // Get emitted components from map (so we can get any override that may exist)
13
13
  const CaseSummaryFields = getComponentFromMap('CaseSummaryFields');
14
14
 
15
- const { getPConnect, children } = props;
15
+ const { children } = props;
16
16
  let { arPrimaryFields = [], arSecondaryFields = [] } = props;
17
17
 
18
- const thePConn = getPConnect && getPConnect();
19
- const theConfigProps: any = thePConn?.getConfigProps();
20
-
21
- const status = theConfigProps?.status;
22
- const showStatus = theConfigProps?.showStatus;
23
18
  const localizedVal = PCore.getLocaleUtils().getLocaleValue;
24
19
  const localeCategory = 'ModalContainer';
25
20
 
@@ -77,7 +72,7 @@ export default function CaseSummary(props: PropsWithChildren<CaseSummaryProps>)
77
72
 
78
73
  return (
79
74
  <div id='CaseSummary'>
80
- <CaseSummaryFields status={status} showStatus={showStatus} theFields={arPrimaryFields} />
75
+ <CaseSummaryFields theFields={arPrimaryFields} />
81
76
  <CaseSummaryFields theFields={arSecondaryFields} />
82
77
  </div>
83
78
  );
@@ -14,6 +14,7 @@ interface FieldGroupTemplateProps extends PConnProps {
14
14
  displayMode?: string;
15
15
  fieldHeader?: string;
16
16
  allowTableEdit: boolean;
17
+ allowActions?: any;
17
18
  }
18
19
 
19
20
  export default function FieldGroupTemplate(props: FieldGroupTemplateProps) {
@@ -30,6 +31,7 @@ export default function FieldGroupTemplate(props: FieldGroupTemplateProps) {
30
31
  heading = '',
31
32
  displayMode,
32
33
  fieldHeader,
34
+ allowActions,
33
35
  allowTableEdit: allowAddEdit
34
36
  } = props;
35
37
  const pConn = getPConnect();
@@ -39,6 +41,11 @@ export default function FieldGroupTemplate(props: FieldGroupTemplateProps) {
39
41
  const isReadonlyMode = renderMode === 'ReadOnly' || displayMode === 'DISPLAY_ONLY';
40
42
  const HEADING = heading ?? 'Row';
41
43
 
44
+ const hasAllowActions = Object.keys(allowActions ?? {}).length > 0;
45
+ const { allowAdd: actionAdd, allowDelete: actionDelete } = allowActions ?? {};
46
+ const allowAdd = hasAllowActions ? (actionAdd ?? true) : (allowAddEdit ?? true);
47
+ const allowDelete = hasAllowActions ? (actionDelete ?? true) : (allowAddEdit ?? true);
48
+
42
49
  useLayoutEffect(() => {
43
50
  if (!isReadonlyMode) {
44
51
  // @ts-expect-error - Expected 3 arguments, but got 1
@@ -72,7 +79,7 @@ export default function FieldGroupTemplate(props: FieldGroupTemplateProps) {
72
79
  pConn.getListActions().deleteEntry(index);
73
80
  }
74
81
  };
75
- if (referenceList.length === 0 && allowAddEdit !== false) {
82
+ if (referenceList.length === 0 && allowAdd) {
76
83
  addFieldGroupItem();
77
84
  }
78
85
 
@@ -87,8 +94,8 @@ export default function FieldGroupTemplate(props: FieldGroupTemplateProps) {
87
94
  return (
88
95
  <FieldGroupList
89
96
  items={MemoisedChildren}
90
- onAdd={allowAddEdit !== false ? addFieldGroupItem : undefined}
91
- onDelete={allowAddEdit !== false ? deleteFieldGroupItem : undefined}
97
+ onAdd={allowAdd ? addFieldGroupItem : undefined}
98
+ onDelete={allowDelete ? deleteFieldGroupItem : undefined}
92
99
  />
93
100
  );
94
101
  }
@@ -0,0 +1,58 @@
1
+ import { type PropsWithChildren } from 'react';
2
+ import { Tab, Tabs, Box, Badge, Tooltip } from '@mui/material';
3
+ import { TabContext, TabPanel } from '@mui/lab';
4
+
5
+ import { useHierarchicalForm, type HierarchicalFormProps } from './hooks';
6
+
7
+ export default function HierarchicalForm(props: PropsWithChildren<HierarchicalFormProps>) {
8
+ const { currentTabId, handleTabClick, processedTabs, instructions } = useHierarchicalForm(props);
9
+
10
+ if (!currentTabId) {
11
+ return null;
12
+ }
13
+
14
+ return (
15
+ <Box display='flex' flexDirection='column'>
16
+ {instructions && (
17
+ <Box mb={2}>
18
+ <div dangerouslySetInnerHTML={{ __html: instructions }} />
19
+ </Box>
20
+ )}
21
+ <Box sx={{ flexGrow: 1 }}>
22
+ <TabContext value={currentTabId.toString()}>
23
+ <Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
24
+ <Tabs onChange={handleTabClick} value={currentTabId.toString()} variant='scrollable'>
25
+ {processedTabs.map((tab: any) => {
26
+ const tabLabel = tab.name || tab.label;
27
+ return (
28
+ <Tab
29
+ key={tab.id}
30
+ sx={{ textTransform: 'none' }}
31
+ label={
32
+ tab.errors ? (
33
+ <Tooltip title={`${tabLabel} has errors`} placement='top'>
34
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
35
+ <span>{tabLabel}</span>
36
+ <Badge badgeContent={tab.errors} color='error' sx={{ '& .MuiBadge-badge': { position: 'static', transform: 'none' } }} />
37
+ </Box>
38
+ </Tooltip>
39
+ ) : (
40
+ tabLabel
41
+ )
42
+ }
43
+ value={tab.id?.toString()}
44
+ />
45
+ );
46
+ })}
47
+ </Tabs>
48
+ </Box>
49
+ {processedTabs.map((tab: any) => (
50
+ <TabPanel key={tab.id} value={tab.id?.toString()} tabIndex={+tab.id} keepMounted sx={{ px: 0 }}>
51
+ <div>{tab.content ? tab.content : 'No content exists'}</div>
52
+ </TabPanel>
53
+ ))}
54
+ </TabContext>
55
+ </Box>
56
+ </Box>
57
+ );
58
+ }