ui-soxo-bootstrap-core 2.4.25-dev.14 β†’ 2.4.25-dev.16

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.
@@ -13,7 +13,7 @@
13
13
  }
14
14
 
15
15
  .ant-collapse > .ant-collapse-item {
16
- margin-bottom: 14px; // GAP between each panel
16
+ margin-bottom: 6px; // GAP between each panel
17
17
  // border: none !important;
18
18
  }
19
19
 
@@ -34,7 +34,8 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
34
34
  const [models, setModels] = useState([]);
35
35
  const [menuList, setMenuList] = useState([]);
36
36
  const [showMenus, setShowMenus] = useState(false);
37
- const [originalMenus, setOriginalMenus] = useState([]); // for deselected menu for editing
37
+ // for deselected menu for editing
38
+ const [originalMenus, setOriginalMenus] = useState([]);
38
39
 
39
40
  // Selected menus (array of IDs)
40
41
  const [selectedMenus, setSelectedMenus] = useState([]);
@@ -57,7 +58,8 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
57
58
  let menus = formContent.menu_ids;
58
59
 
59
60
  setSelectedMenus(menus);
60
- setOriginalMenus(menus); // keep original copy for deselect comparison
61
+ // keep original copy for deselect comparison
62
+ setOriginalMenus(menus);
61
63
  }
62
64
 
63
65
  setLoading(false);
@@ -174,58 +176,18 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
174
176
  <Input placeholder="Enter description" />
175
177
  </Form.Item>
176
178
 
177
- {/* MENUS COLLAPSE */}
179
+ {/* MENU TREE */}
178
180
  {showMenus && menuList.length > 0 && (
179
181
  <div style={{ marginTop: 30 }}>
180
- <Title level={5} style={{ marginBottom: 16 }}>
181
- Menu List
182
- </Title>
183
- <p style={{ marginTop: -10, marginBottom: 20, color: '#999' }}>Choose menus and set permissions</p>
184
-
185
- <Collapse expandIconPosition="left" style={{ marginBottom: '4px' }}>
186
- {menuList.map((item) => {
187
- const hasChildren = item.sub_menus && item.sub_menus.length > 0;
188
-
189
- if (!hasChildren) {
190
- // πŸ‘‰ NO collapse icon
191
- return (
192
- <div
193
- key={item.id}
194
- style={{
195
- padding: '12px 16px',
196
- display: 'flex',
197
- alignItems: 'center',
198
- gap: 8,
199
- borderBottom: '1px solid #f0f0f0',
200
- }}
201
- >
202
- <Checkbox checked={selectedMenus.includes(item.id)} onChange={(e) => toggleMenu(item.id, e.target.checked)} />
203
- <span>{item.title || item.caption}</span>
204
- </div>
205
- );
206
- }
207
-
208
- // πŸ‘‰ WITH collapse icon
209
- return (
210
- <Panel
211
- key={item.id}
212
- header={
213
- <div style={{ display: 'flex', alignItems: 'center', gap: 8, borderBottom: '1px solid #f0f0f0' }}>
214
- <Checkbox checked={selectedMenus.includes(item.id)} onChange={(e) => toggleMenu(item.id, e.target.checked)} />
215
- <span>{item.title || item.caption}</span>
216
- </div>
217
- }
218
- >
219
- <NestedMenu parentId={item.id} step={step + 1} selectedMenus={selectedMenus} toggleMenu={toggleMenu} />
220
- </Panel>
221
- );
222
- })}
223
- </Collapse>
182
+ <Title level={5}>Menu List</Title>
183
+ <p style={{ color: '#999' }}>Choose menus and set permissions</p>
184
+
185
+ <MenuTree menus={menuList} selectedMenus={selectedMenus} toggleMenu={toggleMenu} />
224
186
  </div>
225
187
  )}
226
188
 
227
189
  {/* Submit Button */}
228
- <Form.Item>
190
+ <Form.Item style={{ marginTop: 20 }}>
229
191
  <Button loading={loading} htmlType="submit" type="primary">
230
192
  Save
231
193
  </Button>
@@ -241,45 +203,52 @@ export default RoleAdd;
241
203
  // ------------------------
