@visns-studio/visns-components 5.0.0 → 5.0.1-8.1

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.
Files changed (37) hide show
  1. package/package.json +3 -1
  2. package/src/components/crm/AsyncSelect.jsx +121 -15
  3. package/src/components/crm/DataGrid.jsx +26 -5
  4. package/src/components/crm/Field.jsx +174 -148
  5. package/src/components/crm/Form.jsx +46 -33
  6. package/src/components/crm/MultiSelect.jsx +96 -29
  7. package/src/components/crm/Navigation.jsx +2 -81
  8. package/src/components/crm/QrCode.jsx +12 -8
  9. package/src/components/crm/auth/Login.jsx +9 -1
  10. package/src/components/crm/auth/Profile.jsx +4 -2
  11. package/src/components/crm/auth/styles/Login.module.scss +52 -82
  12. package/src/components/crm/auth/styles/Profile.module.scss +150 -44
  13. package/src/components/crm/auth/styles/Reset.module.scss +55 -37
  14. package/src/components/crm/auth/styles/Verify.module.scss +55 -76
  15. package/src/components/crm/generic/GenericAuth.jsx +17 -6
  16. package/src/components/crm/generic/GenericDetail.jsx +509 -18
  17. package/src/components/crm/generic/GenericEditableTable.jsx +407 -0
  18. package/src/components/crm/generic/styles/AuditLog.module.scss +4 -37
  19. package/src/components/crm/generic/styles/AuditLogs.module.scss +0 -51
  20. package/src/components/crm/generic/styles/GenericDetail.module.scss +263 -193
  21. package/src/components/crm/generic/styles/GenericDynamic.module.scss +100 -198
  22. package/src/components/crm/generic/styles/GenericEditableTable.module.scss +178 -0
  23. package/src/components/crm/generic/styles/GenericFormBuilder.module.scss +63 -96
  24. package/src/components/crm/generic/styles/GenericIndex.module.scss +46 -66
  25. package/src/components/crm/generic/styles/GenericMain.module.scss +7 -7
  26. package/src/components/crm/generic/styles/GenericSort.module.scss +0 -36
  27. package/src/components/crm/generic/styles/NotificationList.module.scss +1 -32
  28. package/src/components/crm/styles/Autocomplete.module.scss +12 -20
  29. package/src/components/crm/styles/DataGrid.module.scss +6 -89
  30. package/src/components/crm/styles/Field.module.scss +72 -42
  31. package/src/components/crm/styles/Form.module.scss +148 -379
  32. package/src/components/crm/styles/Navigation.module.scss +356 -501
  33. package/src/components/crm/styles/Notification.module.scss +1 -0
  34. package/src/components/crm/styles/QrCode.module.scss +4 -0
  35. package/src/components/crm/styles/SwitchAccount.module.scss +0 -1
  36. package/src/components/crm/styles/TableFilter.module.scss +2 -1
  37. package/src/components/styles/global.css +164 -34
