ui-soxo-bootstrap-core 2.4.25-dev.29 → 2.4.25-dev.31

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.
@@ -0,0 +1,78 @@
1
+ import React from "react";
2
+ import { Checkbox, Collapse } from "antd";
3
+
4
+ const { Panel } = Collapse;
5
+
6
+ export const MenuTree = ({ menus, selectedMenus = [], toggleMenu, parentId = null, showCheckbox = true }) => {
7
+ const renderTree = (menuList, parentId = null) => {
8
+ return menuList.map((menu) => {
9
+ const children = menu.sub_menus || [];
10
+
11
+ const toggleMenuRecursive = (m, checked) => {
12
+ toggleMenu && toggleMenu(m.id, checked);
13
+ if (m.sub_menus && m.sub_menus.length > 0) {
14
+ m.sub_menus.forEach((c) => toggleMenuRecursive(c, checked));
15
+ }
16
+ };
17
+
18
+ const onParentChange = (checked) => {
19
+ toggleMenu && toggleMenu(menu.id, checked);
20
+ children.forEach((c) => toggleMenuRecursive(c, checked));
21
+ };
22
+
23
+ if (children.length === 0) {
24
+ return (
25
+ <div
26
+ key={menu.id}
27
+ style={{
28
+ border: "1px solid rgba(198, 195, 195, 0.85)",
29
+ padding: "12px 16px",
30
+ marginBottom: 6,
31
+ background: "#fff",
32
+ display: "flex",
33
+ alignItems: "center",
34
+ gap: 8,
35
+ }}
36
+ >
37
+ {showCheckbox && (
38
+ <Checkbox
39
+ checked={selectedMenus.includes(menu.id)}
40
+ onChange={(e) => onParentChange(e.target.checked)}
41
+ />
42
+ )}
43
+ <span>{menu.title || menu.caption}</span>
44
+ </div>
45
+ );
46
+ }
47
+
48
+ return (
49
+ <Collapse key={menu.id} style={{ marginBottom: 6 }}>
50
+ <Panel
51
+ key={menu.id}
52
+ header={
53
+ <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
54
+ {showCheckbox && (
55
+ <Checkbox
56
+ checked={children.every((c) => selectedMenus.includes(c.id))}
57
+ indeterminate={
58
+ children.some((c) => selectedMenus.includes(c.id)) &&
59
+ !children.every((c) => selectedMenus.includes(c.id))
60
+ }
61
+ onChange={(e) => onParentChange(e.target.checked)}
62
+ />
63
+ )}
64
+ <span>{menu.title || menu.caption}</span>
65
+ </div>
66
+ }
67
+ >
68
+ <div style={{ paddingLeft: 20 }}>
69
+ {renderTree(children, menu.id)}
70
+ </div>
71
+ </Panel>
72
+ </Collapse>
73
+ );
74
+ });
75
+ };
76
+
77
+ return <>{renderTree(menus, parentId)}</>;
78
+ };
@@ -105,6 +105,11 @@ const DoctorAdd = ({ visible, onCancel, attributes, doctorId, doctorData, onSucc
105
105
  return Promise.resolve();
106
106
  }
107
107
 
108
+ if (!/^[a-zA-Z0-9]*$/.test(value)) {
109
+ setCodeStatus({ status: 'error', message: 'Code can only contain letters and numbers.' });
110
+ return Promise.reject(new Error('Code can only contain letters and numbers.'));
111
+ }
112
+
108
113
  if (editMode && doctorData?.do_code === value) {
109
114
  setCodeStatus({ status: 'success', message: 'Code is unchanged' });
110
115
  return Promise.resolve();
@@ -218,7 +223,17 @@ const DoctorAdd = ({ visible, onCancel, attributes, doctorId, doctorData, onSucc
218
223
  {/* NAME + CODE */}
219
224
  <Row gutter={16}>
220
225
  <Col span={12}>
221
- <Form.Item label="Name" name="name" rules={[{ required: true }]}>
226
+ <Form.Item
227
+ label="Name"
228
+ name="name"
229
+ rules={[
230
+ { required: true },
231
+ {
232
+ pattern: /^[A-Za-z]+$/,
233
+ message: 'Name should contain only alphabets',
234
+ },
235
+ ]}
236
+ >
222
237
  <Input autoComplete="off" ref={nameInputRef} placeholder="Enter Name" onKeyDown={handleEnterKey} />
223
238
  </Form.Item>
224
239
  </Col>
@@ -369,7 +384,11 @@ const DoctorAdd = ({ visible, onCancel, attributes, doctorId, doctorData, onSucc
369
384
  </Col>
370
385
 
371
386
  <Col span={8}>
372
- <Form.Item label="Email ID" name="email_id">
387
+ <Form.Item
388
+ label="Email ID"
389
+ name="email_id"
390
+ rules={[{ type: 'email', message: 'Please enter a valid email address' }]}
391
+ >
373
392
  <Input placeholder="Enter Email ID" onKeyDown={handleEnterKey} />
374
393
  </Form.Item>
375
394
  </Col>
@@ -13,6 +13,7 @@ const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
13
13
  * ------------------------------- */
14
14
  const [loading, setLoading] = useState(false);
15
15
  const [designations, setDesignations] = useState([]);
16
+ const [staffList, setStaffList] = useState([]);
16
17
  const [isSubmitDisabled, setIsSubmitDisabled] = useState(true);
17
18
  const [codeStatus, setCodeStatus] = useState({ status: '', message: '' });
18
19
 
@@ -47,6 +48,7 @@ const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
47
48
  useEffect(() => {
48
49
  if (visible) {
49
50
  getDesignations();
51
+ if (!editMode) getStaffList();
50
52
  }
51
53
  }, [visible]);
52
54
 
@@ -95,6 +97,18 @@ const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
95
97
  .finally(() => setLoading(false));
96
98
  };
97
99
 
100
+ /** -------------------------------
101
+ * API CALL – Staff List
102
+ * ------------------------------- */
103
+ const getStaffList = () => {
104
+ UsersAPI.getAllStaff()
105
+ .then((res) => {
106
+ if (Array.isArray(res)) {
107
+ setStaffList(res);
108
+ }
109
+ })
110
+ .catch((err) => console.error(err));
111
+ };
98
112
 
99
113
  /** -------------------------------
100
114
  * VALIDATION – Input
@@ -149,6 +163,16 @@ const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
149
163
  return Promise.resolve();
150
164
  };
151
165
 
166
+ const validateSerial = (_, value) => {
167
+ if (!value) {
168
+ return Promise.resolve();
169
+ }
170
+ const exists = staffList.some((staff) => String(staff.slNo) === value);
171
+ if (exists) {
172
+ return Promise.reject('Serial Number already exists');
173
+ }
174
+ return Promise.resolve();
175
+ };
152
176
 
153
177
  /** -------------------------------
154
178
  * UTIL – Enter Key Navigation
@@ -350,7 +374,11 @@ const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
350
374
  </Col>
351
375
 
352
376
  <Col span={8}>
353
- <Form.Item label="Serial Number" name="slno" rules={[{ message: 'only numbers are allowed', pattern: /^\d*$/ }]}>
377
+ <Form.Item
378
+ label="Serial Number"
379
+ name="slno"
380
+ rules={[{ message: 'only numbers are allowed', pattern: /^\d*$/ }, { validator: validateSerial }]}
381
+ >
354
382
  <Input autoComplete="off" maxLength={5} placeholder="Enter Serial Number" onKeyDown={handleEnterKey} disabled={editMode} />
355
383
  </Form.Item>
356
384
  </Col>
@@ -0,0 +1,228 @@
1
+ import React, { useMemo, useState, useEffect } from 'react';
2
+ import { Checkbox, Input, Typography, Empty, Avatar, List, Card, message, Skeleton } from 'antd';
3
+
4
+ import { UsersAPI, RolesAPI, MenusAPI } from '../../..';
5
+ import { UserRolesAPI } from '../../..';
6
+ import { useParams } from 'react-router-dom';
7
+ import { Button } from '../../../../lib';
8
+ import { MenuTree } from '../../../../lib/elements/basic/menu-tree/menu-tree';
9
+ import './assign-role.scss';
10
+
11
+ const { Text } = Typography;
12
+
13
+ const { Search } = Input;
14
+
15
+ export default function AssignRole() {
16
+ const { id } = useParams();
17
+
18
+ const [user, setUser] = useState(null);
19
+ const [roles, setRoles] = useState([]);
20
+ const [activeRole, setActiveRole] = useState(null);
21
+ const [modules, setModules] = useState([]);
22
+ const [selectedRoles, setSelectedRoles] = useState([]);
23
+ // const [loadingUser, setLoadingUser] = useState(false);
24
+ // const [loadingRoles, setLoadingRoles] = useState(false);
25
+ const [loadingMenus, setLoadingMenus] = useState(false);
26
+ const [selectedMenus, setSelectedMenus] = useState([]);
27
+ const [search, setSearch] = useState('');
28
+
29
+ /** Load user by id */
30
+ useEffect(() => {
31
+ if (id) loadUser();
32
+ }, [id]);
33
+
34
+ const loadUser = async () => {
35
+ setLoadingUser(true);
36
+ try {
37
+ const res = await UsersAPI.getUserRole({ id });
38
+ const list = Array.isArray(res?.result) ? res.result : [];
39
+
40
+ if (!list.length) {
41
+ setUser(null);
42
+ setSelectedRoles([]);
43
+ return;
44
+ }
45
+
46
+ // 1️⃣ Extract user from first record
47
+ const userInfo = list[0].user;
48
+ setUser({
49
+ id: userInfo.id,
50
+ name: userInfo.name,
51
+ });
52
+
53
+ // 2️⃣ Extract VALID role IDs (ignore nulls & duplicates)
54
+ const roleIds = [
55
+ ...new Set(
56
+ list
57
+ .filter((item) => item.role_id) // ignore null role_id
58
+ .map((item) => Number(item.role_id))
59
+ ),
60
+ ];
61
+
62
+ setSelectedRoles(roleIds);
63
+ } catch (e) {
64
+ console.error(e);
65
+ message.error('Unable to load user details');
66
+ } finally {
67
+ setLoadingUser(false);
68
+ }
69
+ };
70
+
71
+ /** Load roles */
72
+ useEffect(() => {
73
+ getRoles();
74
+ }, []);
75
+
76
+ const getRoles = async () => {
77
+ setLoadingRoles(true);
78
+ try {
79
+ const res = await RolesAPI.getRole();
80
+ const activeRoles = Array.isArray(res?.result) ? res.result.filter((r) => r.active === 'Y') : [];
81
+ setRoles(activeRoles);
82
+ } catch (e) {
83
+ console.error(e);
84
+ setRoles([]);
85
+ } finally {
86
+ setLoadingRoles(false);
87
+ }
88
+ };
89
+
90
+ const filterAndSortMenus = (menus, allowedIds) => {
91
+ return menus
92
+ .filter((m) => allowedIds.includes(m.id))
93
+ .map((m) => ({
94
+ ...m,
95
+ sub_menus: filterAndSortMenus(m.sub_menus || [], allowedIds),
96
+ }));
97
+ };
98
+
99
+ /** Load menus for a role */
100
+ const loadRoleMenus = async (role) => {
101
+ setActiveRole(role);
102
+ setSelectedMenus(role.menu_ids || []);
103
+ setLoadingMenus(true);
104
+ try {
105
+ const res = await MenusAPI.getCoreMenuLists();
106
+ const allMenus = res.result || [];
107
+ const filteredMenus = filterAndSortMenus(allMenus, role.menu_ids);
108
+ setModules(filteredMenus);
109
+ } catch (e) {
110
+ console.error(e);
111
+ setModules([]);
112
+ } finally {
113
+ setLoadingMenus(false);
114
+ }
115
+ };
116
+
117
+ /** Toggle menu selection */
118
+ const toggleMenu = (menuId, checked) => {
119
+ setSelectedMenus((prev) => (checked ? [...prev, menuId] : prev.filter((id) => id !== menuId)));
120
+ };
121
+
122
+ /** Filtered roles */
123
+ const filteredRoles = useMemo(() => {
124
+ if (!search) return roles;
125
+ return roles.filter((r) => r.name.toLowerCase().includes(search.toLowerCase()));
126
+ }, [roles, search]);
127
+
128
+ /** Toggle role selection */
129
+ const toggleRole = (roleId, checked) => {
130
+ const next = checked ? [...selectedRoles, roleId] : selectedRoles.filter((v) => v !== roleId);
131
+ setSelectedRoles(next);
132
+ };
133
+
134
+ const handleSaveUserRole = async () => {
135
+ if (!id || !selectedRoles.length) {
136
+ message.warning('User or roles missing');
137
+ return;
138
+ }
139
+
140
+ try {
141
+ message.loading({ key: 'save', content: 'Saving user roles...' });
142
+
143
+ await Promise.all(
144
+ selectedRoles.map((roleId) =>
145
+ UserRolesAPI.addUserRole({
146
+ values: {
147
+ user_id: id,
148
+ role_id: roleId,
149
+ },
150
+ })
151
+ )
152
+ );
153
+
154
+ message.success({ key: 'save', content: 'User roles saved successfully' });
155
+ } catch (err) {
156
+ console.error(err);
157
+ message.error({ key: 'save', content: 'Failed to save user roles' });
158
+ }
159
+ };
160
+
161
+ return (
162
+ <section className="assign-role">
163
+ {/* LEFT PANEL */}
164
+ <Card className="left-panel" bodyStyle={{ padding: 12 }}>
165
+ <div size="small" className="user-card">
166
+ <Avatar size={40}>{user?.name[0]}</Avatar>
167
+
168
+ <div className="user-info">
169
+ <div>{user?.name || '--'}</div>
170
+ <Text className="user-id">ID : {user?.id || '--'}</Text>
171
+ </div>
172
+ </div>
173
+
174
+ <div className="role-list-header">
175
+ <Text strong>Role List ({roles.length})</Text>
176
+ </div>
177
+
178
+ <Search placeholder="Enter Search Value" allowClear style={{ width: 300, marginBottom: '0px' }} onChange={(e) => setSearch(e.target.value)} />
179
+ {/* <Input className="role-search" placeholder="Search Here" allowClear value={search} onChange={(e) => setSearch(e.target.value)} /> */}
180
+
181
+ <List
182
+ itemLayout="horizontal"
183
+ dataSource={filteredRoles}
184
+ renderItem={(role) => (
185
+ <List.Item
186
+ key={role.id}
187
+ onClick={() => loadRoleMenus(role)}
188
+ className={`role-item ${activeRole?.id === role.id ? 'active' : ''}`}
189
+ actions={[
190
+ <Checkbox
191
+ checked={selectedRoles.includes(role.id)}
192
+ onChange={(e) => toggleRole(role.id, e.target.checked)}
193
+ onClick={(e) => e.stopPropagation()}
194
+ />,
195
+ ]}
196
+ >
197
+ <List.Item.Meta avatar={<Avatar size={28}>{role.name[0]}</Avatar>} title={role.name} description={role.description} />
198
+ </List.Item>
199
+ )}
200
+ />
201
+ </Card>
202
+
203
+ {/* RIGHT PANEL */}
204
+ <Card className="right-panel">
205
+ <div className="menus-header">
206
+ <Text>Menus {activeRole ? `– ${activeRole.name}` : ''}</Text>
207
+ <div className="sub-text">It's not editable. To edit it, go to the role editing page.</div>
208
+ </div>
209
+
210
+ <div className="menus-content">
211
+ {loadingMenus ? (
212
+ <Skeleton active paragraph={{ rows: 6 }} />
213
+ ) : modules.length === 0 ? (
214
+ <Empty description="Click a role to view menus" />
215
+ ) : (
216
+ <MenuTree menus={modules} selectedMenus={selectedMenus} toggleMenu={toggleMenu} showCheckbox={false} />
217
+ )}
218
+ </div>
219
+
220
+ <div className="footer-actions">
221
+ <Button type="primary" onClick={handleSaveUserRole}>
222
+ Save
223
+ </Button>
224
+ </div>
225
+ </Card>
226
+ </section>
227
+ );
228
+ }
@@ -0,0 +1,90 @@
1
+ .assign-role {
2
+ display: flex;
3
+ gap: 6px;
4
+
5
+ .left-panel {
6
+ width: 340px;
7
+
8
+ .user-card {
9
+ margin-bottom: 12px;
10
+ background: #fafafa;
11
+ border: none;
12
+ display: flex;
13
+ align-items: center;
14
+ gap: 12px;
15
+ padding: 8px;
16
+
17
+ .user-info {
18
+ font-weight: 500;
19
+
20
+ .user-id {
21
+ font-size: 12px;
22
+ color: rgba(0, 0, 0, 0.45);
23
+ }
24
+ }
25
+ }
26
+
27
+ .role-list-header {
28
+ display: flex;
29
+ justify-content: space-between;
30
+ margin-bottom: 12px;
31
+ strong {
32
+ font-weight: 600;
33
+ }
34
+ }
35
+
36
+ .role-search {
37
+ margin: 12px 0;
38
+ }
39
+
40
+ .role-item {
41
+ cursor: pointer;
42
+ border-radius: 6px;
43
+ padding: 8px 12px;
44
+
45
+ &.active {
46
+ background: #e6f7ff;
47
+ }
48
+
49
+ .ant-list-item-meta-title {
50
+ font-weight: 500;
51
+ }
52
+
53
+ .ant-list-item-meta-description {
54
+ font-size: 12px;
55
+ color: rgba(0, 0, 0, 0.45);
56
+ }
57
+ }
58
+ }
59
+
60
+ .right-panel {
61
+ flex: 1;
62
+ display: flex;
63
+ flex-direction: column;
64
+ justify-content: space-between;
65
+ padding: 16px;
66
+
67
+ .menus-header {
68
+ font-weight: 500;
69
+ margin-bottom: 4px;
70
+ color: #000;
71
+
72
+ .sub-text {
73
+ font-size: 12px;
74
+ color: #888;
75
+ margin-top: 4px;
76
+ }
77
+ }
78
+
79
+ .menus-content {
80
+ margin-top: 16px;
81
+ }
82
+
83
+ .footer-actions {
84
+ margin-top: 16px;
85
+ display: flex;
86
+ justify-content: flex-end;
87
+ gap: 8px;
88
+ }
89
+ }
90
+ }
@@ -728,7 +728,7 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
728
728
  <Input placeholder="Enter User Group" />
729
729
  </Form.Item>
730
730
  </Col>
731
- <Col span={8}>
731
+ {/* <Col span={8}>
732
732
  <Form.Item name="role_id" label="Role" rules={[{ required: true, message: 'Please select a Role' }]}>
733
733
  <Select placeholder="Select Role">
734
734
  {roles.map((role) => (
@@ -738,7 +738,7 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
738
738
  ))}
739
739
  </Select>
740
740
  </Form.Item>
741
- </Col>
741
+ </Col> */}
742
742
 
743
743
  {/* <Form.Item name="mobile" label="Mobile" required={mobileRequired}>
744
744
  <Input placeholder="Enter Mobile" />
@@ -273,8 +273,23 @@ class Users extends Base {
273
273
  updateUser = ({ id, formBody }) => {
274
274
  return ApiUtils.put({ url: `users/update-user/${id}`, formBody });
275
275
  };
276
+
277
+ /* The `getUser` method is a function that takes an object with an `id` property as a parameter. It
278
+ then uses the `ApiUtils.get` function to make a GET request to the endpoint `users/` to fetch
279
+ user data based on the provided `id`. This method is used to retrieve a specific user's
280
+ information from the API. */
281
+
276
282
  getUser = ({ id }) => {
277
283
  return ApiUtils.get({ url: `users/${id}` });
284
+ };
285
+ /* The `getUserRole` method is a function that takes an object with an `id` property as a parameter.
286
+ It then uses the `ApiUtils.get` function to make a GET request to the endpoint `core-user-roles`
287
+ with the `user_id` parameter set to the provided `id`. This method is used to fetch the role or
288
+ roles associated with a specific user from the API. */
289
+
290
+ getUserRole = ({ id }) => {
291
+ return ApiUtils.get({ url: `core-user-roles?user_id=${id}` });
292
+
278
293
  };
279
294
 
280
295
  /**
@@ -8,6 +8,8 @@ import GenericDetail from './generic/components/generic-detail/generic-detail';
8
8
 
9
9
  import ModuleRoutes from './module-routes/module-routes';
10
10
 
11
+ import { AssignRole } from './reporting/components';
12
+
11
13
  // All Dashboard Components
12
14
 
13
15
  import DashboardCard from './dashboard/components/dashboard-card/dashboard-card';
@@ -30,6 +32,7 @@ export {
30
32
  GenericDetail,
31
33
  ModuleRoutes,
32
34
  DashboardCard,
35
+ AssignRole,
33
36
  PopQueryDashboard,
34
37
  HomePageAPI,
35
38
  ReportingDashboard,
@@ -1,5 +1,6 @@
1
1
  import UserEdit from '../../../models/users/components/user-add/user-edit';
2
2
 
3
3
  import UserAdd from '../../../models/users/components/user-add/user-add';
4
+ import AssignRole from '../../../models/users/components/assign-role/assign-role';
4
5
 
5
- export { UserEdit, UserAdd };
6
+ export { UserEdit, UserAdd, AssignRole };
@@ -3,9 +3,10 @@
3
3
  * Handles navigation and action controls for a multi-step process,
4
4
  * including dynamic content rendering and process completion actions.
5
5
  */
6
- import React from 'react';
6
+ import React, { useState } from 'react';
7
7
  import { Skeleton } from 'antd';
8
8
  import { Button } from '../../lib';
9
+ import './action-buttons.scss';
9
10
 
10
11
  export default function ActionButtons({
11
12
  loading,
@@ -21,63 +22,50 @@ export default function ActionButtons({
21
22
  nextProcessId,
22
23
  timelineCollapsed,
23
24
  }) {
25
+ const [showNextProcess, setShowNextProcess] = useState(false);
26
+
24
27
  return (
25
28
  <>
26
29
  <div style={{ minHeight: 300 }}>{loading ? <Skeleton active /> : renderDynamicComponent()}</div>
27
30
  <>
28
- <div style={{ marginTop: 20, display: 'flex', justifyContent: 'flex-start', gap: '10px' }}>
31
+ <div className="action-buttons-container">
29
32
  {/* Back button */}
30
- <Button disabled={activeStep === 0} onClick={handlePrevious} style={{ marginRight: 8, borderRadius: 4 }}>
33
+ <Button disabled={activeStep === 0} onClick={handlePrevious}>
31
34
  Back
32
35
  </Button>
33
36
 
34
37
  {/* Skip button */}
35
38
  {steps.length > 0 && steps[activeStep]?.allow_skip === 'Y' && (
36
- <Button
37
- type="default"
38
- onClick={handleSkip}
39
- style={{
40
- borderRadius: 4,
41
- }}
42
- disabled={activeStep === steps.length - 1}
43
- >
39
+ <Button type="default" onClick={handleSkip} disabled={activeStep === steps.length - 1}>
44
40
  Skip
45
41
  </Button>
46
42
  )}
47
43
 
48
44
  {/* Next / Finish / Start Next */}
49
45
  {steps[activeStep]?.order_seqtype === 'E' ? (
50
- nextProcessId?.next_process_id ? (
51
- <Button
52
- type="primary"
53
- style={{
54
- borderRadius: 4,
55
- }}
56
- onClick={handleStartNextProcess}
57
- >
58
- Start Next {nextProcessId.next_process_name}
59
- </Button>
60
- ) : (
61
- <Button
62
- type="primary"
63
- style={{
64
- borderRadius: 4,
65
- }}
66
- onClick={handleFinish}
67
- >
68
- Finish
69
- </Button>
70
- )
46
+ <>
47
+ {!showNextProcess && (
48
+ <Button
49
+ type="primary"
50
+ onClick={async () => {
51
+ const success = await handleFinish();
52
+ if (success && nextProcessId?.next_process_id) {
53
+ setShowNextProcess(true);
54
+ }
55
+ }}
56
+ >
57
+ Finish
58
+ </Button>
59
+ )}
60
+
61
+ {showNextProcess && nextProcessId?.next_process_id && (
62
+ <Button type="primary" onClick={handleStartNextProcess}>
63
+ Start {nextProcessId.next_process_name}
64
+ </Button>
65
+ )}
66
+ </>
71
67
  ) : (
72
- <Button
73
- type="primary"
74
- // shape="round"
75
- style={{
76
- borderRadius: 4,
77
- }}
78
- disabled={activeStep === steps.length - 1 || !isStepCompleted}
79
- onClick={handleNext}
80
- >
68
+ <Button type="primary" disabled={activeStep === steps.length - 1 || !isStepCompleted} onClick={handleNext}>
81
69
  Next →
82
70
  </Button>
83
71
  )}
@@ -0,0 +1,16 @@
1
+ .action-buttons-container {
2
+ margin-top: 38px;
3
+ display: flex;
4
+ justify-content: flex-start;
5
+ gap: 10px;
6
+ position: sticky;
7
+ bottom: 0;
8
+ z-index: 1000;
9
+ background: #fff;
10
+ padding: 10px 0;
11
+ border-top: 1px solid #f0f0f0;
12
+
13
+ .ant-btn {
14
+ border-radius: 4px;
15
+ }
16
+ }
@@ -204,7 +204,9 @@ export default function ProcessStepsPage({ processId, match, CustomComponents =
204
204
  */
205
205
  const handleFinish = async () => {
206
206
  const final = recordStepTime();
207
- if (await handleProcessSubmit(final)) props.history?.goBack();
207
+ const success = await handleProcessSubmit(final);
208
+ if (success && !nextProcessId) props.history?.goBack();
209
+ return success;
208
210
  };
209
211
  /**
210
212
  * Start Next Process
@@ -268,6 +270,7 @@ export default function ProcessStepsPage({ processId, match, CustomComponents =
268
270
  * and external window view.
269
271
  */
270
272
  const renderContent = () => (
273
+ <div>
271
274
  <Card>
272
275
  <Row gutter={20}>
273
276
  <Col xs={24} sm={24} lg={timelineCollapsed ? 2 : 6}>
@@ -303,6 +306,7 @@ export default function ProcessStepsPage({ processId, match, CustomComponents =
303
306
  </Col>
304
307
  </Row>
305
308
  </Card>
309
+ </div>
306
310
  );
307
311
  /**
308
312
  * Renders content in both the main window and an external window
@@ -1,7 +1,8 @@
1
1
  .timeline-card .ant-card-body {
2
- padding: 20px;
2
+ // padding: 20px;
3
+ border: none;
3
4
  min-height: 400px;
4
- position: fixed; /* For positioning the arrow */
5
+ // position: fixed; /* For positioning the arrow */
5
6
  }
6
7
 
7
8
  .timeline-sidebar {
@@ -46,7 +47,7 @@
46
47
 
47
48
  .vertical-line {
48
49
  width: 2px;
49
- height: 40px;
50
+ height: 20px;
50
51
  background: #d9d9d9;
51
52
  }
52
53
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-soxo-bootstrap-core",
3
- "version": "2.4.25-dev.29",
3
+ "version": "2.4.25-dev.31",
4
4
  "description": "All the Core Components for you to start",
5
5
  "keywords": [
6
6
  "all in one"