242
204
  // Recursive Nested Menu Component
243
205
  // ------------------------
244
- const NestedMenu = ({ parentId, step, selectedMenus, toggleMenu }) => {
245
- const [items, setItems] = useState([]);
246
- const [loading, setLoading] = useState(true);
247
-
248
- useEffect(() => {
249
- MenusAPI.get({
250
- queries: [
251
- { field: 'header_id', value: parentId },
252
- { field: 'step', value: step },
253
- ],
254
- })
255
- .then((res) => {
256
- setItems(res.result || []);
257
- setLoading(false);
258
- })
259
- .catch(() => setLoading(false));
260
- }, [parentId, step]);
261
-
262
- if (loading) return <Skeleton active />;
263
- if (!items.length) return null;
264
-
206
+ const MenuTree = ({ menus, selectedMenus, toggleMenu }) => {
265
207
  return (
266
- <Collapse ghost>
267
- {items.map((menu) => (
268
- <Panel
269
- key={menu.id}
270
- header={
271
- <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
272
- <Checkbox
273
- checked={selectedMenus.includes(Number(menu.id))} // ensure number
274
- onChange={(e) => toggleMenu(Number(menu.id), e.target.checked)}
275
- />
208
+ <>
209
+ {menus.map((menu) => {
210
+ const hasChildren = Array.isArray(menu.sub_menus) && menu.sub_menus.length > 0;
211
+
212
+ // NO CHILD β†’ SIMPLE ROW (NO COLLAPSE)
213
+ if (!hasChildren) {
214
+ return (
215
+ <div
216
+ key={menu.id}
217
+ style={{
218
+ padding: '10px 16px',
219
+ display: 'flex',
220
+ alignItems: 'center',
221
+ gap: 8,
222
+ borderBottom: '1px solid #f0f0f0',
223
+ }}
224
+ >
225
+ <Checkbox checked={selectedMenus.includes(menu.id)} onChange={(e) => toggleMenu(menu.id, e.target.checked)} />
276
226
  <span>{menu.title || menu.caption}</span>
277
227
  </div>
278
- }
279
- >
280
- <NestedMenu parentId={menu.id} step={step + 1} selectedMenus={selectedMenus} toggleMenu={toggleMenu} />
281
- </Panel>
282
- ))}
283
- </Collapse>
228
+ );
229
+ }
230
+
231
+ // HAS CHILD β†’ COLLAPSE WITH ICON
232
+ return (
233
+ <Collapse key={menu.id} collapsible="icon" style={{ marginBottom: '4px' }}>
234
+ <Panel
235
+ key={menu.id}
236
+ header={
237
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
238
+ <Checkbox
239
+ checked={selectedMenus.includes(menu.id)}
240
+ onClick={(e) => e.stopPropagation()}
241
+ onChange={(e) => toggleMenu(menu.id, e.target.checked)}
242
+ />
243
+ <span>{menu.title || menu.caption}</span>
244
+ </div>
245
+ }
246
+ >
247
+ <MenuTree menus={menu.sub_menus} selectedMenus={selectedMenus} toggleMenu={toggleMenu} />
248
+ </Panel>
249
+ </Collapse>
250
+ );
251
+ })}
252
+ </>
284
253
  );
285
254
  };
@@ -160,6 +160,17 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
160
160
  }
161
161
  }
162
162
  }, []);
163
+
164
+ useEffect(() => {
165
+ if (!loading && formContent?.role_id && roles.length > 0) {
166
+ const roleId = Number(formContent.role_id);
167
+
168
+ if (!Number.isNaN(roleId)) {
169
+ form.setFieldsValue({ role_id: roleId });
170
+ }
171
+ }
172
+ }, [loading, formContent, roles]);
173
+
163
174
  /**
164
175
  *Define the options dynamically
165
176
  */
@@ -203,7 +214,6 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
203
214
  }
204
215
  })
205
216
  .catch((error) => {
206
- console.error('Error fetching designations:', error);
207
217
  setDesignations([]);
208
218
  })
209
219
  .finally(() => setLoading(false));
