@fleetbase/ember-core 0.0.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 (224) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +32 -0
  3. package/addon/adapters/application.js +155 -0
  4. package/addon/adapters/user.js +19 -0
  5. package/addon/authenticators/fleetbase.js +72 -0
  6. package/addon/decorators/fetch-from.js +55 -0
  7. package/addon/decorators/from-store.js +56 -0
  8. package/addon/decorators/is-equal.js +15 -0
  9. package/addon/exports/host-services.js +18 -0
  10. package/addon/exports/services.js +18 -0
  11. package/addon/initializers/local-storage-adapter.js +21 -0
  12. package/addon/serializers/application.js +87 -0
  13. package/addon/services/app-cache.js +59 -0
  14. package/addon/services/crud.js +226 -0
  15. package/addon/services/current-user.js +259 -0
  16. package/addon/services/fetch.js +648 -0
  17. package/addon/services/filters.js +183 -0
  18. package/addon/services/loader.js +136 -0
  19. package/addon/services/notifications.js +32 -0
  20. package/addon/services/session.js +221 -0
  21. package/addon/services/theme.js +200 -0
  22. package/addon/services/url-search-params.js +92 -0
  23. package/addon/transforms/array.js +29 -0
  24. package/addon/transforms/object.js +11 -0
  25. package/addon/transforms/raw.js +11 -0
  26. package/addon/utils/api-url.js +11 -0
  27. package/addon/utils/apply-column-filters.js +3 -0
  28. package/addon/utils/auto-serialize.js +100 -0
  29. package/addon/utils/calculate-percentage.js +3 -0
  30. package/addon/utils/close-sidebar.js +7 -0
  31. package/addon/utils/console-url.js +56 -0
  32. package/addon/utils/copy-to-clipboard.js +35 -0
  33. package/addon/utils/corslite.js +95 -0
  34. package/addon/utils/download.js +157 -0
  35. package/addon/utils/env.js +3 -0
  36. package/addon/utils/extract-coordinates.js +35 -0
  37. package/addon/utils/extract-latitude.js +28 -0
  38. package/addon/utils/extract-longitude.js +28 -0
  39. package/addon/utils/find-closest-waypoint.js +20 -0
  40. package/addon/utils/first.js +10 -0
  41. package/addon/utils/frontend-url.js +30 -0
  42. package/addon/utils/generate-slug.js +11 -0
  43. package/addon/utils/generate-uuid.js +3 -0
  44. package/addon/utils/get-length-units.js +44 -0
  45. package/addon/utils/get-meta-field-types.js +3 -0
  46. package/addon/utils/get-mime-type.js +25 -0
  47. package/addon/utils/get-model-name.js +45 -0
  48. package/addon/utils/get-permission-action.js +47 -0
  49. package/addon/utils/get-permission-resource.js +16 -0
  50. package/addon/utils/get-pod-methods.js +7 -0
  51. package/addon/utils/get-routing-host.js +44 -0
  52. package/addon/utils/get-service-name.js +21 -0
  53. package/addon/utils/get-user-options.js +21 -0
  54. package/addon/utils/get-weight-units.js +32 -0
  55. package/addon/utils/get-with-default.js +6 -0
  56. package/addon/utils/group-api-events.js +20 -0
  57. package/addon/utils/group-by.js +26 -0
  58. package/addon/utils/has-extension.js +11 -0
  59. package/addon/utils/has-json-structure.js +10 -0
  60. package/addon/utils/hason-structure.js +3 -0
  61. package/addon/utils/haversine.js +59 -0
  62. package/addon/utils/humanize.js +16 -0
  63. package/addon/utils/is-electron.js +18 -0
  64. package/addon/utils/is-email.js +5 -0
  65. package/addon/utils/is-function.js +3 -0
  66. package/addon/utils/is-image-file.js +3 -0
  67. package/addon/utils/is-iterable.js +7 -0
  68. package/addon/utils/is-json.js +9 -0
  69. package/addon/utils/is-latitude.js +3 -0
  70. package/addon/utils/is-letter.js +3 -0
  71. package/addon/utils/is-longitude.js +3 -0
  72. package/addon/utils/is-model.js +6 -0
  73. package/addon/utils/is-not-empty.js +5 -0
  74. package/addon/utils/is-not-model.js +5 -0
  75. package/addon/utils/is-numeric.js +3 -0
  76. package/addon/utils/is-object.js +3 -0
  77. package/addon/utils/is-proxy.js +5 -0
  78. package/addon/utils/is-relation-missing.js +21 -0
  79. package/addon/utils/is-valid-coordinates.js +38 -0
  80. package/addon/utils/is-video-file.js +3 -0
  81. package/addon/utils/is-waypoint-record.js +5 -0
  82. package/addon/utils/ison.js +3 -0
  83. package/addon/utils/isset.js +10 -0
  84. package/addon/utils/last.js +23 -0
  85. package/addon/utils/lazy-load-script.js +25 -0
  86. package/addon/utils/leaflet-icon.js +13 -0
  87. package/addon/utils/leaflet-points-from-coordinates.js +3 -0
  88. package/addon/utils/load-engines.js +37 -0
  89. package/addon/utils/load-extensions.js +8 -0
  90. package/addon/utils/macros/group-by.js +28 -0
  91. package/addon/utils/make-dataset.js +57 -0
  92. package/addon/utils/map-engines.js +30 -0
  93. package/addon/utils/mock-response.js +15 -0
  94. package/addon/utils/numbers-only.js +11 -0
  95. package/addon/utils/past-tense.js +40 -0
  96. package/addon/utils/path-to-route.js +10 -0
  97. package/addon/utils/polyline.js +161 -0
  98. package/addon/utils/range.js +19 -0
  99. package/addon/utils/refresh-route.js +3 -0
  100. package/addon/utils/replace-table-row.js +17 -0
  101. package/addon/utils/reverse-point.js +3 -0
  102. package/addon/utils/serialize/normalize-polymorphic-type-within-hash.js +27 -0
  103. package/addon/utils/serialize/normalize-polymorphic-type.js +13 -0
  104. package/addon/utils/serialize/normalize-relations-with-hash.js +32 -0
  105. package/addon/utils/set-column-filter-options.js +3 -0
  106. package/addon/utils/strip-html.js +3 -0
  107. package/addon/utils/to-leaflet-bounds.js +6 -0
  108. package/addon/utils/to-model.js +18 -0
  109. package/addon/utils/waypoint-label.js +3 -0
  110. package/addon/utils/with-default-value.js +5 -0
  111. package/addon/utils/words.js +5 -0
  112. package/app/adapters/user.js +1 -0
  113. package/app/authenticators/fleetbase.js +1 -0
  114. package/app/decorators/fetch-from.js +1 -0
  115. package/app/decorators/from-store.js +1 -0
  116. package/app/decorators/is-equal.js +1 -0
  117. package/app/exports/host-services.js +1 -0
  118. package/app/exports/services.js +1 -0
  119. package/app/initializers/local-storage-adapter.js +1 -0
  120. package/app/serializers/application.js +1 -0
  121. package/app/services/app-cache.js +1 -0
  122. package/app/services/crud.js +1 -0
  123. package/app/services/current-user.js +1 -0
  124. package/app/services/fetch.js +1 -0
  125. package/app/services/filters.js +1 -0
  126. package/app/services/loader.js +1 -0
  127. package/app/services/notifications.js +1 -0
  128. package/app/services/session.js +1 -0
  129. package/app/services/theme.js +1 -0
  130. package/app/services/url-search-params.js +1 -0
  131. package/app/storages/local-cache.js +12 -0
  132. package/app/storages/user-options.js +12 -0
  133. package/app/transforms/array.js +1 -0
  134. package/app/transforms/object.js +1 -0
  135. package/app/transforms/raw.js +1 -0
  136. package/app/utils/api-url.js +1 -0
  137. package/app/utils/apply-column-filters.js +1 -0
  138. package/app/utils/auto-serialize.js +1 -0
  139. package/app/utils/calculate-percentage.js +1 -0
  140. package/app/utils/close-sidebar.js +1 -0
  141. package/app/utils/console-url.js +1 -0
  142. package/app/utils/copy-to-clipboard.js +1 -0
  143. package/app/utils/corslite.js +1 -0
  144. package/app/utils/download.js +1 -0
  145. package/app/utils/env.js +1 -0
  146. package/app/utils/extract-coordinates.js +1 -0
  147. package/app/utils/extract-latitude.js +1 -0
  148. package/app/utils/extract-longitude.js +1 -0
  149. package/app/utils/find-closest-waypoint.js +1 -0
  150. package/app/utils/first.js +1 -0
  151. package/app/utils/frontend-url.js +1 -0
  152. package/app/utils/generate-slug.js +1 -0
  153. package/app/utils/generate-uuid.js +1 -0
  154. package/app/utils/get-length-units.js +1 -0
  155. package/app/utils/get-meta-field-types.js +1 -0
  156. package/app/utils/get-mime-type.js +1 -0
  157. package/app/utils/get-model-name.js +1 -0
  158. package/app/utils/get-permission-action.js +1 -0
  159. package/app/utils/get-permission-resource.js +1 -0
  160. package/app/utils/get-pod-methods.js +1 -0
  161. package/app/utils/get-routing-host.js +1 -0
  162. package/app/utils/get-service-name.js +1 -0
  163. package/app/utils/get-user-options.js +1 -0
  164. package/app/utils/get-weight-units.js +1 -0
  165. package/app/utils/get-with-default.js +1 -0
  166. package/app/utils/group-api-events.js +1 -0
  167. package/app/utils/group-by.js +1 -0
  168. package/app/utils/has-extension.js +1 -0
  169. package/app/utils/has-json-structure.js +1 -0
  170. package/app/utils/hason-structure.js +1 -0
  171. package/app/utils/haversine.js +1 -0
  172. package/app/utils/humanize.js +1 -0
  173. package/app/utils/is-electron.js +1 -0
  174. package/app/utils/is-email.js +1 -0
  175. package/app/utils/is-function.js +1 -0
  176. package/app/utils/is-image-file.js +1 -0
  177. package/app/utils/is-iterable.js +1 -0
  178. package/app/utils/is-latitude.js +1 -0
  179. package/app/utils/is-letter.js +1 -0
  180. package/app/utils/is-longitude.js +1 -0
  181. package/app/utils/is-model.js +1 -0
  182. package/app/utils/is-not-empty.js +1 -0
  183. package/app/utils/is-not-model.js +1 -0
  184. package/app/utils/is-numeric.js +1 -0
  185. package/app/utils/is-object.js +1 -0
  186. package/app/utils/is-proxy.js +1 -0
  187. package/app/utils/is-relation-missing.js +1 -0
  188. package/app/utils/is-valid-coordinates.js +1 -0
  189. package/app/utils/is-video-file.js +1 -0
  190. package/app/utils/is-waypoint-record.js +1 -0
  191. package/app/utils/ison.js +1 -0
  192. package/app/utils/isset.js +1 -0
  193. package/app/utils/last.js +1 -0
  194. package/app/utils/lazy-load-script.js +1 -0
  195. package/app/utils/leaflet-icon.js +1 -0
  196. package/app/utils/leaflet-points-from-coordinates.js +1 -0
  197. package/app/utils/load-engines.js +1 -0
  198. package/app/utils/load-extensions.js +1 -0
  199. package/app/utils/macros/group-by.js +1 -0
  200. package/app/utils/make-dataset.js +1 -0
  201. package/app/utils/map-engines.js +1 -0
  202. package/app/utils/mock-response.js +1 -0
  203. package/app/utils/numbers-only.js +1 -0
  204. package/app/utils/past-tense.js +1 -0
  205. package/app/utils/path-to-route.js +1 -0
  206. package/app/utils/polyline.js +1 -0
  207. package/app/utils/range.js +1 -0
  208. package/app/utils/refresh-route.js +1 -0
  209. package/app/utils/replace-table-row.js +1 -0
  210. package/app/utils/reverse-point.js +1 -0
  211. package/app/utils/serialize/normalize-polymorphic-type-within-hash.js +1 -0
  212. package/app/utils/serialize/normalize-polymorphic-type.js +1 -0
  213. package/app/utils/serialize/normalize-relations-with-hash.js +1 -0
  214. package/app/utils/set-column-filter-options.js +1 -0
  215. package/app/utils/strip-html.js +1 -0
  216. package/app/utils/to-leaflet-bounds.js +1 -0
  217. package/app/utils/to-model.js +1 -0
  218. package/app/utils/waypoint-label.js +1 -0
  219. package/app/utils/with-default-value.js +1 -0
  220. package/app/utils/words.js +1 -0
  221. package/config/environment.js +5 -0
  222. package/index.js +26 -0
  223. package/package.json +117 -0
  224. package/pnpm-lock.yaml +12750 -0
