ui-soxo-bootstrap-core 2.4.25-dev.9 → 2.4.25

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 (31) hide show
  1. package/.github/workflows/npm-publish.yml +15 -37
  2. package/core/components/extra-info/extra-info-details.js +126 -109
  3. package/core/components/landing-api/landing-api.js +30 -22
  4. package/core/lib/Store.js +18 -20
  5. package/core/lib/components/index.js +1 -4
  6. package/core/lib/components/sidemenu/sidemenu.js +256 -153
  7. package/core/lib/components/sidemenu/sidemenu.scss +26 -39
  8. package/core/lib/elements/basic/rangepicker/rangepicker.js +29 -118
  9. package/core/lib/hooks/index.js +12 -2
  10. package/core/lib/pages/login/login.js +139 -255
  11. package/core/lib/pages/login/login.scss +32 -140
  12. package/core/models/dashboard/dashboard.js +0 -14
  13. package/core/models/menus/components/menu-add/menu-add.js +268 -230
  14. package/core/models/menus/components/menu-lists/menu-lists.js +89 -126
  15. package/core/models/menus/components/menu-lists/menu-lists.scss +0 -9
  16. package/core/models/menus/menus.js +267 -247
  17. package/core/models/roles/components/role-add/role-add.js +227 -269
  18. package/core/models/roles/components/role-list/role-list.js +6 -8
  19. package/core/models/roles/roles.js +174 -182
  20. package/core/models/users/components/user-add/user-add.js +1 -69
  21. package/core/models/users/users.js +0 -57
  22. package/core/modules/index.js +13 -23
  23. package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +1 -1
  24. package/package.json +2 -2
  25. package/core/lib/hooks/use-otp-timer.js +0 -99
  26. package/core/models/staff/components/staff-add/staff-add.js +0 -352
  27. package/core/models/staff/components/staff-add/staff-add.scss +0 -0
  28. package/core/modules/steps/action-buttons.js +0 -79
  29. package/core/modules/steps/steps.js +0 -553
  30. package/core/modules/steps/steps.scss +0 -158
  31. package/core/modules/steps/timeline.js +0 -49
