underpost 2.8.1 → 2.8.41

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 (74) hide show
  1. package/.dockerignore +1 -0
  2. package/.github/workflows/ghpkg.yml +14 -11
  3. package/.github/workflows/pwa-microservices-template.page.yml +10 -3
  4. package/.vscode/extensions.json +17 -71
  5. package/.vscode/settings.json +10 -4
  6. package/AUTHORS.md +16 -5
  7. package/CHANGELOG.md +63 -3
  8. package/Dockerfile +41 -62
  9. package/README.md +1 -28
  10. package/bin/build.js +278 -0
  11. package/bin/db.js +2 -24
  12. package/bin/deploy.js +105 -55
  13. package/bin/file.js +33 -4
  14. package/bin/index.js +33 -51
  15. package/bin/ssl.js +19 -11
  16. package/bin/util.js +9 -89
  17. package/bin/vs.js +25 -2
  18. package/conf.js +31 -138
  19. package/docker-compose.yml +1 -1
  20. package/manifests/core/kustomization.yaml +11 -0
  21. package/manifests/core/underpost-engine-backup-access.yaml +16 -0
  22. package/manifests/core/underpost-engine-backup-pv-pvc.yaml +22 -0
  23. package/manifests/core/underpost-engine-headless-service.yaml +10 -0
  24. package/manifests/core/underpost-engine-mongodb-backup-cronjob.yaml +40 -0
  25. package/manifests/core/underpost-engine-mongodb-configmap.yaml +26 -0
  26. package/manifests/core/underpost-engine-pv-pvc.yaml +23 -0
  27. package/manifests/core/underpost-engine-statefulset.yaml +91 -0
  28. package/manifests/deployment/mongo-express.yaml +60 -0
  29. package/manifests/deployment/phpmyadmin.yaml +54 -0
  30. package/manifests/kind-config.yaml +12 -0
  31. package/manifests/letsencrypt-prod.yaml +15 -0
  32. package/manifests/mariadb/config.yaml +10 -0
  33. package/manifests/mariadb/kustomization.yaml +9 -0
  34. package/manifests/mariadb/pv.yaml +12 -0
  35. package/manifests/mariadb/pvc.yaml +10 -0
  36. package/manifests/mariadb/secret.yaml +8 -0
  37. package/manifests/mariadb/service.yaml +10 -0
  38. package/manifests/mariadb/statefulset.yaml +55 -0
  39. package/manifests/valkey/kustomization.yaml +7 -0
  40. package/manifests/valkey/underpost-engine-valkey-service.yaml +17 -0
  41. package/manifests/valkey/underpost-engine-valkey-statefulset.yaml +39 -0
  42. package/package.json +115 -136
  43. package/src/api/user/user.model.js +16 -3
  44. package/src/api/user/user.service.js +1 -1
  45. package/src/client/components/core/CalendarCore.js +115 -49
  46. package/src/client/components/core/CommonJs.js +150 -19
  47. package/src/client/components/core/CssCore.js +6 -0
  48. package/src/client/components/core/DropDown.js +5 -1
  49. package/src/client/components/core/Input.js +17 -3
  50. package/src/client/components/core/Modal.js +10 -5
  51. package/src/client/components/core/Panel.js +84 -25
  52. package/src/client/components/core/PanelForm.js +4 -18
  53. package/src/client/components/core/Translate.js +43 -9
  54. package/src/client/components/core/Validator.js +9 -1
  55. package/src/client/services/default/default.management.js +4 -2
  56. package/src/db/mongo/MongooseDB.js +13 -1
  57. package/src/index.js +8 -1
  58. package/src/runtime/lampp/Lampp.js +1 -13
  59. package/src/runtime/xampp/Xampp.js +0 -13
  60. package/src/server/auth.js +3 -3
  61. package/src/server/client-build.js +3 -13
  62. package/src/server/conf.js +296 -29
  63. package/src/server/dns.js +2 -3
  64. package/src/server/logger.js +10 -5
  65. package/src/server/network.js +0 -36
  66. package/src/server/process.js +25 -2
  67. package/src/server/project.js +39 -0
  68. package/src/server/proxy.js +4 -26
  69. package/src/server/runtime.js +6 -7
  70. package/src/server/ssl.js +1 -1
  71. package/src/server/valkey.js +2 -0
  72. package/startup.cjs +12 -0
  73. package/src/server/prompt-optimizer.js +0 -28
  74. package/startup.js +0 -11
@@ -518,25 +518,25 @@ function getDirname(path) {
518
518
  return parts.join('/'); // Adjust separator if needed for Windows ('\')
519
519
  }