@@ -215,16 +225,10 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
215
225
  function getRoles() {
216
226
  RolesAPI.getRole()
217
227
  .then((res) => {
218
- // if API returns { statusCode: 200, result: [...] }
219
- if (Array.isArray(res.result)) {
220
- setRoles(res.result.filter((r) => r.active === 'Y')); // optional: only active roles
221
- } else {
222
- setRoles([]);
223
- }
228
+ const activeRoles = Array.isArray(res.result) ? res.result.filter((r) => r.active === 'Y') : [];
229
+ setRoles(activeRoles);
224
230
  })
225
- .catch((err) => {
226
- setRoles([]);
227
- });
231
+ .catch(() => setRoles([]));
228
232
  }
229
233
 
230
234
  /** Get Department List */
@@ -243,7 +247,6 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
243
247
  }
244
248
  })
245
249
  .catch((error) => {
246
- console.error('Error fetching departments:', error);
247
250
  setDepartments([]);
248
251
  })
249
252
  .finally(() => setLoading(false));
@@ -320,25 +323,24 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
320
323
  form.setFieldsValue({ staff_code: formContent.staff_code });
321
324
  }
322
325
  }
323
- if (formContent?.id && formContent?.organization_details) {
324
- try {
325
- const org = JSON.parse(formContent.organization_details);
326
- const branchIds = org.branch_ids?.map((b) => b.id) || [];
327
- const defaultBranchObj = org.branch_ids?.find((b) => b.DefaultBranch === 'true');
328
- const defaultBr = defaultBranchObj?.id || null;
329
-
330
- setSelectedBranches(branchIds);
331
- setDefaultBranch(defaultBr);
332
-
333
- form.setFieldsValue({
334
- selectedBranches: branchIds,
335
- defaultBranch: defaultBr,
336
- });
337
- } catch (err) {
338
- console.error('Invalid organization_details JSON', err);
339
- }
340
- }
341
- }, [formContent]);
326
+ if (!formContent) return;
327
+
328
+ // normalize branch ids to NUMBER
329
+ const org =
330
+ typeof formContent.organization_details === 'string' ? JSON.parse(formContent.organization_details) : formContent.organization_details;
331
+
332
+ const branchIds = (org?.branch_ids || []).map(Number);
333
+ const defaultBr = formContent.defaultBranch ? Number(formContent.defaultBranch) : null;
334
+
335
+ // state (for filtering)
336
+ setSelectedBranches(branchIds);
337
+
338
+ // form (source of truth)
339
+ form.setFieldsValue({
340
+ selectedBranches: branchIds,
341
+ defaultBranch: defaultBr,
342
+ });
343
+ }, [formContent, form]);
342
344
  // Generate branch options for Select component