@@ -1,99 +0,0 @@
1
- import { useState, useEffect, useRef } from 'react';
2
-
3
- /**
4
- * Custom hook to manage an OTP (One-Time Password) countdown timer.
5
- *
6
- * Features:
7
- * - Can start a timer directly with seconds.
8
- * - Can start a timer using an expiry timestamp (from backend).
9
- * - Provides formatted remaining time (MM:SS).
10
- * - Tracks whether the timer has expired.
11
- *
12
- * @returns {Object} API
13
- * @returns {number} API.remaining - Remaining seconds.
14
- * @returns {boolean} API.expired - Whether the timer has expired.
15
- * @returns {string} API.formatted - Formatted remaining time (MM:SS).
16
- * @returns {(seconds: number) => void} API.start - Start timer with seconds.
17
- * @returns {(expirytime: string) => void} API.startFromExpiry - Start timer using expiry string (e.g. "2025-09-04T12:13:09.000Z").
18
- *
19
- * @example
20
- * const { remaining, expired, formatted, start, startFromExpiry } = useOtpTimer();
21
- *
22
- * // Start with 30 seconds
23
- * useEffect(() => {
24
- * start(30);
25
- * }, []);
26
- *
27
- * // OR start from backend expiry timestamp
28
- * useEffect(() => {
29
- * startFromExpiry("2025-09-04T12:13:09.000Z");
30
- * }, []);
31
- *
32
- * return (
33
- * <div>
34
- * <p>Time left: {formatted}</p>
35
- * {expired && <p>OTP expired!</p>}
36
- * </div>
37
- * );
38
- */
39
-
40
- // helper to format time
41
- const formatTime = (seconds) => {
42
- const minutes = Math.floor((seconds % 3600) / 60);
43
- const secs = seconds % 60;
44
- return `${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
45
- };
46
-
47
- export const useOtpTimer = () => {
48
- const [remaining, setRemaining] = useState(0);
49
- const [expired, setExpired] = useState(false);
50
- const intervalRef = useRef(null);
51
-
52
- // Start timer by passing expiry string from backend
53
- const startFromExpiry = (expirytime) => {
54
- if (!expirytime) {
55
- setRemaining(0);
56
- setExpired(true);
57
- return;
58
- }
59
-
60
- // Handle backend expiry string
61
- const expiryTime = new Date(expirytime).getTime();
62
- const now = Date.now();
63
- const seconds = Math.max(0, Math.floor((expiryTime - now) / 1000));
64
-
65
- start(seconds);
66
- };
67
-
68
- // Generic start (seconds)
69
- const start = (seconds) => {
70
- clearInterval(intervalRef.current);
71
-
72
- if (!seconds || seconds <= 0) {
73
- setRemaining(0);
74
- setExpired(true);
75
- return;
76
- }
77
-
78
- setRemaining(seconds);
79
- setExpired(false);
80
-
81
- intervalRef.current = setInterval(() => {
82
- setRemaining((prev) => {
83
- if (prev <= 1) {
84
- clearInterval(intervalRef.current);
85
- setExpired(true);
86
- return 0;
87
- }
88
- return prev - 1;
89
- });
90
- }, 1000);
91
- };
92
-
93
- // Cleanup on unmount
94
- useEffect(() => {
95
- return () => clearInterval(intervalRef.current);
96
- }, []);
97
-
98
- return { remaining, expired, formatted: formatTime(remaining), start, startFromExpiry };
99
- };
@@ -1,352 +0,0 @@
1
- import React, { useState, useEffect, useRef } from 'react';
2
- import './staff-add.scss';
3
- import { Modal, Tabs, Input, Form, Row, Col, message, Checkbox, Select } from 'antd';
4
- import { useTranslation, Button } from './../../../../lib/';
5
- import { UsersAPI } from '../../..';
6
-
7
- const StaffAdd = ({ visible, onCancel, staffId, staffData, onSuccess }) => {
8
- const [form] = Form.useForm();
9
- const { t } = useTranslation();
10
-
11
- /** -------------------------------
12
- * STATE
13
- * ------------------------------- */
14
- const [loading, setLoading] = useState(false);
15
- const [designations, setDesignations] = useState([]);
16
- const [isSubmitDisabled, setIsSubmitDisabled] = useState(true);
17
- const [codeStatus, setCodeStatus] = useState({ status: '', message: '' });
18
-
19
- const nameInputRef = useRef(null);
20
-
21
- const editMode = Boolean(staffId);
22
-
23
- /** -------------------------------
24
- * FORM WATCHERS
25
- * ------------------------------- */
26
- const watchedName = Form.useWatch('shortName', form);
27
- const watchedCode = Form.useWatch('id', form);
28
- const watchedDesc = Form.useWatch('description', form);
29
-
30
- /** -------------------------------
31
- * EFFECTS
32
- * ------------------------------- */
33
-
34
- // Auto-focus on open
35
- useEffect(() => {
36
- if (visible) {
37
- setTimeout(() => nameInputRef.current?.focus(), 200);
38
- }
39
- }, [visible]);
40
-
41
- // Control submit button
42
- useEffect(() => {
43
- setIsSubmitDisabled(!(watchedName && watchedCode && watchedDesc));
44
- }, [watchedName, watchedCode, watchedDesc]);
45
-
46
- // Load designations when modal opens
47
- useEffect(() => {
48
- if (visible) getDesignations();
49
- }, [visible]);
50
-
51
- // Reset or fill edit form
52
- useEffect(() => {
53
- if (visible) {
54
- if (editMode && staffData) fillEditForm(staffData);
55
- } else {
56
- form.resetFields();
57
- }
58
- }, [visible]);
59
-
60
- /** -------------------------------
61
- * API CALL – Designations
62
- * ------------------------------- */
63
- const getDesignations = () => {
64
- setLoading(true);
65
- UsersAPI.getDesignations()
66
- .then((res) => {
67
- if (res?.success && Array.isArray(res.result)) {
68
- const formatted = res.result.map((ele) => ({
69
- label: ele.dg_desc?.trim() || '',
70
- value: ele.dg_code,
71
- }));
72
- setDesignations(formatted);
73
- } else {
74
- setDesignations([]);
75
- }
76
- })
77
- .catch((err) => {
78
- console.error('Error fetching designations:', err);
79
- setDesignations([]);
80
- })
81
- .finally(() => setLoading(false));
82
- };
83
-
84
- /** -------------------------------
85
- * VALIDATION – Code
86
- * ------------------------------- */
87
- const validateDoctorCode = async (_, value) => {
88
- if (editMode) return Promise.resolve(); // Skip validation if editing
89
-
90
- if (!value) {
91
- setCodeStatus({ status: '', message: '' });
92
- return Promise.resolve();
93
- }
94
-
95
- try {
96
- const res = await UsersAPI.getStaffCode(value);
97
-
98
- if (res?.status === 409 || res?.success === false) {
99
- setCodeStatus({ status: 'error', message: res.message || 'Code already exists' });
100
- return Promise.reject(new Error(res.message || 'Code already exists'));
101
- }
102
-
103
- if (res?.status === 200 && res?.success === true) {
104
- setCodeStatus({
105
- status: 'success',
106
- message: 'The entered code is valid for assigning a staff.',
107
- });
108
- return Promise.resolve();
109
- }
110
-
111
- setCodeStatus({ status: 'error', message: 'Unexpected response' });
112
- return Promise.reject(new Error('Unexpected response'));
113
- } catch {
114
- setCodeStatus({ status: 'error', message: 'Validation failed' });
115
- return Promise.reject(new Error('Validation failed'));
116
- }
117
- };
118
-
119
- /** -------------------------------
120
- * UTIL – Enter Key Navigation
121
- * ------------------------------- */
122
- const handleEnterKey = (e) => {
123
- if (e.key === 'Enter') {
124
- e.preventDefault();
125
- const fields = Array.from(document.querySelectorAll('.ant-input'));
126
- const index = fields.indexOf(e.target);
127
- if (index > -1 && index < fields.length - 1) {
128
- fields[index + 1].focus();
129
- }
130
- }
131
- };
132
-
133
- /** -------------------------------
134
- * FORM FILL – Edit Mode
135
- * ------------------------------- */
136
- const fillEditForm = (data) => {
137
- form.setFieldsValue({
138
- id: data.id,
139
- shortName: data.shortName,
140
- description: data.description,
141
- designation: data.designationPtr,
142
- phone1: data.phone,
143
- phone2: data.mobile || data.alternateMobile,
144
- email1: data.email,
145
- email2: data.alternateEmail,
146
- slno: data.slNo,
147
- active: data.active === 'Y' ? 'Y' : 'N',
148
- remarks: data.remarks,
149
- address1: data.address1,
150
- address2: data.address2,
151
- place: data.place,
152
- zip: data.zip,
153
- });
154
- };
155
-
156
- /** -------------------------------
157
- * SUBMIT HANDLER
158
- * ------------------------------- */
159
- const handleFinish = async (values) => {
160
- setLoading(true);
161
-
162
- const payload = {
163
- id: values.id,
164
- shortName: values.shortName,
165
- description: values.description,
166
- designationPtr: values.designation,
167
- phone: values.phone1,
168
- mobile: values.phone2,
169
- alternateMobile: values.phone2,
170
- email: values.email1,
171
- alternateEmail: values.email2,
172
- remarks: values.remarks,
173
- slNo: values.slno ? Number(values.slno) : null,
174
- active: values.active === 'Y' ? 'Y' : 'N',
175
- address1: values.address1,
176
- address2: values.address2,
177
- place: values.place,
178
- zip: values.zip,
179
- };
180
-
181
- try {
182
- const res = editMode ? await UsersAPI.updateStaff(payload, staffData.id) : await UsersAPI.createStaff(payload);
183
-
184
- if (res?.success) {
185
- message.success(editMode ? 'Staff updated successfully' : 'Staff created successfully');
186
- onSuccess?.();
187
- onCancel();
188
- } else {
189
- message.error(res?.message || 'Operation failed');
190
- }
191
- } catch {
192
- message.error('Something went wrong');
193
- } finally {
194
- setLoading(false);
195
- }
196
- };
197
-
198
- /** -------------------------------
199
- * RENDER
200
- * ------------------------------- */
201
- return (
202
- <Modal
203
- title={editMode ? 'Edit Staff' : 'Add Staff'}
204
- open={visible}
205
- onCancel={onCancel}
206
- width={950}
207
- footer={null}
208
- destroyOnClose
209
- style={{ top: 10 }}
210
- >
211
- <Form form={form} layout="vertical" onFinish={handleFinish}>
212
- {/* TOP SECTION */}
213
- <Row gutter={16}>
214
- <Col span={6}>
215
- <Form.Item
216
- label="Code"
217
- name="id"
218
- validateTrigger="onChange"
219
- hasFeedback={!editMode}
220
- rules={[{ required: true }, { validator: validateDoctorCode }]}
221
- >
222
- <Input placeholder="Enter Code" ref={nameInputRef} onKeyDown={handleEnterKey} disabled={editMode} />
223
- </Form.Item>
224
- </Col>
225
-
226
- <Col span={6}>
227
- <Form.Item label="Name" name="shortName" rules={[{ required: true }]}>
228
- <Input placeholder="Enter Name" onKeyDown={handleEnterKey} />
229
- </Form.Item>
230
- </Col>
231
-
232
- <Col span={12}>
233
- <Form.Item label="Description" name="description" rules={[{ required: true }]}>
234
- <Input placeholder="Enter Description" onKeyDown={handleEnterKey} />
235
- </Form.Item>
236
- </Col>
237
- </Row>
238
-
239
- <Tabs defaultActiveKey="1">
240
- {/* GENERAL TAB */}
241
- <Tabs.TabPane tab="GENERAL" key="1">
242
- <Row gutter={16}>
243
- <Col span={6}>
244
- <Form.Item label="Phone Number 1" name="phone1">
245
- <Input maxLength={10} placeholder="Enter Phone Number" onKeyDown={handleEnterKey} />
246
- </Form.Item>
247
- </Col>
248
-
249
- <Col span={6}>
250
- <Form.Item label="Phone Number 2" name="phone2">
251
- <Input maxLength={10} placeholder="Enter Phone Number" onKeyDown={handleEnterKey} />
252
- </Form.Item>
253
- </Col>
254
-
255
- <Col span={6}>
256
- <Form.Item label="Email ID 1" name="email1">
257
- <Input placeholder="Enter Email" onKeyDown={handleEnterKey} />
258
- </Form.Item>
259
- </Col>
260
-
261
- <Col span={6}>
262
- <Form.Item label="Email ID 2" name="email2">
263
- <Input placeholder="Enter Email" onKeyDown={handleEnterKey} />
264
- </Form.Item>
265
- </Col>
266
- </Row>
267
-
268
- <Row gutter={16}>
269
- <Col span={8}>
270
- <Form.Item name="designation" label="Designation">
271
- <Select placeholder="Select Designation" options={designations} allowClear showSearch optionFilterProp="label" />
272
- </Form.Item>
273
- </Col>
274
-
275
- <Col span={8}>
276
- <Form.Item label="Serial Number" name="slno">
277
- <Input placeholder="Enter Serial Number" onKeyDown={handleEnterKey} />
278
- </Form.Item>
279
- </Col>
280
-
281
- <Col span={8}>
282
- <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginTop: 30 }}>
283
- <label>Active</label>
284
- <Form.Item
285
- name="active"
286
- valuePropName="checked"
287
- getValueFromEvent={(e) => (e.target.checked ? 'Y' : 'N')}
288
- getValueProps={(value) => ({ checked: value === 'Y' })}
289
- style={{ marginBottom: 0 }}
290
- >
291
- <Checkbox />
292
- </Form.Item>
293
- </div>
294
- </Col>
295
- </Row>
296
-
297
- <Row>
298
- <Col span={24}>
299
- <Form.Item label="Remarks" name="remarks">
300
- <Input placeholder="Enter Remarks" onKeyDown={handleEnterKey} />
301
- </Form.Item>
302
- </Col>
303
- </Row>
304
- </Tabs.TabPane>
305
-
306
- {/* ADDRESS TAB */}
307
- <Tabs.TabPane tab="ADDRESS" key="2">
308
- <Row gutter={16}>
309
- <Col span={12}>
310
- <Form.Item label="Address Line 1" name="address1">
311
- <Input placeholder="Enter Address" onKeyDown={handleEnterKey} />
312
- </Form.Item>
313
- </Col>
314
-
315
- <Col span={12}>
316
- <Form.Item label="Address Line 2" name="address2">
317
- <Input placeholder="Enter Address" onKeyDown={handleEnterKey} />
318
- </Form.Item>
319
- </Col>
320
- </Row>
321
-
322
- <Row gutter={16}>
323
- <Col span={12}>
324
- <Form.Item label="Place" name="place">
325
- <Input placeholder="Enter Place" onKeyDown={handleEnterKey} />
326
- </Form.Item>
327
- </Col>
328
-
329
- <Col span={12}>
330
- <Form.Item label="Zip Code" name="zip">
331
- <Input placeholder="Enter Zip Code" maxLength={7} onKeyDown={handleEnterKey} />
332
- </Form.Item>
333
- </Col>
334
- </Row>
335
- </Tabs.TabPane>
336
- </Tabs>
337
-
338
- {/* FOOTER BUTTONS */}
339
- <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 10 }}>
340
- <Button onClick={onCancel} type="secondary">
341
- Cancel
342
- </Button>
343
- <Button type="primary" htmlType="submit" loading={loading} disabled={isSubmitDisabled}>
344
- {editMode ? 'Update' : 'Save'}
345
- </Button>
346
- </div>
347
- </Form>
348
- </Modal>
349
- );
350
- };
351
-
352
- export default StaffAdd;
@@ -1,79 +0,0 @@
1
- import React from 'react';
2
- import { Card, Skeleton } from 'antd';
3
- import { Button } from '../../lib';
4
-
5
- export default function ActionButtons({
6
- loading,
7
- steps,
8
- activeStep,
9
- isStepCompleted,
10
- renderDynamicComponent,
11
- handlePrevious,
12
- handleNext,
13
- handleSkip,
14
- handleFinish,
15
- handleStartNextProcess,
16
- nextProcessId,
17
- timelineCollapsed,
18
- }) {
19
- return (
20
- <>
21
- <div style={{ minHeight: 300 }}>
22
- {loading ? <Skeleton active /> : renderDynamicComponent()}
23
-
24
- </div>
25
- <>
26
- <div style={{ marginTop: 20, display: 'flex', justifyContent: 'flex-start', gap: '10px' }}>
27
- {/* Back button */}
28
- <Button disabled={activeStep === 0} onClick={handlePrevious} style={{ marginRight: 8 ,borderRadius: 4, }}>
29
- Back
30
- </Button>
31
-
32
- {/* Skip button */}
33
- {steps.length > 0 && steps[activeStep]?.allow_skip === 'Y' && (
34
- <Button type="default" onClick={handleSkip}
35
- style={{
36
- borderRadius: 4,
37
- }}
38
- disabled={activeStep === steps.length - 1}>
39
- Skip
40
- </Button>
41
- )}
42
-
43
- {/* Next / Finish / Start Next */}
44
- {steps[activeStep]?.order_seqtype === 'E' ? (
45
- nextProcessId?.next_process_id ? (
46
- <Button type="primary"
47
- style={{
48
- borderRadius: 4,
49
- }}
50
- onClick={handleStartNextProcess}>
51
- Start Next {nextProcessId.next_process_name}
52
- </Button>
53
- ) : (
54
- <Button type="primary"
55
- style={{
56
- borderRadius: 4,
57
- }}
58
- onClick={handleFinish}>
59
- Finish
60
- </Button>
61
- )
62
- ) : (
63
- <Button
64
- type="primary"
65
- // shape="round"
66
- style={{
67
- borderRadius: 4,
68
- }}
69
- disabled={activeStep === steps.length - 1 || !isStepCompleted}
70
- onClick={handleNext}
71
- >
72
- Next →
73
- </Button>
74
- )}
75
- </div>
76
- </>
77
- </>
78
- );
79
- }