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

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.
@@ -4,7 +4,7 @@
4
4
  }
5
5
 
6
6
  .ant-collapse {
7
- background: transparent !important;
7
+ background-color: #fafafa !important;
8
8
  border: none !important;
9
9
  }
10
10
  .ant-collapse > .ant-collapse-item {
@@ -25,7 +25,7 @@
25
25
  }
26
26
 
27
27
  .nested-collapse {
28
- margin-left: 20px; // indent child collapses
28
+ margin-left: 2px; // indent child collapses
29
29
  }
30
30
 
31
31
  .nested-collapse .ant-collapse-item {
@@ -38,13 +38,8 @@
38
38
  padding: 10px 14px;
39
39
  box-shadow: 0 1px 4px rgba(0,0,0,0.04);
40
40
  }
41
- /* OFF state */
42
- .custom-switch.ant-switch {
43
- background-color: #bfbfbf !important; /* gray */
41
+ .ant-collapse > .ant-collapse-item > .ant-collapse-heade{
42
+ align-items: center;
44
43
  }
45
44
 
46
- /* ON state */
47
- .custom-switch.ant-switch-checked {
48
- background-color: #446AA9 !important; /* blue */
49
- }
50
45
 
@@ -179,6 +179,15 @@ class MenusAPI extends Base {
179
179
  }).then((result) => result);
180
180
  };
181
181
 
182
+ // get core-menu list with submenu
183
+ getCoreMenuLists = () => {
184
+ const url = 'core-menus/core-menus?step=1&header_id=null';
185
+
186
+ return this.get({
187
+ url,
188
+ }).then((result) => result);
189
+ };
190
+
182
191
  getCoreMenus = () => {
183
192
  return [
184
193
  // {
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useContext } from 'react';
2
2
  import { useLocation, useParams } from 'react-router-dom';
3
3
  import { Skeleton, Typography, message, Form, Input, Collapse, Checkbox } from 'antd';
4
4
  import { GlobalContext } from './../../../../lib';
5
- import {Button} from './../../../../lib';
5
+ import { Button } from './../../../../lib';
6
6
  import { ModelsAPI, PagesAPI, RolesAPI, MenusAPI } from '../../..';
7
7
 
8
8
  const { Title } = Typography;
@@ -34,8 +34,7 @@ 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
38
-
37
+ const [originalMenus, setOriginalMenus] = useState([]); // for deselected menu for editing
39
38
 
40
39
  // Selected menus (array of IDs)
41
40
  const [selectedMenus, setSelectedMenus] = useState([]);
@@ -54,18 +53,16 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
54
53
  setShowMenus(true);
55
54
 
56
55
  // Preselect menus when editing
57
- if (formContent && formContent.menu_ids) {
58
- let menus = formContent.menu_ids;
59
-
60
- setSelectedMenus(menus);
61
- setOriginalMenus(menus); // keep original copy for deselect comparison
62
- }
56
+ if (formContent && formContent.menu_ids) {
57
+ let menus = formContent.menu_ids;
58
+
59
+ setSelectedMenus(menus);
60
+ setOriginalMenus(menus); // keep original copy for deselect comparison
61
+ }
63
62
 
64
63
  setLoading(false);
65
64
  }, [formContent]);
66
65
 
67
-
68
-
69
66
  // Load pages