@@ -0,0 +1,183 @@
1
+ import Service from '@ember/service';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
+ import { isArray } from '@ember/array';
5
+ import { isBlank } from '@ember/utils';
6
+ import { computed, action, set, get } from '@ember/object';
7
+ import { getOwner } from '@ember/application';
8
+ import { format } from 'date-fns';
9
+
10
+ export default class FiltersService extends Service {
11
+ @service router;
12
+ @service urlSearchParams;
13
+ @tracked pendingQueryParams = {};
14
+ @tracked managedQueryParams = ['limit', 'offset', 'sort', 'query', 'page', 'layout', 'view'];
15
+
16
+ @computed('managedQueryParams', 'pendingQueryParams') get activeFilters() {
17
+ const queryParams = this.getQueryParams();
18
+ const activeQueryParams = [];
19
+
20
+ for (let queryParam in queryParams) {
21
+ const value = get(queryParams, queryParam);
22
+
23
+ if (isBlank(value) || this.managedQueryParams.includes(queryParam)) {
24
+ continue;
25
+ }
26
+
27
+ activeQueryParams.pushObject({ queryParam, label: queryParam, value });
28
+ }
29
+
30
+ return activeQueryParams;
31
+ }
32
+
33
+ @action set(queryParam, value) {
34
+ if (value instanceof InputEvent) {
35
+ value = value.target.value;
36
+ }
37
+
38
+ // special case for status
39
+ if (queryParam === 'status' && value === 'all') {
40
+ value = null;
41
+ }
42
+
43
+ // serialize query param value
44
+ value = this.serializeQueryParamValue(queryParam, value);
45
+
46
+ if (isBlank(value)) {
47
+ return this.clear(queryParam);
48
+ }
49
+
50
+ this.pendingQueryParams = {
51
+ ...this.pendingQueryParams,
52
+ [queryParam]: value,
53
+ };
54
+ }
55
+
56
+ @action mutate(queryParam, value, controller) {
57
+ this.set(queryParam, value);
58
+ this.apply(controller);
59
+ }
60
+
61
+ @action serializeQueryParamValue(queryParam, value) {
62
+ if (value instanceof Date) {
63
+ return format(value, 'yyyy-MM-dd HH:mm');
64
+ }
65
+
66
+ if (isArray(value)) {
67
+ return value
68
+ .filter((value) => !isBlank(value))
69
+ .map((value) => this.serializeQueryParamValue(queryParam, value))
70
+ .join(',');
71
+ }
72
+
73
+ return value;
74
+ }
75
+
76
+ @action apply(controller) {
77
+ const currentQueryParams = this.getQueryParams();
78
+ const updatableQueryParams = { ...currentQueryParams, ...this.pendingQueryParams };
79
+
80
+ for (let queryParam in updatableQueryParams) {
81
+ set(controller, queryParam, get(updatableQueryParams, queryParam));
82
+ }
83
+
84
+ // reset pagination to first page
85
+ set(controller, 'page', 1);
86
+
87
+ this.notifyPropertyChange('activeFilters');
88
+ }
89
+
90
+ @action reset(controller) {
91
+ this.clear((queryParam) => {
92
+ set(controller, queryParam, undefined);
93
+ });
94
+ }
95
+
96
+ @action clear(callback, queryParam = []) {
97
+ const currentQueryParams = this.getQueryParams();
98
+ const callbackIsQp = typeof callback === 'string' || isArray(callback);
99
+ const qpIsCallback = typeof queryParam === 'function' || isBlank(queryParam);
100
+
101
+ // handle reversed arguments
102
+ if (callbackIsQp && qpIsCallback) {
103
+ return this.clear(queryParam, callback);
104
+ }
105
+
106
+ if (isBlank(queryParam)) {
107
+ return Object.keys(currentQueryParams).forEach((qp) => this.clear(callback, qp));
108
+ }
109
+
110
+ if (isArray(queryParam)) {
111
+ return queryParam.forEach((qp) => this.clear(callback, qp));
112
+ }
113
+
114
+ if (typeof queryParam !== 'string') {
115
+ return;
116
+ }
117
+
118
+ set(this.pendingQueryParams, queryParam, undefined);
119
+
120
+ if (typeof callback == 'function') {
121
+ callback(queryParam);
122
+ }
123
+
124
+ this.notifyPropertyChange('activeFilters');
125
+ }
126
+
127
+ @action removeFromController(controller, queryParam, newValue) {
128
+ set(controller, queryParam, newValue);
129
+
130
+ this.set(queryParam, newValue);
131
+ this.notifyPropertyChange('activeFilters');
132
+ }
133
+
134
+ @action lookupCurrentController() {
135
+ const currentRoute = this.lookupCurrentRoute();
136
+ const currentController = currentRoute.controller;
137
+
138
+ return currentController;
139
+ }
140
+
141
+ @action lookupCurrentRoute() {
142
+ const owner = getOwner(this); // ApplicationInstance
143
+ const router = owner.lookup('router:main'); // Router
144
+ const routerMicrolib = router._routerMicrolib; // PrivateRouter
145
+ const currentRouteInfos = routerMicrolib.currentRouteInfos; // Array
146
+ const currentRouteInfo = currentRouteInfos[currentRouteInfos.length - 1]; // ResolvedRouteInfo
147
+
148
+ return currentRouteInfo._route;
149
+ }
150
+
151
+ @action getRouteQueryParams() {
152
+ const currentRoute = this.lookupCurrentRoute();
153
+ return currentRoute.queryParams;
154
+ }
155
+
156
+ @action getQueryParams() {
157
+ const currentRoute = this.lookupCurrentRoute();
158
+ const currentRouteQueryParams = Object.keys(currentRoute.queryParams);
159
+ const queryParams = {};
160
+
161
+ for (let i = 0; i < currentRouteQueryParams.length; i++) {
162
+ const queryParam = currentRouteQueryParams.objectAt(i);
163
+ const value = this.urlSearchParams.get(queryParam);
164
+
165
+ if (this.managedQueryParams.includes(queryParam)) {
166
+ continue;
167
+ }
168
+
169
+ if (value) {
170
+ queryParams[queryParam] = value;
171
+ }
172
+ }
173
+
174
+ return queryParams;
175
+ }
176
+
177
+ @action resetQueryParams() {
178
+ if (!isBlank(this.activeFilters)) {
179
+ this.clear();
180
+ this.router.transitionTo({ queryParams: {} });
181
+ }
182
+ }
183
+ }
@@ -0,0 +1,136 @@
1
+ import Service from '@ember/service';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { later } from '@ember/runloop';
4
+
5
+ export default class LoaderService extends Service {
6
+ @tracked routesLoaded = [];
7
+
8
+ showOnCondition(target, options = {}, condition = null) {
9
+ const { loadingMessage, opacity } = options;
10
+
11
+ if (typeof condition === 'function') {
12
+ condition = condition();
13
+ }
14
+
15
+ if (condition) {
16
+ this.showLoader(target, loadingMessage, opacity);
17
+ }
18
+ }
19
+
20
+ showOnInitialTransition(transition, target, loadingMessage = 'Loading...', opacity = 0.1) {
21
+ const route = transition.to.name;
22
+ const isSameRoute = transition.from ? transition.to.name === transition.from.name : false;
23
+
24
+ if (!this.routesLoaded.includes(route) || !isSameRoute) {
25
+ if (document.querySelectorAll(`.overloader`).length > 0) {
26
+ return;
27
+ }
28
+
29
+ this.showLoader(target, loadingMessage, opacity);
30
+
31
+ transition.finally(() => {
32
+ this.removeLoader(target);
33
+ });
34
+ }
35
+
36
+ this.routesLoaded.pushObject(route);
37
+ }
38
+
39
+ /**
40
+ * Creates an HTML element node for a loading overlay with a message.
41
+ *
42
+ * @param {String|HTMLElement} targetSelector
43
+ * @param {String} loadingMessage
44
+ * @return {HTMLElement} loader
45
+ */
46
+ showLoader(targetSelector, loadingMessage = 'Loading...', opacity = 0.1) {
47
+ let target = typeof targetSelector === 'string' ? document.querySelector(targetSelector) : targetSelector;
48
+
49
+ if (!target) {
50
+ target = document.body;
51
+ }
52
+
53
+ const isDarkMode = document.body.dataset.theme ? document.body.dataset.theme === 'dark' : true;
54
+
55
+ target.style.position = 'relative';
56
+
57
+ let loader = document.createElement('div');
58
+ loader.classList.add('overloader');
59
+ loader.style.backgroundColor = isDarkMode ? `rgba(128, 128, 128, ${opacity})` : `rgba(249, 250, 251, ${opacity})`;
60
+ loader.innerHTML = `<div class="flex items-center justify-center text-center">
61
+ <div>
62
+ <svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" role="img" focusable="false" aria-hidden="true" data-icon="spinner-third" data-prefix="fad" id="ember240" class="svg-inline--fa fa-spinner-third fa-w-16 fa-spin ember-view text-sky-500 fa-spin-800ms mr-3"><g class="fa-group"><path class="fa-secondary" fill="currentColor" d="M478.71 364.58zm-22 6.11l-27.83-15.9a15.92 15.92 0 0 1-6.94-19.2A184 184 0 1 1 256 72c5.89 0 11.71.29 17.46.83-.74-.07-1.48-.15-2.23-.21-8.49-.69-15.23-7.31-15.23-15.83v-32a16 16 0 0 1 15.34-16C266.24 8.46 261.18 8 256 8 119 8 8 119 8 256s111 248 248 248c98 0 182.42-56.95 222.71-139.42-4.13 7.86-14.23 10.55-22 6.11z"></path><path class="fa-primary" fill="currentColor" d="M271.23 72.62c-8.49-.69-15.23-7.31-15.23-15.83V24.73c0-9.11 7.67-16.78 16.77-16.17C401.92 17.18 504 124.67 504 256a246 246 0 0 1-25 108.24c-4 8.17-14.37 11-22.26 6.45l-27.84-15.9c-7.41-4.23-9.83-13.35-6.2-21.07A182.53 182.53 0 0 0 440 256c0-96.49-74.27-175.63-168.77-183.38z"></path></g>
63
+ </svg>
64
+ </div>
65
+
66
+ <span class="font-semibold text-gray-700 dark:text-gray-100 test-xs md:text-sm">${loadingMessage}</span>
67
+ </div>`;
68
+
69
+ target.appendChild(loader);
70
+
71
+ return loader;
72
+ }
73
+
74
+ show(loadingMessage = 'Loading...', opacity = 0.1) {
75
+ return this.showLoader(document.body, loadingMessage, opacity);
76
+ }
77
+
78
+ /**
79
+ * Creates an HTML element node for a loading overlay with a message.
80
+ *
81
+ * @param {String|HTMLElement} targetSelector
82
+ * @return {Service} this
83
+ */
84
+ removeLoader(targetSelector) {
85
+ let target = typeof targetSelector === 'string' ? document.querySelector(targetSelector) : targetSelector;
86
+ const removeStyle = (el, styleProperty) => {
87
+ if (el.style.removeProperty) {
88
+ el.style.removeProperty(styleProperty);
89
+ } else {
90
+ el.style.removeAttribute(styleProperty);
91
+ }
92
+ };
93
+
94
+ if (!target) {
95
+ target = document.body;
96
+ }
97
+
98
+ if (target.classList.contains('overloader')) {
99
+ target.remove();
100
+
101
+ return this;
102
+ }
103
+
104
+ const loader = target.querySelector('.overloader');
105
+
106
+ if (!loader) {
107
+ return;
108
+ }
109
+
110
+ removeStyle(target, 'position');
111
+ target.removeChild(loader);
112
+
113
+ return this;
114
+ }
115
+
116
+ /**
117
+ * Removes the all loader instances.
118
+ *
119
+ * @return {Service} this
120
+ */
121
+ remove(delay = 0) {
122
+ const loaders = document.querySelectorAll(`.overloader`);
123
+
124
+ later(
125
+ this,
126
+ () => {
127
+ loaders.forEach((loader) => {
128
+ loader.remove();
129
+ });
130
+ },
131
+ delay
132
+ );
133
+
134
+ return this;
135
+ }
136
+ }
@@ -0,0 +1,32 @@
1
+ import EmberNotificationsService from 'ember-cli-notifications/services/notifications';
2
+ import { isArray } from '@ember/array';
3
+
4
+ export default class NotificationsService extends EmberNotificationsService {
5
+ /**
6
+ * Handles errors from the server
7
+ *
8
+ * @param {Error} error
9
+ * @void
10
+ */
11
+ serverError(error, fallbackMessage = 'Oops! Something went wrong with your request.', options) {
12
+ if (isArray(error.errors)) {
13
+ const errorMessage = error.errors.firstObject;
14
+
15
+ return this.error(errorMessage ?? fallbackMessage, options);
16
+ }
17
+
18
+ if (error instanceof Error) {
19
+ return this.error(error.message ?? fallbackMessage, options);
20
+ }
21
+
22
+ return this.error(error ?? fallbackMessage, options);
23
+ }
24
+
25
+ invoke(type, message, ...params) {
26
+ if (typeof message === 'function') {
27
+ this[type](message(...params));
28
+ } else {
29
+ this[type](message);
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,221 @@
1
+ import SimpleAuthSessionService from 'ember-simple-auth/services/session';
2
+ import { tracked } from '@glimmer/tracking';
3
+ import { inject as service } from '@ember/service';
4
+ import { later } from '@ember/runloop';
5
+
6
+ export default class SessionService extends SimpleAuthSessionService {
7
+ /**
8
+ * Inject the router service
9
+ *
10
+ * @var {Service}
11
+ */
12
+ @service router;
13
+
14
+ /**
15
+ * Inject the current user service
16
+ *
17
+ * @var {Service}
18
+ */
19
+ @service currentUser;
20
+
21
+ /**
22
+ * Inject the current user service
23
+ *
24
+ * @var {Service}
25
+ */
26
+ @service fetch;
27
+
28
+ /**
29
+ * Set where to transition to
30
+ *
31
+ * @var {String}
32
+ */
33
+ @tracked redirectTo = 'console';
34
+
35
+ /**
36
+ * If session is onboarding a user.
37
+ *
38
+ * @var {String}
39
+ */
40
+ @tracked _isOnboarding = false;
41
+
42
+ /**
43
+ * Set this as onboarding.
44
+ *
45
+ * @return {SessionService}
46
+ */
47
+ isOnboarding() {
48
+ this._isOnboarding = true;
49
+
50
+ return this;
51
+ }
52
+
53
+ /**
54
+ * Manually authenticate user
55
+ */
56
+ manuallyAuthenticate(authToken) {
57
+ return this.session._setup('authenticator:fleetbase', { token: authToken }, true);
58
+ }
59
+
60
+ /**
61
+ * Overwrite the handle authentication method
62
+ *
63
+ * @void
64
+ */
65
+ async handleAuthentication() {
66
+ if (this._isOnboarding) {
67
+ return;
68
+ }
69
+
70
+ const loaderNode = this.showLoader('Starting session...');
71
+
72
+ this.isLoaderNodeOpen = true;
73
+
74
+ this.router
75
+ .transitionTo(this.redirectTo)
76
+ .finally(() => {
77
+ later(
78
+ this,
79
+ () => {
80
+ // remove node from body
81
+ document.body.removeChild(loaderNode);
82
+ this.isLoaderNodeOpen = false;
83
+ },
84
+ 600 * 6
85
+ );
86
+ })
87
+ .catch((error) => console.log(error));
88
+ }
89
+
90
+ /**
91
+ * Loads the current authenticated user
92
+ *
93
+ * @void
94
+ */
95
+ async loadCurrentUser() {
96
+ try {
97
+ const user = await this.currentUser.load();
98
+
99
+ if (!user) {
100
+ return this.invalidateWithLoader(`Session authentication failed...`);
101
+ }
102
+
103
+ return user;
104
+ } catch (error) {
105
+ await this.invalidateWithLoader(error.message ?? `Session authentication failed...`);
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Loads the current authenticated user
111
+ *
112
+ * @param {Transition} transition
113
+ * @void
114
+ */
115
+ promiseCurrentUser(transition = null) {
116
+ const invalidateWithLoader = this.invalidateWithLoader.bind(this);
117
+
118
+ return new Promise((resolve, reject) => {
119
+ return this.currentUser
120
+ .promiseUser()
121
+ .then((user) => {
122
+ if (!user) {
123
+ if (transition !== null) {
124
+ transition.abort();
125
+ }
126
+
127
+ reject(invalidateWithLoader(`Session authentication failed...`));
128
+ }
129
+
130
+ resolve(user);
131
+ })
132
+ .catch((error) => {
133
+ if (transition !== null) {
134
+ transition.abort();
135
+ }
136
+
137
+ reject(invalidateWithLoader(error.message ?? `Session authentication failed...`));
138
+ });
139
+ });
140
+ }
141
+
142
+ /**
143
+ * Creates an HTML element node for a loading overlay with a message.
144
+ *
145
+ * @param {String} loadingMessage
146
+ * @return {HTMLElement} loader
147
+ */
148
+ showLoader(loadingMessage) {
149
+ const loader = document.createElement('div');
150
+ loader.classList.add('overloader');
151
+ loader.innerHTML = `<div class="flex items-center justify-center">
152
+ <div>
153
+ <svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" role="img" focusable="false" aria-hidden="true" data-icon="spinner-third" data-prefix="fad" id="ember240" class="svg-inline--fa fa-spinner-third fa-w-16 fa-spin ember-view text-sky-500 fa-spin-800ms mr-3"><g class="fa-group"><path class="fa-secondary" fill="currentColor" d="M478.71 364.58zm-22 6.11l-27.83-15.9a15.92 15.92 0 0 1-6.94-19.2A184 184 0 1 1 256 72c5.89 0 11.71.29 17.46.83-.74-.07-1.48-.15-2.23-.21-8.49-.69-15.23-7.31-15.23-15.83v-32a16 16 0 0 1 15.34-16C266.24 8.46 261.18 8 256 8 119 8 8 119 8 256s111 248 248 248c98 0 182.42-56.95 222.71-139.42-4.13 7.86-14.23 10.55-22 6.11z"></path><path class="fa-primary" fill="currentColor" d="M271.23 72.62c-8.49-.69-15.23-7.31-15.23-15.83V24.73c0-9.11 7.67-16.78 16.77-16.17C401.92 17.18 504 124.67 504 256a246 246 0 0 1-25 108.24c-4 8.17-14.37 11-22.26 6.45l-27.84-15.9c-7.41-4.23-9.83-13.35-6.2-21.07A182.53 182.53 0 0 0 440 256c0-96.49-74.27-175.63-168.77-183.38z"></path></g>
154
+ </svg>
155
+ </div>
156
+
157
+ <span class="font-semibold text-gray-700 dark:text-gray-100 test-sm">${loadingMessage}</span>
158
+ </div>`;
159
+ document.body.appendChild(loader);
160
+
161
+ return loader;
162
+ }
163
+
164
+ /**
165
+ * Invalidates the current session while displaying a loading message on the page.
166
+ *
167
+ * @param {String} loadingMessage
168
+ * @return {Promise}
169
+ */
170
+ invalidateWithLoader(loadingMessage = 'Ending session...') {
171
+ // if loader node is open already just invalidate
172
+ if (this.isLoaderNodeOpen === true) {
173
+ return this.session.invalidate();
174
+ }
175
+
176
+ const loaderNode = this.showLoader(loadingMessage);
177
+
178
+ this.isLoaderNodeOpen = false;
179
+
180
+ return this.session.invalidate().then(() => {
181
+ later(
182
+ this,
183
+ () => {
184
+ document.body.removeChild(loaderNode);
185
+ this.isLoaderNodeOpen = false;
186
+ },
187
+ 600
188
+ );
189
+ });
190
+ }
191
+
192
+ /**
193
+ * Set the redirect route after authentication
194
+ *
195
+ * @void
196
+ */
197
+ setRedirect(whereTo = 'console') {
198
+ this.redirectTo = whereTo;
199
+ }
200
+
201
+ /**
202
+ * Get session time expiry date in moment
203
+ *
204
+ * @return {Date}
205
+ */
206
+ getExpiresAtDate() {
207
+ return new Date(this.data.authenticated.expires_at);
208
+ }
209
+
210
+ /**
211
+ * Get session time expiry in seconds
212
+ *
213
+ * @return {Integer}
214
+ */
215
+ getSessionSecondsRemaining() {
216
+ const date = this.getExpiresAtDate();
217
+ const now = new Date();
218
+
219
+ return Math.round((now - date) / 1000);
220
+ }
221
+ }