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

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.
@@ -1,61 +1,138 @@
1
- import React, { useRef } from "react";
2
- import { useDrag, useDrop } from "react-dnd";
1
+ import React, { useRef } from 'react';
2
+ import { useDrag, useDrop } from 'react-dnd';
3
3
 
4
- const ItemTypes = { PANEL: "panel" };
4
+ export default function DraggableWrapper({ id, index, movePanel, item, dragEnabled, level, parentId, onCrossLevelMove, canAcceptChildren }) {
5
5
 
6
- export default function DraggableWrapper({ id, index, movePanel, title, dragEnabled = true }) {
7
- const ref = useRef(null);
6
+ const autoScrollWindow = (monitor) => {
7
+ const offset = monitor.getClientOffset();
8
+ if (!offset) return;
8
9
 
9
- const [, drop] = useDrop({
10
- accept: ItemTypes.PANEL,
11
- hover(item, monitor) {
12
- if (!dragEnabled) return; // ignore hover if dragging is disabled
13
- if (!ref.current) return;
10
+ const EDGE = 80;
11
+ const SPEED = 20;
14
12
 
15
- const dragIndex = item.index;
16
- const hoverIndex = index;
13
+ const viewportHeight = window.innerHeight;
17
14
 
18
- if (dragIndex === hoverIndex) return;
15
+ // 🔼 scroll UP
16
+ if (offset.y < EDGE) {
17
+ window.scrollBy(0, -SPEED);
18
+ }
19
19
 
20
- const hoverBoundingRect = ref.current.getBoundingClientRect();
21
- const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
22
- const clientOffset = monitor.getClientOffset();
23
- const hoverClientY = clientOffset.y - hoverBoundingRect.top;
20
+ // 🔽 scroll DOWN
21
+ if (offset.y > viewportHeight - EDGE) {
22
+ window.scrollBy(0, SPEED);
23
+ }
24
+ };
24
25
 
25
- if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) return;
26
- if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) return;
26
+ const [{ isDragging }, drag] = useDrag({
27
+ type: 'PANEL',
28
+ item: { id, index, level, parentId },
29
+ canDrag: dragEnabled,
30
+ collect: (monitor) => ({
31
+ isDragging: monitor.isDragging(),
32
+ }),
33
+ });
27
34
 
28
- movePanel(dragIndex, hoverIndex);
29
- item.index = hoverIndex;
30
- },
35
+ const [{ isOver, canDrop }, drop] = useDrop({
36
+ accept: 'PANEL',
37
+ hover: (dragItem, monitor) => {
38
+ // THIS FIXES BOTTOM → TOP
39
+ autoScrollWindow(monitor);
40
+
41
+ if (dragItem.index === index) return;
42
+
43
+ if (
44
+ dragItem.level === level &&
45
+ dragItem.parentId === parentId
46
+ ) {
47
+ movePanel(dragItem.index, index);
48
+ dragItem.index = index; // keep in sync
49
+ }
50
+ },
51
+
52
+ canDrop: (item) => dragEnabled,
53
+ drop: (dragItem, monitor) => {
54
+ if (monitor.didDrop()) return;
55
+
56
+ if (
57
+ dragItem.level !== level ||
58
+ dragItem.parentId !== parentId
59
+ ) {
60
+ onCrossLevelMove?.(dragItem, {
61
+ targetLevel: level,
62
+ targetParentId: parentId,
63
+ targetIndex: index,
64
+ });
65
+ }
66
+ },
67
+ collect: (monitor) => ({
68
+ isOver: monitor.isOver({ shallow: true }),
69
+ canDrop: monitor.canDrop(),
70
+ }),
31
71
  });
32
72
 
33
- const [{ isDragging }, drag] = useDrag({
34
- type: ItemTypes.PANEL,
35
- item: { id, index },
36
- canDrag: dragEnabled, // only allow dragging if dragEnabled
73
+ // Drop zone for making items children of this item
74
+ const [{ isOverChild, canDropChild }, dropChild] = useDrop({
75
+ accept: 'PANEL',
76
+ canDrop: (item) => dragEnabled && item.id !== id && canAcceptChildren,
77
+ drop: (item, monitor) => {
78
+ if (monitor.didDrop()) return;
79
+
80
+ if (onCrossLevelMove && item.id !== id) {
81
+ // Drop as child of this item
82
+ onCrossLevelMove(item, { targetLevel: level + 1, targetParentId: id, targetIndex: 0 });
83
+ }
84
+ },
37
85
  collect: (monitor) => ({
38
- isDragging: monitor.isDragging(),
86
+ isOverChild: monitor.isOver({ shallow: true }),
87
+ canDropChild: monitor.canDrop(),
39
88
  }),
40
89
  });
41
90
 
42
- drag(drop(ref));
91
+ const backgroundColor = isOver && canDrop ? '#bae7ff' : isDragging ? '#e6f7ff' : 'transparent';
92
+ const childZoneBackgroundColor = isOverChild && canDropChild ? '#d4f4dd' : 'transparent';
43
93
 
44
94
  return (
45
- <div
46
- ref={ref}
47
- style={{
48
- display: "flex",
49
- width: "100%",
50
- cursor: dragEnabled ? "grab" : "default", // show cursor only if draggable
51
- opacity: isDragging ? 0.6 : 1,
52
- transition: "transform 0.2s ease, opacity 0.2s ease",
53
- transform: isDragging ? "scale(1.05)" : "scale(1)",
54
- boxShadow: isDragging ? "0px 5px 10px rgba(0,0,0,0.15)" : "none",
55
- padding: "5px 0",
56
- }}
57
- >
58
- {title}
95
+ <div ref={drop} style={{ width: '100%' }}>
96
+ <div
97
+ ref={drag}
98
+ style={{
99
+ opacity: isDragging ? 0.5 : 1,
100
+ cursor: dragEnabled ? 'move' : 'default',
101
+ // padding: '8px',
102
+ backgroundColor,
103
+ transition: 'all 0.2s ease',
104
+ border: isOver && canDrop ? '2px dashed #1890ff' : '2px solid transparent',
105
+ display: 'flex',
106
+ justifyContent: 'space-between',
107
+ alignItems: 'center',
108
+ }}
109
+ >
110
+ <div style={{ flex: 1 }}>
111
+ {dragEnabled && <span style={{ marginRight: 8, color: '#999' }}>⋮⋮</span>}
112
+ <strong>{item.name}</strong>
113
+ {dragEnabled ?( <span style={{ marginLeft: 8, fontSize: 11, color: '#999' }}>(Level {level})</span>):(<span style={{ marginLeft: 8, fontSize: 11, color: '#999' }}>{item.path}</span>)}
114
+ </div>
115
+
116
+ {canAcceptChildren && dragEnabled && (
117
+ <div
118
+ ref={dropChild}
119
+ onClick={(e) => e.stopPropagation()}
120
+ style={{
121
+ padding: '4px 12px',
122
+ marginLeft: 8,
123
+ backgroundColor: childZoneBackgroundColor,
124
+ border: isOverChild && canDropChild ? '2px solid #52c41a' : '1px dashed #d9d9d9',
125
+ borderRadius: 4,
126
+ fontSize: 11,
127
+ color: isOverChild && canDropChild ? '#52c41a' : '#999',
128
+ cursor: 'default',
129
+ transition: 'all 0.2s ease',
130
+ }}
131
+ >
132
+ {isOverChild && canDropChild ? '✓ Drop as child' : '↳ Drop here'}
133
+ </div>
134
+ )}
135
+ </div>
59
136
  </div>
60
137
  );
61
138
  }
@@ -65,4 +65,4 @@ Switch.propTypes = {
65
65
  className: PropTypes.string,
66
66
  /** A custom style object for the switch component. */
67
67
  style: PropTypes.object,
68
- };
68
+ };
@@ -16,25 +16,6 @@ import { useState, useEffect, useRef } from 'react';
16
16
  * @returns {(seconds: number) => void} API.start - Start timer with seconds.
17
17
  * @returns {(expirytime: string) => void} API.startFromExpiry - Start timer using expiry string (e.g. "2025-09-04T12:13:09.000Z").
18
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
19
  */
39
20
 
40
21
  // helper to format time
@@ -121,9 +121,11 @@ function LoginPhone({ history, appSettings }) {
121
121
  .then((result) => {
122
122
  setLoading(false);
123
123
 
124
- const { user, access_token, refresh_token } = result;
124
+ const { user, access_token, refresh_token, insider_token } = result;
125
125
  if (access_token) localStorage.access_token = access_token;
126
126
 
127
+ if (insider_token) localStorage.insider_token = insider_token;
128
+
127
129
  if (result.success) {
128
130
  //two_factor_authentication variable is present then proceed Two factor authentication
129
131
  if (result.data && result.data.two_factor_authentication) {
@@ -516,28 +518,28 @@ function LoginPhone({ history, appSettings }) {
516
518
  return user.username;
517
519
  };
518
520
 
519
- const { globalCustomerHeader = () => {} } = appSettings;
521
+ const { globalCustomerHeader = () => { } } = appSettings;
520
522
 
521
523
  const themeName = process.env.REACT_APP_THEME; // e.g., 'purple'
522
524
  const isPurple = themeName === 'purple';
523
525
 
524
526
  const sectionStyle = isPurple
525
527
  ? {
526
- width: '100%',
527
- height: '100vh',
528
- backgroundImage: `${state.theme.colors.loginPageBackground}`,
529
- backgroundPosition: 'center bottom, center',
530
- backgroundRepeat: 'no-repeat, no-repeat',
531
- backgroundSize: 'cover, cover',
532
- }
528
+ width: '100%',
529
+ height: '100vh',
530
+ backgroundImage: `${state.theme.colors.loginPageBackground}`,
531
+ backgroundPosition: 'center bottom, center',
532
+ backgroundRepeat: 'no-repeat, no-repeat',
533
+ backgroundSize: 'cover, cover',
534
+ }
533
535
  : {
534
- width: '100%',
535
- height: '100vh',
536
- backgroundImage: `url(${backgroundImage}), ${state.theme.colors.loginPageBackground}`,
537
- backgroundPosition: 'center bottom, center',
538
- backgroundRepeat: 'no-repeat, no-repeat',
539
- backgroundSize: 'cover, cover',
540
- };
536
+ width: '100%',
537
+ height: '100vh',
538
+ backgroundImage: `url(${backgroundImage}), ${state.theme.colors.loginPageBackground}`,
539
+ backgroundPosition: 'center bottom, center',
540
+ backgroundRepeat: 'no-repeat, no-repeat',
541
+ backgroundSize: 'cover, cover',
542
+ };
541
543
 
542
544
  return (
543
545
  <section className="full-page" style={sectionStyle}>
@@ -610,10 +612,10 @@ function LoginPhone({ history, appSettings }) {
610
612
  <Text type="primary">Select Preferred OTP Verification Method</Text>
611
613
  <div className="otp-method-group">
612
614
  <Radio checked={communicationMode === 'email'} onChange={() => setCommunicationMode('email')}>
613
- Email <MailOutlined style={{ marginLeft: 6 }} />
615
+ Email <MailOutlined className="otp-icon" style={{ marginLeft: 6 }} />
614
616
  </Radio>
615
617
  <Radio checked={communicationMode === 'mobile'} onChange={() => setCommunicationMode('mobile')}>
616
- SMS <MessageOutlined style={{ marginLeft: 6 }} />
618
+ SMS <MessageOutlined className="otp-icon" style={{ marginLeft: 6 }} />
617
619
  </Radio>
618
620
  </div>
619
621
  {modeError && <p className="otp-mode-error">Please select a communication mode.</p>}
@@ -352,10 +352,14 @@ body {
352
352
  gap: 30px;
353
353
  font-size: 12px;
354
354
  }
355
+ .otp-icon {
356
+ position: relative;
357
+ top: 2px;
358
+ }
355
359
 
356
360
  .ant-radio-wrapper {
357
361
  display: flex;
358
- align-items: center;
362
+ // align-items: center;
359
363
  gap: 6px;
360
364
 
361
365
  svg {
@@ -107,13 +107,14 @@ const MenuAdd = ({ model, callback, edit, history, formContent, match, additiona
107
107
  * Submit values
108
108
  */
109
109
  const onSubmit = (values) => {
110
- // console.log(values);
111
110
  setLoading(true);
112
111
 
113
112
  let id = formContent.id;
114
113
 
115
- // Add the step to form content
116
- // values.step = step;
114
+ // ONLY set step if it's NOT already provided
115
+ if (!values.step) {
116
+ values.step = formContent.step || step;
117
+ }
117
118
 
118
119
  if (values.attributes && typeof values === 'object') {
119
120
  values = {
@@ -123,30 +124,19 @@ const MenuAdd = ({ model, callback, edit, history, formContent, match, additiona
123
124
  }
124
125
 
125
126
  if (id) {
126
- // Update of model
127
127
  model.update({ id, values }).then(() => {
128
- // callback();
129
128
  message.success('Menu Updated');
130
-
131
129
  setLoading(false);
132
-
133
130
  callback();
134
131
  });
135
132
  } else {
136
- values.step = step;
137
-
138
- // Append the additional queries to the object
139
133
  additional_queries.forEach(({ field, value }) => {
140
134
  values[field] = value;
141
135
  });
142
136
 
143
- // add new model
144
137
  model.add({ values }).then(() => {
145
- // callback();
146
138
  message.success('Menu Added');
147
-
148
139
  setLoading(false);
149
-
150
140
  callback();
151
141
  });
152
142
  }
@@ -154,7 +144,7 @@ const MenuAdd = ({ model, callback, edit, history, formContent, match, additiona
154
144
 
155
145
  return (
156
146
  <section className="collection-add menu-add">
157
- <Title level={4}>{mode} Menu</Title>
147
+ {/* <Title level={4}>{mode} Menu</Title> */}
158
148
 
159
149
  {loading ? (
160
150
  <Skeleton />
@@ -163,21 +153,21 @@ const MenuAdd = ({ model, callback, edit, history, formContent, match, additiona
163
153
  <div className="form-container">
164
154
  <div className="left-container">
165
155
  {/* Caption */}
166
- <Form.Item name={'caption'} label="Caption" required>
156
+ <Form.Item name={'caption'} label="Caption" rules={[{ required: true, message: 'Caption is required' }]}>
167
157
  <Input placeholder="Enter caption" />
168
158
  </Form.Item>
169
159
  {/* Caption Ends */}
170
160
 
171
161
  {/* Name */}
172
- <Form.Item name={'name'} label="Name" required>
162
+ <Form.Item name={'name'} label="Name" rules={[{ required: true, message: 'Name is required' }]}>
173
163
  <Input placeholder="Enter name" />
174
164
  </Form.Item>
175
165
  {/* Name Ends */}
176
166
 
177
167
  {/* Description */}
178
- <Form.Item name={'description'} label="Description">
179
- <TextArea placeholder="Enter Description" />
180
- </Form.Item>
168
+ {/* <Form.Item name={"description"} label="Description">
169
+ <TextArea placeholder="Enter Description" />
170
+ </Form.Item> */}
181
171
  {/* Description Ends */}
182
172
 
183
173
  {/* Model */}
@@ -229,37 +219,38 @@ const MenuAdd = ({ model, callback, edit, history, formContent, match, additiona
229
219
  {/* Pages Ends */}
230
220
 
231
221
  {/* Path */}
232
- <Form.Item name="path" label="Path" required>
233
- <Input placeholder="Enter path" />
234
- </Form.Item>
222
+ {/* <Form.Item name="path" label="Path" required>
223
+ <Input placeholder="Enter path" />
224
+ </Form.Item> */}
235
225
  {/* Path Ends */}
236
226
 
237
227
  {/* Route */}
238
- <Form.Item name="route" label="Route" required>
228
+ <Form.Item name="route" label="Route" rules={[{ required: true, message: 'Route is required' }]}>
239
229
  <Input placeholder="Enter route" />
240
230
  </Form.Item>
241
231
  {/* Route Ends */}
242
232
 
243
233
  {/* Switch */}
244
- <Form.Item name="is_visible" label="Visible" required>
245
- <Switch defaultChecked={body.is_visible} />
234
+ <Form.Item name="is_visible" label="Visible" valuePropName="checked" rules={[{ required: true, message: 'Visibility is required' }]}>
235
+ <Switch />
246
236
  </Form.Item>
237
+
247
238
  {/* Switch Ends */}
248
239
 
249
240
  {/* Step */}
250
- <Form.Item name={'order'} label="Order" required>
251
- <InputNumber placeholder="Enter order" />
252
- </Form.Item>
241
+ {/* <Form.Item name={"order"} label="Order" required>
242
+ <InputNumber placeholder="Enter order" />
243
+ </Form.Item> */}
253
244
  {/* Step Ends */}
254
245
 
255
246
  {/* Icon Name*/}
256
- <Form.Item name="icon_name" label="Icon Name" required>
247
+ <Form.Item name="icon_name" label="Icon Name" rules={[{ required: true, message: 'Icon name is required' }]}>
257
248
  <Input placeholder="Enter icon name" />
258
249
  </Form.Item>
259
250
  {/* Icon Name Ends */}
260
251
 
261
252
  {/* Step */}
262
- <Form.Item name={'step'} label="Step" required>
253
+ <Form.Item name={'step'} label="Step" rules={[{ required: true, message: 'Step is required' }]}>
263
254
  <InputNumber placeholder="Enter step" />
264
255
  </Form.Item>
265
256
  {/* Step Ends */}