@retalia/sidebar-navigation 21.1.13 → 21.1.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.
Files changed (93) hide show
  1. package/README.md +42 -42
  2. package/karma.conf.js +44 -0
  3. package/ng-package.json +8 -0
  4. package/package.json +14 -26
  5. package/{lib → src}/assets/fonts/Effra_Std_It.svg +5123 -5123
  6. package/{lib → src}/assets/fonts/Effra_Std_Md.svg +4128 -4128
  7. package/{lib → src}/assets/fonts/Effra_Std_Rg.svg +4690 -4690
  8. package/{lib → src}/assets/icons/sidebar-burger.svg +3 -3
  9. package/{lib → src}/assets/icons/sidebar-cross.svg +3 -3
  10. package/{lib → src}/assets/icons/subItem-arrow.svg +3 -3
  11. package/{lib → src}/assets/icons/topbar-info.svg +3 -3
  12. package/{lib → src}/assets/icons/topbar-logout.svg +4 -4
  13. package/{lib → src}/assets/icons/topbar-menu-burger.svg +5 -5
  14. package/{lib → src}/assets/icons/topbar-menu-close.svg +3 -3
  15. package/{lib → src}/assets/icons/topbar-notification.svg +3 -3
  16. package/{lib → src}/assets/icons/topbar-profile.svg +4 -4
  17. package/{lib → src}/assets/icons/topbar-settings.svg +3 -3
  18. package/{lib → src}/assets/icons/topmenu-arrow.svg +3 -3
  19. package/{lib → src}/assets/logo/_K3_imagine_White.svg +14 -14
  20. package/src/lib/data/support-routes.ts +56 -0
  21. package/src/lib/data/test-data.ts +628 -0
  22. package/src/lib/data/test-data2.ts +1742 -0
  23. package/src/lib/models/cookie-names.ts +12 -0
  24. package/src/lib/models/environment.ts +4 -0
  25. package/src/lib/models/http-response.ts +3 -0
  26. package/src/lib/models/module.ts +39 -0
  27. package/src/lib/navigation-lib.component.html +20 -0
  28. package/src/lib/navigation-lib.component.scss +30 -0
  29. package/src/lib/navigation-lib.component.ts +26 -0
  30. package/src/lib/navigation-lib.module.ts +26 -0
  31. package/src/lib/navigation-lib.service.ts +9 -0
  32. package/src/lib/services/auth.service.ts +39 -0
  33. package/src/lib/services/cookie.service.ts +33 -0
  34. package/src/lib/services/http.service.ts +26 -0
  35. package/src/lib/services/module.service.ts +153 -0
  36. package/src/lib/services/sub-item.service.ts +56 -0
  37. package/src/lib/sidebar/shop-modal/shop-modal.component.html +17 -0
  38. package/src/lib/sidebar/shop-modal/shop-modal.component.scss +73 -0
  39. package/src/lib/sidebar/shop-modal/shop-modal.component.ts +36 -0
  40. package/src/lib/sidebar/sidebar.component.html +86 -0
  41. package/src/lib/sidebar/sidebar.component.scss +260 -0
  42. package/src/lib/sidebar/sidebar.component.ts +267 -0
  43. package/src/lib/sidebar/sub-item/sub-item.component.html +18 -0
  44. package/src/lib/sidebar/sub-item/sub-item.component.scss +154 -0
  45. package/src/lib/sidebar/sub-item/sub-item.component.ts +61 -0
  46. package/src/lib/topbar/topbar.component.html +29 -0
  47. package/src/lib/topbar/topbar.component.scss +176 -0
  48. package/src/lib/topbar/topbar.component.ts +76 -0
  49. package/src/public-api.ts +14 -0
  50. package/src/styles/app.scss +53 -0
  51. package/src/test.ts +15 -0
  52. package/tsconfig.lib.json +21 -0
  53. package/tsconfig.lib.prod.json +11 -0
  54. package/tsconfig.spec.json +17 -0
  55. package/fesm2022/retalia-sidebar-navigation.mjs +0 -1388
  56. package/fesm2022/retalia-sidebar-navigation.mjs.map +0 -1
  57. package/production-0.0.2.tgz +0 -0
  58. package/types/retalia-sidebar-navigation.d.ts +0 -232
  59. /package/{lib → src}/assets/fonts/Effra_Std_It.eot +0 -0
  60. /package/{lib → src}/assets/fonts/Effra_Std_It.ttf +0 -0
  61. /package/{lib → src}/assets/fonts/Effra_Std_It.woff +0 -0
  62. /package/{lib → src}/assets/fonts/Effra_Std_It.woff2 +0 -0
  63. /package/{lib → src}/assets/fonts/Effra_Std_Md.eot +0 -0
  64. /package/{lib → src}/assets/fonts/Effra_Std_Md.ttf +0 -0
  65. /package/{lib → src}/assets/fonts/Effra_Std_Md.woff +0 -0
  66. /package/{lib → src}/assets/fonts/Effra_Std_Md.woff2 +0 -0
  67. /package/{lib → src}/assets/fonts/Effra_Std_Rg.eot +0 -0
  68. /package/{lib → src}/assets/fonts/Effra_Std_Rg.ttf +0 -0
  69. /package/{lib → src}/assets/fonts/Effra_Std_Rg.woff +0 -0
  70. /package/{lib → src}/assets/fonts/Effra_Std_Rg.woff2 +0 -0
  71. /package/{lib → src}/assets/fonts/Poppins-Black.ttf +0 -0
  72. /package/{lib → src}/assets/fonts/Poppins-BlackItalic.ttf +0 -0
  73. /package/{lib → src}/assets/fonts/Poppins-Bold.ttf +0 -0
  74. /package/{lib → src}/assets/fonts/Poppins-BoldItalic.ttf +0 -0
  75. /package/{lib → src}/assets/fonts/Poppins-ExtraBold.ttf +0 -0
  76. /package/{lib → src}/assets/fonts/Poppins-ExtraBoldItalic.ttf +0 -0
  77. /package/{lib → src}/assets/fonts/Poppins-ExtraLight.ttf +0 -0
  78. /package/{lib → src}/assets/fonts/Poppins-ExtraLightItalic.ttf +0 -0
  79. /package/{lib → src}/assets/fonts/Poppins-Italic.ttf +0 -0
  80. /package/{lib → src}/assets/fonts/Poppins-Light.ttf +0 -0
  81. /package/{lib → src}/assets/fonts/Poppins-LightItalic.ttf +0 -0
  82. /package/{lib → src}/assets/fonts/Poppins-Medium.ttf +0 -0
  83. /package/{lib → src}/assets/fonts/Poppins-MediumItalic.ttf +0 -0
  84. /package/{lib → src}/assets/fonts/Poppins-Regular.ttf +0 -0
  85. /package/{lib → src}/assets/fonts/Poppins-SemiBold.ttf +0 -0
  86. /package/{lib → src}/assets/fonts/Poppins-SemiBoldItalic.ttf +0 -0
  87. /package/{lib → src}/assets/fonts/Poppins-Thin.ttf +0 -0
  88. /package/{lib → src}/assets/fonts/Poppins-ThinItalic.ttf +0 -0
  89. /package/{lib → src}/assets/icons/topbar-avatar.png +0 -0
  90. /package/{lib → src}/assets/logo/full-white.png +0 -0
  91. /package/{lib → src}/assets/logo/full.png +0 -0
  92. /package/{lib → src}/assets/logo/mobile-white.png +0 -0
  93. /package/{lib → src}/assets/logo/mobile.png +0 -0
