@pega/react-sdk-overrides 0.25.8 → 0.25.12
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.
- package/lib/designSystemExtension/AlertBanner/AlertBanner.css +46 -0
- package/lib/designSystemExtension/AlertBanner/AlertBanner.tsx +37 -20
- package/lib/designSystemExtension/FieldGroupList/FieldGroupList.tsx +1 -1
- package/lib/field/SelectableCard/SelectableCard.tsx +17 -3
- package/lib/infra/Assignment/Assignment.tsx +10 -1
- package/lib/infra/Assignment/useValidationBanner.ts +29 -0
- package/lib/infra/Containers/FlowContainer/FlowContainer.tsx +5 -6
- package/lib/infra/Containers/ModalViewContainer/ModalViewContainer.tsx +6 -1
- package/lib/infra/Containers/ViewContainer/ViewContainer.tsx +4 -8
- package/lib/infra/DeferLoad/DeferLoad.tsx +24 -9
- package/lib/infra/NavBar/NavBar.tsx +6 -2
- package/lib/infra/Reference/Reference.tsx +5 -0
- package/lib/infra/RootContainer/RootContainer.tsx +4 -5
- package/lib/infra/View/View.tsx +5 -6
- package/lib/template/AppShell/AppShell.css +0 -4
- package/lib/template/CaseSummary/CaseSummary.tsx +2 -7
- package/lib/template/FieldGroupTemplate/FieldGroupTemplate.tsx +10 -3
- package/lib/template/HierarchicalForm/HierarchicalForm.tsx +58 -0
- package/lib/template/HierarchicalForm/hooks.ts +224 -0
- package/lib/template/HierarchicalForm/index.tsx +1 -0
- package/lib/template/ListView/ListView.tsx +73 -15
- package/lib/template/MultiReferenceReadOnly/MultiReferenceReadOnly.tsx +16 -1
- package/lib/template/ObjectPage/index.tsx +1 -0
- package/lib/template/SelfServiceCaseView/SelfServiceCaseView.tsx +51 -43
- package/lib/template/SimpleTable/SimpleTableManual/SimpleTableManual.tsx +14 -6
- package/lib/template/SimpleTable/SimpleTableSelectReadonly/SimpleTableSelectReadonly.tsx +179 -0
- package/lib/template/SimpleTable/SimpleTableSelectReadonly/index.tsx +1 -0
- 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
|
|
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
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
{
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
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',
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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]
|
|
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=
|
|
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
|
-
|
|
21
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
package/lib/infra/View/View.tsx
CHANGED
|
@@ -21,13 +21,12 @@ interface ViewProps extends PConnProps {
|
|
|
21
21
|
type?: any;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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',
|
|
@@ -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 {
|
|
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
|
|
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 &&
|
|
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={
|
|
91
|
-
onDelete={
|
|
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
|
+
}
|