ui-soxo-bootstrap-core 2.6.42 → 2.6.44
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/core/components/index.js +2 -11
- package/core/components/landing-api/landing-api.js +23 -0
- package/core/components/license-management/license-alert.js +97 -0
- package/core/lib/components/global-header/global-header.js +19 -75
- package/core/lib/components/global-header/global-header.scss +3 -1
- package/core/models/menus/menus.js +6 -0
- package/core/modules/steps/steps.js +4 -2
- package/core/modules/steps/steps.scss +37 -7
- package/package.json +1 -1
package/core/components/index.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
1
|
import LandingAPI from './landing-api/landing-api';
|
|
6
2
|
|
|
7
3
|
import ExtraInfoDetail from './extra-info/extra-info-details';
|
|
@@ -11,11 +7,6 @@ import RootApplicationAPI from './root-application-api/root-application-api';
|
|
|
11
7
|
import { HomePageAPI } from '../modules';
|
|
12
8
|
|
|
13
9
|
import { ExternalWindow } from './external-window/external-window';
|
|
10
|
+
import LicenseAlert from './license-management/license-alert';
|
|
14
11
|
|
|
15
|
-
export {
|
|
16
|
-
LandingAPI,
|
|
17
|
-
RootApplicationAPI,
|
|
18
|
-
ExtraInfoDetail,
|
|
19
|
-
HomePageAPI,
|
|
20
|
-
ExternalWindow
|
|
21
|
-
}
|
|
12
|
+
export { LandingAPI, RootApplicationAPI, ExtraInfoDetail, HomePageAPI, ExternalWindow, LicenseAlert };
|
|
@@ -59,11 +59,29 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
59
59
|
|
|
60
60
|
const [meta, setMeta] = useState({});
|
|
61
61
|
const [loadingMessage, setLoadingMessage] = useState('');
|
|
62
|
+
const [licenseData, setLicenseData] = useState(null);
|
|
63
|
+
const [licAlert, setLicAlert] = useState(false);
|
|
62
64
|
|
|
63
65
|
// const [reports, setReports] = useState([]);
|
|
64
66
|
|
|
65
67
|
var config = {};
|
|
66
68
|
|
|
69
|
+
//fetch license summary
|
|
70
|
+
const fetchSummary = async () => {
|
|
71
|
+
try {
|
|
72
|
+
const res = await MenusAPI.getSummary();
|
|
73
|
+
if (res?.data) {
|
|
74
|
+
setLicenseData(res?.data);
|
|
75
|
+
setLicAlert(true);
|
|
76
|
+
} else {
|
|
77
|
+
setLicenseData(null);
|
|
78
|
+
setLicAlert(false);
|
|
79
|
+
}
|
|
80
|
+
} catch (err) {
|
|
81
|
+
console.error(err);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
67
85
|
// Variable decides the control of homepage
|
|
68
86
|
// #TODO This is a temporary fix - Homemage
|
|
69
87
|
|
|
@@ -138,9 +156,12 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
138
156
|
*/
|
|
139
157
|
async function initializeUserMenus() {
|
|
140
158
|
// need to find what implement, with a login who has the respective value ("wug_custreportids")
|
|
159
|
+
|
|
141
160
|
const report = await loadScripts(user);
|
|
142
161
|
|
|
143
162
|
await loadMenus(report);
|
|
163
|
+
// fetch license summary
|
|
164
|
+
fetchSummary();
|
|
144
165
|
}
|
|
145
166
|
|
|
146
167
|
// const keyMap = {
|
|
@@ -292,6 +313,8 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
292
313
|
modules={allModules}
|
|
293
314
|
user={user}
|
|
294
315
|
history={history}
|
|
316
|
+
licenseData={licenseData}
|
|
317
|
+
licAlert={licAlert}
|
|
295
318
|
>
|
|
296
319
|
{loader ? (
|
|
297
320
|
<Card className="skeleton-card">
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Alert } from 'antd';
|
|
2
|
+
import React, { useState, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
export default function LicenseAlert({ data }) {
|
|
5
|
+
// setting visibility of alert based on license status
|
|
6
|
+
const [visible, setVisible] = useState(true);
|
|
7
|
+
// resolve alert configuration based on license data
|
|
8
|
+
const alertConfig = resolveLicenseAlert(data);
|
|
9
|
+
// auto-hide alert after 10 seconds or when data changes
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (alertConfig) {
|
|
12
|
+
setVisible(true);
|
|
13
|
+
|
|
14
|
+
const timer = setTimeout(() => {
|
|
15
|
+
setVisible(false);
|
|
16
|
+
}, 10000); // 10 seconds
|
|
17
|
+
|
|
18
|
+
return () => {
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}, [data]);
|
|
23
|
+
// if no alert configuration or not visible, render nothing
|
|
24
|
+
if (!alertConfig || !visible) return null;
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
// render the alert with appropriate type, message, and description
|
|
28
|
+
<Alert
|
|
29
|
+
type={alertConfig.type}
|
|
30
|
+
message={alertConfig.message}
|
|
31
|
+
description={alertConfig.description}
|
|
32
|
+
showIcon
|
|
33
|
+
closable
|
|
34
|
+
onClose={() => setVisible(false)}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
// function to determine alert configuration based on license data
|
|
39
|
+
function resolveLicenseAlert(data) {
|
|
40
|
+
if (!data) return null;
|
|
41
|
+
// destructure relevant fields from license data
|
|
42
|
+
const { status, expiresInDays, isExpiringSoon, gracePeriod } = data;
|
|
43
|
+
|
|
44
|
+
// ===== NOT INSTALLED =====
|
|
45
|
+
if (status === 'NOT_INSTALLED') {
|
|
46
|
+
return {
|
|
47
|
+
type: 'error',
|
|
48
|
+
message: 'License not found',
|
|
49
|
+
description: 'Please install a valid license to continue.',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ===== GRACE PERIOD =====
|
|
54
|
+
if (gracePeriod) {
|
|
55
|
+
return {
|
|
56
|
+
type: 'warning',
|
|
57
|
+
message: 'Grace period mode',
|
|
58
|
+
description: 'License expired. Running in read-only mode.',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ===== EXPIRING SOON =====
|
|
63
|
+
if (status === 'ACTIVE' && isExpiringSoon) {
|
|
64
|
+
let descriptionText = '';
|
|
65
|
+
// customize message based on how soon the license is expiring
|
|
66
|
+
if (expiresInDays === 0) {
|
|
67
|
+
descriptionText = 'Your license will expire today. Please renew immediately.';
|
|
68
|
+
} else {
|
|
69
|
+
descriptionText = `Your license will expire in ${expiresInDays} days. Please plan for renewal.`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
type: 'warning',
|
|
74
|
+
message: 'License expiring soon',
|
|
75
|
+
description: descriptionText,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ===== NOT INSTALLED =====
|
|
80
|
+
if (status === 'NOT_INSTALLED') {
|
|
81
|
+
return {
|
|
82
|
+
type: 'error',
|
|
83
|
+
message: 'License not found',
|
|
84
|
+
description: 'Please install a valid license to continue.',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// =====EXPIRED=====
|
|
88
|
+
if (status === 'EXPIRED') {
|
|
89
|
+
return {
|
|
90
|
+
type: 'error',
|
|
91
|
+
message: 'License expired',
|
|
92
|
+
description: 'Your license has expired. Please renew or install a new license.',
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
@@ -2,136 +2,93 @@
|
|
|
2
2
|
*
|
|
3
3
|
* Global header component
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
5
|
import { useContext, useEffect, useRef, useState } from 'react';
|
|
7
|
-
|
|
8
6
|
import { motion, useAnimation } from 'framer-motion';
|
|
9
|
-
|
|
10
7
|
import { boxVariants } from './animations';
|
|
11
|
-
|
|
12
8
|
import { GlobalContext, GlobalProvider } from './../../Store';
|
|
13
|
-
|
|
14
9
|
import { Link, useLocation } from 'react-router-dom';
|
|
15
|
-
|
|
16
10
|
import { Avatar, Input, Tooltip, Typography } from 'antd';
|
|
17
|
-
|
|
18
11
|
import ProgressBar from '../progress-bar/progress-bar'; // Adjust the path as needed
|
|
19
|
-
|
|
20
12
|
import { Button } from '../../elements';
|
|
21
|
-
|
|
22
13
|
import GenericHeader from '../header/generic-header';
|
|
23
|
-
|
|
24
|
-
|
|
25
14
|
import { CustomerServiceOutlined, MenuOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
|
|
26
|
-
|
|
27
15
|
import { Drawer } from 'antd';
|
|
28
|
-
|
|
29
16
|
import { ReloadOutlined, SearchOutlined } from '@ant-design/icons';
|
|
30
|
-
|
|
31
17
|
import SideMenu from './../sidemenu/sidemenu';
|
|
32
|
-
|
|
33
18
|
import './global-header.scss';
|
|
34
|
-
|
|
35
19
|
import LanguageSwitcher from '../language-switcher/language-switcher';
|
|
36
|
-
|
|
37
20
|
import { useTranslation } from 'react-i18next';
|
|
38
|
-
|
|
39
21
|
import SettingsUtil from '../../../utils/settings.utils';
|
|
40
22
|
import SpotlightSearch from '../spotlight-search/spotlight-search.component';
|
|
23
|
+
import LicenseAlert from '../../../components/license-management/license-alert';
|
|
41
24
|
|
|
42
25
|
const { Title } = Typography;
|
|
43
|
-
|
|
44
|
-
|
|
26
|
+
function GlobalHeaderContent({ loading, appSettings, children, isConnected, history, modules = [], sidemenu = [], reload, meta = {}, licAlert,
|
|
27
|
+
licenseData,...props }) {
|
|
45
28
|
let location = useLocation();
|
|
46
29
|
// let location = {};
|
|
47
|
-
|
|
48
30
|
const { isMobile, user = { locations: [] }, kiosk, state, settings } = useContext(GlobalContext);
|
|
49
|
-
|
|
50
31
|
const [visible, setVisible] = useState(false);
|
|
51
32
|
const helpDeskSetting = settings?.HELPATR || {};
|
|
52
|
-
|
|
53
33
|
// Variable to handle toggling of menu
|
|
54
34
|
const [collapsed, setCollapsed] = useState(false);
|
|
55
35
|
// varibale handle branch switcher
|
|
56
|
-
|
|
57
36
|
// const [searchModalVisible, setSearchModalVisible] = useState(false);
|
|
58
|
-
|
|
59
37
|
const { globalCustomerHeader = () => {} } = appSettings;
|
|
60
|
-
|
|
61
38
|
const { t, i18n } = useTranslation();
|
|
62
|
-
|
|
63
39
|
const spotlightRef = useRef();
|
|
64
|
-
|
|
65
40
|
useEffect(() => {
|
|
66
41
|
setTimeout(() => {
|
|
67
42
|
i18n.changeLanguage(localStorage.selectedLanguage);
|
|
68
43
|
}, 0);
|
|
69
44
|
}, []);
|
|
70
|
-
|
|
71
45
|
/**
|
|
72
46
|
* Function to handle toggling of menu
|
|
73
47
|
*/
|
|
74
|
-
|
|
75
48
|
const toggleCollapsed = () => {
|
|
76
49
|
setVisible(true);
|
|
77
|
-
|
|
78
50
|
collapsed === true ? setCollapsed(false) : setCollapsed(true);
|
|
79
51
|
};
|
|
80
|
-
|
|
81
52
|
const openSearchModal = () => {
|
|
82
53
|
// input to avoid typing
|
|
83
54
|
SettingsUtil.openSpotlightModal();
|
|
84
55
|
};
|
|
85
|
-
|
|
86
56
|
/**
|
|
87
57
|
* Function to remove toggling on mobile view
|
|
88
58
|
*/
|
|
89
59
|
const hideToggle = () => {
|
|
90
60
|
setVisible(true);
|
|
91
|
-
|
|
92
61
|
setCollapsed(false);
|
|
93
62
|
};
|
|
94
|
-
|
|
95
63
|
/**
|
|
96
64
|
* onClose Function
|
|
97
65
|
*/
|
|
98
|
-
|
|
99
66
|
const onClose = () => {
|
|
100
67
|
setVisible(false);
|
|
101
68
|
};
|
|
102
|
-
|
|
103
69
|
// const { model = {} } = menu;
|
|
104
|
-
|
|
105
70
|
const { model: BaseModel = {}, menu = {} } = meta;
|
|
106
|
-
|
|
107
71
|
const { model = {} } = menu;
|
|
108
|
-
|
|
109
72
|
//Animations//
|
|
110
73
|
const boxControls = useAnimation();
|
|
111
|
-
|
|
112
74
|
async function animate() {
|
|
113
75
|
await boxControls.start('entered');
|
|
114
|
-
|
|
115
76
|
//await boxControls.start("show");
|
|
116
77
|
}
|
|
117
|
-
|
|
118
78
|
useEffect(() => {
|
|
119
79
|
animate();
|
|
120
80
|
}, []);
|
|
121
|
-
|
|
122
81
|
useEffect(() => {}, [state.theme]);
|
|
123
|
-
|
|
124
82
|
return (
|
|
83
|
+
<>
|
|
125
84
|
<div
|
|
126
85
|
className={`global-header ${process.env.REACT_APP_THEME} ${isConnected && !kiosk ? 'connected' : ''}`}
|
|
127
86
|
style={{
|
|
128
87
|
// background: state.theme.colors.bodyBackground,
|
|
129
|
-
|
|
130
|
-
height: 10,
|
|
88
|
+
height: auto,
|
|
131
89
|
}}
|
|
132
90
|
>
|
|
133
91
|
{/* <MenuOutlined style={{left:'1%',top:'1%', fontSize:18, position:'absolute', zIndex:999}} onClick={showSidebar}/> */}
|
|
134
|
-
|
|
135
92
|
<div className="layout-content">
|
|
136
93
|
<div
|
|
137
94
|
//whileHover="hover"
|
|
@@ -174,7 +131,6 @@ function GlobalHeaderContent({ loading, appSettings, children, isConnected, hist
|
|
|
174
131
|
</Drawer>
|
|
175
132
|
)}
|
|
176
133
|
</div>
|
|
177
|
-
|
|
178
134
|
{/* Right Section of the Component Loader */}
|
|
179
135
|
<div
|
|
180
136
|
className={`right-section ${!collapsed ? 'open' : 'close'} ${kiosk ? 'kioskon' : ''}`}
|
|
@@ -189,11 +145,9 @@ function GlobalHeaderContent({ loading, appSettings, children, isConnected, hist
|
|
|
189
145
|
{/* */}
|
|
190
146
|
<div className="page-header-name">
|
|
191
147
|
<ProgressBar isLoading={loading} />
|
|
192
|
-
|
|
193
148
|
<span type onClick={!isMobile ? toggleCollapsed : hideToggle} className="toggle-box toggle-menu">
|
|
194
149
|
<MenuOutlined />
|
|
195
150
|
</span>
|
|
196
|
-
|
|
197
151
|
{/* Back Button */}
|
|
198
152
|
{location.pathname !== '/' ? (
|
|
199
153
|
<span
|
|
@@ -206,39 +160,30 @@ function GlobalHeaderContent({ loading, appSettings, children, isConnected, hist
|
|
|
206
160
|
</span>
|
|
207
161
|
) : null}
|
|
208
162
|
{/* Back Button Ends */}
|
|
209
|
-
|
|
210
163
|
{location.pathname !== '/' ? (
|
|
211
164
|
<h4 className="menu-caption header-caption" style={{ color: state.theme.colors.headerColor }}>
|
|
212
165
|
{menu.caption}
|
|
213
166
|
</h4>
|
|
214
167
|
) : null}
|
|
215
168
|
</div>
|
|
216
|
-
|
|
217
169
|
{/* Page Menu Actions */}
|
|
218
|
-
|
|
219
170
|
{user.role || user.id ? (
|
|
220
171
|
<div className="page-menu">
|
|
221
172
|
{/* Search Input in header start */}
|
|
222
173
|
{!isMobile && (
|
|
223
174
|
<div>
|
|
224
175
|
<Input placeholder="Search (Shift + F)" prefix={<SearchOutlined />} onClick={openSearchModal} readOnly style={{ width: 250 }} />
|
|
225
|
-
|
|
226
176
|
<SpotlightSearch ref={(elem) => SettingsUtil.registerModal(elem)} props={props} />
|
|
227
177
|
</div>
|
|
228
178
|
)}
|
|
229
179
|
{/* Search Input in header start */}
|
|
230
|
-
|
|
231
180
|
{/** branchswitcher Option */}
|
|
232
181
|
{/* branch switcher controlled with env for matria and nura */}
|
|
233
182
|
{!process.env.REACT_APP_SHOW_BRANCH_SWITCHER ? <div className="branch-switcher">{globalCustomerHeader()}</div> : null}
|
|
234
183
|
{/* <div className="branch-switcher">{globalCustomerHeader()}</div> */}
|
|
235
|
-
|
|
236
184
|
{/* Search Option */}
|
|
237
|
-
|
|
238
185
|
{/* <ModalSearch /> */}
|
|
239
|
-
|
|
240
186
|
{/* Search Option Ends */}
|
|
241
|
-
|
|
242
187
|
{/* Configurator Actions */}
|
|
243
188
|
{user.isAdmin ? (
|
|
244
189
|
<>
|
|
@@ -252,13 +197,9 @@ function GlobalHeaderContent({ loading, appSettings, children, isConnected, hist
|
|
|
252
197
|
</>
|
|
253
198
|
) : null}
|
|
254
199
|
{/* Configurator Actions Ends */}
|
|
255
|
-
|
|
256
200
|
{/* Reload Button */}
|
|
257
|
-
|
|
258
201
|
<Button onClick={reload} icon={<ReloadOutlined />} type="default" size={'small'}></Button>
|
|
259
|
-
|
|
260
202
|
{/* Reload Button Ends */}
|
|
261
|
-
|
|
262
203
|
{/* Help-desk-btn */}
|
|
263
204
|
{helpDeskSetting?.showSupportBtn
|
|
264
205
|
? (() => {
|
|
@@ -277,11 +218,9 @@ function GlobalHeaderContent({ loading, appSettings, children, isConnected, hist
|
|
|
277
218
|
);
|
|
278
219
|
})()
|
|
279
220
|
: null}
|
|
280
|
-
|
|
281
221
|
{/** Switch Languages starts */}
|
|
282
222
|
{process.env.REACT_APP_ENABLE_LANGUAGE_SWITCHER ? <LanguageSwitcher /> : null}
|
|
283
223
|
{/** Switch Languages ends */}
|
|
284
|
-
|
|
285
224
|
{/* User Profile */}
|
|
286
225
|
<div style={{ padding: '10px' }}>
|
|
287
226
|
<ProfileAvatar />
|
|
@@ -290,11 +229,9 @@ function GlobalHeaderContent({ loading, appSettings, children, isConnected, hist
|
|
|
290
229
|
{/* User Profile Ends */}
|
|
291
230
|
</div>
|
|
292
231
|
) : null}
|
|
293
|
-
|
|
294
232
|
{/* Page Menu Actions Ends */}
|
|
295
233
|
</div>
|
|
296
234
|
) : null}
|
|
297
|
-
|
|
298
235
|
{/* The children is rendered */}
|
|
299
236
|
{children}
|
|
300
237
|
{/* The children is rendered */}
|
|
@@ -302,32 +239,40 @@ function GlobalHeaderContent({ loading, appSettings, children, isConnected, hist
|
|
|
302
239
|
{/* Right Section of the Component Loader Ends */}
|
|
303
240
|
</div>
|
|
304
241
|
</div>
|
|
242
|
+
{licAlert && licenseData && (
|
|
243
|
+
<div
|
|
244
|
+
style={{
|
|
245
|
+
top: 0,
|
|
246
|
+
marginTop: '3rem',
|
|
247
|
+
right: '2%',
|
|
248
|
+
position: 'absolute',
|
|
249
|
+
zIndex: 1008,
|
|
250
|
+
}}
|
|
251
|
+
>
|
|
252
|
+
<LicenseAlert data={licenseData} />
|
|
253
|
+
</div>
|
|
254
|
+
)}
|
|
255
|
+
</>
|
|
305
256
|
);
|
|
306
257
|
}
|
|
307
|
-
|
|
308
258
|
export default function GlobalHeader(props) {
|
|
309
259
|
const context = useContext(GlobalContext);
|
|
310
|
-
|
|
311
260
|
if (context.dispatch) {
|
|
312
261
|
return <GlobalHeaderContent {...props} />;
|
|
313
262
|
}
|
|
314
|
-
|
|
315
263
|
return (
|
|
316
264
|
<GlobalProvider {...props} appSettings={props.appSettings}>
|
|
317
265
|
<GlobalHeaderContent {...props} />
|
|
318
266
|
</GlobalProvider>
|
|
319
267
|
);
|
|
320
268
|
}
|
|
321
|
-
|
|
322
269
|
/**
|
|
323
270
|
*
|
|
324
271
|
* @returns
|
|
325
272
|
*/
|
|
326
273
|
function ProfileAvatar() {
|
|
327
274
|
const { user = { locations: [] } } = useContext(GlobalContext);
|
|
328
|
-
|
|
329
275
|
useEffect(() => {}, []);
|
|
330
|
-
|
|
331
276
|
return (
|
|
332
277
|
<Link className="profile-avatar" to="/profile">
|
|
333
278
|
{user.photograph ? (
|
|
@@ -337,7 +282,6 @@ function ProfileAvatar() {
|
|
|
337
282
|
) : (
|
|
338
283
|
<Avatar shape="square" size="small" icon={<UserOutlined />} />
|
|
339
284
|
)}
|
|
340
|
-
|
|
341
285
|
{/* {user.name} */}
|
|
342
286
|
</Link>
|
|
343
287
|
);
|
|
@@ -123,7 +123,9 @@
|
|
|
123
123
|
.right-section {
|
|
124
124
|
margin-top: 0px;
|
|
125
125
|
width: 100%;
|
|
126
|
-
|
|
126
|
+
/* `auto` so the horizontal scrollbar only appears when content genuinely
|
|
127
|
+
overflows — `scroll` forced it to show permanently on every page. */
|
|
128
|
+
overflow-x: auto;
|
|
127
129
|
position: relative;
|
|
128
130
|
padding: 15px;
|
|
129
131
|
margin-left: calc(var(--sidemenu-width, 211px) + 1px);
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* - Renders a single active step view with compact breadcrumb controls.
|
|
12
12
|
*/
|
|
13
13
|
import { ArrowLeftOutlined, ArrowRightOutlined, CompressOutlined, ExpandOutlined, SoundOutlined } from '@ant-design/icons';
|
|
14
|
-
import { Empty, Select,
|
|
14
|
+
import { Empty, Select, Skeleton, message } from 'antd';
|
|
15
15
|
import moment from 'moment';
|
|
16
16
|
import { useEffect, useRef, useState } from 'react';
|
|
17
17
|
import { ExternalWindow } from '../../components';
|
|
@@ -1507,7 +1507,9 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
|
|
|
1507
1507
|
{/* <div className="steps-chat-step-component"> */}
|
|
1508
1508
|
{loading ? (
|
|
1509
1509
|
<div className="steps-chat-loading">
|
|
1510
|
-
<
|
|
1510
|
+
<div className="steps-chat-loading__card">
|
|
1511
|
+
<Skeleton active title={{ width: '32%' }} paragraph={{ rows: 6, width: ['90%', '82%', '86%', '70%', '78%', '60%'] }} />
|
|
1512
|
+
</div>
|
|
1511
1513
|
</div>
|
|
1512
1514
|
) : null}
|
|
1513
1515
|
{!loading ? renderDynamicStep() : null}
|
|
@@ -3,7 +3,13 @@
|
|
|
3
3
|
.process-steps-page {
|
|
4
4
|
display: flex;
|
|
5
5
|
flex-direction: column;
|
|
6
|
-
height:
|
|
6
|
+
/* The app shell's `.right-section` is `height: 100vh` with a fixed 56px header
|
|
7
|
+
(`.page-wrapper`) on top of it plus 15px padding. A plain `height: 100%`
|
|
8
|
+
makes this page a full 100vh — so its bottom runs past the viewport under the
|
|
9
|
+
header and forces a page scroll in normal view. Bound it to the space that is
|
|
10
|
+
actually visible below the header instead. (In full screen the promoted
|
|
11
|
+
`.steps-viewport` is 100vh via the Fullscreen API, so this doesn't apply.) */
|
|
12
|
+
height: calc(100vh - 86px);
|
|
7
13
|
min-height: 0;
|
|
8
14
|
width: 100%;
|
|
9
15
|
max-width: 100%;
|
|
@@ -171,7 +177,7 @@
|
|
|
171
177
|
/* Clean solid header on the soft sage → ivory gradient (no glass/frost).
|
|
172
178
|
Matches the onboarding reference without the frosted-glass treatment. */
|
|
173
179
|
border-bottom: 1px solid rgba(255, 255, 255, 0.4);
|
|
174
|
-
background: linear-gradient(180deg, #a7c2c6
|
|
180
|
+
background: linear-gradient(180deg, #a7c2c6 100%, #eff3e3 100%);
|
|
175
181
|
}
|
|
176
182
|
|
|
177
183
|
.steps-breadcrumb-strip {
|
|
@@ -179,8 +185,12 @@
|
|
|
179
185
|
min-width: 0;
|
|
180
186
|
display: flex;
|
|
181
187
|
align-items: center;
|
|
182
|
-
|
|
183
|
-
|
|
188
|
+
/* `safe center` keeps the steps centered when they fit, but falls back to
|
|
189
|
+
start-aligned when there are too many to fit (>6). Plain `center` would clip
|
|
190
|
+
BOTH ends on overflow — cutting off the active step's label (e.g. "Guest
|
|
191
|
+
Details") and making it unreachable even though the strip scrolls. */
|
|
192
|
+
justify-content: safe center;
|
|
193
|
+
gap: clamp(28px, 3vw, 52px);
|
|
184
194
|
overflow-x: auto;
|
|
185
195
|
overflow-y: hidden;
|
|
186
196
|
white-space: nowrap;
|
|
@@ -332,13 +342,14 @@
|
|
|
332
342
|
-webkit-tap-highlight-color: transparent;
|
|
333
343
|
}
|
|
334
344
|
|
|
335
|
-
/* Thin connecting line drawn into the gap toward the next step.
|
|
345
|
+
/* Thin connecting line drawn into the gap toward the next step.
|
|
346
|
+
Width matches the strip's gap so it spans the space without overhanging. */
|
|
336
347
|
.steps-breadcrumb-item:not(:last-child)::after {
|
|
337
348
|
content: '';
|
|
338
349
|
position: absolute;
|
|
339
350
|
left: 100%;
|
|
340
351
|
top: 50%;
|
|
341
|
-
width: 52px;
|
|
352
|
+
width: clamp(28px, 3vw, 52px);
|
|
342
353
|
height: 1px;
|
|
343
354
|
background: rgba(37, 58, 91, 0.2);
|
|
344
355
|
transform: translateY(-50%);
|
|
@@ -826,10 +837,29 @@
|
|
|
826
837
|
}
|
|
827
838
|
|
|
828
839
|
.steps-chat-loading {
|
|
840
|
+
flex: 1 1 auto;
|
|
829
841
|
min-height: 120px;
|
|
830
842
|
display: flex;
|
|
831
|
-
align-items:
|
|
843
|
+
align-items: stretch;
|
|
832
844
|
justify-content: center;
|
|
845
|
+
padding: 20px;
|
|
846
|
+
box-sizing: border-box;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/* Frosted skeleton card that fills the stage while a step loads (replaces the
|
|
850
|
+
old centered spinner) — matches the soft gradient step surfaces. */
|
|
851
|
+
.steps-chat-loading__card {
|
|
852
|
+
width: 100%;
|
|
853
|
+
max-width: 1100px;
|
|
854
|
+
align-self: stretch;
|
|
855
|
+
padding: 28px 32px;
|
|
856
|
+
box-sizing: border-box;
|
|
857
|
+
border-radius: 16px;
|
|
858
|
+
background: rgba(255, 255, 255, 0.42);
|
|
859
|
+
border: 1px solid rgba(255, 255, 255, 0.6);
|
|
860
|
+
box-shadow: 0 2px 10px rgba(20, 50, 64, 0.05);
|
|
861
|
+
backdrop-filter: blur(4px);
|
|
862
|
+
-webkit-backdrop-filter: blur(4px);
|
|
833
863
|
}
|
|
834
864
|
|
|
835
865
|
/* ── Narration bar (hidden by default via NARRATION_CONTROLS_ENABLED) ── */
|