@@ -0,0 +1,86 @@
1
+ <div id="sidebar-panel">
2
+ <div class="panel-item" (click)="toggleSidebar($event, 'isModuleOpen')" data-testid="nav-menu">
3
+ @if (!panelButtonStates.isModuleOpen) {
4
+ <svg class="panel-icon" height="14" width="21" viewBox="0 0 18 12"
5
+ fill="#3e3e3e" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet">
6
+ <path d="M0 12H18V10H0V12ZM0 7H18V5H0V7ZM0 0V2H18V0H0Z" />
7
+ </svg>
8
+ }
9
+ @if (panelButtonStates.isModuleOpen) {
10
+ <svg class="panel-icon" height="13" width="13" viewBox="0 0 14 14"
11
+ fill="#3e3e3e" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet">
12
+ <path
13
+ d="M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z" />
14
+ </svg>
15
+ }
16
+ </div>
17
+ <div class="panel-item" (click)="toggleSidebar($event, 'isSupportOpen')" data-testid="nav-support">
18
+ @if (!panelButtonStates.isSupportOpen) {
19
+ <svg class="panel-icon" width="18" height="21" viewBox="0 0 18 21"
20
+ fill="#3e3e3e" xmlns="http://www.w3.org/2000/svg">
21
+ <path
22
+ d="M16 0H2C0.89 0 0 0.9 0 2V16C0 17.1 0.89 18 2 18H6L9 21L12 18H16C17.1 18 18 17.1 18 16V2C18 0.9 17.1 0 16 0ZM10 16H8V14H10V16ZM12.07 8.25L11.17 9.17C10.45 9.9 10 10.5 10 12H8V11.5C8 10.4 8.45 9.4 9.17 8.67L10.41 7.41C10.78 7.05 11 6.55 11 6C11 4.9 10.1 4 9 4C7.9 4 7 4.9 7 6H5C5 3.79 6.79 2 9 2C11.21 2 13 3.79 13 6C13 6.88 12.64 7.68 12.07 8.25Z" />
23
+ </svg>
24
+ }
25
+ @if (panelButtonStates.isSupportOpen) {
26
+ <svg class="panel-icon" height="13" width="13" viewBox="0 0 14 14"
27
+ fill="#3e3e3e" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet">
28
+ <path
29
+ d="M14 1.41L12.59 0L7 5.59L1.41 0L0 1.41L5.59 7L0 12.59L1.41 14L7 8.41L12.59 14L14 12.59L8.41 7L14 1.41Z" />
30
+ </svg>
31
+ }
32
+ </div>
33
+
34
+ @if (!isSidebarOpen) {
35
+ <div class="collapsed-module-icons">
36
+ @for (module of modules; track module.authorizationId) {
37
+ @if (module.icon) {
38
+ <div class="collapsed-module-icon" [attr.title]="module.translatedName || module.name">
39
+ <img [src]="module.icon" [alt]="module.translatedName || module.name" />
40
+ </div>
41
+ }
42
+ }
43
+ </div>
44
+ }
45
+
46
+
47
+ <div class="status-button" (mouseover)="hideStatus = false" (mouseleave)="hideStatus = true"
48
+ (click)="goToStatusPage()" data-testid="sidebar-status">
49
+ <svg class="panel-icon" width="22" height="22" viewBox="0 0 22 22" fill="#FFFFFF"
50
+ xmlns="http://www.w3.org/2000/svg">
51
+ <path
52
+ d="M20.625 6.5H1.375C0.615613 6.5 0 5.82842 0 5V2C0 1.17158 0.615613 0.5 1.375 0.5H20.625C21.3844 0.5 22 1.17158 22 2V5C22 5.82842 21.3844 6.5 20.625 6.5ZM18.5625 2.375C17.9929 2.375 17.5312 2.87867 17.5312 3.5C17.5312 4.12133 17.9929 4.625 18.5625 4.625C19.1321 4.625 19.5938 4.12133 19.5938 3.5C19.5938 2.87867 19.1321 2.375 18.5625 2.375ZM15.8125 2.375C15.2429 2.375 14.7812 2.87867 14.7812 3.5C14.7812 4.12133 15.2429 4.625 15.8125 4.625C16.3821 4.625 16.8438 4.12133 16.8438 3.5C16.8438 2.87867 16.3821 2.375 15.8125 2.375ZM20.625 14H1.375C0.615613 14 0 13.3284 0 12.5V9.5C0 8.67158 0.615613 8 1.375 8H20.625C21.3844 8 22 8.67158 22 9.5V12.5C22 13.3284 21.3844 14 20.625 14ZM18.5625 9.875C17.9929 9.875 17.5312 10.3787 17.5312 11C17.5312 11.6213 17.9929 12.125 18.5625 12.125C19.1321 12.125 19.5938 11.6213 19.5938 11C19.5938 10.3787 19.1321 9.875 18.5625 9.875ZM15.8125 9.875C15.2429 9.875 14.7812 10.3787 14.7812 11C14.7812 11.6213 15.2429 12.125 15.8125 12.125C16.3821 12.125 16.8438 11.6213 16.8438 11C16.8438 10.3787 16.3821 9.875 15.8125 9.875ZM20.625 21.5H1.375C0.615613 21.5 0 20.8284 0 20V17C0 16.1716 0.615613 15.5 1.375 15.5H20.625C21.3844 15.5 22 16.1716 22 17V20C22 20.8284 21.3844 21.5 20.625 21.5ZM18.5625 17.375C17.9929 17.375 17.5312 17.8787 17.5312 18.5C17.5312 19.1213 17.9929 19.625 18.5625 19.625C19.1321 19.625 19.5938 19.1213 19.5938 18.5C19.5938 17.8787 19.1321 17.375 18.5625 17.375ZM15.8125 17.375C15.2429 17.375 14.7812 17.8787 14.7812 18.5C14.7812 19.1213 15.2429 19.625 15.8125 19.625C16.3821 19.625 16.8438 19.1213 16.8438 18.5C16.8438 17.8787 16.3821 17.375 15.8125 17.375Z" />
53
+ </svg>
54
+ <iframe id="status-frame" [ngClass]="{'isOpen': !hideStatus, 'isClosed': hideStatus }"
55
+ src="//status.retalia.cloud/embed-status/light-sm" name="statusPage" width="230" height="61" frameBorder="0"
56
+ scrolling="no">
57
+ </iframe>
58
+ </div>
59
+ </div>
60
+
61
+ <div id="sidebar-container" [class.open-sidebar]="isSidebarOpen">
62
+ <div id="sticky-header">
63
+ <img class="logo-full" height="40px" src="https://imagineshared.blob.core.windows.net/portal/retalia_files/logos/sidebar_logo.png" (click)="goToPortal()"
64
+ data-testid="nav-logo" />
65
+ <div class="module-divider"></div>
66
+ @if (panelButtonStates.isModuleOpen) {
67
+ <div class="moduleSearchbar">
68
+ <input class="moduleSearchInput" type="search" autocomplete="off" id="search" [value]="moduleSearchValue"
69
+ required="false" [placeholder]="moduleSearchPlaceholder" (keyup)="handleModuleSearch($event)"
70
+ (click)="handleModuleSearch($event)" />
71
+ </div>
72
+ }
73
+ </div>
74
+ @if (panelButtonStates.isModuleOpen) {
75
+ <div id="menu">
76
+ <sub-item [items]="modules"></sub-item>
77
+ </div>
78
+ }
79
+ @if (panelButtonStates.isSupportOpen) {
80
+ <div id="menu">
81
+ <sub-item [items]="supportRoutes"></sub-item>
82
+ </div>
83
+ }
84
+ </div>
85
+
86
+ <shop-modal [availableShops]="availableShops" (selectedShop)="selectedShop($event)"></shop-modal>
@@ -0,0 +1,260 @@
1
+ @import "../../styles/app.scss";
2
+
3
+
4
+
5
+ #sidebar-panel {
6
+ z-index: 1;
7
+ position: fixed;
8
+ float: left;
9
+ width: 60px;
10
+ height: 100vh;
11
+ color: black;
12
+ text-align: center;
13
+ transition: 0.2s linear;
14
+ background-color: rgba(#ffffff, 1);
15
+ -webkit-box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.2);
16
+ -moz-box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.2);
17
+ box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.2);
18
+ @include responsive(mobileonly) {
19
+ height: 100%;
20
+ }
21
+ }
22
+
23
+ .panel-item {
24
+ cursor: pointer;
25
+ position: relative;
26
+ height: 40px;
27
+ margin: 10px;
28
+ transition: 0.2s linear;
29
+ border-radius: 50%;
30
+
31
+ &:hover {
32
+ background-color: imagine-color(nav-inactive-text-color);
33
+
34
+ .panel-icon {
35
+ fill: white;
36
+ }
37
+ }
38
+
39
+ &:active,
40
+ &:focus {
41
+ background: rgba(imagine-color(nav-active-text-color), 0.2);
42
+
43
+ .panel-icon {
44
+ fill: imagine-color(nav-active-text-color);
45
+ }
46
+ }
47
+
48
+ .panel-icon {
49
+ margin: 0;
50
+ position: absolute;
51
+ top: 50%;
52
+ left: 50%;
53
+ -ms-transform: translate(-50%, -50%);
54
+ transform: translate(-50%, -50%);
55
+ }
56
+ }
57
+
58
+ .collapsed-module-icons {
59
+ margin-top: 8px;
60
+ max-height: calc(100vh - 170px);
61
+ overflow-y: auto;
62
+ overflow-x: hidden;
63
+ }
64
+
65
+ .collapsed-module-icon {
66
+ width: 40px;
67
+ height: 40px;
68
+ margin: 10px;
69
+ display: flex;
70
+ align-items: center;
71
+ justify-content: center;
72
+ border-radius: 50%;
73
+
74
+ img {
75
+ width: 20px;
76
+ height: 20px;
77
+ object-fit: contain;
78
+ }
79
+ }
80
+
81
+ #sidebar-container {
82
+ position: sticky;
83
+ top: 0; /* Sticky requires top/left */
84
+ height: 100vh; /* Set height by vh not % to fill viewport, handles sticky better */
85
+ overflow: hidden;
86
+ font-size: 12px;
87
+ font-family: "Chakra Petch", Arial, sans-serif;
88
+ width: 0px;
89
+ background: rgba(#ffffff, 1);
90
+ padding-left: 60px;
91
+ transition: 0.3s;
92
+ transition-timing-function: cubic-bezier(0.11, 0.95, 0.91, 0.93);
93
+ -ms-overflow-style: none; /* IE and Edge */
94
+ scrollbar-width: none; /* Firefox */
95
+
96
+ -webkit-box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.2);
97
+ -moz-box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.2);
98
+ box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.2);
99
+ @include responsive(mobileonly) {
100
+ top: auto;
101
+ position: relative;
102
+ border-right: 0px;
103
+ }
104
+ }
105
+
106
+ .open-sidebar {
107
+ width: 260px !important;
108
+ @include responsive(mobileonly) {
109
+ width: 160px !important;
110
+ }
111
+ }
112
+
113
+ @media print {
114
+ #container {
115
+ display: none;
116
+ }
117
+ }
118
+
119
+ .module-divider {
120
+ width: 240px;
121
+ margin: 10px;
122
+ border-top: 0.2px solid rgba(grey, 0.2);
123
+ -webkit-background-clip: padding-box; /* for Safari */
124
+ background-clip: padding-box; /* for IE9+, Firefox 4+, Opera, Chrome */
125
+ @include responsive(mobileonly) {
126
+ width: 240px !important;
127
+ }
128
+ }
129
+
130
+ div#menu {
131
+ display: flex;
132
+ flex-flow: column;
133
+ background-color: transparent;
134
+ z-index: 10;
135
+ direction: ltr;
136
+ float: left;
137
+ width: 258px;
138
+ height: 85%;
139
+ overflow-y: overlay;
140
+ overflow-x: hidden;
141
+ transition: 0.2s linear;
142
+ @include responsive(mobileonly) {
143
+ width: 160px !important;
144
+ }
145
+ }
146
+
147
+ .moduleSearchbar {
148
+ z-index: 20;
149
+ position: inherit;
150
+ cursor: pointer;
151
+ width: 240px;
152
+ height: 28px;
153
+ border-radius: 4px;
154
+ margin: 10px;
155
+ background: rgba(#e2e2e2, 1);
156
+ @include responsive(mobileonly) {
157
+ width: 140px !important;
158
+ height: 40px;
159
+ }
160
+ .moduleSearchInput {
161
+ direction: ltr;
162
+ width: inherit;
163
+ height: inherit;
164
+ padding-left: 10px;
165
+ color: imagine-color(nav-active-text-color);
166
+ font-family: "Chakra Petch", Arial, sans-serif;
167
+
168
+ background-color: transparent;
169
+ border-style: none;
170
+ outline: none;
171
+ appearance: none;
172
+ -webkit-appearance: none; /* safari */
173
+ }
174
+
175
+ input:invalid {
176
+ box-shadow: none; /* prevent Mozilla Firefox from creating a red border */
177
+ }
178
+
179
+ ::placeholder {
180
+ color: imagine-color(nav-inactive-text-color);
181
+ /* text-transform: uppercase; */
182
+ font-size: 11px;
183
+ opacity: 1;
184
+ }
185
+ }
186
+
187
+ #sticky-header {
188
+ margin-top: 10px;
189
+ width: 100%;
190
+ text-align: center;
191
+ position: sticky;
192
+ transition: 1s ease;
193
+
194
+ .logo-full {
195
+ cursor: pointer;
196
+ padding: 5px 21px 0px 21px;
197
+ }
198
+ }
199
+
200
+ .status-button {
201
+ cursor: pointer;
202
+ position: inherit;
203
+ bottom: 0;
204
+ margin-left: 8px;
205
+ margin-bottom: 15px;
206
+ width: 40px;
207
+ height: 32px;
208
+ padding-top: 8px;
209
+ border-radius: 50%;
210
+ transition: 0.2s linear;
211
+ .panel-icon {
212
+ fill: #0c1830;
213
+ }
214
+
215
+ &:hover {
216
+ background-color: imagine-color(nav-active-text-color);
217
+
218
+ .panel-icon {
219
+ fill: #ffffff;
220
+ }
221
+ }
222
+ }
223
+
224
+ #status-frame {
225
+ position: inherit;
226
+ bottom: -8px;
227
+ padding-left: 25px;
228
+ transition: 0.2s linear;
229
+ }
230
+
231
+ .isOpen {
232
+ opacity: 1;
233
+ visibility: visible;
234
+ }
235
+ .isClosed {
236
+ opacity: 0;
237
+ visibility: hidden;
238
+ }
239
+
240
+ ::-webkit-scrollbar {
241
+ width: 5px; /* remove scrollbar space */
242
+ background: transparent;
243
+ }
244
+
245
+ ::-webkit-scrollbar-thumb {
246
+ background-color: rgba(imagine-color(nav-active-text-color), 0.3);
247
+ border-radius: 10px;
248
+ transition: 0.2s linear;
249
+
250
+ &:hover {
251
+ background-color: rgba(imagine-color(nav-active-text-color), 0.9);
252
+ }
253
+ }
254
+
255
+ input[type="search"]::-webkit-search-decoration,
256
+ input[type="search"]::-webkit-search-cancel-button,
257
+ input[type="search"]::-webkit-search-results-button,
258
+ input[type="search"]::-webkit-search-results-decoration {
259
+ -webkit-appearance: none;
260
+ }
@@ -0,0 +1,267 @@
1
+ import {
2
+ ChangeDetectorRef,
3
+ Component,
4
+ EventEmitter,
5
+ Input,
6
+ OnChanges,
7
+ OnDestroy,
8
+ Output,
9
+ SimpleChanges,
10
+ } from '@angular/core';
11
+ import { ModuleItem, Shop, SidebarEntry } from '../models/module';
12
+ import { SubItemService } from '../services/sub-item.service';
13
+ import { NgxSmartModalService } from 'ngx-smart-modal';
14
+ import { ModuleService } from '../services/module.service';
15
+ import { Environment } from '../models/environment';
16
+ import { supportRoutes } from '../data/support-routes';
17
+ import { CookieNames } from '../models/cookie-names';
18
+ import { CookieService } from '../services/cookie.service';
19
+
20
+ @Component({
21
+ selector: 'sidebar',
22
+ templateUrl: './sidebar.component.html',
23
+ styleUrls: ['./sidebar.component.scss'],
24
+ standalone: false
25
+ })
26
+ export class SidebarComponent implements OnChanges, OnDestroy {
27
+ private moduleRetryInterval: any;
28
+ private moduleFetchInProgress = false;
29
+
30
+ ngOnInit(): void {
31
+ this.tryFetchModulesUntilReady();
32
+ }
33
+ @Output() selectedRoute: EventEmitter<SidebarEntry> = new EventEmitter();
34
+ @Input() environment: Environment;
35
+ @Input() moduleSearchValue: any = '';
36
+ modules: ModuleItem[] = [];
37
+ modulesCopy: ModuleItem[] = [];
38
+ supportRoutes: ModuleItem[] = supportRoutes;
39
+ selectedModule: ModuleItem;
40
+ selectedItem;
41
+ availableShops: Shop[] = [];
42
+ availableShopsCopy: Shop[] = [];
43
+ moduleSearchPlaceholder = 'Search Modules ...';
44
+ isSidebarOpen: boolean = this.getSidebarState();
45
+ hideStatus: boolean = true;
46
+ panelButtonStates = {
47
+ isModuleOpen: this.isSidebarOpen,
48
+ isSupportOpen: false,
49
+ };
50
+
51
+ constructor(
52
+ private cdr: ChangeDetectorRef,
53
+ private moduleService: ModuleService,
54
+ private subItemService: SubItemService,
55
+ private cookieService: CookieService,
56
+ public ngxSmartModalService: NgxSmartModalService
57
+ ) {
58
+ this.subItemService.subItemClickEvent.subscribe((item) => {
59
+ this.subItemClicked(item);
60
+ });
61
+ }
62
+
63
+ ngOnChanges(changes: SimpleChanges): void {
64
+ if (changes['environment'] && changes['environment'].currentValue.apiRequestUrl) {
65
+ this.panelButtonStates.isModuleOpen = this.isSidebarOpen;
66
+ this.tryFetchModulesUntilReady();
67
+ }
68
+ }
69
+
70
+ ngOnDestroy(): void {
71
+ if (this.moduleRetryInterval) {
72
+ clearInterval(this.moduleRetryInterval);
73
+ }
74
+ }
75
+
76
+ private tryFetchModulesUntilReady(): void {
77
+ if (this.moduleRetryInterval) {
78
+ clearInterval(this.moduleRetryInterval);
79
+ }
80
+ const tryFetch = () => {
81
+ if (
82
+ this.environment &&
83
+ this.environment.apiRequestUrl &&
84
+ this.modules.length === 0 &&
85
+ !this.moduleFetchInProgress
86
+ ) {
87
+ this.getModules(true);
88
+ }
89
+ };
90
+ tryFetch();
91
+ this.moduleRetryInterval = setInterval(tryFetch, 500);
92
+ }
93
+
94
+ public toggleSidebar(e: Event, option: string): void {
95
+ e.stopPropagation();
96
+ // Allows us to expand the panel items in future without needing to change the toggle logic
97
+ for (let item in Object(this.panelButtonStates)) {
98
+ item === option
99
+ ? (this.panelButtonStates[item] = !this.panelButtonStates[item])
100
+ : (this.panelButtonStates[item] = false);
101
+ }
102
+ this.isSidebarOpen = this.panelButtonStates[option];
103
+ this.setSidebarState();
104
+
105
+ if (this.modules.length === 0) {
106
+ this.tryFetchModulesUntilReady();
107
+ }
108
+ }
109
+
110
+ public setSidebarState(): void {
111
+ this.cookieService.setCookie(CookieNames.isSidebarOpen, this.isSidebarOpen);
112
+ }
113
+
114
+ public getSidebarState(): boolean {
115
+ const cookieValue = this.cookieService.getCookie(
116
+ CookieNames.isSidebarOpen
117
+ );
118
+ //If the cookie doesn't exist, default to "open" so modules load immediately.
119
+ if (typeof cookieValue === 'undefined' || cookieValue === null) return true;
120
+ return cookieValue === 'true';
121
+ }
122
+
123
+ public goToPortal(): void {
124
+ window.location.href = this.environment.portalUrl;
125
+ }
126
+
127
+ public handleModuleSearch(e): void {
128
+ if (this.modulesCopy.length < 1) {
129
+ this.modulesCopy = [...this.modules];
130
+ }
131
+
132
+ this.moduleSearchValue = e && e.target ? e.target.value : '';
133
+ const searchString = this.moduleSearchValue.toLowerCase();
134
+
135
+ const moduleCopy = JSON.parse(JSON.stringify(this.modulesCopy));
136
+ this.modules = this.filterModules(moduleCopy, searchString);
137
+ }
138
+
139
+ public filterModules(
140
+ moduleItems: ModuleItem[] | SidebarEntry[],
141
+ searchTerm: string
142
+ ): any[] {
143
+ const matches = [];
144
+ if (!Array.isArray(this.modules)) return matches;
145
+
146
+ moduleItems.forEach((i) => {
147
+ if (i.name.toLowerCase().includes(searchTerm)) {
148
+ i.visible = true;
149
+ matches.push(i);
150
+ } else {
151
+ const childResults: SidebarEntry[] = this.filterModules(
152
+ i.children,
153
+ searchTerm
154
+ );
155
+ if (childResults.length) {
156
+ matches.push(Object.assign({}, i, { children: childResults }));
157
+ }
158
+ }
159
+ });
160
+
161
+ return matches;
162
+ }
163
+
164
+ public subItemClicked(item): void {
165
+ this.selectedModule = this.modules.find(
166
+ (m) =>
167
+ m.authorizationId ===
168
+ (item.authorizationId || item.moduleAuthorizationId)
169
+ );
170
+
171
+ if (this.selectedItem) this.selectedItem.active = false;
172
+ this.selectedItem = item;
173
+ this.selectedItem.active = true;
174
+
175
+ if (this.selectedItem.relationName === 'Documentation') {
176
+ window.open(this.selectedItem.url, '_blank', 'noopener noreferrer');
177
+ } else if (
178
+ (this.selectedModule && !this.selectedModule.entityAccess) ||
179
+ !this.selectedModule.entityAccess.length
180
+ ) {
181
+ this.routeToUrl();
182
+ } else if (
183
+ this.selectedModule &&
184
+ this.selectedModule.entityAccess.length === 1
185
+ ) {
186
+ const soleShop = this.selectedModule.entityAccess[0];
187
+ this.subItemService.saveItemInfo(this.selectedModule, soleShop);
188
+ this.routeToUrl();
189
+ } else {
190
+ this.mapShopModal();
191
+ this.ngxSmartModalService.getModal('shopModal').open();
192
+ }
193
+ }
194
+
195
+ public selectedShop(selectedShop: Shop): void {
196
+ this.subItemService.saveItemInfo(this.selectedModule, selectedShop.id);
197
+ this.routeToUrl();
198
+ }
199
+
200
+ public mapShopModal(): void {
201
+ if (this.availableShopsCopy.length < 1) {
202
+ this.availableShopsCopy = [...this.availableShops];
203
+ }
204
+ this.availableShops = this.availableShopsCopy.filter((shop) =>
205
+ this.selectedModule.entityAccess.includes(shop.id)
206
+ );
207
+ }
208
+
209
+ public goToStatusPage(): void {
210
+ window.open(
211
+ 'https://status.retalia.cloud',
212
+ '_blank',
213
+ 'noopener noreferrer'
214
+ );
215
+ }
216
+
217
+ public routeToUrl(): void {
218
+ let currentModule = this.moduleService.getCurrentModule(this.selectedItem);
219
+ if (
220
+ currentModule &&
221
+ this.selectedItem &&
222
+ (currentModule.authorizationId === this.selectedItem.authorizationId ||
223
+ currentModule.authorizationId === this.selectedItem.moduleAuthorizationId)
224
+ ) {
225
+ let currentItem = JSON.parse(JSON.stringify(this.selectedItem));
226
+ const pathArray = this.selectedItem.url.split('/');
227
+ pathArray.splice(0, 2);
228
+ currentItem.url = pathArray.join('/');
229
+ this.selectedRoute.emit(currentItem);
230
+ } else {
231
+ const itemUrl = this.selectedItem.url;
232
+ window.location.href = itemUrl;
233
+ }
234
+ }
235
+
236
+ private getModules(isFirstCall: boolean): void {
237
+ this.moduleFetchInProgress = true;
238
+ let apiUrl = this.environment.apiRequestUrl;
239
+ if (apiUrl.endsWith("/"))
240
+ apiUrl = apiUrl.slice(0, -1);
241
+
242
+ this.moduleService.getUserModules(apiUrl, '').then((moduleObject) => {
243
+ if (!moduleObject || !moduleObject.modulesDetails) {
244
+ if (!isFirstCall) {
245
+ console.log(`No modules were found using: ${apiUrl}`);
246
+ }
247
+ this.moduleFetchInProgress = false;
248
+ return;
249
+ }
250
+
251
+ this.availableShops = moduleObject.shops;
252
+ this.modules = this.moduleService.mapModuleItems(
253
+ moduleObject.modulesDetails
254
+ );
255
+ this.selectedItem = this.moduleService.findNestedUrlMatch(this.modules,window.location.pathname);
256
+ this.panelButtonStates.isModuleOpen = this.isSidebarOpen;
257
+ this.moduleFetchInProgress = false;
258
+ if (this.moduleRetryInterval) {
259
+ clearInterval(this.moduleRetryInterval);
260
+ this.moduleRetryInterval = null;
261
+ }
262
+ this.cdr.detectChanges();
263
+ }).catch(() => {
264
+ this.moduleFetchInProgress = false;
265
+ });
266
+ }
267
+ }
@@ -0,0 +1,18 @@
1
+ @for (item of items; track item; let i = $index) {
2
+ <div
3
+ [attr.data-testid]="'nav-' + (item.authorizationId || item.name)"
4
+ [class]="this.getClass(item, items)" (click)="subItemClicked($event, item)">
5
+ @if (item.visible) {
6
+ <div class="item-label">
7
+ @if (item.icon && item.authorizationId) {
8
+ <img [src]="item.icon" class="item-icon" alt="" />
9
+ }
10
+ {{ item.translatedName || item.name }}
11
+ </div>
12
+ }
13
+ @if (item.children) {
14
+ <sub-item [items]="item.children">
15
+ </sub-item>
16
+ }
17
+ </div>
18
+ }