ui-soxo-bootstrap-core 2.4.24 → 2.4.25-dev.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/.github/workflows/npm-publish.yml +37 -15
  2. package/core/components/extra-info/extra-info-details.js +109 -126
  3. package/core/components/landing-api/landing-api.js +22 -30
  4. package/core/lib/Store.js +20 -18
  5. package/core/lib/components/index.js +4 -1
  6. package/core/lib/components/sidemenu/sidemenu.js +153 -256
  7. package/core/lib/components/sidemenu/sidemenu.scss +39 -26
  8. package/core/lib/elements/basic/rangepicker/rangepicker.js +118 -29
  9. package/core/lib/elements/basic/switch/switch.js +34 -24
  10. package/core/lib/hooks/index.js +2 -12
  11. package/core/lib/hooks/use-otp-timer.js +99 -0
  12. package/core/lib/pages/login/login.js +255 -139
  13. package/core/lib/pages/login/login.scss +140 -32
  14. package/core/models/dashboard/dashboard.js +14 -0
  15. package/core/models/doctor/components/doctor-add/doctor-add.js +403 -0
  16. package/core/models/doctor/components/doctor-add/doctor-add.scss +32 -0
  17. package/core/models/menus/components/menu-add/menu-add.js +230 -268
  18. package/core/models/menus/components/menu-lists/menu-lists.js +126 -89
  19. package/core/models/menus/components/menu-lists/menu-lists.scss +9 -0
  20. package/core/models/menus/menus.js +247 -267
  21. package/core/models/roles/components/role-add/role-add.js +269 -227
  22. package/core/models/roles/components/role-list/role-list.js +8 -6
  23. package/core/models/roles/roles.js +182 -174
  24. package/core/models/staff/components/staff-add/staff-add.js +352 -0
  25. package/core/models/staff/components/staff-add/staff-add.scss +0 -0
  26. package/core/models/users/components/user-add/user-add.js +686 -364
  27. package/core/models/users/components/user-add/user-edit.js +90 -0
  28. package/core/models/users/users.js +318 -165
  29. package/core/modules/index.js +5 -8
  30. package/core/modules/reporting/components/index.js +5 -0
  31. package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +65 -2
  32. package/core/modules/steps/action-buttons.js +79 -0
  33. package/core/modules/steps/steps.js +553 -0
  34. package/core/modules/steps/steps.scss +158 -0
  35. package/core/modules/steps/timeline.js +49 -0
  36. package/package.json +2 -2