70
67
  const getPages = () => {
71
68
  PagesAPI.getPages().then((res) => setPages(res.result || []));
@@ -78,112 +75,102 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
78
75
 
79
76
  // Load top-level menus
80
77
  const loadMenus = () => {
81
- MenusAPI.get({
82
- queries: [
83
- { field: 'step', value: 1 },
84
- { field: 'header_id', value: null },
85
- ],
86
- })
78
+ MenusAPI.getCoreMenuLists()
87
79
  .then((res) => setMenuList(res.result || []))
88
80
  .catch(console.error);
89
81
  };
90
82
 
91
83
  // Toggle menu selection
92
84
  const toggleMenu = (id, checked) => {
93
- setSelectedMenus((prev) =>
94
- checked ? [...prev, id] : prev.filter((m) => m !== id)
95
- );
85
+ setSelectedMenus((prev) => (checked ? [...prev, id] : prev.filter((m) => m !== id)));
96
86
  };
97
87
 
98
88
  // Submit handler
99
- // const onSubmit = (values) => {
100
- // setLoading(true);
101
-
102
- // const payload = {
103
- // ...values,
104
- // menu_ids
105
- // : selectedMenus,
106
- // attributes: JSON.stringify(values.attributes || {}),
107
- // };
108
-
109
- // if (formContent.id) {
110
- // RolesAPI.updateRole({ id: formContent.id, formBody: payload })
111
- // .then(() => {
112
- // message.success('Role Updated');
113
- // setLoading(false);
114
- // callback();
115
- // })
116
- // .catch(() => setLoading(false));
117
- // } else {
118
- // additional_queries.forEach(({ field, value }) => {
119
- // payload[field] = value;
120
- // });
121
- // RolesAPI.createRole(payload)
122
- // .then(() => {
123
- // message.success('Role Added');
124
- // setLoading(false);
125
- // callback();
126
- // })
127
- // .catch(() => setLoading(false));
128
- // }
129
- // };
130
- const onSubmit = async (values) => {
131
- setLoading(true);
132
-
133
-
134
- // Find menus that were originally selected but now deselected
135
- const deselectedMenus = originalMenus.filter(
136
- (id) => !selectedMenus.includes(id)
137
- );
89
+ // const onSubmit = (values) => {
90
+ // setLoading(true);
91
+
92
+ // const payload = {
93
+ // ...values,
94
+ // menu_ids
95
+ // : selectedMenus,
96
+ // attributes: JSON.stringify(values.attributes || {}),
97
+ // };
98
+
99
+ // if (formContent.id) {
100
+ // RolesAPI.updateRole({ id: formContent.id, formBody: payload })
101
+ // .then(() => {
102
+ // message.success('Role Updated');
103
+ // setLoading(false);
104
+ // callback();
105
+ // })
106
+ // .catch(() => setLoading(false));
107
+ // } else {
108
+ // additional_queries.forEach(({ field, value }) => {
109
+ // payload[field] = value;
110
+ // });
111
+ // RolesAPI.createRole(payload)
112
+ // .then(() => {
113
+ // message.success('Role Added');
114
+ // setLoading(false);
115
+ // callback();
116
+ // })
117
+ // .catch(() => setLoading(false));
118
+ // }
119
+ // };
120
+ const onSubmit = async (values) => {
121
+ setLoading(true);
122
+
123
+ // Find menus that were originally selected but now deselected
124
+ const deselectedMenus = originalMenus.filter((id) => !selectedMenus.includes(id));
125
+
126
+ const payload = {
127
+ ...values,
128
+ menu_ids: selectedMenus,
129
+ deselect_array: deselectedMenus, // add deselected menus when edit time
130
+ attributes: JSON.stringify(values.attributes || {}),
131
+ };
132
+
133
+ // include id ONLY for edit
134
+ if (formContent?.id) {
135
+ payload.id = formContent.id;
136
+ }
138
137
 
139
- const payload = {
140
- ...values,
141
- menu_ids: selectedMenus,
142
- deselect_array: deselectedMenus, // add deselected menus when edit time
143
- attributes: JSON.stringify(values.attributes || {}),
138
+ // include additional queries for both cases
139
+ additional_queries.forEach(({ field, value }) => {
140
+ payload[field] = value;
141
+ });
142
+
143
+ try {
144
+ await RolesAPI.createRole(payload); // single API
145
+ message.success(formContent?.id ? 'Role Updated' : 'Role Added');
146
+ callback();
147
+ } catch (err) {
148
+ // message.error('Something went wrong');
149
+ } finally {
150
+ setLoading(false);
151
+ }
144
152
  };
145
153
 
146
- // include id ONLY for edit
147
- if (formContent?.id) {
148
- payload.id = formContent.id;
149
- }
150
-
151
- // include additional queries for both cases
152
- additional_queries.forEach(({ field, value }) => {
153
- payload[field] = value;
154
- });
155
-
156
- try {
157
- await RolesAPI.createRole(payload); // single API
158
- message.success(formContent?.id ? 'Role Updated' : 'Role Added');
159
- callback();
160
- } catch (err) {
161
- // message.error('Something went wrong');
162
- } finally {
163
- setLoading(false);
164
- }
165
- };
166
-
167
-
168
-
169
154
  return (
170
155
  <section className="collection-add">
171
156
  {loading ? (
172
157
  <Skeleton />
173
158
  ) : (
174
- <Form
175
- initialValues={{ ...formContent }}
176
- form={form}
177
- layout="vertical"
178
- onFinish={onSubmit}
179
- >
159
+ <Form initialValues={{ ...formContent }} form={form} layout="vertical" onFinish={onSubmit}>
180
160
  {/* Role Name */}
181
- <Form.Item name="name" label="Enter Role Name" required>
161
+ <Form.Item name="name" label="Enter Role Name" rules={[{ required: true, message: 'Role name is required' }]}>
182
162
  <Input placeholder="Enter name" />
183
163
  </Form.Item>
184
164
 
185
165
  {/* Description */}
186
- <Form.Item name="description" label="Enter Description" required>
166
+ <Form.Item
167
+ name="description"
168
+ label="Enter Description"
169
+ rules={[
170
+ { required: true, message: 'Description is required' },
171
+ { max: 250, message: 'Description cannot exceed 255 characters' },
172
+ ]}
173
+ >
187
174
  <Input placeholder="Enter description" />
188
175
  </Form.Item>
189
176
 
@@ -193,33 +180,46 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
193
180
  <Title level={5} style={{ marginBottom: 16 }}>
194
181
  Menu List
195
182
  </Title>
196
- <p style={{ marginTop: -10, marginBottom: 20, color: '#999' }}>
197
- Choose menus and set permissions
198
- </p>
199
-
200
- <Collapse expandIconPosition="left">
201
- {menuList.map((item) => (
202
- <Panel
203
- key={item.id}
204
- header={
205
- <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
206
- <Checkbox
207
- checked={selectedMenus.includes(item.id)}
208
- onChange={(e) => toggleMenu(item.id, e.target.checked)}
209
- />
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)} />
210
203
  <span>{item.title || item.caption}</span>
211
204
  </div>
212
- }
213
- >
214
- {/* Nested submenus */}
215
- <NestedMenu
216
- parentId={item.id}
217
- step={step + 1}
218
- selectedMenus={selectedMenus}
219
- toggleMenu={toggleMenu}
220
- />
221
- </Panel>
222
- ))}
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
223
  </Collapse>
224
224
  </div>
225
225
  )}
@@ -257,13 +257,13 @@ const NestedMenu = ({ parentId, step, selectedMenus, toggleMenu }) => {
257
257
  setLoading(false);
258
258
  })
259
259
  .catch(() => setLoading(false));
260
- }, [parentId, step]);
260
+ }, [parentId, step]);
261
261
 
262
262
  if (loading) return <Skeleton active />;
263
263
  if (!items.length) return null;
264
264
 
265
265
  return (
266
- <Collapse ghost style={{ marginLeft: 20 }}>
266
+ <Collapse ghost>
267
267
  {items.map((menu) => (
268
268
  <Panel
269
269
  key={menu.id}
@@ -277,12 +277,7 @@ const NestedMenu = ({ parentId, step, selectedMenus, toggleMenu }) => {
277
277
  </div>
278
278
  }
279
279
  >
280
- <NestedMenu
281
- parentId={menu.id}
282
- step={step + 1}
283
- selectedMenus={selectedMenus}
284
- toggleMenu={toggleMenu}
285
- />
280
+ <NestedMenu parentId={menu.id} step={step + 1} selectedMenus={selectedMenus} toggleMenu={toggleMenu} />
286
281
  </Panel>
287
282
  ))}
288
283
  </Collapse>