@visns-studio/visns-components 5.3.11 → 5.3.13

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.
package/package.json CHANGED
@@ -78,7 +78,7 @@
78
78
  "react-dom": "^17.0.0 || ^18.0.0"
79
79
  },
80
80
  "name": "@visns-studio/visns-components",
81
- "version": "5.3.11",
81
+ "version": "5.3.13",
82
82
  "description": "Various packages to assist in the development of our Custom Applications.",
83
83
  "main": "src/index.js",
84
84
  "files": [
@@ -140,6 +140,7 @@ const DataGrid = forwardRef(
140
140
  settings,
141
141
  setConfig,
142
142
  setFilterData,
143
+ setTableData,
143
144
  setTotal,
144
145
  setRowsSelected,
145
146
  style,
@@ -209,7 +210,9 @@ const DataGrid = forwardRef(
209
210
  setTotal(res.count);
210
211
  }
211
212
  setDataCount(res.count);
212
-
213
+ if (setTableData) {
214
+ setTableData(res.data);
215
+ }
213
216
  dataRef.current = res.data;
214
217
  });
215
218
 
@@ -2625,6 +2628,11 @@ const DataGrid = forwardRef(
2625
2628
  /<\/?[^>]+(>|$)/g,
2626
2629
  ''
2627
2630
  ); // Simple regex to strip HTML tags
2631
+ } else {
2632
+ // Replace p tags with br tags
2633
+ content = content
2634
+ .replace(/<p>/g, '')
2635
+ .replace(/<\/p>/g, '<br>');
2628
2636
  }
2629
2637
 
2630
2638
  // If truncate has a value, limit content to specified number of characters
