@underpostnet/underpost 2.97.1 → 2.98.0

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 (63) hide show
  1. package/README.md +2 -2
  2. package/cli.md +3 -1
  3. package/conf.js +2 -0
  4. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  5. package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
  6. package/package.json +1 -1
  7. package/scripts/rocky-pwa.sh +200 -0
  8. package/src/api/core/core.service.js +0 -5
  9. package/src/api/default/default.service.js +7 -5
  10. package/src/api/document/document.model.js +1 -1
  11. package/src/api/document/document.router.js +5 -0
  12. package/src/api/document/document.service.js +176 -128
  13. package/src/api/file/file.model.js +112 -4
  14. package/src/api/file/file.ref.json +42 -0
  15. package/src/api/file/file.service.js +380 -32
  16. package/src/api/user/user.model.js +38 -1
  17. package/src/api/user/user.router.js +96 -63
  18. package/src/api/user/user.service.js +81 -48
  19. package/src/cli/db.js +424 -166
  20. package/src/cli/index.js +8 -0
  21. package/src/cli/repository.js +1 -1
  22. package/src/cli/run.js +1 -0
  23. package/src/cli/ssh.js +10 -10
  24. package/src/client/components/core/Account.js +327 -36
  25. package/src/client/components/core/AgGrid.js +3 -0
  26. package/src/client/components/core/Auth.js +11 -3
  27. package/src/client/components/core/Chat.js +2 -2
  28. package/src/client/components/core/Content.js +161 -80
  29. package/src/client/components/core/Css.js +30 -0
  30. package/src/client/components/core/CssCore.js +16 -12
  31. package/src/client/components/core/FileExplorer.js +813 -49
  32. package/src/client/components/core/Input.js +207 -12
  33. package/src/client/components/core/LogIn.js +42 -20
  34. package/src/client/components/core/Modal.js +138 -24
  35. package/src/client/components/core/Panel.js +71 -32
  36. package/src/client/components/core/PanelForm.js +262 -77
  37. package/src/client/components/core/PublicProfile.js +888 -0
  38. package/src/client/components/core/Responsive.js +15 -7
  39. package/src/client/components/core/Router.js +117 -15
  40. package/src/client/components/core/SearchBox.js +322 -116
  41. package/src/client/components/core/SignUp.js +26 -7
  42. package/src/client/components/core/SocketIo.js +6 -3
  43. package/src/client/components/core/Translate.js +148 -0
  44. package/src/client/components/core/Validator.js +15 -0
  45. package/src/client/components/core/windowGetDimensions.js +6 -6
  46. package/src/client/components/default/MenuDefault.js +59 -12
  47. package/src/client/components/default/RoutesDefault.js +1 -0
  48. package/src/client/services/core/core.service.js +163 -1
  49. package/src/client/services/default/default.management.js +454 -76
  50. package/src/client/services/default/default.service.js +13 -6
  51. package/src/client/services/file/file.service.js +43 -16
  52. package/src/client/services/user/user.service.js +13 -9
  53. package/src/client/sw/default.sw.js +107 -184
  54. package/src/db/DataBaseProvider.js +1 -1
  55. package/src/db/mongo/MongooseDB.js +1 -1
  56. package/src/index.js +1 -1
  57. package/src/mailer/MailerProvider.js +4 -4
  58. package/src/runtime/express/Express.js +2 -1
  59. package/src/runtime/lampp/Lampp.js +2 -2
  60. package/src/server/auth.js +3 -6
  61. package/src/server/data-query.js +449 -0
  62. package/src/server/object-layer.js +0 -3
  63. package/src/ws/IoInterface.js +2 -2
