ui-soxo-bootstrap-core 2.4.25-dev.7 → 2.4.25-dev.8

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,352 @@
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;
@@ -12,6 +12,7 @@ import { ModelsAPI, PagesAPI } from '../../..';
12
12
 
13
13
  import { UsersAPI } from '../../..';
14
14
  import DoctorAdd from '../../../doctor/components/doctor-add/doctor-add';
15
+ import StaffAdd from '../../../staff/components/staff-add/staff-add';
15
16
 
16
17
  const { Title } = Typography;
17
18
 
@@ -37,6 +38,9 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
37
38
  const [users, setUsers] = useState([]);
38
39
  const [doctorID, setDoctorID] = useState(false);
39
40
  const [selectedDoctor, setSelectedDoctor] = useState(null);
41
+ const [staffID, setStaffID] = useState(false);
42
+ const [selectedStaff, setSelectedStaff] = useState(null);
43
+ const [staffList, setStaffList] = useState([]);
40
44
  /**Converting to JSON */
41
45
  let firmDetails = JSON.parse(user.firm.f_otherdetails1);
42
46
 
@@ -160,6 +164,7 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
160
164
  { label: 'General', value: 'GEN' },
161
165
  { label: 'Doctor', value: 'RAD' },
162
166
  { label: 'Radiographer', value: 'TECH' },
167
+ { label: 'Staff', value: 'STAFF' },
163
168
  ];
164
169
 
165
170
  /**
@@ -237,6 +242,25 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
237
242
  })
238
243
  .catch((err) => console.error('Doctor API Error:', err));
239
244
  };
245
+ /** Get staff List */
246
+ const getStaff = () => {
247
+ UsersAPI.getAllStaff()
248
+ .then((res) => {
249
+ console.log('Staff List Response:', res);
250
+
251
+ if (Array.isArray(res)) {
252
+ const list = res.map((staff) => ({
253
+ label: `${staff.shortName || 'No Name'} (${staff.id})`,
254
+ value: staff.id,
255
+ }));
256
+
257
+ setStaffList(list);
258
+ } else {
259
+ console.error('API did not return an array!');
260
+ }
261
+ })
262
+ .catch((err) => console.error('staff API Error:', err));
263
+ };
240
264
 
241
265
  // Function to handle user type change
242
266
  const handleUserTypeChange = (value) => {
@@ -247,6 +271,11 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
247
271
  } else {
248
272
  form.setFieldsValue({ default_code: undefined });
249
273
  }
274
+ if (value === 'STAFF') {
275
+ getStaff(); // load staff list
276
+ } else {
277
+ form.setFieldsValue({ staff_code: undefined });
278
+ }
250
279
  };
251
280
 
252
281
  useEffect(() => {
@@ -254,6 +283,7 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
254
283
  getDesignations();
255
284
  getDepartments();
256
285
  getDoctors(); // load doctor list
286
+ getStaff();
257
287
  }, []);
258
288
 