@@ -82,6 +82,7 @@ function Field({
82
82
  useEffect(() => {
83
83
  const fetchOptions = () => {
84
84
  let filter = {};
85
+
85
86
  if (settings.where?.length > 0) {
86
87
  filter = {
87
88
  where: settings.where,
@@ -207,6 +208,12 @@ function Field({
207
208
  filter.orderBy = s.orderBy;
208
209
  }
209
210
 
211
+ if (a.where) {
212
+ forEach(a.where, (w) => {
213
+ filter.where.push(w);
214
+ });
215
+ }
216
+
210
217
  CustomFetch(s.child.url, 'POST', filter, function (result) {
211
218
  childDropdownCallback({
212
219
  id: s.child.id,
@@ -253,65 +260,70 @@ function Field({
253
260
  }, [settings.options, settings, counter]);
254
261
 
255
262
  const renderLabel = () => {
256
- if (
257
- [
258
- 'address',
259
- 'async-dropdown-ajax',
260
- 'checkbox',
261
- 'colour',
262
- 'copytoclipboard',
263
- 'date',
264
- 'datetime',
265
- 'dropdown',
266
- 'dropdown-ajax',
267
- 'dynamicdata',
268
- 'file',
269
- 'gallery',
270
- 'image',
271
- 'multi-dropdown',
272
- 'multi-dropdown-ajax',
273
- 'plaindate',
274
- 'plaindatetime',
275
- 'plainrelation',
276
- 'plainrelationarray',
277
- 'radio',
278
- 'radio-ajax',
279
- 'richeditor',
280
- 'signature',
281
- 'table_radio',
282
- 'table_text',
283
- 'time',
284
- ].includes(settings.type)
285
- ) {
286
- return (
287
- <span className={`${styles.fi__span} ${styles.prefocus}`}>
288
- {settings.label} {settings.required ? '*' : ''}
289
- </span>
290
- );
291
- } else if (['plaintext'].includes(settings.type)) {
292
- if (settings.description) {
293
- return (
294
- <span>
295
- {settings.description
296
- ? parse(settings.description)
297
- : ''}
298
- </span>
299
- );
300
- } else {
263
+ if (settings.label && settings.label !== '') {
264
+ if (
265
+ [
266
+ 'address',
267
+ 'async-dropdown-ajax',
268
+ 'checkbox',
269
+ 'colour',
270
+ 'copytoclipboard',
271
+ 'date',
272
+ 'datetime',
273
+ 'dropdown',
274
+ 'dropdown-ajax',
275
+ 'dynamicdata',
276
+ 'file',
277
+ 'gallery',
278
+ 'image',
279
+ 'multi-dropdown',
280
+ 'multi-dropdown-ajax',
281
+ 'plaindate',
282
+ 'plaindatetime',
283
+ 'plainrelation',
284
+ 'plainrelationarray',
285
+ 'radio',
286
+ 'radio-ajax',
287
+ 'signature',
288
+ 'table_radio',
289
+ 'table_text',
290
+ 'time',
291
+ ].includes(settings.type)
292
+ ) {
301
293
  return (
302
294
  <span className={`${styles.fi__span} ${styles.prefocus}`}>
303
295
  {settings.label} {settings.required ? '*' : ''}
304
296
  </span>
305
297
  );
298
+ } else if (['plaintext'].includes(settings.type)) {
299
+ if (settings.description) {
300
+ return (
301
+ <span>
302
+ {settings.description
303
+ ? parse(settings.description)
304
+ : ''}
305
+ </span>
306
+ );
307
+ } else {
308
+ return (
309
+ <span
310
+ className={`${styles.fi__span} ${styles.prefocus}`}
311
+ >
312
+ {settings.label} {settings.required ? '*' : ''}
313
+ </span>
314
+ );
315
+ }
316
+ } else if (['plaintextheading'].includes(settings.type)) {
317
+ return <h2>{settings.label ? settings.label : ''}</h2>;
318
+ } else if (['section'].includes(settings.type)) {
319
+ return (
320
+ <div className={styles.gridtxt__header}>
321
+ <span>{settings.label}</span>
322
+ </div>
323
+ );
324
+ } else if (['richeditor', 'textarea'].includes(settings.type)) {
325
+ return settings.label;
306
326
  }
307
- } else if (['plaintextheading'].includes(settings.type)) {
308
- return <h2>{settings.label ? settings.label : ''}</h2>;
309
- } else if (['section'].includes(settings.type)) {
310
- return (
311
- <div className={styles.gridtxt__header}>
312
- <span>{settings.label}</span>
313
- </div>
314
- );
315
327
  }
316
328
  };
317
329
 
@@ -368,23 +380,26 @@ function Field({
368
380
  );
369
381
  case 'async-dropdown-ajax':
370
382
  return (
371
- <VisnsAsyncSelect
372
- settings={settings}
373
- className={inputClass[settings.id]}
374
- multi={
375
- settings.hasOwnProperty('multi')
376
- ? settings.multi
377
- : false
378
- }
379
- onChange={onChangeSelect}
380
- inputValue={
381
- inputValue === null || inputValue === undefined
382
- ? ''
383
- : inputValue
384
- }
385
- options={options}
386
- dataOptions={dataOptions}
387
- />
383
+ <div className={styles.dropdownContainer}>
384
+ <VisnsAsyncSelect
385
+ settings={settings}
386
+ className={inputClass[settings.id]}
387
+ multi={
388
+ settings.hasOwnProperty('multi')
389
+ ? settings.multi
390
+ : false
391
+ }
392
+ onChange={onChangeSelect}
393
+ inputValue={
394
+ inputValue === null || inputValue === undefined
395
+ ? ''
396
+ : inputValue
397
+ }
398
+ options={options}
399
+ dataOptions={dataOptions}
400
+ isCreatable={settings.isCreatable || false}
401
+ />
402
+ </div>
388
403
  );
389
404
  case 'checkbox':
390
405
  return (
@@ -673,7 +688,6 @@ function Field({
673
688
  className={inputClass[settings.id]}
674
689
  onChange={onChange}
675
690
  />
676
- {console.info(inputValue)}
677
691
  {inputValue && inputValue.length > 0 && (
678
692
  <ul className="file-list">
679
693
  {inputValue.map((file, index) => (
@@ -807,6 +821,13 @@ function Field({
807
821
 
808
822
  return htmlContainer;
809
823
  case 'html5_date':
824
+ // Convert inputValue to 'YYYY-MM-DD' if necessary
825
+ const formattedDate =
826
+ inputValue &&
827
+ moment(inputValue, moment.ISO_8601, true).isValid()
828
+ ? moment(inputValue).format('YYYY-MM-DD')
829
+ : inputValue; // Use as-is if it's already in the correct format or invalid
830
+
810
831
  return (
811
832
  <input
812
833
  type="date"
@@ -814,11 +835,7 @@ function Field({
814
835
  className={inputClass[settings.id]}
815
836
  placeholder=" "
816
837
  onChange={onChange}
817
- value={
818
- inputValue && inputValue !== 'null'
819
- ? inputValue
820
- : ''
821
- }
838
+ value={formattedDate || ''}
822
839
  autoComplete="off"
823
840
  />
824
841
  );
@@ -970,6 +987,7 @@ function Field({
970
987
  ? settings.options
971
988
  : options
972
989
  }
990
+ isCreatable={settings.isCreatable || false}
973
991
  dataOptions={dataOptions}
974
992
  style={style}
975
993
  />
@@ -1172,77 +1190,6 @@ function Field({
1172
1190
  ))}
1173
1191
  </div>
1174
1192
  ) : null;
1175
- case 'richeditor':
1176
- return (
1177
- <Editor
1178
- tinymceScriptSrc="https://d16lktya8ojp5z.cloudfront.net/generic/packages/tinymce_v701/tinymce.min.js"
1179
- licenseKey="gpl"
1180
- onEditorChange={(value, e) => {
1181
- onChangeRicheditor(e, value, settings.id);
1182
- }}
1183
- onInit={(evt, editor) => (editorRef.current = editor)}
1184
- value={inputValue || ''}
1185
- init={{
1186
- branding: false,
1187
- height: settings.height ? settings.height : 500,
1188
- menubar: false,
1189
- plugins: [
1190
- 'advlist',
1191
- 'autolink',
1192
- 'lists',
1193
- 'link',
1194
- 'image',
1195
- 'charmap',
1196
- 'preview',
1197
- 'anchor',
1198
- 'searchreplace',
1199
- 'visualblocks',
1200
- 'code',
1201
- 'fullscreen',
1202
- 'insertdatetime',
1203
- 'media',
1204
- 'table',
1205
- 'code',
1206
- ],
1207
- toolbar:
1208
- 'insertTextDropdown | undo redo | blocks | ' +
1209
- 'bold italic forecolor | alignleft aligncenter ' +
1210
- 'alignright alignjustify | bullist numlist outdent indent | ' +
1211
- 'removeformat | table',
1212
- content_style:
1213
- 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }',
1214
- setup: (editor) => {
1215
- // Check if insertText exists and is an array of objects
1216
- if (
1217
- settings.insertText &&
1218
- Array.isArray(settings.insertText) &&
1219
- settings.insertText.length > 0
1220
- ) {
1221
- editor.ui.registry.addMenuButton(
1222
- 'insertTextDropdown',
1223
- {
1224
- text: 'Insert Text',
1225
- fetch: (callback) => {
1226
- const items =
1227
- settings.insertText.map(
1228
- (textObj) => ({
1229
- type: 'menuitem',
1230
- text: textObj.text,
1231
- onAction: () =>
1232
- editor.insertContent(
1233
- textObj.content
1234
- ),
1235
- })
1236
- );
1237
- callback(items);
1238
- },
1239
- }
1240
- );
1241
- }
1242
- },
1243
- }}
1244
- />
1245
- );
1246
1193
  case 'signature':
1247
1194
  return (
1248
1195
  <>
@@ -1724,6 +1671,7 @@ function Field({
1724
1671
  'signature',
1725
1672
  'table_radio',
1726
1673
  'table_text',
1674
+ 'textarea',
1727
1675
  'time',
1728
1676
  'toggle',
1729
1677
  ].includes(settings.type)
@@ -1767,6 +1715,78 @@ function Field({
1767
1715
  shouldCloseOnSelect={true}
1768
1716
  />
1769
1717
  );
1718
+ case 'richeditor':
1719
+ return (
1720
+ <Editor
1721
+ tinymceScriptSrc="https://d16lktya8ojp5z.cloudfront.net/generic/packages/tinymce_v701/tinymce.min.js"
1722
+ licenseKey="gpl"
1723
+ onEditorChange={(value, e) => {
1724
+ onChangeRicheditor(e, value, settings.id);
1725
+ }}
1726
+ onInit={(evt, editor) => (editorRef.current = editor)}
1727
+ value={inputValue || ''}
1728
+ init={{
1729
+ branding: false,
1730
+ height: settings.height ? settings.height : 500,
1731
+ menubar: false,
1732
+ plugins: [
1733
+ 'advlist',
1734
+ 'autolink',
1735
+ 'lists',
1736
+ 'link',
1737
+ 'image',
1738
+ 'charmap',
1739
+ 'preview',
1740
+ 'anchor',
1741
+ 'searchreplace',
1742
+ 'visualblocks',
1743
+ 'code',
1744
+ 'fullscreen',
1745
+ 'insertdatetime',
1746
+ 'media',
1747
+ 'table',
1748
+ 'code',
1749
+ 'hr',
1750
+ ],
1751
+ toolbar:
1752
+ 'insertTextDropdown | undo redo | blocks | ' +
1753
+ 'bold italic forecolor | alignleft aligncenter ' +
1754
+ 'alignright alignjustify | bullist numlist outdent indent | ' +
1755
+ 'hr | removeformat | table | code',
1756
+ content_style:
1757
+ 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }',
1758
+ setup: (editor) => {
1759
+ // Check if insertText exists and is an array of objects
1760
+ if (
1761
+ settings.insertText &&
1762
+ Array.isArray(settings.insertText) &&
1763
+ settings.insertText.length > 0
1764
+ ) {
1765
+ editor.ui.registry.addMenuButton(
1766
+ 'insertTextDropdown',
1767
+ {
1768
+ text: 'Insert Text',
1769
+ fetch: (callback) => {
1770
+ const items =
1771
+ settings.insertText.map(
1772
+ (textObj) => ({
1773
+ type: 'menuitem',
1774
+ text: textObj.text,
1775
+ onAction: () =>
1776
+ editor.insertContent(
1777
+ textObj.content
1778
+ ),
1779
+ })
1780
+ );
1781
+ callback(items);
1782
+ },
1783
+ }
1784
+ );
1785
+ }
1786
+ },
1787
+ }}
1788
+ />
1789
+ );
1770
1790
  case 'time':
1771
1791
  return (
1772
1792
  <DatePicker
@@ -1868,7 +1888,13 @@ function Field({
1868
1888
 
1869
1889
  return (
1870
1890
  <div className={formItemClass} style={formItemStyle}>
1871
- <label className={styles.fi__label}>
1891
+ <label
1892
+ className={
1893
+ ['richeditor', 'textarea'].includes(settings.type)
1894
+ ? ''
1895
+ : styles.fi__label
1896
+ }
1897
+ >
1872
1898
  {renderLabel()}
1873
1899
  {renderInput()}
1874
1900
  {renderFieldLabel()}
@@ -215,9 +215,9 @@ function Form({
215
215
  const handleSelectOption = () => {
216
216
  updateFormData({ [id]: inputValue });
217
217
 
218
- // Update any matching form data properties
219
- Object.keys(inputValue).forEach((key) => {
220
- if (formData.hasOwnProperty(key)) {
218
+ // Update any matching form data properties except for the second item
219
+ Object.keys(inputValue).forEach((key, index) => {
220
+ if (index !== 1 && formData.hasOwnProperty(key)) {
221
221
  updateFormData({ [key]: inputValue[key] });
222
222
  }
223
223
  });
@@ -287,6 +287,7 @@ function Form({
287
287
  // Switch cases to handle different actions
288
288
  switch (action.action) {
289
289
  case 'select-option':
290
+ case 'create-option': // New case to handle create-option
290
291
  handleSelectOption();
291
292
  break;
292
293
  case 'clear':
@@ -584,41 +585,39 @@ function Form({
584
585
 
585
586
  if (['create', 'update'].includes(formType)) {
586
587
  fields.forEach((item) => {
588
+ // Skip createOnly fields if formType is 'update'
587
589
  if (formType === 'update' && item.createOnly) {
588
590
  return;
589
591
  }
590
592
 
593
+ // Check if the item is required
591
594
  if (item.required) {
592
- if (
593
- formData[item.id] === null ||
594
- formData[item.id] === ''
595
- ) {
595
+ // Check if formData[item.id] is null, undefined, or an empty string
596
+ if (!formData[item.id]) {
597
+ // Check for required_check dependency
596
598
  if (
597
- item.hasOwnProperty('required_check') &&
598
599
  item.required_check &&
599
600
  !formData[item.required_check]
600
601
  ) {
601
602
  validation += `${item.label} is a required field.<br/>`;
602
603
  _inputClass[item.id] = styles.inputError;
603
604
  } else {
604
- console.info('b', formData, item.id);
605
605
  validation += `${item.label} is a required field.<br/>`;
606
606
  _inputClass[item.id] = styles.inputError;
607
607
  }
608
608
  } else {
609
- _inputClass[item.id] = '';
609
+ _inputClass[item.id] = ''; // Clear error class if valid
610
610
  }
611
611
  } else if (
612
- item.hasOwnProperty('required_rely') &&
613
612
  item.required_rely &&
614
613
  formData[item.required_rely] &&
615
- (formData[item.id] === null ||
616
- formData[item.id] === '')
614
+ !formData[item.id]
617
615
  ) {
616
+ // Check if the dependent required field is empty when it should be filled
618
617
  validation += `${item.label} is a required field.<br/>`;
619
618
  _inputClass[item.id] = styles.inputError;
620
619
  } else {
621
- _inputClass[item.id] = '';
620
+ _inputClass[item.id] = ''; // Clear error class if not required
622
621
  }
623
622
  });
624
623
 
@@ -671,7 +670,14 @@ function Form({
671
670
 
672
671
  if (!_.isEmpty(formData)) {
673
672
  Object.keys(formData).forEach((item) => {
674
- if (formData[item] instanceof File) {
673
+ const value = formData[item];
674
+
675
+ // Check if it's a single File or an array of File objects
676
+ if (
677
+ value instanceof File ||
678
+ (Array.isArray(value) &&
679
+ value.every((file) => file instanceof File))
680
+ ) {
675
681
  fileUpload = true;
676
682
  fileKey = item;
677
683
 
@@ -865,7 +871,9 @@ function Form({
865
871
  _formData[id] =
866
872
  newValue instanceof Date
867
873
  ? newValue
868
- : moment(newValue).toDate();
874
+ : newValue !== undefined
875
+ ? moment(newValue).toDate()
876
+ : '';
869
877
  } else {
870
878
  _formData[id] = newValue;
871
879
  }
@@ -882,23 +890,25 @@ function Form({
882
890
  method,
883
891
  payload = {};
884
892
 
885
- const getDataId = () => {
886
- for (const item of ajaxSetting.where) {
887
- if (
888
- item.hasOwnProperty('urlParam') &&
889
- item.urlParam === 'dataId'
890
- ) {
891
- return item.value;
893
+ if (ajaxSetting?.where) {
894
+ const getDataId = () => {
895
+ for (const item of ajaxSetting.where) {
896
+ if (
897
+ item.hasOwnProperty('urlParam') &&
898
+ item.urlParam === 'dataId'
899
+ ) {
900
+ return item.value;
901
+ }
892
902
  }
893
- }
894
- return 0;
895
- };
903
+ return 0;
904
+ };
905
+ }
896
906
 
897
907
  if (
898
908
  formSettings.hasOwnProperty('json') &&
899
909
  formSettings.json === true
900
910
  ) {
901
- let dataId = getDataId();
911
+ let dataId = paramValue || getDataId();
902
912
 
903
913
  urlSuffix = '/json/get';
904
914
  method = 'POST';
@@ -1367,15 +1377,18 @@ function Form({
1367
1377
  );
1368
1378
  } else {
1369
1379
  return (
1370
- <div className="modalwrap top--modal">
1371
- <div className="modal">
1372
- <div className="modal__header">
1380
+ <div className={styles.modalwrap}>
1381
+ <div className={styles.modal}>
1382
+ <div className={styles.modal__header}>
1373
1383
  <h1>{formSettings[formType]?.title}</h1>
1374
- <button className="modal__close" onClick={closeModal}>
1375
- <CircleX strokeWidth={1} size={24} />
1384
+ <button
1385
+ className={styles.modal__close}
1386
+ onClick={closeModal}
1387
+ >
1388
+ <CircleX strokeWidth={2} size={24} />
1376
1389
  </button>
1377
1390
  </div>
1378
- <div className="modal__content">
1391
+ <div className={styles.modal__content}>
1379
1392
  <div className={styles.formcontainer}>
1380
1393
  <form className={styles.modalForm}>
1381
1394
  {formSettings?.fields?.map((item, index) => {