520
520
 
521
- const isDayValid = (day) => {
522
- const date = new Date();
523
- date.setDate(day);
524
- return date.getDate() === day;
525
- };
526
-
527
- const isMonthValid = (month) => {
528
- const date = new Date();
529
- date.setMonth(month - 1);
530
- return date.getMonth() === month - 1;
531
- };
532
-
533
521
  const isValidDate = (day, month, year) => {
534
- if (!isDayValid(day) || !isMonthValid(month)) {
535
- return false;
536
- }
522
+ if (!month && !year) return !(new Date(day) == 'Invalid Date');
523
+ // new Date('2025-12-28')
524
+ // Sat Dec 27 2025 19:00:00 GMT-0500 (Eastern Standard Time)
525
+ // new Date('2025/12/28')
526
+ // Sun Dec 28 2025 00:00:00 GMT-0500 (Eastern Standard Time)
527
+ return !(new Date(`${year}/${month}/${day}`) == 'Invalid Date');
528
+ };
537
529
 
538
- const date = new Date(year, month - 1, day);
539
- return !isNaN(date.getTime());
530
+ // console.log(req.body.timeZoneClient, Intl.DateTimeFormat().resolvedOptions().timeZone);
531
+ // DateTime.fromISO("2017-05-15T09:10:23", { zone: "Europe/Paris" });
532
+ const strToDateUTC = (date = '2025-01-30T14:32') => {
533
+ const year = parseInt(date.split('-')[0]);
534
+ const month = parseInt(date.split('-')[1]);
535
+ const day = parseInt(date.split('-')[2].split('T')[0]);
536
+ const hour = parseInt(date.split('T')[1].split(':')[0]);
537
+ const minute = parseInt(date.split('T')[1].split(':')[1]);
538
+ date = new Date(Date.UTC(year, month - 1, day, hour, minute, 0, 0));
539
+ return date;
540
540
  };
541
541
 