@@ -0,0 +1,553 @@
1
+ import React, { useEffect, useState, useCallback } from 'react';
2
+ import { Skeleton, Row, Col, Empty } from 'antd';
3
+ import {Card} from './../../lib';
4
+ import * as genericComponents from './../../lib';
5
+ import { LeftOutlined, RightOutlined } from '@ant-design/icons';
6
+ import moment from 'moment';
7
+ import { Location } from './../../lib';
8
+ import ActionButtons from './action-buttons';
9
+ import { Dashboard } from '../../models';
10
+
11
+ import './steps.scss';
12
+ import TimelinePanel from './timeline';
13
+
14
+ /**
15
+ * ProcessStepsPage Component
16
+ *
17
+ * This component manages a multi-step process workflow. It dynamically loads steps from an API,
18
+ * renders the appropriate component for each step, tracks user progress and timings,
19
+ * and submits the final data upon completion. It features a collapsible timeline for navigation.
20
+ *
21
+ * @param {object} props - The component props.
22
+ * @returns {JSX.Element} The rendered ProcessStepsPage component.
23
+ */
24
+ // export default function ProcessStepsPage({ processId = 1, match, CustomComponents = {}, ...props }) {
25
+ // const allComponents = { ...genericComponents, ...CustomComponents }; // CustomComponents will default to an empty object
26
+ // const [loading, setLoading] = useState(false);
27
+ // const [steps, setSteps] = useState([]);
28
+ // const [activeStep, setActiveStep] = useState(0);
29
+ // const [isStepCompleted, setIsStepCompleted] = useState(false);
30
+ // const [currentProcessId, setCurrentProcessId] = useState(processId);
31
+ // const [nextProcessId, setNextProcessId] = useState(null);
32
+ // const [stepStartTime, setStepStartTime] = useState(null);
33
+ // const [processStartTime, setProcessStartTime] = useState(null);
34
+ // const [processTimings, setProcessTimings] = useState([]);
35
+ // const [timelineCollapsed, setTimelineCollapsed] = useState(true);
36
+ // const urlParams = Location.search();
37
+
38
+ // /**
39
+ // * useEffect: Load process data on initial mount or when `currentProcessId` changes.
40
+ // * This hook is responsible for fetching the process steps, initializing timings from localStorage,
41
+ // * and setting the start time for the process and the first step.
42
+ // */
43
+ // useEffect(() => {
44
+ // loadProcess(currentProcessId);
45
+ // // Load existing timings from localStorage or initialize
46
+ // const savedTimings = localStorage.getItem(`processTimings_${currentProcessId}`);
47
+ // setProcessTimings(savedTimings ? JSON.parse(savedTimings) : []);
48
+ // setProcessStartTime(Date.now()); // Record the start time of the process
49
+ // // setActiveStep(0); // Reset to the first step
50
+ // setStepStartTime(Date.now()); // Start timer for the first step
51
+ // }, [currentProcessId]);
52
+
53
+ // /**
54
+ // * useEffect: Reset the step timer whenever the active step changes.
55
+ // * This ensures that the duration for each step is calculated accurately from the moment it becomes active.
56
+ // */
57
+ // useEffect(() => {
58
+ // setStepStartTime(Date.now());
59
+ // }, [activeStep]);
60
+
61
+ // /**
62
+ // * useEffect: Manage the completion status of the current step.
63
+ // * If a step is not mandatory, it is considered "completed" by default,
64
+ // * allowing the user to proceed. For mandatory steps, the component waits for
65
+ // * a signal from the child component via `onStepComplete`.
66
+ // */
67
+ // useEffect(() => {
68
+ // if (steps.length > 0) {
69
+ // const currentStep = steps[activeStep];
70
+ // // If the new step is not mandatory, we can proceed.
71
+ // // Otherwise, we must wait for the child component to signal completion.
72
+ // setIsStepCompleted(currentStep?.is_mandatory !== true);
73
+ // }
74
+ // }, [activeStep, steps]);
75
+
76
+ // /**
77
+ // * Callback passed to child components to signal completion of a mandatory step.
78
+ // * This allows the parent to enable the "Next" button.
79
+ // */
80
+ // const handleStepCompletion = () => {
81
+ // setIsStepCompleted(true);
82
+ // };
83
+
84
+ // /**
85
+ // * Records the timing information for the current step.
86
+ // * Calculates duration, formats start/end times, and adds or updates the step's
87
+ // * timing data in the `processTimings` array.
88
+ // * @param {Array} currentTimings - The existing array of step timings.
89
+ // * @param {string} [status='completed'] - The status of the step ('completed' or 'skipped').
90
+ // * @returns {Array} The updated array of step timings.
91
+ // */
92
+ // const recordStepTime = useCallback(
93
+ // (currentTimings, status = 'completed') => {
94
+ // if (stepStartTime && steps[activeStep]) {
95
+ // const endTime = Date.now();
96
+ // const duration = endTime - stepStartTime;
97
+ // const currentStepId = steps[activeStep].step_id;
98
+
99
+ // const existingEntryIndex = currentTimings.findIndex((t) => t.step_id === currentStepId);
100
+ // const newTimings = [...currentTimings];
101
+
102
+ // const newEntry = {
103
+ // step_id: currentStepId,
104
+ // start_time: moment(stepStartTime).format('DD-MM-YYYY HH:mm:ss'),
105
+ // end_time: moment(endTime).format('DD-MM-YYYY HH:mm:ss'),
106
+ // duration: duration,
107
+ // status: status,
108
+ // };
109
+
110
+ // if (existingEntryIndex > -1) {
111
+ // // If re-visiting a step, add to duration and update end_time/status
112
+ // newTimings[existingEntryIndex].duration += duration;
113
+ // newTimings[existingEntryIndex].start_time = newEntry.start_time;
114
+ // newTimings[existingEntryIndex].end_time = newEntry.end_time;
115
+ // newTimings[existingEntryIndex].status = newEntry.status;
116
+ // } else {
117
+ // newTimings.push(newEntry);
118
+ // }
119
+ // return newTimings;
120
+ // }
121
+ // return currentTimings; // Return original if no update
122
+ // },
123
+ // [activeStep, steps, stepStartTime]
124
+ // );
125
+
126
+ // /**
127
+ // * Fetches the process data, including steps and next process information, from the API.
128
+ // * @param {number} processId - The ID of the process to load.
129
+ // * @returns {Promise<object|undefined>} A promise that resolves with the process data.
130
+ // */
131
+ // async function loadProcess(processId) {
132
+ // setLoading(true);
133
+ // setNextProcessId(null); // Reset on each new process load
134
+
135
+ // try {
136
+ // const response = await fetch(`http://localhost:8002/dev/process/${processId}`, {
137
+ // method: 'GET',
138
+ // headers: {
139
+ // 'Content-Type': 'application/json',
140
+ // },
141
+ // });
142
+
143
+ // if (!response.ok) {
144
+ // throw new Error('API request failed');
145
+ // }
146
+
147
+ // const result = await response.json();
148
+
149
+ // const fetchedSteps = result?.data?.steps || [];
150
+
151
+ // if (result?.data?.next_process_id) {
152
+ // setNextProcessId(result.data);
153
+ // //next_process_id
154
+ // }
155
+ // setLoading(false);
156
+
157
+ // setSteps(fetchedSteps);
158
+
159
+ // return result.data;
160
+ // } catch (e) {
161
+ // console.error('Error loading process steps:', e);
162
+ // } finally {
163
+ // setLoading(false);
164
+ // }
165
+ // }
166
+
167
+ // /**
168
+ // * Submits the collected process and step timings to the backend API.
169
+ // * On success, it clears the timings from localStorage.
170
+ // * On failure, it saves the latest timings to localStorage for a potential retry.
171
+ // * @param {Array} finalStepTimings - The complete array of timings for all steps.
172
+ // * @returns {Promise<boolean>} A promise that resolves to `true` on success and `false` on failure.
173
+ // */
174
+ // const submitProcessTimings = useCallback(
175
+ // async (finalStepTimings) => {
176
+ // const payload = {
177
+ // process_id: currentProcessId,
178
+ // status: 'completed',
179
+ // reference_id: urlParams?.opb_id || urlParams?.reference_id,
180
+ // reference_number: urlParams?.opno || urlParams?.reference_number,
181
+ // mode: urlParams?.mode,
182
+ // process: {
183
+ // process_start_time: moment(processStartTime).format('DD-MM-YYYY HH:mm'),
184
+ // process_end_time: moment().format('DD-MM-YYYY HH:mm'),
185
+ // steps: finalStepTimings,
186
+ // },
187
+ // };
188
+
189
+ // console.log('Submitting process timings:', payload);
190
+
191
+ // try {
192
+ // const response = await fetch(`http://localhost:8002/dev/process/process-log`, {
193
+ // method: 'POST',
194
+ // headers: { 'Content-Type': 'application/json' },
195
+ // body: JSON.stringify(payload),
196
+ // });
197
+
198
+ // if (!response.ok) {
199
+ // throw new Error('API request to save timings failed');
200
+ // }
201
+
202
+ // console.log('Process timings saved successfully.'); // Clear timings from state and localStorage after successful submission
203
+ // localStorage.removeItem(`processTimings_${currentProcessId}`);
204
+ // setProcessTimings([]);
205
+ // return true; // Indicate success
206
+ // } catch (e) {
207
+ // console.error('Error saving process timings:', e);
208
+ // // If API fails, save the latest timings to state and localStorage to allow for a retry.
209
+ // setProcessTimings(finalStepTimings);
210
+ // localStorage.setItem(`processTimings_${currentProcessId}`, JSON.stringify(finalStepTimings));
211
+ // return false; // Indicate failure
212
+ // }
213
+ // },
214
+ // [processStartTime, currentProcessId]
215
+ // );
216
+
217
+ // /**
218
+ // * Handles the transition to the next process.
219
+ // * It submits the timings for the current process and, upon success,
220
+ // * loads and transitions to the next process.
221
+ // */
222
+ // const handleStartNextProcess = async () => {
223
+ // const finalTimings = recordStepTime(processTimings, 'completed');
224
+ // const success = await submitProcessTimings(finalTimings);
225
+ // if (success) {
226
+ // const nextProcess = await loadProcess(nextProcessId.next_process_id);
227
+ // setCurrentProcessId(nextProcessId.next_process_id); // Trigger the useEffect to load the next process
228
+ // setActiveStep(0);
229
+ // }
230
+ // };
231
+
232
+ // /**
233
+ // * Handles the final submission when the entire workflow is finished.
234
+ // * Submits the final timings and navigates the user back to the previous page
235
+ // * (e.g., a listing page).
236
+ // */
237
+ // const handleFinish = useCallback(async () => {
238
+ // const finalTimings = recordStepTime(processTimings, 'completed');
239
+ // const success = await submitProcessTimings(finalTimings);
240
+ // if (success && props.history) {
241
+ // props.history.goBack(); // Navigate to the previous page
242
+ // }
243
+ // }, [processTimings, recordStepTime, submitProcessTimings, props.history]);
244
+
245
+ // /**
246
+ // * Dynamically renders the component for the active step.
247
+ // * It looks up the component name from the step's configuration and passes
248
+ // * the necessary props to it.
249
+ // * @returns {JSX.Element} The component for the current step or an Empty state.
250
+ // */
251
+ // const renderDynamicComponent = () => {
252
+ // const step = steps[activeStep];
253
+ // if (!step) return <Empty description="No step selected" />;
254
+
255
+ // const componentName = step.related_page;
256
+ // const LoadedComponent = allComponents[componentName];
257
+
258
+ // if (!LoadedComponent) {
259
+ // return <Empty description={`Component "${componentName}" not found`} />;
260
+ // }
261
+
262
+ // return <LoadedComponent {...step.config} {...props} step={step} params={urlParams} refresh={loadProcess} onStepComplete={handleStepCompletion} />;
263
+ // };
264
+
265
+ // /**
266
+ // * Handles the "Next" button click. Records the current step's time and advances to the next step.
267
+ // */
268
+ // const handleNext = () => {
269
+ // const updatedTimings = recordStepTime(processTimings, 'completed');
270
+ // setProcessTimings(updatedTimings);
271
+ // localStorage.setItem(`processTimings_${currentProcessId}`, JSON.stringify(updatedTimings));
272
+ // setActiveStep(activeStep + 1);
273
+ // };
274
+
275
+ // /**
276
+ // * Handles the "Back" button click. Records the current step's time and moves to the previous step.
277
+ // */
278
+ // const handlePrevious = () => {
279
+ // const updatedTimings = recordStepTime(processTimings, 'completed');
280
+ // setProcessTimings(updatedTimings);
281
+ // localStorage.setItem(`processTimings_${currentProcessId}`, JSON.stringify(updatedTimings));
282
+ // setActiveStep(activeStep - 1);
283
+ // };
284
+
285
+ // /**
286
+ // * Handles clicks on the timeline steps. Records the current step's time and navigates to the clicked step.
287
+ // * @param {number} index - The index of the step that was clicked.
288
+ // */
289
+ // const handleTimelineClick = (index) => {
290
+ // const updatedTimings = recordStepTime(processTimings, 'completed');
291
+ // setProcessTimings(updatedTimings);
292
+ // localStorage.setItem(`processTimings_${currentProcessId}`, JSON.stringify(updatedTimings));
293
+ // setActiveStep(index);
294
+ // };
295
+
296
+ // /**
297
+ // * Handles the "Skip" button click. Records the current step with a 'skipped' status and advances to the next step.
298
+ // */
299
+ // const handleSkip = () => {
300
+ // const updatedTimings = recordStepTime(processTimings, 'skipped');
301
+ // setProcessTimings(updatedTimings);
302
+ // localStorage.setItem(`processTimings_${currentProcessId}`, JSON.stringify(updatedTimings));
303
+ // setActiveStep(activeStep + 1);
304
+ // };
305
+
306
+ // return (
307
+ // <Card style={{ padding: 20 }}>
308
+ // <Row gutter={20}>
309
+ // {/* LEFT PANEL – Step Timeline */}
310
+ // <Col span={timelineCollapsed ? 2 : 6}>
311
+ // <LeftTimelinePanel
312
+ // loading={loading}
313
+ // steps={steps}
314
+ // activeStep={activeStep}
315
+ // timelineCollapsed={timelineCollapsed}
316
+ // handleTimelineClick={handleTimelineClick}
317
+ // setTimelineCollapsed={setTimelineCollapsed}
318
+ // />
319
+ // </Col>
320
+
321
+ // {/* RIGHT PANEL – Dynamic Component Loading */}
322
+ // <Col span={timelineCollapsed ? 22 : 18}>
323
+ // <Col span={timelineCollapsed ? 22 : 18}>
324
+ // <RenderDynamicComponent
325
+ // loading={loading}
326
+ // steps={steps}
327
+ // activeStep={activeStep}
328
+ // isStepCompleted={isStepCompleted}
329
+ // renderDynamicComponent={renderDynamicComponent}
330
+ // handlePrevious={handlePrevious}
331
+ // handleNext={handleNext}
332
+ // handleSkip={handleSkip}
333
+ // handleFinish={handleFinish}
334
+ // handleStartNextProcess={handleStartNextProcess}
335
+ // nextProcessId={nextProcessId}
336
+ // timelineCollapsed={timelineCollapsed}
337
+ // />
338
+ // </Col>
339
+ // </Col>
340
+ // </Row>
341
+ // </Card>
342
+ // );
343
+ // }
344
+ export default function ProcessStepsPage({ processId = 1, match, CustomComponents = {}, ...props }) {
345
+ const allComponents = { ...genericComponents, ...CustomComponents };
346
+
347
+ const [loading, setLoading] = useState(false);
348
+ const [steps, setSteps] = useState([]);
349
+ const [activeStep, setActiveStep] = useState(0);
350
+ const [isStepCompleted, setIsStepCompleted] = useState(false);
351
+ const [currentProcessId, setCurrentProcessId] = useState(processId);
352
+ const [nextProcessId, setNextProcessId] = useState(null);
353
+ const [stepStartTime, setStepStartTime] = useState(null);
354
+ const [processStartTime, setProcessStartTime] = useState(null);
355
+ const [processTimings, setProcessTimings] = useState([]);
356
+ const [timelineCollapsed, setTimelineCollapsed] = useState(true);
357
+
358
+ const urlParams = Location.search();
359
+
360
+ useEffect(() => {
361
+ loadProcess(currentProcessId);
362
+
363
+ const saved = localStorage.getItem(`processTimings_${currentProcessId}`);
364
+ setProcessTimings(saved ? JSON.parse(saved) : []);
365
+
366
+ setProcessStartTime(Date.now());
367
+ setStepStartTime(Date.now());
368
+ }, [currentProcessId]);
369
+
370
+ useEffect(() => {
371
+ setStepStartTime(Date.now());
372
+ }, [activeStep]);
373
+
374
+ useEffect(() => {
375
+ if (steps.length > 0) {
376
+ setIsStepCompleted(steps[activeStep]?.is_mandatory !== true);
377
+ }
378
+ }, [activeStep, steps]);
379
+
380
+ const saveTimings = (updated) => {
381
+ setProcessTimings(updated);
382
+ localStorage.setItem(`processTimings_${currentProcessId}`, JSON.stringify(updated));
383
+ };
384
+
385
+ const recordStepTime = (status = 'completed') => {
386
+ if (!stepStartTime || !steps[activeStep]) return processTimings;
387
+
388
+ const endTime = Date.now();
389
+ const duration = endTime - stepStartTime;
390
+ const stepId = steps[activeStep].step_id;
391
+
392
+ const updated = [...processTimings];
393
+ const index = updated.findIndex((t) => t.step_id === stepId);
394
+
395
+ const entry = {
396
+ step_id: stepId,
397
+ start_time: moment(stepStartTime).format('DD-MM-YYYY HH:mm:ss'),
398
+ end_time: moment(endTime).format('DD-MM-YYYY HH:mm:ss'),
399
+ duration,
400
+ status,
401
+ };
402
+
403
+ if (index > -1) {
404
+ updated[index] = { ...updated[index], ...entry, duration: updated[index].duration + duration };
405
+ } else {
406
+ updated.push(entry);
407
+ }
408
+
409
+ return updated;
410
+ };
411
+
412
+ async function loadProcess(processId) {
413
+ setLoading(true);
414
+ setNextProcessId(null);
415
+
416
+ try {
417
+ const result = await Dashboard.loadProcess(processId);
418
+
419
+ setSteps(result?.data?.steps || []);
420
+ if (result?.data?.next_process_id) setNextProcessId(result.data);
421
+ } catch (e) {
422
+ console.error('Error loading process steps:', e);
423
+ } finally {
424
+ setLoading(false);
425
+ }
426
+ }
427
+
428
+ const handleProcessSubmit = async (finalTimings) => {
429
+ const payload = {
430
+ process_id: currentProcessId,
431
+ status: 'completed',
432
+ reference_id: urlParams?.opb_id || urlParams?.reference_id,
433
+ reference_number: urlParams?.opno || urlParams?.reference_number,
434
+ mode: urlParams?.mode,
435
+ process: {
436
+ process_start_time: moment(processStartTime).format('DD-MM-YYYY HH:mm'),
437
+ process_end_time: moment().format('DD-MM-YYYY HH:mm'),
438
+ steps: finalTimings,
439
+ },
440
+ };
441
+
442
+ try {
443
+ const response = await Dashboard.processLog(payload);
444
+
445
+ if (response.success) {
446
+ localStorage.removeItem(`processTimings_${currentProcessId}`);
447
+ setProcessTimings([]);
448
+ return true;
449
+ }
450
+ } catch (e) {
451
+ console.error('Error:', e);
452
+ saveTimings(finalTimings);
453
+ }
454
+ return false;
455
+ };
456
+
457
+ const gotoStep = (index, status = 'completed') => {
458
+ const updated = recordStepTime(status);
459
+ saveTimings(updated);
460
+ setActiveStep(index);
461
+ };
462
+
463
+ const handleNext = () => gotoStep(activeStep + 1);
464
+ const handlePrevious = () => gotoStep(activeStep - 1);
465
+ const handleSkip = () => gotoStep(activeStep + 1, 'skipped');
466
+ const handleTimelineClick = (i) => gotoStep(i);
467
+
468
+ const handleFinish = async () => {
469
+ const final = recordStepTime();
470
+ if (await handleProcessSubmit(final)) props.history?.goBack();
471
+ };
472
+
473
+ const handleStartNextProcess = async () => {
474
+ const final = recordStepTime();
475
+ if (await handleProcessSubmit(final)) {
476
+ await loadProcess(nextProcessId.next_process_id);
477
+ setCurrentProcessId(nextProcessId.next_process_id);
478
+ setActiveStep(0);
479
+ }
480
+ };
481
+
482
+ const DynamicComponent = () => {
483
+ const step = steps[activeStep];
484
+ if (!step) return <Empty description="No step selected" />;
485
+
486
+ const Component = allComponents[step.related_page];
487
+ if (!Component) return <Empty description={`Component "${step.related_page}" not found`} />;
488
+
489
+ return <Component {...step.config} {...props} step={step} params={urlParams} onStepComplete={() => setIsStepCompleted(true)} />;
490
+ };
491
+
492
+ useEffect(() => {
493
+ const handleKeyDown = (event) => {
494
+ // Handle Left Arrow key press to go to the previous step
495
+ if (event.key === 'ArrowLeft') {
496
+ if (activeStep > 0) {
497
+ handlePrevious();
498
+ }
499
+ }
500
+ // Handle Right Arrow key press to go to the next step
501
+ else if (event.key === 'ArrowRight') {
502
+ if (activeStep < steps.length - 1) {
503
+ handleNext();
504
+ }
505
+ }
506
+ };
507
+
508
+ window.addEventListener('keydown', handleKeyDown);
509
+ return () => window.removeEventListener('keydown', handleKeyDown);
510
+ }, [activeStep, steps, handlePrevious, handleNext]);
511
+
512
+ return (
513
+ <Card>
514
+ <Row gutter={20}>
515
+ <Col xs={24} sm={24} lg={timelineCollapsed ? 2 : 6}>
516
+
517
+
518
+
519
+ <TimelinePanel
520
+ loading={loading}
521
+ steps={steps}
522
+ activeStep={activeStep}
523
+ timelineCollapsed={timelineCollapsed}
524
+ handleTimelineClick={handleTimelineClick}
525
+ setTimelineCollapsed={setTimelineCollapsed}
526
+ />
527
+ </Col>
528
+
529
+ <Col xs={24} sm={24} lg={timelineCollapsed ? 21 : 18}>
530
+
531
+ <div style={{ marginBottom: 20 }}>
532
+ <h2 style={{ margin: 0, fontSize: 16, fontWeight: 600 }}>{steps[activeStep]?.step_name}</h2>
533
+ <p style={{ margin: 0, color: '#666' }}>{steps[activeStep]?.step_description}</p>
534
+ </div>
535
+ <ActionButtons
536
+ loading={loading}
537
+ steps={steps}
538
+ activeStep={activeStep}
539
+ isStepCompleted={isStepCompleted}
540
+ renderDynamicComponent={DynamicComponent}
541
+ handlePrevious={handlePrevious}
542
+ handleNext={handleNext}
543
+ handleSkip={handleSkip}
544
+ handleFinish={handleFinish}
545
+ handleStartNextProcess={handleStartNextProcess}
546
+ nextProcessId={nextProcessId}
547
+ timelineCollapsed={timelineCollapsed}
548
+ />
549
+ </Col>
550
+ </Row>
551
+ </Card>
552
+ );
553
+ }