@visns-studio/visns-components 5.2.13 → 5.2.14

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.2.13",
81
+ "version": "5.2.14",
82
82
  "description": "Various packages to assist in the development of our Custom Applications.",
83
83
  "main": "src/index.js",
84
84
  "files": [
@@ -242,7 +242,6 @@ function Field({
242
242
  }
243
243
  });
244
244
  } else {
245
- console.info(s);
246
245
  filter = {
247
246
  where: [
248
247
  {
@@ -266,8 +265,6 @@ function Field({
266
265
  });
267
266
  }
268
267
 
269
- console.info(filter, s.fields);
270
-
271
268
  CustomFetch(s.child.url, 'POST', filter, function (result) {
272
269
  childDropdownCallback({
273
270
  id: s.child.id,
@@ -893,7 +893,9 @@ function GenericDashboard({ setting, userProfile, dynamicDashboard = false }) {
893
893
  </table>
894
894
  </div>
895
895
  ) : (
896
- <div>No data available</div>
896
+ <div className={styles.noData}>
897
+ No data available
898
+ </div>
897
899
  )}
898
900
  </div>
899
901
  );
@@ -3,6 +3,7 @@ import '../../styles/global.css';
3
3
  import React, { useEffect, useRef, useState } from 'react';
4
4
  import { Link, useParams } from 'react-router-dom';
5
5
  import { Calendar, momentLocalizer } from 'react-big-calendar';
6
+ import withDragAndDrop from 'react-big-calendar/lib/addons/dragAndDrop';
6
7
  import moment from 'moment';
7
8
  import parse from 'html-react-parser';
8
9
  import Popup from 'reactjs-popup';
@@ -25,6 +26,7 @@ import {
25
26
 
26
27
  import 'react-confirm-alert/src/react-confirm-alert.css';
27
28
  import 'react-big-calendar/lib/css/react-big-calendar.css';
29
+ import 'react-big-calendar/lib/addons/dragAndDrop/styles.css';
28
30
 
29
31
  import Breadcrumb from '../Breadcrumb';
30
32
  import CustomFetch from '../Fetch';
@@ -39,6 +41,7 @@ import TableFilter from '../TableFilter';
39
41
  import styles from './styles/GenericDetail.module.scss';
40
42
 
41
43
  const localizer = momentLocalizer(moment);
44
+ const DnDCalendar = withDragAndDrop(Calendar);
42
45
 
43
46
  function GenericDetail({
44
47
  extraUrlParam,
@@ -884,11 +887,11 @@ function GenericDetail({
884
887
 
885
888
  if (activeTabConfig && activeTabConfig.type) {
886
889
  switch (activeTabConfig.type) {
887
- case 'calendar':
890
+ case 'calendar': {
891
+ // Prepare events from your data, converting moment objects to Date objects
888
892
  const events =
889
893
  data?.[activeTabConfig?.dataKey]
890
894
  ?.map((event) => {
891
- // Safely access keys using optional chaining and defaults
892
895
  const keys =
893
896
  activeTabConfig?.config?.keys || {};
894
897
  const eventStart = event?.[keys.start]
@@ -898,23 +901,100 @@ function GenericDetail({
898
901
  ? moment(event[keys.end])
899
902
  : null;
900
903
 
901
- // Only include valid events
902
904
  if (eventStart && eventEnd) {
903
905
  return {
904
906
  title:
905
907
  event?.[keys.title] ||
906
- 'Untitled Event', // Default title if not provided
907
- start: eventStart,
908
- end: eventEnd,
908
+ 'Untitled Event',
909
+ start: eventStart.toDate(),
910
+ end: eventEnd.toDate(),
911
+ entryId: event?.[keys.id],
909
912
  };
910
913
  }
911
-
912
- return null; // Filter out invalid events
914
+ return null;
913
915
  })
914
916
  ?.filter(Boolean) || [];
915
917
 
918
+ // Callback when an event is dragged and dropped to a new slot
919
+ const onEventDrop = async ({
920
+ event,
921
+ start,
922
+ end,
923
+ allDay,
924
+ }) => {
925
+ try {
926
+ await CustomFetch(
927
+ activeTabConfig.config.urls.update,
928
+ 'PUT',
929
+ {
930
+ dataId: routeParams[
931
+ activeTabConfig.urlParam
932
+ ],
933
+ key: activeTabConfig.dataKey,
934
+ id: event.entryId,
935
+ [activeTabConfig.config.keys.start]:
936
+ start,
937
+ [activeTabConfig.config.keys.end]:
938
+ end,
939
+ }
940
+ );
941
+
942
+ // Optionally, handle the response if needed
943
+ toast.success(
944
+ activeTabConfig.config.messages
945
+ .dragAndDrop ||
946
+ 'Event updated via drag and drop!'
947
+ );
948
+ handleReload();
949
+ } catch (error) {
950
+ console.error(
951
+ 'Error updating event:',
952
+ error
953
+ );
954
+ toast.error('Error updating event.');
955
+ }
956
+ };
957
+
958
+ // Callback when an event is resized
959
+ const onEventResize = async ({
960
+ event,
961
+ start,
962
+ end,
963
+ }) => {
964
+ try {
965
+ await CustomFetch(
966
+ activeTabConfig.config.urls.update,
967
+ 'PUT',
968
+ {
969
+ dataId: routeParams[
970
+ activeTabConfig.urlParam
971
+ ],
972
+ key: activeTabConfig.dataKey,
973
+ id: event.entryId,
974
+ [activeTabConfig.config.keys.start]:
975
+ start,
976
+ [activeTabConfig.config.keys.end]:
977
+ end,
978
+ }
979
+ );
980
+
981
+ // Optionally, update your event in the backend here using an API call.
982
+ toast.success(
983
+ activeTabConfig.config.messages
984
+ .resize || 'Event resized!'
985
+ );
986
+ handleReload();
987
+ } catch (error) {
988
+ console.error(
989
+ 'Error resizing event:',
990
+ error
991
+ );
992
+ toast.error('Error resizing event.');
993
+ }
994
+ };
995
+
916
996
  return (
917
- <Calendar
997
+ <DnDCalendar
918
998
  localizer={localizer}
919
999
  events={events}
920
1000
  startAccessor="start"
@@ -931,8 +1011,12 @@ function GenericDetail({
931
1011
  'day',
932
1012
  ]
933
1013
  }
1014
+ onEventDrop={onEventDrop}
1015
+ resizable
1016
+ onEventResize={onEventResize}
934
1017
  />
935
1018
  );
1019
+ }
936
1020
  case 'dropzone':
937
1021
  const downloadFile = (file) => {
938
1022
  const { id, file_url, file_name } = file;
@@ -233,8 +233,6 @@ const GenericEditableTable = ({
233
233
  const groupKey = categoryId || 'no_category';
234
234
  // Use the category's sort_order if provided; otherwise, default to a high number (e.g. 9999)
235
235
 
236
- console.info(categoryInfo);
237
-
238
236
  const catSortOrder =
239
237
  categoryInfo && typeof categoryInfo.sort_order !== 'undefined'
240
238
  ? categoryInfo.sort_order
@@ -270,8 +268,6 @@ const GenericEditableTable = ({
270
268
  return a.catSortOrder - b.catSortOrder;
271
269
  });
272
270
 
273
- console.info(groupedArray);
274
-
275
271
  return groupedArray;
276
272
  };
277
273
 
@@ -310,7 +306,7 @@ const GenericEditableTable = ({
310
306
  .catch(() => toast.error('Failed to copy to clipboard'));
311
307
  };
312
308
 
313
- // Update an existing field’s value
309
+ // Updated handleFieldChange to support "add" functionality
314
310
  const handleFieldChange = (value, fieldId, keyCounter) => {
315
311
  setLocalData((prev) => {
316
312
  const updatedDetail = [...prev];
@@ -319,6 +315,34 @@ const GenericEditableTable = ({
319
315
  [fieldId]: value,
320
316
  };
321
317
 
318
+ // Loop through columns to see if any has an "add" property.
319
+ columns.forEach((col) => {
320
+ if (
321
+ col.add &&
322
+ (fieldId === col.id || fieldId === col.add.from)
323
+ ) {
324
+ const fromVal = updatedDetail[keyCounter][col.add.from];
325
+ const daysVal = parseFloat(
326
+ updatedDetail[keyCounter][col.id]
327
+ );
328
+ if (fromVal && !isNaN(daysVal)) {
329
+ const newDate = new Date(fromVal);
330
+ if (!isNaN(newDate)) {
331
+ newDate.setDate(newDate.getDate() + daysVal);
332
+ // Format the date as YYYY-MM-DD
333
+ const yyyy = newDate.getFullYear();
334
+ let mm = newDate.getMonth() + 1;
335
+ let dd = newDate.getDate();
336
+ if (mm < 10) mm = '0' + mm;
337
+ if (dd < 10) dd = '0' + dd;
338
+ updatedDetail[keyCounter][
339
+ col.add.to
340
+ ] = `${yyyy}-${mm}-${dd}`;
341
+ }
342
+ }
343
+ }
344
+ });
345
+
322
346
  debouncedUpdate({ [rows.key]: updatedDetail });
323
347
  return updatedDetail;
324
348
  });
@@ -445,6 +469,7 @@ const GenericEditableTable = ({
445
469
  )
446
470
  }
447
471
  style={{ textAlign: 'right' }}
472
+ readOnly={column.readOnly || false}
448
473
  />
449
474
  );
450
475
  case 'date':
@@ -460,6 +485,7 @@ const GenericEditableTable = ({
460
485
  )
461
486
  }
462
487
  style={{ textAlign: 'right' }}
488
+ readOnly={column.readOnly || false}
463
489
  />
464
490
  );
465
491
  case 'dropdown':
@@ -473,6 +499,7 @@ const GenericEditableTable = ({
473
499
  keyCounter
474
500
  )
475
501
  }
502
+ readOnly={column.readOnly || false}
476
503
  >
477
504
  {column.options.map((option) => (
478
505
  <option key={option.id} value={option.id}>
@@ -493,6 +520,7 @@ const GenericEditableTable = ({
493
520
  keyCounter
494
521
  )
495
522
  }
523
+ readOnly={column.readOnly || false}
496
524
  >
497
525
  <option>Select an Option</option>
498
526
  {options.map((option) => (
@@ -537,6 +565,7 @@ const GenericEditableTable = ({
537
565
  keyCounter
538
566
  )
539
567
  }
568
+ readOnly={column.readOnly || false}
540
569
  />
541
570
  );
542
571
  }
@@ -544,21 +573,71 @@ const GenericEditableTable = ({
544
573
 
545
574
  // --- New row functions for inline creation ---
546
575
 
547
- // Update new entry fields (for grouped or ungrouped)
576
+ // Updated handleNewFieldChange to support "add" functionality in new rows
548
577
  const handleNewFieldChange = (value, fieldId, groupId = null) => {
549
578
  if (hasCategory) {
550
- setNewEntries((prev) => ({
551
- ...prev,
552
- [groupId]: {
553
- ...prev[groupId],
554
- [fieldId]: value,
555
- },
556
- }));
579
+ setNewEntries((prev) => {
580
+ const updated = {
581
+ ...prev,
582
+ [groupId]: {
583
+ ...prev[groupId],
584
+ [fieldId]: value,
585
+ },
586
+ };
587
+ columns.forEach((col) => {
588
+ if (
589
+ col.add &&
590
+ (fieldId === col.id || fieldId === col.add.from)
591
+ ) {
592
+ const fromVal = updated[groupId][col.add.from];
593
+ const daysVal = parseFloat(updated[groupId][col.id]);
594
+ if (fromVal && !isNaN(daysVal)) {
595
+ const newDate = new Date(fromVal);
596
+ if (!isNaN(newDate)) {
597
+ newDate.setDate(newDate.getDate() + daysVal);
598
+ const yyyy = newDate.getFullYear();
599
+ let mm = newDate.getMonth() + 1;
600
+ let dd = newDate.getDate();
601
+ if (mm < 10) mm = '0' + mm;
602
+ if (dd < 10) dd = '0' + dd;
603
+ updated[groupId][
604
+ col.add.to
605
+ ] = `${yyyy}-${mm}-${dd}`;
606
+ }
607
+ }
608
+ }
609
+ });
610
+ return updated;
611
+ });
557
612
  } else {
558
- setNewEntry((prev) => ({
559
- ...prev,
560
- [fieldId]: value,
561
- }));
613
+ setNewEntry((prev) => {
614
+ const updated = {
615
+ ...prev,
616
+ [fieldId]: value,
617
+ };
618
+ columns.forEach((col) => {
619
+ if (
620
+ col.add &&
621
+ (fieldId === col.id || fieldId === col.add.from)
622
+ ) {
623
+ const fromVal = updated[col.add.from];
624
+ const daysVal = parseFloat(updated[col.id]);
625
+ if (fromVal && !isNaN(daysVal)) {
626
+ const newDate = new Date(fromVal);
627
+ if (!isNaN(newDate)) {
628
+ newDate.setDate(newDate.getDate() + daysVal);
629
+ const yyyy = newDate.getFullYear();
630
+ let mm = newDate.getMonth() + 1;
631
+ let dd = newDate.getDate();
632
+ if (mm < 10) mm = '0' + mm;
633
+ if (dd < 10) dd = '0' + dd;
634
+ updated[col.add.to] = `${yyyy}-${mm}-${dd}`;
635
+ }
636
+ }
637
+ }
638
+ });
639
+ return updated;
640
+ });
562
641
  }
563
642
  };
564
643
 
@@ -568,3 +568,10 @@
568
568
  color: #888;
569
569
  padding: 20px;
570
570
  }
571
+
572
+ .noData {
573
+ text-align: center;
574
+ font-size: 1rem;
575
+ color: #888;
576
+ padding: 20px;
577
+ }