542
542
  const isValidFormat = (value, format) => {
@@ -662,6 +662,22 @@ function componentFromStr(numStr, percent) {
662
662
  return percent ? Math.floor((255 * Math.min(100, num)) / 100) : Math.min(255, num);
663
663
  }
664
664
 
665
+ const isChileanIdentityDocument = function (rutCompleto) {
666
+ const dv = function (T) {
667
+ let M = 0,
668
+ S = 1;
669
+ for (; T; T = Math.floor(T / 10)) S = (S + (T % 10) * (9 - (M++ % 6))) % 11;
670
+ return S ? S - 1 : 'k';
671
+ };
672
+ rutCompleto = rutCompleto.replace('‐', '-');
673
+ if (!/^[0-9]+[-|‐]{1}[0-9kK]{1}$/.test(rutCompleto)) return false;
674
+ var tmp = rutCompleto.split('-');
675
+ var digv = tmp[1];
676
+ var rut = tmp[0];
677
+ if (digv == 'K') digv = 'k';
678
+ return dv(rut) == digv;
679
+ };
680
+
665
681
  function rgbToHex(rgb) {
666
682
  const rgbRegex = /^rgb\(\s*(-?\d+)(%?)\s*,\s*(-?\d+)(%?)\s*,\s*(-?\d+)(%?)\s*\)$/;
667
683
  let result,
@@ -685,10 +701,121 @@ const hexToNumber = (hex = 0xdc) => Number(hex) || parseFloat(hex, 16);
685
701
 
686
702
  const numberToHex = (number = 0) => number.toString(16);
687
703
 
704
+ const generateRandomPasswordSelection = (length) => {
705
+ const _random = (arr) => {
706
+ const rand = Math.floor(Math.random() * arr.length);
707
+ return arr[rand];
708
+ };
709
+
710
+ const uppercase = [
711
+ 'A',
712
+ 'B',
713
+ 'C',
714
+ 'D',
715
+ 'E',
716
+ 'F',
717
+ 'G',
718
+ 'H',
719
+ 'I',
720
+ 'J',
721
+ 'K',
722
+ 'L',
723
+ 'M',
724
+ 'N',
725
+ 'O',
726
+ 'P',
727
+ 'Q',
728
+ 'R',
729
+ 'S',
730
+ 'T',
731
+ 'U',
732
+ 'V',
733
+ 'W',
734
+ 'X',
735
+ 'Y',
736
+ 'Z',
737
+ ];
738
+ const lowercase = [
739
+ 'a',
740
+ 'b',
741
+ 'c',
742
+ 'd',
743
+ 'e',
744
+ 'f',
745
+ 'g',
746
+ 'h',
747
+ 'i',
748
+ 'j',
749
+ 'k',
750
+ 'l',
751
+ 'm',
752
+ 'n',
753
+ 'o',
754
+ 'p',
755
+ 'q',
756
+ 'r',
757
+ 's',
758
+ 't',
759
+ 'u',
760
+ 'v',
761
+ 'w',
762
+ 'x',
763
+ 'y',
764
+ 'z',
765
+ ];
766
+ const special = [
767
+ '~',
768
+ '!',
769
+ '@',
770
+ '#',
771
+ '$',
772
+ '%',
773
+ '^',
774
+ '&',
775
+ '*',
776
+ '(',
777
+ ')',
778
+ '_',
779
+ '+',
780
+ '-',
781
+ '=',
782
+ '{',
783
+ '}',
784
+ '[',
785
+ ']',
786
+ ':',
787
+ ';',
788
+ '?',
789
+ ',',
790
+ '.',
791
+ '|',
792
+ '\\',
793
+ ];
794
+ const numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
795
+
796
+ const nonSpecial = [...uppercase, ...lowercase, ...numbers];
797
+
798
+ let password = '';
799
+
800
+ for (let i = 0; i < length; i++) {
801
+ // Previous character is a special character
802
+ if (i !== 0 && special.includes(password[i - 1])) {
803
+ password += _random(nonSpecial);
804
+ } else password += _random([...nonSpecial, ...special]);
805
+ }
806
+
807
+ return password;
808
+ };
809
+
688
810
  // 0x = Hexadecimal
689
811
  // 0b = Binary
690
812
  // 0o = Octal
691
813
 
814
+ const userRoleEnum = ['admin', 'moderator', 'user', 'guest'];
815
+
816
+ const commonAdminGuard = (role) => userRoleEnum.indexOf(role) === userRoleEnum.indexOf('admin');
817
+ const commonModeratorGuard = (role) => userRoleEnum.indexOf(role) <= userRoleEnum.indexOf('moderator');
818
+
692
819
  export {
693
820
  s4,
694
821
  range,
@@ -727,10 +854,9 @@ export {
727
854
  getSubpaths,
728
855
  formatBytes,
729
856
  getDirname,
730
- isDayValid,
731
- isMonthValid,
732
857
  isValidDate,
733
858
  isValidFormat,
859
+ strToDateUTC,
734
860
  getTimezoneOffset,
735
861
  cleanString,
736
862
  splitEveryXChar,
@@ -742,4 +868,9 @@ export {
742
868
  getCapVariableName,
743
869
  hexToNumber,
744
870
  numberToHex,
871
+ generateRandomPasswordSelection,
872
+ userRoleEnum,
873
+ commonAdminGuard,
874
+ commonModeratorGuard,
875
+ isChileanIdentityDocument,
745
876
  };
@@ -117,6 +117,12 @@ const CssCommonCore = async () => {
117
117
  animation: ripple 600ms linear;
118
118
  background-color: rgba(137, 137, 137, 0.503);
119
119
  }
120
+ .slide-menu-top-bar-fix {
121
+ top: 0;
122
+ left: 0;
123
+ width: 100%;
124
+ z-index: 1;
125
+ }
120
126
  @keyframes ripple {
121
127
  to {
122
128
  transform: scale(4);
@@ -16,7 +16,11 @@ const DropDown = {
16
16
  onClick: () => {
17
17
  console.log('DropDown onClick', this.value);
18
18
  if (options && options.resetOnClick) options.resetOnClick();
19
- this.Tokens[id].value = undefined;
19
+ if (options && options.type === 'checkbox')
20
+ for (const opt of DropDown.Tokens[id].value) {
21
+ s(`.dropdown-option-${id}-${opt}`).click();
22
+ }
23
+ else this.Tokens[id].value = undefined;
20
24
  },
21
25
  });
22
26
 
@@ -1,6 +1,8 @@
1
1
  import { AgGrid } from './AgGrid.js';
2
2
  import { BtnIcon } from './BtnIcon.js';
3
+ import { isValidDate } from './CommonJs.js';
3
4
  import { darkTheme } from './Css.js';
5
+ import { DropDown } from './DropDown.js';
4
6
  import { loggerFactory } from './Logger.js';
5
7
  import { RichText } from './RichText.js';
6
8
  import { ToggleSwitch } from './ToggleSwitch.js';
@@ -147,6 +149,10 @@ const Input = {
147
149
  htmls(`.file-name-render-${inputData.id}`, `${s(`.${inputData.id}`).fileNameInputExtDefaultContent}`);
148
150
  continue;
149
151
  break;
152
+ case 'dropdown-checkbox': {
153
+ s(`.dropdown-option-${inputData.id}-reset`).click();
154
+ break;
155
+ }
150
156
  case 'md':
151
157
  RichText.Tokens[inputData.id].easyMDE.value('');
152
158
  continue;
@@ -196,6 +202,12 @@ const Input = {
196
202
  RichText.Tokens[inputData.id].easyMDE.value(fileObj[inputData.model].mdPlain);
197
203
  continue;
198
204
  break;
205
+
206
+ case 'dropdown-checkbox': {
207
+ s(`.dropdown-option-${inputData.id}-reset`).click();
208
+ for (const opt of originObj[inputData.model]) s(`.dropdown-option-${inputData.id}-${opt}`).click();
209
+ break;
210
+ }
199
211
  case 'checkbox':
200
212
  case 'checkbox-on-off':
201
213
  if (
@@ -207,9 +219,11 @@ const Input = {
207
219
  break;
208
220
  case 'datetime-local':
209
221
  {
210
- const date = new Date(originObj[inputData.model]);
211
- date.setMinutes(date.getMinutes() - date.getTimezoneOffset());
212
- s(`.${inputData.id}`).value = date.toISOString().slice(0, 16);
222
+ if (isValidDate(originObj[inputData.model])) {
223
+ const date = new Date(originObj[inputData.model]);
224
+ // date.setMinutes(date.getMinutes() - date.getTimezoneOffset());
225
+ s(`.${inputData.id}`).value = date.toISOString().slice(0, 16);
226
+ } else s(`.${inputData.id}`).value = null;
213
227
  }
214
228
  continue;
215
229
  break;
@@ -403,6 +403,11 @@ const Modal = {
403
403
  })}
404
404
  </div>
405
405
  </div>
406
+ ${options?.slideMenuTopBarFix
407
+ ? html`<div class="abs modal slide-menu-top-bar-fix" style="height: ${options.heightTopBar}px">
408
+ ${await options.slideMenuTopBarFix()}
409
+ </div>`
410
+ : ''}
406
411
  </div>`,
407
412
  );
408
413
  EventsUI.onClick(`.action-btn-profile-log-in`, () => {
@@ -537,12 +542,12 @@ const Modal = {
537
542
  if (routerId) {
538
543
  if (
539
544
  s(`.main-btn-${routerId}`) &&
540
- (routerId.toLocaleLowerCase().match(s(`.${id}`).value.toLocaleLowerCase()) ||
545
+ (routerId.toLowerCase().match(s(`.${id}`).value.toLowerCase()) ||
541
546
  (Translate.Data[routerId] &&
542
547
  Object.keys(Translate.Data[routerId]).filter((keyLang) =>
543
548
  Translate.Data[routerId][keyLang]
544
- .toLocaleLowerCase()
545
- .match(s(`.${id}`).value.toLocaleLowerCase()),
549
+ .toLowerCase()
550
+ .match(s(`.${id}`).value.toLowerCase()),
546
551
  ).length > 0))
547
552
  ) {
548
553
  const fontAwesomeIcon = getAllChildNodes(s(`.main-btn-${routerId}`)).find((e) => {
@@ -1691,7 +1696,7 @@ const Modal = {
1691
1696
  const htmlRender = html`
1692
1697
  <br />
1693
1698
  <div class="in section-mp" style="font-size: 40px; text-align: center">
1694
- <i class="fas fa-question-circle"></i>
1699
+ ${options.icon ? options.icon : html` <i class="fas fa-question-circle"></i>`}
1695
1700
  </div>
1696
1701
  ${await options.html()}
1697
1702
  <div class="in section-mp">
@@ -1702,7 +1707,7 @@ const Modal = {
1702
1707
  style: `margin: auto`,
1703
1708
  })}
1704
1709
  </div>
1705
- <div class="in section-mp">
1710
+ <div class="in section-mp ${options.disableBtnCancel ? 'hide' : ''}">
1706
1711
  ${await BtnIcon.Render({
1707
1712
  class: `in section-mp form-button btn-cancel-${id}`,
1708
1713
  label: Translate.Render('cancel'),
@@ -1,4 +1,4 @@
1
- import { getId } from './CommonJs.js';
1
+ import { getId, isValidDate, newInstance } from './CommonJs.js';
2
2
  import { LoadingAnimation } from '../core/LoadingAnimation.js';
3
3
  import { Validator } from '../core/Validator.js';
4
4
  import { Input } from '../core/Input.js';
@@ -51,8 +51,6 @@ const Panel = {
51
51
  <i style="font-size: 25px" class="fa-solid fa-cloud"></i>
52
52
  </div>`;
53
53
 
54
- let editId;
55
-
56
54
  const openPanelForm = () => {
57
55
  s(`.${idPanel}-form-body`).classList.remove('hide');
58
56
  s(`.btn-${idPanel}-add`).classList.add('hide');
@@ -72,7 +70,7 @@ const Panel = {
72
70
  };
73
71
 
74
72
  const renderPanel = async (payload) => {
75
- const obj = payload;
73
+ const obj = newInstance(payload);
76
74
  if ('_id' in obj) obj.id = obj._id;
77
75
  const { id } = obj;
78
76
 
@@ -98,8 +96,8 @@ const Panel = {
98
96
  });
99
97
  EventsUI.onClick(`.${idPanel}-btn-edit-${id}`, async () => {
100
98
  logger.warn('edit', obj);
101
- if (obj._id) editId = obj._id;
102
- else if (obj.id) editId = obj.id;
99
+ if (obj._id) Panel.Tokens[idPanel].editId = obj._id;
100
+ else if (obj.id) Panel.Tokens[idPanel].editId = obj.id;
103
101
 
104
102
  s(`.btn-${idPanel}-label-edit`).classList.remove('hide');
105
103
  s(`.btn-${idPanel}-label-add`).classList.add('hide');
@@ -171,6 +169,10 @@ const Panel = {
171
169
  const valueIcon = formObjData?.panel?.icon?.value ? formObjData.panel.icon.value : '';
172
170
  const keyIcon = formObjData?.panel?.icon?.key ? formObjData.panel.icon.key : '';
173
171
 
172
+ if (formObjData && ['datetime-local'].includes(formObjData.inputType) && isValidDate(obj[infoKey])) {
173
+ obj[infoKey] = `${obj[infoKey]}`.replace('T', ' ').replace('.000Z', '');
174
+ }
175
+
174
176
  if (formData.find((f) => f.model === infoKey && f.panel && f.panel.type === 'tags')) {
175
177
  setTimeout(async () => {
176
178
  let tagRender = html``;
@@ -186,22 +188,50 @@ const Panel = {
186
188
  });
187
189
  return html``;
188
190
  }
191
+ {
192
+ const formDataObj = formData.find((f) => f.model === infoKey && f.panel && f.panel.type === 'list');
193
+ if (obj[infoKey] && obj[infoKey].length > 0 && formDataObj)
194
+ return html`<div class="in ${idPanel}-row">
195
+ <span class="${idPanel}-row-key capitalize ${formObjData.label?.disabled ? 'hide' : ''}">
196
+ ${keyIcon} ${Translate.Render(infoKey)}:</span
197
+ >
198
+ <span class="${idPanel}-row-value"
199
+ >${valueIcon} ${obj[infoKey].map((k) => Translate.Render(k)).join(', ')}</span
200
+ >
201
+ </div> `;
202
+ }
189
203
 
190
- if (formData.find((f) => f.model === infoKey && f.panel && f.panel.type === 'info-row-pin'))
191
- return html`<div class="in ${idPanel}-row">
192
- <span class="${idPanel}-row-pin-key capitalize ${formObjData.label?.disabled ? 'hide' : ''}">
193
- ${keyIcon} ${infoKey}:</span
194
- >
195
- <span class="${idPanel}-row-pin-value">${valueIcon} ${obj[infoKey]}</span>
196
- </div> `;
204
+ {
205
+ const formDataObj = formData.find(
206
+ (f) => f.model === infoKey && f.panel && f.panel.type === 'info-row-pin',
207
+ );
208
+ if (obj[infoKey] && formDataObj)
209
+ return html`<div class="in ${idPanel}-row">
210
+ <span class="${idPanel}-row-pin-key capitalize ${formObjData.label?.disabled ? 'hide' : ''}">
211
+ ${keyIcon}
212
+ ${formDataObj.translateCode
213
+ ? Translate.Render(formDataObj.translateCode)
214
+ : Translate.Render(infoKey)}:</span
215
+ >
216
+ <span class="${idPanel}-row-pin-value">${valueIcon} ${obj[infoKey]}</span>
217
+ </div> `;
218
+ }
197
219
 
198
- if (formData.find((f) => f.model === infoKey && f.panel && f.panel.type === 'info-row'))
199
- return html`<div class="in ${idPanel}-row">
200
- <span class="${idPanel}-row-key capitalize ${formObjData.label?.disabled ? 'hide' : ''}">
201
- ${keyIcon} ${infoKey}:</span
202
- >
203
- <span class="${idPanel}-row-value"> ${valueIcon} ${obj[infoKey]}</span>
204
- </div> `;
220
+ {
221
+ const formDataObj = formData.find(
222
+ (f) => f.model === infoKey && f.panel && f.panel.type === 'info-row',
223
+ );
224
+ if (obj[infoKey] && formDataObj)
225
+ return html`<div class="in ${idPanel}-row">
226
+ <span class="${idPanel}-row-key capitalize ${formObjData.label?.disabled ? 'hide' : ''}">
227
+ ${keyIcon}
228
+ ${formDataObj.translateCode
229
+ ? Translate.Render(formDataObj.translateCode)
230
+ : Translate.Render(infoKey)}:</span
231
+ >
232
+ <span class="${idPanel}-row-value"> ${valueIcon} ${obj[infoKey]}</span>
233
+ </div> `;
234
+ }
205
235
 
206
236
  return html``;
207
237
  })
@@ -224,6 +254,30 @@ const Panel = {
224
254
  for (const modelData of formData) {
225
255
  if (modelData.disableRender) continue;
226
256
  switch (modelData.inputType) {
257
+ case 'dropdown-checkbox': {
258
+ renderForm += html`<div class="in section-mp">
259
+ ${await DropDown.Render({
260
+ id: `${modelData.id}`,
261
+ label: html`${Translate.Render(modelData.model)}`,
262
+ type: 'checkbox',
263
+ value: modelData.dropdown.options[0],
264
+ resetOption: true,
265
+ containerClass: `${idPanel}-dropdown-checkbox`,
266
+ data: modelData.dropdown.options.map((dKey) => {
267
+ return {
268
+ value: dKey,
269
+ data: dKey,
270
+ checked: false,
271
+ display: html`${Translate.Render(dKey)}`,
272
+ onClick: function () {
273
+ logger.info('DropDown onClick', this.checked);
274
+ },
275
+ };
276
+ }),
277
+ })}
278
+ </div>`;
279
+ break;
280
+ }
227
281
  case 'dropdown':
228
282
  renderForm += html` <div class="in section-mp">
229
283
  ${await DropDown.Render({
@@ -418,21 +472,23 @@ const Panel = {
418
472
  obj.id = `${data.length}`;
419
473
  let documents;
420
474
  if (options && options.on && options.on.add) {
421
- const { status, data } = await options.on.add({ data: obj, editId });
475
+ const { status, data } = await options.on.add({ data: obj, editId: Panel.Tokens[idPanel].editId });
422
476
  if (status === 'error') return;
423
477
  documents = data;
424
478
  }
425
479
  s(`.btn-${idPanel}-clean`).click();
426
- if (editId && s(`.${idPanel}-${editId}`)) s(`.${idPanel}-${editId}`).remove();
480
+ if (Panel.Tokens[idPanel].editId && s(`.${idPanel}-${Panel.Tokens[idPanel].editId}`))
481
+ s(`.${idPanel}-${Panel.Tokens[idPanel].editId}`).remove();
427
482
  if (Array.isArray(documents)) {
428
483
  htmls(`.${idPanel}-render`, '');
429
484
  for (const doc of documents) {
430
485
  append(`.${idPanel}-render`, await renderPanel(doc));
431
486
  }
432
- } else htmls(`.${idPanel}-render`, await renderPanel(obj));
487
+ } else htmls(`.${idPanel}-render`, await renderPanel({ ...obj, ...documents }));
433
488
  Input.cleanValues(formData);
434
489
  s(`.btn-${idPanel}-close`).click();
435
490
  s(`.${scrollClassContainer}`).scrollTop = 0;
491
+ if (s(`.${scrollClassContainer}`)) s(`.${scrollClassContainer}`).style.overflow = 'auto';
436
492
  });
437
493
  s(`.btn-${idPanel}-clean`).onclick = () => {
438
494
  Input.cleanValues(formData);
@@ -458,13 +514,14 @@ const Panel = {
458
514
  s(`.btn-${idPanel}-add`).onclick = (e) => {
459
515
  e.preventDefault();
460
516
  // s(`.btn-${idPanel}-clean`).click();
461
- editId = undefined;
517
+ Panel.Tokens[idPanel].editId = undefined;
462
518
  s(`.btn-${idPanel}-label-add`).classList.remove('hide');
463
519
  s(`.btn-${idPanel}-label-edit`).classList.add('hide');
464
520
  s(`.${scrollClassContainer}`).scrollTop = 0;
465
521
 
466
522
  openPanelForm();
467
523
  };
524
+ if (s(`.${scrollClassContainer}`)) s(`.${scrollClassContainer}`).style.overflow = 'auto';
468
525
  });
469
526
 
470
527
  if (data.length > 0) for (const obj of data) render += await renderPanel(obj);
@@ -597,7 +654,9 @@ const Panel = {
597
654
  >
598
655
  <div class="in ${idPanel}-form-header">
599
656
  ${await BtnIcon.Render({
600
- class: `section-mp btn-custom btn-${idPanel}-add`,
657
+ class: `section-mp btn-custom btn-${idPanel}-add ${
658
+ options?.role?.add ? (!options.role.add() ? 'hide' : '') : ''
659
+ }`,
601
660
  label: html`<i class="fas fa-plus"></i> ${Translate.Render('add')}`,
602
661
  type: 'button',
603
662
  })}
@@ -90,16 +90,7 @@ const PanelForm = {
90
90
  },
91
91
  },
92
92
  ];
93
- const dateFormat = (date) =>
94
- html`<span
95
- style="${renderCssAttr({
96
- style: {
97
- 'font-size': '14px',
98
- color: '#888',
99
- },
100
- })}"
101
- >${new Date(date).toLocaleString().replaceAll(',', '')}</span
102
- >`;
93
+
103
94
  const titleIcon = html`<i class="fa-solid fa-quote-left"></i>`;
104
95
  const panelRender = async ({ data }) =>
105
96
  await Panel.Render({
@@ -271,7 +262,7 @@ const PanelForm = {
271
262
  fileId: file ? URL.createObjectURL(file) : undefined,
272
263
  _id: documentData._id,
273
264
  id: documentData._id,
274
- createdAt: dateFormat(documentData.createdAt),
265
+ createdAt: documentData.createdAt,
275
266
  };
276
267
 
277
268
  if (documentStatus === 'error') status = 'error';
@@ -376,7 +367,7 @@ const PanelForm = {
376
367
  PanelForm.Data[idPanel].data.push({
377
368
  id: documentObject._id,
378
369
  title: documentObject.title,
379
- createdAt: dateFormat(documentObject.createdAt),
370
+ createdAt: documentObject.createdAt,
380
371
  tags: documentObject.tags.filter((t) => !prefixTags.includes(t)),
381
372
  mdFileId: marked.parse(mdFileId),
382
373
  userId: documentObject.userId._id,
@@ -431,14 +422,9 @@ const PanelForm = {
431
422
  })),
432
423
  });
433
424
 
434
- let lastCid;
435
- let lasUserId;
436
425
  this.Data[idPanel].updatePanel = async () => {
437
426
  const cid = getQueryParams().cid ? getQueryParams().cid : '';
438
- if (lastCid === cid && lasUserId === Elements.Data.user.main.model.user._id) return;
439
427
  if (options.route === 'home') Modal.homeCid = newInstance(cid);
440
- lastCid = cid;
441
- lasUserId = newInstance(Elements.Data.user.main.model.user._id);
442
428
  htmls(`.${options.parentIdModal ? 'html-' + options.parentIdModal : 'main-body'}`, await renderSrrPanelData());
443
429
  await getPanelData();
444
430
  htmls(
@@ -451,7 +437,7 @@ const PanelForm = {
451
437
  id: options.parentIdModal ? 'html-' + options.parentIdModal : 'main-body',
452
438
  routeId: options.route,
453
439
  event: async (path) => {
454
- await this.Data[idPanel].updatePanel();
440
+ if (!PanelForm.Data[idPanel].sessionIn) await this.Data[idPanel].updatePanel();
455
441
  },
456
442
  });
457
443
 
@@ -11,35 +11,49 @@ const Translate = {
11
11
  Data: {},
12
12
  Token: {},
13
13
  Event: {},
14
+ Options: {},
14
15
  Parse: function (lang) {
15
16
  s('html').lang = lang;
16
17
  Object.keys(this.Token).map((translateHash) => {
17
18
  if (translateHash in this.Token && lang in this.Token[translateHash]) {
18
- if (!('placeholder' in this.Token[translateHash]) && s(`.${translateHash}`))
19
- htmls(`.${translateHash}`, textFormatted(this.Token[translateHash][lang]));
19
+ if (!('placeholder' in this.Options[translateHash]) && s(`.${translateHash}`))
20
+ htmls(
21
+ `.${translateHash}`,
22
+ this.Options[translateHash]?.disableTextFormat
23
+ ? this.Token[translateHash][lang]
24
+ : textFormatted(this.Token[translateHash][lang]),
25
+ );
20
26
  else if ('placeholder' in this.Token[translateHash] && s(this.Token[translateHash].placeholder))
21
- s(this.Token[translateHash].placeholder).placeholder = textFormatted(this.Token[translateHash][lang]);
27
+ s(this.Token[translateHash].placeholder).placeholder = this.Options[translateHash]?.disableTextFormat
28
+ ? this.Token[translateHash][lang]
29
+ : textFormatted(this.Token[translateHash][lang]);
22
30
  }
23
31
  });
24
32
  for (const keyEvent of Object.keys(this.Event)) this.Event[keyEvent]();
25
33
  },
26
- Render: function (keyLang, placeholder) {
34
+ Render: function (keyLang, placeholder, options = { disableTextFormat: false }) {
27
35
  if (!(keyLang in this.Data)) {
28
36
  // TODO: add translate package or library for this case
29
37
  logger.warn('translate key lang does not exist: ', keyLang);
30
- return textFormatted(keyLang);
38
+ return options.disableTextFormat ? keyLang : textFormatted(keyLang);
31
39
  }
32
40
  if (placeholder) this.Data[keyLang].placeholder = placeholder;
33
41
  keyLang = this.Data[keyLang];
34
42
  const translateHash = getId(this.Token, 'trans');
43
+ this.Options[translateHash] = options;
35
44
  this.Token[translateHash] = newInstance(keyLang);
36
45
  if ('placeholder' in keyLang) {
37
- if (s('html').lang in keyLang) return textFormatted(keyLang[s('html').lang]);
38
- return textFormatted(keyLang['en']);
46
+ if (s('html').lang in keyLang)
47
+ return options.disableTextFormat ? keyLang[s('html').lang] : textFormatted(keyLang[s('html').lang]);
48
+ return options.disableTextFormat ? keyLang['en'] : textFormatted(keyLang['en']);
39
49
  }
40
50
  if (s('html').lang in keyLang)
41
- return html`<span class="${translateHash}">${textFormatted(keyLang[s('html').lang])}</span>`;
42
- return html`<span class="${translateHash}">${textFormatted(keyLang['en'])}</span>`;
51
+ return html`<span class="${translateHash}"
52
+ >${options.disableTextFormat ? keyLang[s('html').lang] : textFormatted(keyLang[s('html').lang])}</span
53
+ >`;
54
+ return html`<span class="${translateHash}"
55
+ >${options.disableTextFormat ? keyLang['en'] : textFormatted(keyLang['en'])}</span
56
+ >`;
43
57
  },
44
58
  renderLang: function (language) {
45
59
  localStorage.setItem('lang', language);
@@ -474,6 +488,26 @@ const TranslateCore = {
474
488
  Translate.Data['friday'] = { es: 'Viernes', en: 'Friday' };
475
489
  Translate.Data['saturday'] = { es: 'Sábado', en: 'Saturday' };
476
490
  Translate.Data['sunday'] = { es: 'Domingo', en: 'Sunday' };
491
+
492
+ Translate.Data['description'] = { es: 'Descripción', en: 'Description' };
493
+ Translate.Data['daysOfWeek'] = { es: 'Días de la semana', en: 'Days of the week' };
494
+ Translate.Data['startTime'] = { es: 'Hora de inicio', en: 'Start time' };
495
+ Translate.Data['endTime'] = { es: 'Hora de finalizacion', en: 'End time' };
496
+ Translate.Data['appointment-scheduled'] = {
497
+ en: 'Your appointment has been scheduled',
498
+ es: 'Tu cita ha sido programada',
499
+ };
500
+ Translate.Data['info'] = { es: 'Información', en: 'Info' };
501
+ Translate.Data['complete-name'] = { es: 'Nombre completo', en: 'Complete name' };
502
+ Translate.Data['identityDocument'] = { es: 'Rut', en: 'Identity document' };
503
+ Translate.Data['day'] = { es: 'Día', en: 'Day' };
504
+ Translate.Data['month'] = { es: 'Mes', en: 'Month' };
505
+ Translate.Data['year'] = { es: 'Año', en: 'Year' };
506
+ Translate.Data['phone'] = { es: 'Teléfono', en: 'Phone' };
507
+ Translate.Data['invalid-identity-document'] = {
508
+ en: 'Invalid identity document',
509
+ es: 'Documento de identidad inválido',
510
+ };
477
511
  },
478
512
  };
479
513