@@ -34,12 +34,133 @@ const SearchBox = {
34
34
  * - search: async (query, context) => Promise<Array<result>>
35
35
  * - renderResult: (result, index, context) => string (HTML)
36
36
  * - onClick: (result, context) => void
37
- * - priority: number (lower = higher priority)
37
+ * - priority: number (lower number = higher priority)
38
38
  * @type {Array<object>}
39
39
  * @memberof SearchBoxClient.SearchBox
40
40
  */
41
41
  providers: [],
42
42
 
43
+ /**
44
+ * Recent search results manager with localStorage persistence.
45
+ * Tracks clicked results from all providers (routes and custom).
46
+ * Maintains order of most-recent-first results across sessions.
47
+ * @type {object}
48
+ * @memberof SearchBoxClient.SearchBox
49
+ */
50
+ RecentResults: {
51
+ /**
52
+ * Storage key for localStorage persistence
53
+ * @type {string}
54
+ */
55
+ storageKey: 'searchbox_recent_results',
56
+
57
+ /**
58
+ * Maximum number of recent results to keep in history
59
+ * @type {number}
60
+ */
61
+ maxResults: 20,
62
+
63
+ /**
64
+ * Get all cached recent results from localStorage
65
+ * @returns {Array<object>} Array of recent result objects
66
+ */
67
+ getAll: function () {
68
+ try {
69
+ const stored = localStorage.getItem(this.storageKey);
70
+ return stored ? JSON.parse(stored) : [];
71
+ } catch (error) {
72
+ logger.warn('Error reading search history from localStorage:', error);
73
+ return [];
74
+ }
75
+ },
76
+
77
+ /**
78
+ * Save recent results to localStorage
79
+ * @param {Array<object>} results - Array of results to save
80
+ */
81
+ saveAll: function (results) {
82
+ try {
83
+ localStorage.setItem(this.storageKey, JSON.stringify(results.slice(0, this.maxResults)));
84
+ } catch (error) {
85
+ logger.warn('Error saving search history to localStorage:', error);
86
+ }
87
+ },
88
+
89
+ /**
90
+ * Add a result to recent history (moves to front if duplicate)
91
+ * Removes duplicates and maintains max size limit.
92
+ * Only stores serializable data (excludes DOM elements).
93
+ * @param {object} result - Result object to add (must have id and providerId/routerId)
94
+ */
95
+ add: function (result) {
96
+ if (!result || (!result.id && !result.routerId)) {
97
+ logger.warn('SearchBox.RecentResults.add: Invalid result, missing id or routerId');
98
+ return;
99
+ }
100
+
101
+ // Create a clean copy excluding DOM elements (fontAwesomeIcon, imgElement)
102
+ const cleanResult = {
103
+ id: result.id,
104
+ routerId: result.routerId,
105
+ type: result.type,
106
+ providerId: result.providerId,
107
+ title: result.title,
108
+ subtitle: result.subtitle,
109
+ tags: result.tags,
110
+ createdAt: result.createdAt,
111
+ data: result.data,
112
+ };
113
+
114
+ const recent = this.getAll();
115
+
116
+ // Remove duplicate if it exists (based on id and providerId/routerId)
117
+ const filteredRecent = recent.filter((r) => {
118
+ if (cleanResult.providerId && r.providerId) {
119
+ return !(r.id === cleanResult.id && r.providerId === cleanResult.providerId);
120
+ } else if (cleanResult.routerId && r.routerId) {
121
+ return r.routerId !== cleanResult.routerId;
122
+ }
123
+ return true;
124
+ });
125
+
126
+ // Add new result to front
127
+ filteredRecent.unshift(cleanResult);
128
+
129
+ // Save to localStorage
130
+ this.saveAll(filteredRecent);
131
+ },
132
+
133
+ /**
134
+ * Clear all recent results from localStorage
135
+ */
136
+ clear: function () {
137
+ try {
138
+ localStorage.removeItem(this.storageKey);
139
+ } catch (error) {
140
+ logger.warn('Error clearing search history:', error);
141
+ }
142
+ },
143
+
144
+ /**
145
+ * Remove a single result from recent history by ID and provider
146
+ * @param {string} resultId - Result ID to remove
147
+ * @param {string} [providerId] - Provider ID of the result (optional, for routes use null)
148
+ */
149
+ remove: function (resultId, providerId) {
150
+ const recent = this.getAll();
151
+ const filtered = recent.filter((r) => {
152
+ // Match by ID and providerId (or routerId for routes)
153
+ if (providerId) {
154
+ return !(r.id === resultId && r.providerId === providerId);
155
+ } else {
156
+ // For routes (providerId is null), match by routerId instead
157
+ return !(r.routerId === resultId);
158
+ }
159
+ });
160
+ this.saveAll(filtered);
161
+ },
162
+ },
163
+
43
164
  /**
44
165
  * Registers a search provider plugin for extensible search functionality.
45
166
  * Replaces any existing provider with the same ID.
@@ -290,24 +411,56 @@ const SearchBox = {
290
411
  return;
291
412
  }
292
413
 
293
- let html = '';
414
+ // Check if this is rendering recently clicked items (not search results)
415
+ // context.isRecentHistory is set when rendering from history, not from search query
416
+ const isRecentHistory = context.isRecentHistory === true;
417
+
418
+ let htmlContent = '';
294
419
  results.forEach((result, index) => {
295
420
  const provider = this.providers.find((p) => p.id === result.providerId);
296
421
 
422
+ let resultHtml = '';
297
423
  if (result.type === 'route' || !provider) {
298
424
  // Default route rendering (backward compatible)
299
- html += this.renderRouteResult(result, index, context);
425
+ resultHtml = this.renderRouteResult(result, index, context);
300
426
  } else {
301
427
  // Custom provider rendering
302
- html += provider.renderResult(result, index, context);
428
+ resultHtml = provider.renderResult(result, index, context);
429
+ }
430
+
431
+ // Only add delete button for recently clicked items (not search results)
432
+ if (isRecentHistory) {
433
+ // Wrapper with relative position for absolute delete button
434
+ htmlContent += `
435
+ <div class="search-result-wrapper search-result-history-item" data-result-id="${result.id || result.routerId}" data-provider-id="${result.providerId || 'default-routes'}">
436
+ ${resultHtml}
437
+ <button
438
+ class="search-result-delete-btn"
439
+ data-result-id="${result.id || result.routerId}"
440
+ data-provider-id="${result.providerId || 'default-routes'}"
441
+ title="Remove from history"
442
+ aria-label="Remove from history"
443
+ >
444
+ <i class="fas fa-trash-alt"></i>
445
+ </button>
446
+ </div>
447
+ `;
448
+ } else {
449
+ // Search results: no delete button, no wrapper overhead
450
+ htmlContent += resultHtml;
303
451
  }
304
452
  });
305
453
 
306
- container.innerHTML = html;
454
+ container.innerHTML = htmlContent;
307
455
 
308
456
  // Attach click handlers
309
457
  this.attachClickHandlers(results, containerId, context);
310
458
 
459
+ // Only attach delete handlers for recently clicked items
460
+ if (isRecentHistory) {
461
+ this.attachDeleteHandlers(container, results, containerId, context);
462
+ }
463
+
311
464
  // Call post-render callbacks from providers
312
465
  results.forEach((result) => {
313
466
  const provider = this.providers.find((p) => p.id === result.providerId);
@@ -317,6 +470,68 @@ const SearchBox = {
317
470
  });
318
471
  },
319
472
 
473
+ /**
474
+ * Attaches delete event handlers to result delete buttons within a specific container.
475
+ * Removes only the clicked result from history with smooth animation feedback.
476
+ * Only affects delete buttons within the specified container.
477
+ * @memberof SearchBoxClient.SearchBox
478
+ * @param {HTMLElement} container - The container element to search within.
479
+ * @param {Array<object>} results - Array of search results.
480
+ * @param {string} containerId - Results container element ID or class name.
481
+ * @param {object} [context={}] - Context object.
482
+ * @returns {void}
483
+ */
484
+ attachDeleteHandlers: function (container, results, containerId, context = {}) {
485
+ // Only select delete buttons within this specific container
486
+ const deleteButtons = container.querySelectorAll('.search-result-delete-btn');
487
+ deleteButtons.forEach((btn) => {
488
+ btn.addEventListener('click', (e) => {
489
+ e.preventDefault();
490
+ e.stopPropagation();
491
+
492
+ const resultId = btn.getAttribute('data-result-id');
493
+ const providerId = btn.getAttribute('data-provider-id');
494
+
495
+ // Animate removal
496
+ const wrapper = btn.closest('.search-result-history-item');
497
+ if (wrapper) {
498
+ wrapper.classList.add('search-result-removing');
499
+ setTimeout(() => {
500
+ // Remove only this specific result from history storage
501
+ if (providerId === 'default-routes') {
502
+ this.RecentResults.remove(resultId, null);
503
+ } else {
504
+ this.RecentResults.remove(resultId, providerId);
505
+ }
506
+
507
+ // Filter out only the deleted result from the array
508
+ const remaining = results.filter((r) => {
509
+ if (providerId === 'default-routes') {
510
+ // For routes, match by routerId
511
+ return r.routerId !== resultId;
512
+ } else {
513
+ // For providers, match by id and providerId
514
+ return !(r.id === resultId && r.providerId === providerId);
515
+ }
516
+ });
517
+
518
+ // Re-render with remaining results (context.isRecentHistory should stay true)
519
+ if (remaining.length > 0) {
520
+ this.renderResults(remaining, containerId, context);
521
+ } else {
522
+ // If no results left, clear container and hide clear-all button
523
+ container.innerHTML = '';
524
+ const clearAllBtn = document.querySelector('.btn-search-history-clear-all');
525
+ if (clearAllBtn) {
526
+ clearAllBtn.style.display = 'none';
527
+ }
528
+ }
529
+ }, 200);
530
+ }
531
+ });
532
+ });
533
+ },
534
+
320
535
  /**
321
536
  * Renders a default route search result.
322
537
  * Backward compatible with Modal.js search functionality.
@@ -338,10 +553,35 @@ const SearchBox = {
338
553
  const imgElement = result.imgElement;
339
554
 
340
555
  let iconHtml = '';
341
- if (imgElement) {
342
- iconHtml = imgElement.outerHTML;
343
- } else if (fontAwesomeIcon) {
344
- iconHtml = fontAwesomeIcon.outerHTML;
556
+
557
+ // For route results from history, reconstruct icons from DOM
558
+ if (!fontAwesomeIcon && !imgElement && routerId) {
559
+ const routeBtn = s(`.main-btn-${routerId}`);
560
+ if (routeBtn) {
561
+ const icon = getAllChildNodes(routeBtn).find((e) => {
562
+ return e.classList && Array.from(e.classList).find((e) => e.match('fa-') && !e.match('fa-grip-vertical'));
563
+ });
564
+ const img = getAllChildNodes(routeBtn).find((e) => {
565
+ return (
566
+ e.classList &&
567
+ Array.from(e.classList).find((e) =>
568
+ options.searchCustomImgClass ? e.match(options.searchCustomImgClass) : e.match('img-btn-square-menu'),
569
+ )
570
+ );
571
+ });
572
+ if (img) {
573
+ iconHtml = img.outerHTML;
574
+ } else if (icon) {
575
+ iconHtml = icon.outerHTML;
576
+ }
577
+ }
578
+ } else {
579
+ // For fresh search results, use provided DOM elements
580
+ if (imgElement) {
581
+ iconHtml = imgElement.outerHTML;
582
+ } else if (fontAwesomeIcon) {
583
+ iconHtml = fontAwesomeIcon.outerHTML;
584
+ }
345
585
  }
346
586
 
347
587
  const translatedText = Translate.Render(routerId);
@@ -381,6 +621,9 @@ const SearchBox = {
381
621
  e.preventDefault();
382
622
  e.stopPropagation();
383
623
 
624
+ // Track result in persistent history for all result types
625
+ this.RecentResults.add(result);
626
+
384
627
  const provider = this.providers.find((p) => p.id === result.providerId);
385
628
 
386
629
  if (result.type === 'route') {
@@ -503,109 +746,6 @@ const SearchBox = {
503
746
  }, 0);
504
747
  },
505
748
 
506
- /**
507
- * Debounce helper for search-while-typing
508
- */
509
- debounce: function (func, wait) {
510
- let timeout;
511
- return function executedFunction(...args) {
512
- const later = () => {
513
- clearTimeout(timeout);
514
- func(...args);
515
- };
516
- clearTimeout(timeout);
517
- timeout = setTimeout(later, wait);
518
- };
519
- },
520
-
521
- /**
522
- * Sets up a search input element with automatic search on typing.
523
- * Attaches debounced input event handler and manages search lifecycle.
524
- * @memberof SearchBoxClient.SearchBox
525
- * @param {string} inputId - Input element ID or class name.
526
- * @param {string} resultsContainerId - Results container element ID or class name.
527
- * @param {object} [context={}] - Configuration context object.
528
- * @param {number} [context.debounceTime=300] - Debounce delay in milliseconds.
529
- * @param {number} [context.minQueryLength=1] - Minimum query length to trigger search.
530
- * @returns {Function} Cleanup function to remove event listeners.
531
- */
532
- setupSearchInput: function (inputId, resultsContainerId, context = {}) {
533
- const input = s(`#${inputId}`) || s(`.${inputId}`);
534
- if (!input) {
535
- logger.warn(`Input ${inputId} not found`);
536
- return;
537
- }
538
-
539
- const debounceTime = context.debounceTime || 300;
540
-
541
- const performSearch = this.debounce(async (query) => {
542
- const trimmedQuery = query ? query.trim() : '';
543
- const minLength = context.minQueryLength !== undefined ? context.minQueryLength : 1;
544
-
545
- // Support single character searches by default (minQueryLength: 1)
546
- // Can be configured via context.minQueryLength for different use cases
547
- if (trimmedQuery.length < minLength) {
548
- this.renderResults([], resultsContainerId, context);
549
- return;
550
- }
551
-
552
- const results = await this.search(trimmedQuery, context);
553
- this.renderResults(results, resultsContainerId, context);
554
- }, debounceTime);
555
-
556
- // Store the handler reference
557
- const handlerId = `search-handler-${inputId}`;
558
- if (this.Data[handlerId]) {
559
- input.removeEventListener('input', this.Data[handlerId]);
560
- }
561
-
562
- this.Data[handlerId] = (e) => {
563
- performSearch(e.target.value);
564
- };
565
-
566
- input.addEventListener('input', this.Data[handlerId]);
567
-
568
- logger.info(`Setup search input: ${inputId}`);
569
-
570
- return () => {
571
- input.removeEventListener('input', this.Data[handlerId]);
572
- delete this.Data[handlerId];
573
- };
574
- },
575
-
576
- /**
577
- * Debounces a function call to reduce excessive invocations.
578
- * Used for search input to prevent searching on every keystroke.
579
- * @memberof SearchBoxClient.SearchBox
580
- * @param {Function} func - The function to debounce.
581
- * @param {number} wait - Delay in milliseconds before invoking the function.
582
- * @returns {Function} Debounced function that delays invocation.
583
- */
584
- debounce: function (func, wait) {
585
- let timeout;
586
-
587
- const later = function (...args) {
588
- timeout = null;
589
- func(...args);
590
- };
591
-
592
- return function (...args) {
593
- if (timeout) clearTimeout(timeout);
594
- timeout = setTimeout(() => later(...args), wait);
595
- };
596
- },
597
-
598
- /**
599
- * Clears all registered search providers.
600
- * Useful for cleanup or resetting search functionality.
601
- * @memberof SearchBoxClient.SearchBox
602
- * @returns {void}
603
- */
604
- clearProviders: function () {
605
- this.providers = [];
606
- logger.info('Cleared all search providers');
607
- },
608
-
609
749
  /**
610
750
  * Gets base CSS styles for SearchBox with theme-aware styling.
611
751
  * Uses subThemeManager colors for consistent theming across light and dark modes.
@@ -665,13 +805,17 @@ const SearchBox = {
665
805
  display: flex;
666
806
  align-items: center;
667
807
  gap: 10px;
668
- padding: 8px 10px;
808
+ padding: 12px 14px;
669
809
  margin: 4px 0;
670
810
  cursor: pointer;
671
811
  border-radius: 4px;
672
812
  transition: all 0.15s ease;
673
813
  border: 1px solid transparent;
674
814
  background: transparent;
815
+ min-height: 44px;
816
+ box-sizing: border-box;
817
+ width: 100%;
818
+ max-width: 100%;
675
819
  }
676
820
 
677
821
  .search-result-item:hover {
@@ -687,16 +831,19 @@ const SearchBox = {
687
831
  }
688
832
 
689
833
  .search-result-route {
690
- padding: 3px;
834
+ padding: 10px 12px;
691
835
  margin: 2px;
692
836
  text-align: left;
837
+ min-height: 40px;
693
838
  }
694
839
 
695
840
  .search-result-icon {
696
841
  display: flex;
697
842
  align-items: center;
698
843
  justify-content: center;
699
- min-width: 24px;
844
+ min-width: 28px;
845
+ min-height: 28px;
846
+ font-size: 16px;
700
847
  color: ${iconColor} !important;
701
848
  }
702
849
 
@@ -723,8 +870,9 @@ const SearchBox = {
723
870
 
724
871
  .search-result-title {
725
872
  font-size: 14px;
726
- font-weight: normal;
873
+ font-weight: 500;
727
874
  margin-bottom: 2px;
875
+ line-height: 1.4;
728
876
  }
729
877
 
730
878
  .search-result-subtitle {
@@ -761,6 +909,64 @@ const SearchBox = {
761
909
  border-color: ${activeBorder};
762
910
  color: ${hasThemeColor ? (darkTheme ? lightenHex(themeColor, 0.8) : darkenHex(themeColor, 0.4)) : tagColor};
763
911
  }
912
+
913
+ /* Wrapper for history items with delete button - maintains original width */
914
+ .search-result-history-item {
915
+ position: relative;
916
+ box-sizing: border-box;
917
+ width: 100%;
918
+ max-width: 100%;
919
+ }
920
+
921
+ .search-result-history-item .search-result-item {
922
+ width: 100%;
923
+ box-sizing: border-box;
924
+ padding-right: 30px; /* Make room for delete button */
925
+ }
926
+
927
+ /* Delete button - absolute positioned in top-right corner */
928
+ .search-result-delete-btn {
929
+ position: absolute;
930
+ top: 4px;
931
+ right: 4px;
932
+ background: none;
933
+ border: none;
934
+ color: #999;
935
+ cursor: pointer;
936
+ padding: 3px 6px;
937
+ border-radius: 3px;
938
+ transition: all 0.2s ease;
939
+ opacity: 0;
940
+ width: 22px;
941
+ height: 22px;
942
+ display: flex;
943
+ align-items: center;
944
+ justify-content: center;
945
+ font-size: 10px;
946
+ z-index: 5;
947
+ }
948
+
949
+ .search-result-history-item:hover .search-result-delete-btn {
950
+ opacity: 1;
951
+ color: ${darkTheme ? '#ff8a8a' : '#e53935'};
952
+ background: ${darkTheme ? 'rgba(255, 107, 107, 0.2)' : 'rgba(211, 47, 47, 0.15)'};
953
+ }
954
+
955
+ .search-result-delete-btn:hover {
956
+ background: ${darkTheme ? 'rgba(255, 107, 107, 0.35)' : 'rgba(211, 47, 47, 0.25)'};
957
+ transform: scale(1.1);
958
+ }
959
+
960
+ .search-result-delete-btn:active {
961
+ transform: scale(0.95);
962
+ }
963
+
964
+ /* Animation for removal */
965
+ .search-result-history-item.search-result-removing {
966
+ opacity: 0;
967
+ transform: translateX(20px);
968
+ transition: all 0.2s ease;
969
+ }
764
970
  `;
765
971
  },
766
972
 
@@ -19,7 +19,7 @@ const SignUp = {
19
19
  {
20
20
  model: 'username',
21
21
  id: `sign-up-username`,
22
- rules: [{ type: 'isEmpty' }, { type: 'isLength', options: { min: 2, max: 20 } }],
22
+ rules: [{ type: 'isEmpty' }, { type: 'isLength', options: { min: 2, max: 20 } }, { type: 'isValidUsername' }],
23
23
  },
24
24
  { model: 'email', id: `sign-up-email`, rules: [{ type: 'isEmpty' }, { type: 'isEmail' }] },
25
25
  {
@@ -43,23 +43,42 @@ const SignUp = {
43
43
  if ('model' in inputData) body[inputData.model] = s(`.${inputData.id}`).value;
44
44
  }
45
45
  const result = await UserService.post({ body });
46
+ const handleSignUpError = (data) => {
47
+ let error = '';
48
+ if (data.message) {
49
+ if (data.message.match('duplicate')) {
50
+ if (data.message.match('username')) error += Translate.Render('error-username-taken');
51
+ if (data.message.match('email')) error += Translate.Render('error-email-taken');
52
+ } else {
53
+ if (data.message.match('username')) error += Translate.Render('error-username-invalid');
54
+ if (data.message.match('email')) error += Translate.Render('error-email-invalid');
55
+ if (data.message.match('password')) error += Translate.Render('error-password-invalid');
56
+ }
57
+ return error;
58
+ }
59
+ return Translate.Render('error-register-user');
60
+ };
46
61
  NotificationManager.Push({
47
62
  html:
48
63
  typeof result.data === 'string'
49
64
  ? result.data
50
65
  : result.status === 'success'
51
- ? Translate.Render(`success-register-user`)
52
- : Translate.Render(`no-valid-register`),
66
+ ? Translate.Render(`success-register-user`)
67
+ : handleSignUpError(result),
53
68
  status: result.status,
54
69
  });
55
70
  if (result.status === 'success') {
56
71
  await Auth.sessionIn(result);
57
- s(`.btn-close-${options.idModal}`).click();
72
+ setTimeout(() => {
73
+ if (s(`.btn-close-${options.idModal}`)) s(`.btn-close-${options.idModal}`).click();
74
+ });
58
75
  }
59
76
  });
60
- s(`.btn-sign-up-i-have-account`).onclick = () => {
61
- s(`.main-btn-log-in`).click();
62
- };
77
+ setTimeout(() => {
78
+ s(`.btn-sign-up-i-have-account`).onclick = () => {
79
+ s(`.main-btn-log-in`).click();
80
+ };
81
+ });
63
82
  });
64
83
  return html`
65
84
  ${await BtnIcon.Render({
@@ -21,9 +21,12 @@ const SocketIo = {
21
21
  },
22
22
  Init: async function (options) {
23
23
  if (this.socket) this.socket.disconnect();
24
- this.host = options.host ?? getWsBaseUrl({ wsBasePath: '' });
25
- logger.info(`ws host:`, this.host);
26
- const path = typeof options.path === 'string' ? options.path : getWsBasePath();
24
+ const path = getWsBasePath();
25
+ this.host = options.host ? options.host : getWsBaseUrl({ wsBasePath: '' });
26
+ logger.info(`ws host:`, {
27
+ host: this.host,
28
+ path,
29
+ });
27
30
  const connectOptions = {
28
31
  path: path === '/' ? undefined : path,
29
32
  // auth: {