343
345
  const branchOptions = branches.map((branch) => ({
344
346
  label: branch.br_desc,
@@ -349,6 +351,8 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
349
351
  * Submit values
350
352
  */
351
353
  const onSubmit = (values) => {
354
+ values.defaultBranch = String(values.defaultBranch);
355
+
352
356
  /**If PanelOpen is open and edit mode then password will be existing password else new password*/
353
357
  if (!isPasswordVisible && mode === 'Edit') {
354
358
  values.password = body.password;
@@ -377,6 +381,7 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
377
381
  if (props?.ldap && selectedOption && selectedOption.value) {
378
382
  values = {
379
383
  ...values,
384
+
380
385
  addAllBranches: props.ldap.addAllBranches,
381
386
  auth_user: selectedOption.value,
382
387
  auth_type: 'LDAP',
@@ -387,6 +392,7 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
387
392
  if (values.attributes && typeof values === 'object') {
388
393
  values = {
389
394
  ...values,
395
+
390
396
  auth_type: 'LDAP',
391
397
 
392
398
  attributes: JSON.stringify(values.attributes),
@@ -624,22 +630,19 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
624
630
  <Select
625
631
  mode="multiple"
626
632
  placeholder="Select Branches"
627
- value={selectedBranches}
633
+ options={branchOptions}
634
+ allowClear
635
+ showSearch
636
+ optionFilterProp="label"
628
637
  onChange={(value) => {
629
- setSelectedBranches(value);
638
+ const normalized = value.map(Number);
639
+ setSelectedBranches(normalized);
630
640
 
631
- // β¬…NEW: remove defaultBranch if it’s not in selectedBranches
632
- if (!value.includes(defaultBranch)) {
633
- setDefaultBranch(undefined);
641
+ const currentDefault = form.getFieldValue('defaultBranch');
642
+ if (currentDefault && !normalized.includes(currentDefault)) {
634
643
  form.setFieldsValue({ defaultBranch: undefined });
635
644
  }
636
645
  }}
637
- options={branchOptions}
638
- allowClear
639
- showSearch
640
- optionFilterProp="label"
641
- maxTagCount={5} // Show only 5 tags
642
- maxTagPlaceholder={(omittedValues) => `+${omittedValues.length}`} // Show "+n"
643
646
  />
644
647
  </Form.Item>
645
648
  </Col>
@@ -648,9 +651,9 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
648
651
  <Form.Item label="Default Branch" name="defaultBranch" rules={[{ required: true, message: 'Please select default branch' }]}>
649
652
  <Select placeholder="Select Default Branch" onChange={setDefaultBranch}>
650
653
  {branchOptions
651
- .filter((opt) => selectedBranches.includes(opt.value))
654
+ .filter((opt) => selectedBranches.includes(Number(opt.value)))
652
655
  .map((opt) => (
653
- <Option key={opt.value} value={opt.value}>
656
+ <Option key={opt.value} value={Number(opt.value)}>
654
657
  {opt.label}
655
658
  </Option>
656
659
  ))}
@@ -726,8 +729,8 @@ const UserAdd = ({ model, callback, edit, history, formContent, match, additiona
726
729
  </Form.Item>
727
730
  </Col>
728
731
  <Col span={8}>
729
- <Form.Item name="role_id" label="Role" rules={[{ required: true, message: 'Please Input Role' }]}>
730
- <Select placeholder="Select Role" style={{ width: '100%' }}>
732
+ <Form.Item name="role_id" label="Role" rules={[{ required: true, message: 'Please select a Role' }]}>
733
+ <Select placeholder="Select Role">
731
734
  {roles.map((role) => (
732
735
  <Option key={role.id} value={role.id}>
733
736
  {role.name}
@@ -11,7 +11,6 @@ export default function UserEdit(record) {
11
11
  // Handle edit button click
12
12
  const handleEditClick = () => {
13
13
  if (!record.id) {
14
- console.warn('Invalid record: Missing ID');
15
14
  return;
16
15
  }
17
16
 
@@ -29,9 +28,7 @@ export default function UserEdit(record) {
29
28
  // Try parsing other_details, handle error if invalid JSON
30
29
  try {
31
30
  otherDetails = JSON.parse(apiData.other_details);
32
- } catch (err) {
33
- console.warn('Failed to parse other_details:', apiData.other_details);
34
- }
31
+ } catch (err) {}
35
32
  }
36
33
  let orgDetails = {};
37
34
  try {
@@ -50,8 +47,10 @@ export default function UserEdit(record) {
50
47
  designation: apiData.designation_code,
51
48
  department: apiData.department_id,
52
49
  // Handle selected branches and default branch
50
+ organization_details: orgDetails,
53
51
  selectedBranches: orgDetails.branch_ids || [],
54
- defaultBranch: apiData.firm_id || null,
52
+ defaultBranch: apiData.branch_id || null,
53
+ role_id: apiData.role_id,
55
54
  password: apiData.password,
56
55
  user_group: apiData.user_group,
57
56
  // Handle doctor codes
@@ -1042,7 +1042,8 @@ function GuestList({
1042
1042
  ) : (
1043
1043
  <TableComponent
1044
1044
  size="small"
1045
- scroll={{ x: true }}
1045
+ scroll={{ x: true, y: '60vh' }}
1046
+ sticky
1046
1047
  rowKey={(record) => record.OpNo}
1047
1048
  dataSource={filtered ? filtered : patients} // In case if there is no filtered values we can use patient data
1048
1049
  columns={cols}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-soxo-bootstrap-core",
3
- "version": "2.4.25-dev.14",
3
+ "version": "2.4.25-dev.16",
4
4
  "description": "All the Core Components for you to start",
5
5
  "keywords": [
6
6
  "all in one"