@@ -2985,10 +2993,16 @@ const DataGrid = forwardRef(
2985
2993
  </div>
2986
2994
  <div className={styles.modal__content}>
2987
2995
  <div className={styles.formcontainer}>
2988
- <Gallery
2989
- images={galleryData}
2990
- enableImageSelection={false}
2991
- />
2996
+ {galleryData.length > 0 ? (
2997
+ <Gallery
2998
+ images={galleryData}
2999
+ enableImageSelection={false}
3000
+ />
3001
+ ) : (
3002
+ <div className={styles.noDataMessage}>
3003
+ No images available in the gallery
3004
+ </div>
3005
+ )}
2992
3006
  </div>
2993
3007
  </div>
2994
3008
  </div>
@@ -72,6 +72,7 @@ function Field({
72
72
  }) {
73
73
  const editorRef = useRef(null);
74
74
  const fileImageInputRef = useRef(null);
75
+ const [autoValue, setAutoValue] = useState('');
75
76
  const [colorPickerShow, setColorPickerShow] = useState(false);
76
77
  const [counter, setCounter] = useState(0);
77
78
  const [dataOptions, setDataOptions] = useState([]);
@@ -117,6 +118,31 @@ function Field({
117
118
  marginTop: '6px',
118
119
  };
119
120
 
121
+ useEffect(() => {
122
+ const fetchAutoValue = async () => {
123
+ if (settings.type === 'text' && settings.url) {
124
+ try {
125
+ const res = await CustomFetch(settings.url, 'POST', {});
126
+ if (res.data) {
127
+ setAutoValue(res.data);
128
+ // Simulate an onChange event to update the form data
129
+ const simulatedEvent = {
130
+ target: {
131
+ dataset: { name: settings.id },
132
+ value: res.data,
133
+ },
134
+ };
135
+ onChange(simulatedEvent);
136
+ }
137
+ } catch (error) {
138
+ console.error('Error fetching auto value:', error);
139
+ }
140
+ }
141
+ };
142
+
143
+ fetchAutoValue();
144
+ }, [settings.url, settings.type, settings.id]);
145
+
120
146
  useEffect(() => {
121
147
  const fetchOptions = () => {
122
148
  let filter = {};
@@ -1669,6 +1695,18 @@ function Field({
1669
1695
  ></textarea>
1670
1696
  );
1671
1697
  case 'text':
1698
+ return (
1699
+ <input
1700
+ type={settings.type}
1701
+ data-name={settings.id}
1702
+ className={inputClass[settings.id]}
1703
+ placeholder=" "
1704
+ onChange={onChange}
1705
+ value={inputValue || autoValue || ''}
1706
+ readOnly={settings.readOnly ? settings.readOnly : false}
1707
+ autoComplete="off"
1708
+ />
1709
+ );
1672
1710
  case 'password':
1673
1711
  return (
1674
1712
  <input
@@ -799,8 +799,43 @@ function Form({
799
799
  if (closeModal) {
800
800
  closeModal();
801
801
  if (type === 'saveAnother') {
802
- fetchData();
803
- modalOpen('create', 0);
802
+ const { saveAnother } =
803
+ formSettings?.update || {};
804
+
805
+ if (
806
+ saveAnother?.url &&
807
+ saveAnother?.method &&
808
+ saveAnother?.data
809
+ ) {
810
+ // Process data object to handle now() timestamps
811
+ const processedData = Object.entries(
812
+ saveAnother.data
813
+ ).reduce((acc, [key, value]) => {
814
+ acc[key] =
815
+ value === 'now()'
816
+ ? moment().format(
817
+ 'YYYY-MM-DD HH:mm:ss'
818
+ ) // Format that strtotime() can parse
819
+ : value;
820
+ return acc;
821
+ }, {});
822
+
823
+ const saveAnotherUrl = `${
824
+ saveAnother.url
825
+ }/${formData[saveAnother.dataKey]}`;
826
+ const resSaveAnother = await CustomFetch(
827
+ saveAnotherUrl,
828
+ saveAnother.method,
829
+ processedData
830
+ );
831
+
832
+ if (resSaveAnother.data.error === '') {
833
+ toast.success(saveAnother.message);
834
+ }
835
+ } else {
836
+ fetchData();
837
+ modalOpen('create', 0);
838
+ }
804
839
  }
805
840
  }
806
841
  toast.success(
@@ -1493,30 +1528,48 @@ function Form({
1493
1528
  formSettings.create?.hasOwnProperty(
1494
1529
  'saveAnother'
1495
1530
  ) ? (
1496
- <>
1497
- <div
1498
- className={`${styles.formItem} ${styles.lastItem}`}
1531
+ <div
1532
+ className={`${styles.formItem} ${styles.fwItem} ${styles.buttonContainer}`}
1533
+ >
1534
+ <button
1535
+ className={styles.btn}
1536
+ onClick={handleSubmit}
1537
+ data-type="save"
1538
+ >
1539
+ Save & Close
1540
+ </button>
1541
+ <button
1542
+ className={styles.btn}
1543
+ onClick={handleSubmit}
1544
+ data-type="saveAnother"
1499
1545
  >
1500
- <button
1501
- className={styles.btn}
1502
- onClick={handleSubmit}
1503
- data-type="save"
1504
- >
1505
- Save & Close
1506
- </button>
1507
- </div>
1508
- <div
1509
- className={`${styles.formItem} ${styles.lastItem}`}
1546
+ Save & Create Another
1547
+ </button>
1548
+ </div>
1549
+ ) : formType === 'update' &&
1550
+ formSettings.update?.hasOwnProperty(
1551
+ 'saveAnother'
1552
+ ) ? (
1553
+ <div
1554
+ className={`${styles.formItem} ${styles.fwItem} ${styles.buttonContainer}`}
1555
+ >
1556
+ <button
1557
+ className={styles.btn}
1558
+ onClick={handleSubmit}
1559
+ data-type="save"
1560
+ >
1561
+ Save & Close
1562
+ </button>
1563
+ <button
1564
+ className={styles.btn}
1565
+ onClick={handleSubmit}
1566
+ data-type="saveAnother"
1510
1567
  >
1511
- <button
1512
- className={styles.btn}
1513
- onClick={handleSubmit}
1514
- data-type="saveAnother"
1515
- >
1516
- Save & Create Another
1517
- </button>
1518
- </div>
1519
- </>
1568
+ {formSettings.update.saveAnother
1569
+ .label ||
1570
+ 'Save & Create Another'}
1571
+ </button>
1572
+ </div>
1520
1573
  ) : (
1521
1574
  <div
1522
1575
  className={`${styles.formItem} ${styles.fwItem} ${styles.lastItem}`}
@@ -137,7 +137,7 @@ const GenericAuth = ({
137
137
  </Route>
138
138
  </Routes>
139
139
  <ToastContainer
140
- position="bottom-right"
140
+ position="bottom-center"
141
141
  autoClose={7000}
142
142
  hideProgressBar={false}
143
143
  newestOnTop
@@ -185,6 +185,7 @@ function GenericIndex({
185
185
  const gridRef = useRef(null);
186
186
  const params = useParams();
187
187
  const [rowsSelected, setRowsSelected] = useState({});
188
+ const [tableData, setTableData] = useState([]);
188
189
  const [tableInfo, setTableInfo] = useState({});
189
190
  const [total, setTotal] = useState(0);
190
191
  const windowHeight = useWindowHeight();
@@ -196,30 +197,57 @@ function GenericIndex({
196
197
  setting.tableSetting
197
198
  );
198
199
 
200
+ // Add a ref to store the latest rowsSelected value
201
+ const rowsSelectedRef = useRef({});
202
+
203
+ // Update the ref whenever rowsSelected changes
204
+ useEffect(() => {
205
+ rowsSelectedRef.current = rowsSelected;
206
+ }, [rowsSelected]);
207
+
199
208
  const handleCheckboxUpdate = useCallback(async () => {
200
209
  try {
201
210
  const { data, method, message, url } = setting?.functions
202
211
  ?.checkboxUpdate
203
212
  ? setting.functions.checkboxUpdate
204
213
  : config.form.functions.checkboxUpdate;
205
- if (!Object.keys(rowsSelected).length) {
214
+
215
+ if (!Object.keys(rowsSelectedRef.current).length) {
206
216
  toast.warning(message.warning);
207
217
  return;
208
218
  }
219
+
220
+ // Create a map of selected rows with full data from tableData
221
+ const selectedRowsData = {};
222
+ Object.entries(rowsSelectedRef.current).forEach(([id, value]) => {
223
+ if (value) {
224
+ // Only include if selected (value is true)
225
+ const fullRowData = tableData.find(
226
+ (row) => row.id === parseInt(id)
227
+ );
228
+ if (fullRowData) {
229
+ selectedRowsData[id] = fullRowData;
230
+ }
231
+ }
232
+ });
233
+
209
234
  const res = await CustomFetch(url, method, {
210
235
  ...data,
211
- rows: rowsSelected,
236
+ rows: selectedRowsData,
212
237
  });
238
+
213
239
  if (res.data.error === '') {
214
240
  toast.success(message.success);
215
241
  gridRef.current.reload();
216
- } else {
217
- toast.error(message.error);
218
242
  }
219
243
  } catch (error) {
220
244
  console.error(error);
221
245
  }
222
- }, [rowsSelected, setting?.functions?.checkboxUpdate]);
246
+ }, [
247
+ setting?.functions?.checkboxUpdate,
248
+ config.form?.functions?.checkboxUpdate,
249
+ tableData,
250
+ ]);
223
251
 
224
252
  const handleExport = async (e) => {
225
253
  try {
@@ -268,6 +296,7 @@ function GenericIndex({
268
296
  ref={gridRef}
269
297
  gridHeight={windowHeight * 0.65}
270
298
  setConfig={setConfig}
299
+ setTableData={setTableData}
271
300
  setTotal={setTotal}
272
301
  style={userProfile?.settings?.style || {}}
273
302
  userProfile={userProfile}
@@ -124,4 +124,82 @@
124
124
  height: 18px;
125
125
  }
126
126
  }
127
- }
127
+ }
128
+
129
+ .modalwrap {
130
+ background: var(--tertiary-color);
131
+ border-radius: 3px;
132
+ overflow: visible;
133
+ border-top-left-radius: var(--br);
134
+ border-top-right-radius: var(--br);
135
+ }
136
+
137
+ .top--modal {
138
+ position: relative;
139
+ z-index: 1000;
140
+ }
141
+
142
+ .modal {
143
+ width: 100%;
144
+ position: relative;
145
+ }
146
+
147
+ .modal__header {
148
+ width: 100%;
149
+ border-bottom: 1px solid rgba(var(--primary-rgb), 0.15);
150
+ display: flex;
151
+ align-items: center;
152
+ justify-content: space-between;
153
+ padding: 0.75rem;
154
+
155
+ h1 {
156
+ color: var(--secondary-color);
157
+ margin: 0;
158
+ padding: 0;
159
+ font-size: 1.15em;
160
+ }
161
+ }
162
+
163
+ .modal__close {
164
+ width: max-content;
165
+ background: rgba(var(--paragraph-rgb), 0.05);
166
+ cursor: pointer;
167
+ padding: 0.25rem;
168
+ margin: 0;
169
+ outline: none;
170
+ border: none;
171
+ border-radius: var(--br);
172
+ display: block;
173
+ line-height: 1;
174
+
175
+ svg {
176
+ color: var(--primary-color);
177
+ display: block;
178
+ }
179
+ }
180
+
181
+ .modal__content {
182
+ width: 100%;
183
+ position: relative;
184
+ padding: 0.75rem;
185
+ max-height: 80vh;
186
+ overflow-y: auto;
187
+ }
188
+
189
+ .formcontainer {
190
+ width: 100%;
191
+ }
192
+
193
+ .noDataMessage {
194
+ text-align: center;
195
+ color: var(--secondary-color);
196
+ font-size: 1.125em;
197
+ padding: 2rem;
198
+ margin: 2rem auto;
199
+ background: var(--tertiary-color);
200
+ border: 1px solid rgba(var(--primary-rgb), 0.15);
201
+ border-radius: var(--br);
202
+ max-width: 80%;
203
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
204
+ font-style: italic;
205
+ }
@@ -240,7 +240,7 @@ input[type='file'] {
240
240
  position: relative;
241
241
  padding: 0.75rem;
242
242
  max-height: 80vh;
243
- // overflow-y: auto;
243
+ overflow-y: auto;
244
244
 
245
245
  .btn {
246
246
  background: var(--primary-color);
@@ -265,6 +265,30 @@ input[type='file'] {
265
265
  }
266
266
  }
267
267
 
268
+ .btn {
269
+ width: max-content;
270
+ display: inline-block;
271
+ position: relative;
272
+ padding: 0.65rem 1rem;
273
+ cursor: pointer;
274
+ font-size: 1rem;
275
+ color: var(--tertiary-color);
276
+ text-decoration: none;
277
+ overflow: hidden;
278
+ background: var(--primary-color);
279
+ border: 1px solid rgba(var(--primary-color--rgb), 1.1);
280
+ border-radius: var(--br);
281
+ outline: none;
282
+ transition: all 0.2s cubic-bezier(0.85, 0, 0.15, 1) 0s;
283
+ font-size: 1.25em;
284
+
285
+ &:hover {
286
+ color: var(--primary-color);
287
+ background: var(--highlight-color);
288
+ border: 1px solid rgba(var(--highlight-rgb), 1.05);
289
+ }
290
+ }
291
+
268
292
  .saveBtn {
269
293
  width: max-content;
270
294
  display: inline-block;
@@ -302,3 +326,14 @@ input[type='file'] {
302
326
  padding: 0.35em 1em;
303
327
  margin: 0 0.15em;
304
328
  }
329
+
330
+ .buttonContainer {
331
+ display: flex;
332
+ justify-content: center;
333
+ gap: 1rem;
334
+
335
+ button {
336
+ flex: 0 1 auto;
337
+ min-width: 200px;
338
+ }
339
+ }