ui-soxo-bootstrap-core 2.6.15 → 2.6.17
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/DEVELOPER_GUIDE.md +294 -0
- package/core/components/landing-api/landing-api.js +66 -2
- package/core/components/landing-api/landing-api.scss +22 -0
- package/core/models/menus/components/menu-lists/menu-lists.js +49 -46
- package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.js +22 -7
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +3 -1
- package/core/modules/steps/action-buttons.js +6 -0
- package/core/modules/steps/action-buttons.scss +9 -3
- package/core/modules/steps/steps.js +46 -5
- package/core/modules/steps/steps.scss +82 -13
- package/package.json +1 -1
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# 📦 NPM Release Workflow — Task-Based Development → Develop → Master
|
|
2
|
+
|
|
3
|
+
This guide describes the complete workflow for publishing **task-based development builds** and **stable releases** using GitHub Actions, Git tags, and npm versioning.
|
|
4
|
+
|
|
5
|
+
Incorrect versioning or incorrect tags will break the publish pipeline — follow the process exactly.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# 📘 Table of Contents
|
|
10
|
+
|
|
11
|
+
- Overview
|
|
12
|
+
- Branch Strategy
|
|
13
|
+
- Task Branch Workflow
|
|
14
|
+
- Versioning Rules
|
|
15
|
+
- Development Release Workflow (`develop`)
|
|
16
|
+
- Promotion Workflow (Dev → Master)
|
|
17
|
+
- Publishing via GitHub Release UI
|
|
18
|
+
- How GitHub Action Detects Release Type
|
|
19
|
+
- Summary Table
|
|
20
|
+
- Common Mistakes & Fixes
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# 🧩 Overview
|
|
25
|
+
|
|
26
|
+
We maintain two release channels:
|
|
27
|
+
|
|
28
|
+
| Channel | Branch | Tag | Purpose |
|
|
29
|
+
| ------------------- | ------- | -------------- | ------------------- |
|
|
30
|
+
| Stable (`latest`) | master | `vX.Y.Z` | Production release |
|
|
31
|
+
| Development (`dev`) | develop | `vX.Y.Z-dev.N` | QA/internal testing |
|
|
32
|
+
|
|
33
|
+
Releases are triggered using Git tags:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
npm version
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
# 🌿 Branch Strategy
|
|
42
|
+
|
|
43
|
+
## `master`
|
|
44
|
+
|
|
45
|
+
- Production-ready code
|
|
46
|
+
- Publishes **stable** builds
|
|
47
|
+
- Versions never include a `dev` suffix
|
|
48
|
+
|
|
49
|
+
## `develop`
|
|
50
|
+
|
|
51
|
+
- Used for QA/internal testing
|
|
52
|
+
- Publishes **development** builds
|
|
53
|
+
- Versions always include `-dev.N`
|
|
54
|
+
|
|
55
|
+
## Task Branches
|
|
56
|
+
|
|
57
|
+
- Create from `master`
|
|
58
|
+
- Naming: `task/<JIRA-ID>-<description>`
|
|
59
|
+
- After completing work, merge into `develop`
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
# 🏗️ Task Branch Workflow
|
|
64
|
+
|
|
65
|
+
### 1. Create a task branch
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
git checkout master
|
|
69
|
+
git pull origin master
|
|
70
|
+
git checkout -b task/<JIRA-ID>-<description>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2. Work on the task
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git add .
|
|
77
|
+
git commit -m "TASK-123: Implement XYZ"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3. Merge into develop
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
git checkout develop
|
|
84
|
+
git pull origin develop
|
|
85
|
+
git merge task/<JIRA-ID>-<description>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
# 🔧 Versioning Rules
|
|
91
|
+
|
|
92
|
+
## Development Versions
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
X.Y.Z-dev.N
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Example:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
2.4.30-dev.0
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Stable Versions
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
X.Y.Z
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Rules
|
|
111
|
+
|
|
112
|
+
- Each version must be unique
|
|
113
|
+
- Dev versions do **not** convert into stable versions
|
|
114
|
+
- Git tag must exactly match `package.json` version
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
# 🟦 Development Release Workflow (`develop`)
|
|
119
|
+
|
|
120
|
+
### 1. Switch to develop
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
git checkout develop
|
|
124
|
+
git pull origin develop
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 2. Create a new development version
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npm version prerelease --preid=dev
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 3. Push commit + tag
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
git push --follow-tags
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 4. GitHub Action publishes dev build
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
npm publish --tag dev
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 5. Install the dev build
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npm install ui-soxo-bootstrap-core@dev
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
# 🟩 Promotion Workflow (Dev → Master)
|
|
154
|
+
|
|
155
|
+
### 1. Switch to master
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
git checkout master
|
|
159
|
+
git pull origin master
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### 2. Merge develop into master
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
git merge develop
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 3. Bump the stable version
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npm version patch
|
|
172
|
+
# or
|
|
173
|
+
npm version minor
|
|
174
|
+
# or
|
|
175
|
+
npm version major
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 4. Push with tags
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
git push --follow-tags
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 5. GitHub Action publishes stable build
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
npm publish
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### 6. Install stable version
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
npm install ui-soxo-bootstrap-core
|
|
194
|
+
# or
|
|
195
|
+
npm install ui-soxo-bootstrap-core@<version>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
# 🟧 Publishing via GitHub Release UI
|
|
201
|
+
|
|
202
|
+
### Important
|
|
203
|
+
|
|
204
|
+
**The tag must match the `package.json` version exactly**, including the `v` prefix.
|
|
205
|
+
|
|
206
|
+
Example:
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
package.json: "version": "2.4.31-dev.0"
|
|
210
|
+
GitHub Tag: v2.4.31-dev.0
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Steps
|
|
216
|
+
|
|
217
|
+
### 1. Ensure `develop` branch is up to date
|
|
218
|
+
|
|
219
|
+
Push your changes.
|
|
220
|
+
|
|
221
|
+
### 2. Ensure version contains a dev suffix
|
|
222
|
+
|
|
223
|
+
If not:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
npm version prerelease --preid=dev
|
|
227
|
+
git push --follow-tags
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### 3. Open GitHub → Releases → Draft a new release
|
|
231
|
+
|
|
232
|
+
### 4. Create tag matching the version
|
|
233
|
+
|
|
234
|
+
Example:
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
v2.4.31-dev.0
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### 5. Select target branch = `develop`
|
|
241
|
+
|
|
242
|
+
### 6. Title = tag version
|
|
243
|
+
|
|
244
|
+
Description is optional.
|
|
245
|
+
|
|
246
|
+
### 7. Publish the release
|
|
247
|
+
|
|
248
|
+
Triggers:
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
npm publish --tag dev
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
# ⚙️ How GitHub Action Detects Release Type
|
|
257
|
+
|
|
258
|
+
If version contains `dev`:
|
|
259
|
+
|
|
260
|
+
```
|
|
261
|
+
npm publish --tag dev
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Otherwise:
|
|
265
|
+
|
|
266
|
+
```
|
|
267
|
+
npm publish
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
# 📊 Summary Table
|
|
273
|
+
|
|
274
|
+
| Step | Branch | Command | Tag | Publish Type |
|
|
275
|
+
| ---------------------- | ------- | ------------------------------------ | -------------- | ------------ |
|
|
276
|
+
| Create task branch | master | `git checkout -b task/<ID>` | — | — |
|
|
277
|
+
| Merge task → develop | develop | `git merge task/<ID>` | — | — |
|
|
278
|
+
| Dev version | develop | `npm version prerelease --preid=dev` | `vX.Y.Z-dev.N` | dev |
|
|
279
|
+
| Publish dev build | develop | `git push --follow-tags` | — | dev |
|
|
280
|
+
| Merge develop → master | master | `git merge develop` | — | — |
|
|
281
|
+
| Stable version | master | `npm version patch/minor/major` | `vX.Y.Z` | latest |
|
|
282
|
+
| Publish stable build | master | `git push --follow-tags` | — | latest |
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
# ⚠️ Common Mistakes & Fixes
|
|
287
|
+
|
|
288
|
+
| Mistake | Issue | Fix |
|
|
289
|
+
| ------------------------- | ------------------ | ---------------------------- |
|
|
290
|
+
| Tag doesn't match version | Publish fails | Always use `npm version` |
|
|
291
|
+
| Manual tag creation | Version mismatch | Avoid manual tagging |
|
|
292
|
+
| Dev publish from master | Wrong channel | Verify branch before tagging |
|
|
293
|
+
| Not pushing tags | Workflow won’t run | Use `git push --follow-tags` |
|
|
294
|
+
| Merging incomplete tasks | Breaks dev builds | Test before merging |
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect, useContext } from 'react';
|
|
1
|
+
import React, { useState, useEffect, useContext, useRef } from 'react';
|
|
2
2
|
|
|
3
3
|
import { Route, Switch } from 'react-router-dom';
|
|
4
4
|
|
|
@@ -18,13 +18,33 @@ import PropTypes from 'prop-types';
|
|
|
18
18
|
|
|
19
19
|
import { MenusAPI, CoreScripts } from '../../models';
|
|
20
20
|
|
|
21
|
+
const motivatingMessages = [
|
|
22
|
+
'Setting things up for a great start...',
|
|
23
|
+
'Good things are loading. Stay with us.',
|
|
24
|
+
'Almost there. Preparing your workspace.',
|
|
25
|
+
'You are one moment away from your dashboard.',
|
|
26
|
+
'Getting everything ready for you.',
|
|
27
|
+
'Great care takes a second. Loading now.',
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
function getRandomMessage(previousMessage = '') {
|
|
31
|
+
const options = motivatingMessages.filter((message) => message !== previousMessage);
|
|
32
|
+
|
|
33
|
+
if (!options.length) {
|
|
34
|
+
return motivatingMessages[0];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const randomIndex = Math.floor(Math.random() * options.length);
|
|
38
|
+
return options[randomIndex];
|
|
39
|
+
}
|
|
40
|
+
|
|
21
41
|
/**
|
|
22
42
|
* Landing API
|
|
23
43
|
*
|
|
24
44
|
* @param {*} param0
|
|
25
45
|
* @returns
|
|
26
46
|
*/
|
|
27
|
-
export default function LandingApi({ history, CustomComponents, CustomModels, appSettings, ...props }) {
|
|
47
|
+
export default function LandingApi({ history, CustomComponents, CustomModels, appSettings, transitionPending = false, onHomeReady, ...props }) {
|
|
28
48
|
const [loader, setLoader] = useState(false);
|
|
29
49
|
|
|
30
50
|
// const [modules, setModules] = useState([]);
|
|
@@ -34,8 +54,11 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
34
54
|
const { dispatch, user = {} } = useContext(GlobalContext);
|
|
35
55
|
|
|
36
56
|
const [allModules, setAllModules] = useState();
|
|
57
|
+
const homeReadyNotifiedRef = useRef(false);
|
|
58
|
+
const messageIntervalRef = useRef(null);
|
|
37
59
|
|
|
38
60
|
const [meta, setMeta] = useState({});
|
|
61
|
+
const [loadingMessage, setLoadingMessage] = useState('');
|
|
39
62
|
|
|
40
63
|
// const [reports, setReports] = useState([]);
|
|
41
64
|
|
|
@@ -70,6 +93,46 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
70
93
|
initializeUserMenus();
|
|
71
94
|
}, []);
|
|
72
95
|
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (!transitionPending) {
|
|
98
|
+
homeReadyNotifiedRef.current = false;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!loader && allModules && !homeReadyNotifiedRef.current) {
|
|
103
|
+
homeReadyNotifiedRef.current = true;
|
|
104
|
+
if (typeof onHomeReady === 'function') {
|
|
105
|
+
onHomeReady();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}, [transitionPending, loader, allModules, onHomeReady]);
|
|
109
|
+
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (!loader) {
|
|
112
|
+
setLoadingMessage('');
|
|
113
|
+
|
|
114
|
+
if (messageIntervalRef.current) {
|
|
115
|
+
clearInterval(messageIntervalRef.current);
|
|
116
|
+
messageIntervalRef.current = null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
setLoadingMessage((previousMessage) => getRandomMessage(previousMessage));
|
|
123
|
+
|
|
124
|
+
messageIntervalRef.current = setInterval(() => {
|
|
125
|
+
setLoadingMessage((previousMessage) => getRandomMessage(previousMessage));
|
|
126
|
+
}, 2200);
|
|
127
|
+
|
|
128
|
+
return () => {
|
|
129
|
+
if (messageIntervalRef.current) {
|
|
130
|
+
clearInterval(messageIntervalRef.current);
|
|
131
|
+
messageIntervalRef.current = null;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}, [loader]);
|
|
135
|
+
|
|
73
136
|
/**
|
|
74
137
|
* Initialize the user menus
|
|
75
138
|
*/
|
|
@@ -245,6 +308,7 @@ export default function LandingApi({ history, CustomComponents, CustomModels, ap
|
|
|
245
308
|
<Card className="skeleton-card">
|
|
246
309
|
<div className="skeleton-wrapper">
|
|
247
310
|
<Skeleton paragraph={{ rows: 4 }} />
|
|
311
|
+
<p className="motivating-text">{loadingMessage}</p>
|
|
248
312
|
</div>
|
|
249
313
|
</Card>
|
|
250
314
|
) : (
|
|
@@ -11,9 +11,31 @@
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
.skeleton-wrapper {
|
|
14
|
+
.motivating-text {
|
|
15
|
+
margin-top: 14px;
|
|
16
|
+
margin-bottom: 4px;
|
|
17
|
+
font-size: 14px;
|
|
18
|
+
line-height: 20px;
|
|
19
|
+
text-align: center;
|
|
20
|
+
color: #5e6d86;
|
|
21
|
+
font-weight: 500;
|
|
22
|
+
animation: skeletonTextFade 0.4s ease-in-out;
|
|
23
|
+
}
|
|
14
24
|
}
|
|
15
25
|
|
|
16
26
|
.wrapper-loader {
|
|
17
27
|
margin: 20px;
|
|
18
28
|
}
|
|
19
29
|
}
|
|
30
|
+
|
|
31
|
+
@keyframes skeletonTextFade {
|
|
32
|
+
from {
|
|
33
|
+
opacity: 0;
|
|
34
|
+
transform: translateY(2px);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
to {
|
|
38
|
+
opacity: 1;
|
|
39
|
+
transform: translateY(0);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState, useCallback, useEffect } from 'react';
|
|
2
|
-
import { Space, Popconfirm, Input, Drawer, Skeleton, Collapse, message, Tag } from 'antd';
|
|
2
|
+
import { Space, Popconfirm, Input, Drawer, Skeleton, Collapse, message, Tag, Empty } from 'antd';
|
|
3
3
|
import { ReloadOutlined, DeleteOutlined, EditOutlined, PlusCircleFilled, CopyOutlined } from '@ant-design/icons';
|
|
4
4
|
import { Button, Card, Switch, DraggableWrapper } from '../../../../lib';
|
|
5
5
|
// for draggable menu list import { DndProvider } from "react-dnd";
|
|
@@ -203,7 +203,7 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
|
|
|
203
203
|
model.delete(rec).then(loadMenus);
|
|
204
204
|
};
|
|
205
205
|
|
|
206
|
-
const filtered = records.filter((r) => r.
|
|
206
|
+
const filtered = records.filter((r) => r.caption?.toUpperCase().includes(query.toUpperCase()));
|
|
207
207
|
|
|
208
208
|
const visibleItems = filtered;
|
|
209
209
|
|
|
@@ -362,50 +362,53 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
|
|
|
362
362
|
<Card>
|
|
363
363
|
<DndProvider backend={HTML5Backend}>
|
|
364
364
|
<Collapse accordion>
|
|
365
|
-
{visibleItems.
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
365
|
+
{visibleItems && visibleItems.length > 0 ? (
|
|
366
|
+
visibleItems.map((item, index) => (
|
|
367
|
+
<Panel
|
|
368
|
+
key={item.id}
|
|
369
|
+
header={
|
|
370
|
+
<DraggableWrapper
|
|
371
|
+
id={item.id}
|
|
372
|
+
index={index}
|
|
373
|
+
movePanel={movePanel}
|
|
374
|
+
item={item}
|
|
375
|
+
dragEnabled={dragMode}
|
|
376
|
+
level={1}
|
|
377
|
+
parentId={null}
|
|
378
|
+
onCrossLevelMove={handleCrossLevelMove}
|
|
379
|
+
canAcceptChildren={true}
|
|
380
|
+
/>
|
|
381
|
+
}
|
|
382
|
+
showArrow={item.sub_menus && item.sub_menus.length > 0}
|
|
383
|
+
extra={panelActions(item, model, setSelectedRecord, setDrawerTitle, setDrawerVisible, deleteRecord)}
|
|
384
|
+
>
|
|
385
|
+
{item.sub_menus && item.sub_menus.length > 0 && (
|
|
386
|
+
<NestedMenu
|
|
387
|
+
parentId={item.id}
|
|
388
|
+
step={item.step + 1}
|
|
389
|
+
items={item.sub_menus || []}
|
|
390
|
+
model={model}
|
|
391
|
+
dragMode={dragMode}
|
|
392
|
+
setSelectedRecord={setSelectedRecord}
|
|
393
|
+
setDrawerTitle={setDrawerTitle}
|
|
394
|
+
setDrawerVisible={setDrawerVisible}
|
|
395
|
+
deleteRecord={deleteRecord}
|
|
396
|
+
level={2}
|
|
397
|
+
onCrossLevelMove={handleCrossLevelMove}
|
|
398
|
+
onChange={(subMenus) => {
|
|
399
|
+
const updated = records.map((r) => (r.id === item.id ? { ...r, sub_menus: subMenus } : r));
|
|
400
|
+
setRecords(updated);
|
|
401
|
+
setOrderChanged(true);
|
|
402
|
+
}}
|
|
403
|
+
/>
|
|
404
|
+
)}
|
|
405
|
+
</Panel>
|
|
406
|
+
))
|
|
407
|
+
) : (
|
|
408
|
+
<div style={{ textAlign: 'center', padding: '40px 0' }}>
|
|
409
|
+
<Empty description="No Menu Items" />
|
|
410
|
+
</div>
|
|
411
|
+
)}
|
|
409
412
|
</Collapse>
|
|
410
413
|
</DndProvider>
|
|
411
414
|
</Card>
|
|
@@ -27,9 +27,13 @@ function getExportDefinition(entry, record) {
|
|
|
27
27
|
* @param {boolean} root0.isFixedIndex
|
|
28
28
|
* @param {Object} root0.CustomComponents
|
|
29
29
|
* @param {Function} root0.refresh
|
|
30
|
+
* @param {Object} [root0.otherDetails={}] - Optional details from the report configuration.
|
|
31
|
+
* @param {boolean} [root0.otherDetails.isFilterEnabled] - Fallback to enable filtering on all columns.
|
|
32
|
+
* @param {boolean} [root.otherDetails.isSortingEnabled] - Fallback to enable sorting on all columns.
|
|
33
|
+
* @param {boolean} [root0.otherDetails.isHeaderWrapEnabled] - Fallback to enable header text wrapping for all columns.
|
|
30
34
|
* @returns {Array}
|
|
31
35
|
*/
|
|
32
|
-
export default function buildDisplayColumns({ columns = [], patients = [], isFixedIndex, CustomComponents, refresh }) {
|
|
36
|
+
export default function buildDisplayColumns({ columns = [], patients = [], isFixedIndex, CustomComponents, refresh, otherDetails = {} }) {
|
|
33
37
|
const displayColumns = [
|
|
34
38
|
{
|
|
35
39
|
title: '#',
|
|
@@ -42,6 +46,11 @@ export default function buildDisplayColumns({ columns = [], patients = [], isFix
|
|
|
42
46
|
];
|
|
43
47
|
|
|
44
48
|
columns.forEach((entry, index) => {
|
|
49
|
+
const isFilterEnabled = entry.isFilterEnabled || otherDetails?.isFilterEnabled;
|
|
50
|
+
const isSortingEnabled = entry.isSortingEnabled || otherDetails?.isSortingEnabled;
|
|
51
|
+
const isHeaderWrapEnabled = otherDetails?.isHeaderWrapEnabled;
|
|
52
|
+
|
|
53
|
+
const titleStyle = isHeaderWrapEnabled ? { whiteSpace: 'pre-wrap', overflowWrap: 'break-word' } : {};
|
|
45
54
|
displayColumns.push({
|
|
46
55
|
render: (record) =>
|
|
47
56
|
renderDisplayCell({
|
|
@@ -52,20 +61,26 @@ export default function buildDisplayColumns({ columns = [], patients = [], isFix
|
|
|
52
61
|
}),
|
|
53
62
|
field: entry.field,
|
|
54
63
|
title: (
|
|
55
|
-
<Tooltip
|
|
56
|
-
|
|
64
|
+
<Tooltip
|
|
65
|
+
title={entry.tooltip || entry.title}
|
|
66
|
+
overlayInnerStyle={{
|
|
67
|
+
whiteSpace: 'normal',
|
|
68
|
+
overflowWrap: 'break-word',
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
<span style={titleStyle}>{entry.title}</span>
|
|
57
72
|
</Tooltip>
|
|
58
73
|
),
|
|
59
74
|
key: entry.field || `display_column_${index}`,
|
|
60
75
|
width: entry.width ? parseInt(entry.width, 10) : 160,
|
|
61
76
|
fixed: entry.isFixedColumn ? entry.isFixedColumn : null,
|
|
62
77
|
filters:
|
|
63
|
-
|
|
78
|
+
isFilterEnabled && Array.isArray(patients)
|
|
64
79
|
? [...new Set(patients.map((item) => item[entry.field]).filter(Boolean))].map((value) => ({ text: value, value }))
|
|
65
80
|
: null,
|
|
66
|
-
onFilter:
|
|
67
|
-
sorter:
|
|
68
|
-
filterSearch:
|
|
81
|
+
onFilter: isFilterEnabled ? (value, record) => record[entry.field] === value : null,
|
|
82
|
+
sorter: isSortingEnabled ? (a, b) => String(a[entry.field]).localeCompare(String(b[entry.field])) : null,
|
|
83
|
+
filterSearch: isFilterEnabled ? isFilterEnabled : false,
|
|
69
84
|
exportDefinition: (record) => getExportDefinition(entry, record),
|
|
70
85
|
align: entry.type === 'number' ? 'right' : 'left',
|
|
71
86
|
});
|
|
@@ -299,7 +299,7 @@ export default function ReportingDashboard({
|
|
|
299
299
|
// Update patients
|
|
300
300
|
setPatients(resultDetails || []);
|
|
301
301
|
console.log(parsedColumns);
|
|
302
|
-
|
|
302
|
+
|
|
303
303
|
// Check if columns are not yet defined
|
|
304
304
|
if (parsedColumns.length === 0 && resultDetails.length > 0) {
|
|
305
305
|
// Create columns dynamically from resultDetails keys
|
|
@@ -599,6 +599,7 @@ function GuestList({
|
|
|
599
599
|
|
|
600
600
|
const { isMobile, dispatch } = useContext(GlobalContext);
|
|
601
601
|
const [single, setSingle] = useState({});
|
|
602
|
+
const otherDetails = config.other_details1 ? JSON.parse(config.other_details1) : {};
|
|
602
603
|
|
|
603
604
|
// const [view, setView] = useState(isMobile ? true : false); //Need to check this condition
|
|
604
605
|
const cols = buildDisplayColumns({
|
|
@@ -607,6 +608,7 @@ function GuestList({
|
|
|
607
608
|
isFixedIndex,
|
|
608
609
|
CustomComponents,
|
|
609
610
|
refresh,
|
|
611
|
+
otherDetails
|
|
610
612
|
});
|
|
611
613
|
|
|
612
614
|
/**
|
|
@@ -13,6 +13,8 @@ export default function ActionButtons({
|
|
|
13
13
|
steps,
|
|
14
14
|
activeStep,
|
|
15
15
|
isStepCompleted,
|
|
16
|
+
isFullscreen,
|
|
17
|
+
onToggleFullscreen,
|
|
16
18
|
renderDynamicComponent,
|
|
17
19
|
handlePrevious,
|
|
18
20
|
handleNext,
|
|
@@ -35,6 +37,10 @@ export default function ActionButtons({
|
|
|
35
37
|
Back
|
|
36
38
|
</Button>
|
|
37
39
|
|
|
40
|
+
<Button type="default" onClick={onToggleFullscreen}>
|
|
41
|
+
{isFullscreen ? 'Exit Full Screen' : 'Switch to Full Screen'}
|
|
42
|
+
</Button>
|
|
43
|
+
|
|
38
44
|
{/* Skip button */}
|
|
39
45
|
{steps.length > 0 && steps[activeStep]?.allow_skip === 'Y' && (
|
|
40
46
|
<Button type="default" onClick={handleSkip} disabled={activeStep === steps.length - 1}>
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
overflow-y: auto;
|
|
12
12
|
overflow-x: hidden;
|
|
13
13
|
overscroll-behavior: contain;
|
|
14
|
+
|
|
14
15
|
padding-bottom: 8px;
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -27,7 +28,6 @@
|
|
|
27
28
|
border-top: 1px solid #f0f0f0;
|
|
28
29
|
box-shadow: 0 -2px 10px rgba(15, 23, 42, 0.04);
|
|
29
30
|
|
|
30
|
-
|
|
31
31
|
width: 61%;
|
|
32
32
|
padding: 10px;
|
|
33
33
|
position: fixed;
|
|
@@ -37,9 +37,15 @@
|
|
|
37
37
|
display: flex;
|
|
38
38
|
border-radius: 4px;
|
|
39
39
|
box-shadow: -1px -4px 10px 2px #f7f7f76e;
|
|
40
|
-
|
|
40
|
+
flex-wrap: wrap;
|
|
41
41
|
|
|
42
42
|
.ant-btn {
|
|
43
43
|
border-radius: 4px;
|
|
44
44
|
}
|
|
45
|
-
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.process-steps-page.is-fullscreen .action-buttons-container {
|
|
48
|
+
width: calc(100% - 24px);
|
|
49
|
+
left: 12px;
|
|
50
|
+
right: 12px;
|
|
51
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - Handles process submission and optional chaining to the next process.
|
|
9
9
|
* - Provides a collapsible timeline view and action controls.
|
|
10
10
|
*/
|
|
11
|
-
import React, { useEffect, useState } from 'react';
|
|
11
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
12
12
|
import { Row, Col, Empty } from 'antd';
|
|
13
13
|
import { Card } from './../../lib';
|
|
14
14
|
import * as genericComponents from './../../lib';
|
|
@@ -36,8 +36,11 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
|
|
|
36
36
|
const [timelineCollapsed, setTimelineCollapsed] = useState(false);
|
|
37
37
|
const [showExternalWindow, setShowExternalWindow] = useState(false);
|
|
38
38
|
const [externalWin, setExternalWin] = useState(null);
|
|
39
|
+
const [isFullscreen, setIsFullscreen] = useState(false);
|
|
40
|
+
const processStepsRef = useRef(null);
|
|
39
41
|
|
|
40
42
|
const urlParams = Location.search();
|
|
43
|
+
const isConsultationMode = String(urlParams?.consultation).toLowerCase() === 'true';
|
|
41
44
|
let processId = urlParams.processId;
|
|
42
45
|
const [currentProcessId, setCurrentProcessId] = useState(processId);
|
|
43
46
|
// Load process details based on the current process ID
|
|
@@ -271,13 +274,49 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
|
|
|
271
274
|
};
|
|
272
275
|
}, [activeStep, steps, externalWin]);
|
|
273
276
|
|
|
277
|
+
useEffect(() => {
|
|
278
|
+
const syncFullscreenState = () => {
|
|
279
|
+
setIsFullscreen(document.fullscreenElement === processStepsRef.current);
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
document.addEventListener('fullscreenchange', syncFullscreenState);
|
|
283
|
+
syncFullscreenState();
|
|
284
|
+
|
|
285
|
+
return () => {
|
|
286
|
+
document.removeEventListener('fullscreenchange', syncFullscreenState);
|
|
287
|
+
};
|
|
288
|
+
}, []);
|
|
289
|
+
|
|
290
|
+
const handleToggleFullscreen = async () => {
|
|
291
|
+
const element = processStepsRef.current;
|
|
292
|
+
if (!element) return;
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
if (document.fullscreenElement === element) {
|
|
296
|
+
if (document.exitFullscreen) {
|
|
297
|
+
await document.exitFullscreen();
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (element.requestFullscreen) {
|
|
303
|
+
await element.requestFullscreen();
|
|
304
|
+
}
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.error('Unable to toggle full screen for steps page:', error);
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
274
310
|
/**
|
|
275
311
|
* Renders the main process UI including timeline, step details,
|
|
276
312
|
* and action buttons. This content is reused in both normal view
|
|
277
313
|
* and external window view.
|
|
278
314
|
*/
|
|
279
|
-
const renderContent = () => (
|
|
280
|
-
<div
|
|
315
|
+
const renderContent = (attachRef = true) => (
|
|
316
|
+
<div
|
|
317
|
+
ref={attachRef ? processStepsRef : null}
|
|
318
|
+
className={`process-steps-page ${isConsultationMode ? 'consultation-mode' : ''} ${isFullscreen ? 'is-fullscreen' : ''}`}
|
|
319
|
+
>
|
|
281
320
|
<Card className="process-steps-card">
|
|
282
321
|
<Row gutter={20} className="process-steps-row" align="stretch">
|
|
283
322
|
<Col xs={24} sm={24} lg={timelineCollapsed ? 2 : 6} className="process-steps-timeline-col">
|
|
@@ -302,6 +341,8 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
|
|
|
302
341
|
steps={steps}
|
|
303
342
|
activeStep={activeStep}
|
|
304
343
|
isStepCompleted={isStepCompleted}
|
|
344
|
+
isFullscreen={isFullscreen}
|
|
345
|
+
onToggleFullscreen={handleToggleFullscreen}
|
|
305
346
|
renderDynamicComponent={DynamicComponent}
|
|
306
347
|
handlePrevious={handlePrevious}
|
|
307
348
|
handleNext={handleNext}
|
|
@@ -335,9 +376,9 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
|
|
|
335
376
|
width={props.ExternalWindowWidth || 1000}
|
|
336
377
|
height={props.ExternalWindowHeight || 1000}
|
|
337
378
|
>
|
|
338
|
-
{renderContent()}
|
|
379
|
+
{renderContent(false)}
|
|
339
380
|
</ExternalWindow>
|
|
340
|
-
{renderContent()}
|
|
381
|
+
{renderContent(true)}
|
|
341
382
|
</>
|
|
342
383
|
);
|
|
343
384
|
}
|
|
@@ -3,6 +3,25 @@
|
|
|
3
3
|
display: flex;
|
|
4
4
|
flex-direction: column;
|
|
5
5
|
overflow: hidden;
|
|
6
|
+
|
|
7
|
+
&.consultation-mode {
|
|
8
|
+
background: linear-gradient(180deg, #c8d2d6 0%, #d5d7d1 21%, #d9d8c6 40%, #c6cebe 58%, #a8c0c0 71%, #6e9fbb 84%, #367495 93%, #0a4d6e 100%);
|
|
9
|
+
padding: 12px;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
&.is-fullscreen {
|
|
13
|
+
padding: 12px;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.process-steps-page.consultation-mode .process-steps-card {
|
|
18
|
+
border: none;
|
|
19
|
+
background: transparent;
|
|
20
|
+
box-shadow: none;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.process-steps-page.consultation-mode .process-steps-card > .ant-card-body {
|
|
24
|
+
padding: 10px;
|
|
6
25
|
}
|
|
7
26
|
|
|
8
27
|
.process-steps-card {
|
|
@@ -49,11 +68,11 @@
|
|
|
49
68
|
flex: 1;
|
|
50
69
|
max-height: 100%;
|
|
51
70
|
overflow: hidden;
|
|
52
|
-
background: #fff;
|
|
53
|
-
border: 1px solid #e5ebf3;
|
|
54
|
-
border-radius: 14px;
|
|
55
|
-
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.08);
|
|
56
|
-
padding: 14px 16px;
|
|
71
|
+
// background: #fff;
|
|
72
|
+
// border: 1px solid #e5ebf3;
|
|
73
|
+
// border-radius: 14px;
|
|
74
|
+
// box-shadow: 0 8px 20px rgba(15, 23, 42, 0.08);
|
|
75
|
+
// padding: 14px 16px;
|
|
57
76
|
position: relative;
|
|
58
77
|
z-index: 2;
|
|
59
78
|
}
|
|
@@ -84,8 +103,18 @@
|
|
|
84
103
|
height: 100%;
|
|
85
104
|
border: none !important;
|
|
86
105
|
border-radius: 12px;
|
|
87
|
-
background: linear-gradient(
|
|
88
|
-
|
|
106
|
+
background: linear-gradient(135deg, color-mix(in srgb, var(--accent-color) 15%, var(--surface-color)), var(--surface-color));
|
|
107
|
+
|
|
108
|
+
border: 1px solid color-mix(in srgb, var(--accent-color) 30%, transparent);
|
|
109
|
+
|
|
110
|
+
box-shadow: 0 8px 20px color-mix(in srgb, var(--accent-color) 20%, transparent);
|
|
111
|
+
|
|
112
|
+
transition: all 0.3s ease;
|
|
113
|
+
// background: linear-gradient(180deg, #f4f7fc 0%, #edf2f9 100%);
|
|
114
|
+
// box-shadow:
|
|
115
|
+
// inset 0 1px 2px rgba(255, 255, 255, 0.8),
|
|
116
|
+
// inset 0 8px 16px rgba(15, 23, 42, 0.08),
|
|
117
|
+
// inset 0 -2px 8px rgba(15, 23, 42, 0.04);
|
|
89
118
|
}
|
|
90
119
|
|
|
91
120
|
.timeline-card .ant-card-body {
|
|
@@ -108,6 +137,35 @@
|
|
|
108
137
|
transition: all 0.3s ease;
|
|
109
138
|
height: 100%;
|
|
110
139
|
overflow-y: auto;
|
|
140
|
+
|
|
141
|
+
scrollbar-width: thin; /* Firefox */
|
|
142
|
+
scrollbar-color: rgba(0, 0, 0, 0.25) transparent;
|
|
143
|
+
|
|
144
|
+
&::-webkit-scrollbar-thumb {
|
|
145
|
+
background: rgba(0, 0, 0, 0);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
&:hover::-webkit-scrollbar-thumb {
|
|
149
|
+
background: rgba(0, 0, 0, 0.25);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
&::-webkit-scrollbar {
|
|
153
|
+
width: 6px;
|
|
154
|
+
height: 6px;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
&::-webkit-scrollbar-track {
|
|
158
|
+
background: transparent;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
&::-webkit-scrollbar-thumb {
|
|
162
|
+
background: rgba(0, 0, 0, 0.25);
|
|
163
|
+
border-radius: 10px;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
&::-webkit-scrollbar-thumb:hover {
|
|
167
|
+
background: rgba(0, 0, 0, 0.4);
|
|
168
|
+
}
|
|
111
169
|
padding-right: 4px;
|
|
112
170
|
}
|
|
113
171
|
|
|
@@ -119,7 +177,10 @@
|
|
|
119
177
|
border-radius: 10px;
|
|
120
178
|
background: #fff;
|
|
121
179
|
padding: 10px 12px;
|
|
122
|
-
transition:
|
|
180
|
+
transition:
|
|
181
|
+
border-color 0.2s ease,
|
|
182
|
+
box-shadow 0.2s ease,
|
|
183
|
+
transform 0.2s ease;
|
|
123
184
|
}
|
|
124
185
|
|
|
125
186
|
.timeline-step:hover {
|
|
@@ -233,7 +294,11 @@
|
|
|
233
294
|
color: #1f3f74;
|
|
234
295
|
box-shadow: 0 6px 14px rgba(15, 23, 42, 0.2);
|
|
235
296
|
z-index: 40;
|
|
236
|
-
transition:
|
|
297
|
+
transition:
|
|
298
|
+
background-color 0.2s ease,
|
|
299
|
+
border-color 0.2s ease,
|
|
300
|
+
box-shadow 0.2s ease,
|
|
301
|
+
transform 0.2s ease;
|
|
237
302
|
|
|
238
303
|
&:hover {
|
|
239
304
|
background: #f0f6ff;
|
|
@@ -265,9 +330,8 @@
|
|
|
265
330
|
MOBILE & TABLET VIEW FIXES
|
|
266
331
|
============================ */
|
|
267
332
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
@media (max-width: 992px) { // iPad & tablets
|
|
333
|
+
@media (max-width: 992px) {
|
|
334
|
+
// iPad & tablets
|
|
271
335
|
.process-steps-page,
|
|
272
336
|
.process-steps-row {
|
|
273
337
|
min-height: auto;
|
|
@@ -275,6 +339,10 @@
|
|
|
275
339
|
overflow: visible;
|
|
276
340
|
}
|
|
277
341
|
|
|
342
|
+
.process-steps-page.consultation-mode {
|
|
343
|
+
padding: 8px;
|
|
344
|
+
}
|
|
345
|
+
|
|
278
346
|
.process-steps-card,
|
|
279
347
|
.process-steps-card .ant-card-body,
|
|
280
348
|
.process-steps-content-col {
|
|
@@ -327,7 +395,8 @@
|
|
|
327
395
|
}
|
|
328
396
|
}
|
|
329
397
|
|
|
330
|
-
@media (max-width: 768px) {
|
|
398
|
+
@media (max-width: 768px) {
|
|
399
|
+
// mobile screens
|
|
331
400
|
.timeline-sidebar {
|
|
332
401
|
gap: 8px;
|
|
333
402
|
}
|