259
289
  useEffect(() => {
@@ -264,6 +294,9 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
264
294
  if (formContent.user_type === 'RAD' && formContent.doctor_code) {
265
295
  form.setFieldsValue({ default_code: formContent.doctor_code });
266
296
  }
297
+ if (formContent.user_type === 'STAFF' && formContent.staff_code) {
298
+ form.setFieldsValue({ staff_code: formContent.staff_code });
299
+ }
267
300
  }
268
301
  if (formContent?.id && formContent?.organization_details) {
269
302
  try {
@@ -294,7 +327,6 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
294
327
  * Submit values
295
328
  */
296
329
  const onSubmit = (values) => {
297
- console.log('values____________', values);
298
330
  /**If PanelOpen is open and edit mode then password will be existing password else new password*/
299
331
  if (!isPasswordVisible && mode === 'Edit') {
300
332
  values.password = body.password;
@@ -306,6 +338,11 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
306
338
  } else {
307
339
  values.doctor_code = null;
308
340
  }
341
+ if (values.user_type === 'STAFF') {
342
+ values.staff_code = values.staff_code;
343
+ } else {
344
+ values.staff_code = null;
345
+ }
309
346
  values = {
310
347
  ...values,
311
348
  auth_type: 'LDAP',
@@ -481,6 +518,37 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
481
518
  />
482
519
  </Form.Item>
483
520
  )}
521
+ {userType === 'STAFF' && (
522
+ <Form.Item name="staff_code" label="Staff Code" rules={[{ required: true, message: 'Please select a staff code' }]}>
523
+ <Select
524
+ placeholder="Select Code"
525
+ options={staffList}
526
+ showSearch
527
+ optionFilterProp="label"
528
+ dropdownRender={(menu) => (
529
+ <>
530
+ {menu}
531
+ <div
532
+ style={{
533
+ padding: '8px',
534
+ cursor: 'pointer',
535
+ borderTop: '1px solid #f0f0f0',
536
+ color: '#1890ff',
537
+ }}
538
+ onClick={() => setVisible(true)}
539
+ >
540
+ + Add New Staff
541
+ </div>
542
+ </>
543
+ )}
544
+ />
545
+ {/* Render DoctorAdd OUTSIDE the Select */}
546
+
547
+ <StaffAdd visible={visible} onCancel={() => setVisible(false)} staffData={selectedStaff} staffId={staffID} onSuccess={getStaff} />
548
+
549
+ <></>
550
+ </Form.Item>
551
+ )}
484
552
  </Col>
485
553
  </Row>
486
554
  <Row gutter={16}>
@@ -276,6 +276,63 @@ class Users extends Base {
276
276
  getUser = ({ id }) => {
277
277
  return ApiUtils.get({ url: `users/${id}` });
278
278
  };
279
+
280
+ /**
281
+ *Cutsom Api for creating staff
282
+ * @returns
283
+ */
284
+
285
+ createStaff = (formBody) => {
286
+ return ApiUtils.post({
287
+ url: `staff/create-staff`,
288
+ formBody,
289
+ });
290
+ };
291
+
292
+ /**
293
+ * Update a staff by ID
294
+ * @param {Object} formBody - Data to update
295
+ * @param {string|number} id - Staff ID
296
+ * @returns {Promise} API response
297
+ */
298
+ updateStaff = (formBody, id) => {
299
+ return ApiUtils.put({
300
+ url: `staff/update-staff/${id}`,
301
+ formBody,
302
+ });
303
+ };
304
+
305
+ /**
306
+ * edit staff with id
307
+ * @returns
308
+ */
309
+
310
+ getStaff = (id) => {
311
+ return ApiUtils.get({
312
+ url: `staff/get-staff/${id}`,
313
+ });
314
+ };
315
+
316
+ /**
317
+ * staff code validation
318
+ *
319
+ */
320
+ getStaffCode = (code) => {
321
+ return ApiUtils.get({
322
+ url: `staff/check-staff-exists/${code}`,
323
+ });
324
+ };
325
+
326
+ /**
327
+ * get all staff
328
+ * @returns
329
+ */
330
+
331
+ getAllStaff = () => {
332
+ return ApiUtils.get({
333
+ url: 'staff/get-all-staff',
334
+ });
335
+ };
279
336
  }
280
337
 
281
338
  export default Users;
@@ -512,7 +512,7 @@ export default function ProcessStepsPage({ processId = 1, match, CustomComponent
512
512
  return (
513
513
  <Card>
514
514
  <Row gutter={20}>
515
- <Col xs={24} sm={24} lg={timelineCollapsed ? 3 : 6}>
515
+ <Col xs={24} sm={24} lg={timelineCollapsed ? 2 : 6}>
516
516
 
517
517
 
518
518
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-soxo-bootstrap-core",
3
- "version": "2.4.25-dev.7",
3
+ "version": "2.4.25-dev.8",
4
4
  "description": "All the Core Components for you to start",
5
5
  "keywords": [
6
6
  "all in one"