@iapps/ng-dhis2-ui 1.0.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 (79) hide show
  1. package/README.md +7 -0
  2. package/esm2022/iapps-ng-dhis2-ui.mjs +5 -0
  3. package/esm2022/index.mjs +5 -0
  4. package/esm2022/lib/modules/app-shell/app-shell.component.mjs +33 -0
  5. package/esm2022/lib/modules/app-shell/app-shell.module.mjs +19 -0
  6. package/esm2022/lib/modules/app-shell/index.mjs +6 -0
  7. package/esm2022/lib/modules/button/button.component.mjs +55 -0
  8. package/esm2022/lib/modules/button/button.module.mjs +19 -0
  9. package/esm2022/lib/modules/button/index.mjs +6 -0
  10. package/esm2022/lib/modules/header-bar/header-bar.component.mjs +39 -0
  11. package/esm2022/lib/modules/header-bar/header-bar.module.mjs +19 -0
  12. package/esm2022/lib/modules/header-bar/index.mjs +6 -0
  13. package/esm2022/lib/modules/header-bar/models/header-bar-app.model.mjs +20 -0
  14. package/esm2022/lib/modules/header-bar/models/header-bar-data.model.mjs +46 -0
  15. package/esm2022/lib/modules/header-bar/models/header-bar-notification.model.mjs +11 -0
  16. package/esm2022/lib/modules/header-bar/models/index.mjs +6 -0
  17. package/esm2022/lib/modules/header-bar/utils/apps.mjs +221 -0
  18. package/esm2022/lib/modules/header-bar/utils/debug-info/debug-info-menu-item.mjs +51 -0
  19. package/esm2022/lib/modules/header-bar/utils/debug-info/debug-info-modal.mjs +30 -0
  20. package/esm2022/lib/modules/header-bar/utils/debug-info/debug-info-table.mjs +39 -0
  21. package/esm2022/lib/modules/header-bar/utils/debug-info/use-debug-info.mjs +12 -0
  22. package/esm2022/lib/modules/header-bar/utils/header-bar-context.mjs +17 -0
  23. package/esm2022/lib/modules/header-bar/utils/header-bar.mjs +56 -0
  24. package/esm2022/lib/modules/header-bar/utils/join-path.mjs +5 -0
  25. package/esm2022/lib/modules/header-bar/utils/logo-image.mjs +52 -0
  26. package/esm2022/lib/modules/header-bar/utils/logo.mjs +35 -0
  27. package/esm2022/lib/modules/header-bar/utils/notification-icon.mjs +76 -0
  28. package/esm2022/lib/modules/header-bar/utils/notifications.mjs +35 -0
  29. package/esm2022/lib/modules/header-bar/utils/profile/use-on-doc-click.mjs +21 -0
  30. package/esm2022/lib/modules/header-bar/utils/profile-menu/index.mjs +2 -0
  31. package/esm2022/lib/modules/header-bar/utils/profile-menu/profile-header.mjs +95 -0
  32. package/esm2022/lib/modules/header-bar/utils/profile-menu/profile-menu.mjs +101 -0
  33. package/esm2022/lib/modules/header-bar/utils/profile-menu/update-notification.mjs +54 -0
  34. package/esm2022/lib/modules/header-bar/utils/profile.mjs +58 -0
  35. package/esm2022/lib/modules/header-bar/utils/title.mjs +22 -0
  36. package/esm2022/lib/ng-dhis2-ui.module.mjs +15 -0
  37. package/esm2022/lib/shared/components/index.mjs +7 -0
  38. package/esm2022/lib/shared/components/react-component-wrapper/react-wrapper.component.mjs +53 -0
  39. package/esm2022/lib/shared/shared.module.mjs +18 -0
  40. package/fesm2022/iapps-ng-dhis2-ui.mjs +1239 -0
  41. package/fesm2022/iapps-ng-dhis2-ui.mjs.map +1 -0
  42. package/index.d.ts +4 -0
  43. package/lib/modules/app-shell/app-shell.component.d.ts +8 -0
  44. package/lib/modules/app-shell/app-shell.module.d.ts +9 -0
  45. package/lib/modules/app-shell/index.d.ts +2 -0
  46. package/lib/modules/button/button.component.d.ts +19 -0
  47. package/lib/modules/button/button.module.d.ts +9 -0
  48. package/lib/modules/button/index.d.ts +2 -0
  49. package/lib/modules/header-bar/header-bar.component.d.ts +14 -0
  50. package/lib/modules/header-bar/header-bar.module.d.ts +9 -0
  51. package/lib/modules/header-bar/index.d.ts +2 -0
  52. package/lib/modules/header-bar/models/header-bar-app.model.d.ts +10 -0
  53. package/lib/modules/header-bar/models/header-bar-data.model.d.ts +16 -0
  54. package/lib/modules/header-bar/models/header-bar-notification.model.d.ts +5 -0
  55. package/lib/modules/header-bar/models/index.d.ts +2 -0
  56. package/lib/modules/header-bar/utils/apps.d.ts +16 -0
  57. package/lib/modules/header-bar/utils/debug-info/debug-info-menu-item.d.ts +15 -0
  58. package/lib/modules/header-bar/utils/debug-info/debug-info-modal.d.ts +8 -0
  59. package/lib/modules/header-bar/utils/debug-info/debug-info-table.d.ts +8 -0
  60. package/lib/modules/header-bar/utils/debug-info/use-debug-info.d.ts +7 -0
  61. package/lib/modules/header-bar/utils/header-bar-context.d.ts +14 -0
  62. package/lib/modules/header-bar/utils/header-bar.d.ts +19 -0
  63. package/lib/modules/header-bar/utils/join-path.d.ts +1 -0
  64. package/lib/modules/header-bar/utils/logo-image.d.ts +8 -0
  65. package/lib/modules/header-bar/utils/logo.d.ts +12 -0
  66. package/lib/modules/header-bar/utils/notification-icon.d.ts +21 -0
  67. package/lib/modules/header-bar/utils/notifications.d.ts +16 -0
  68. package/lib/modules/header-bar/utils/profile/use-on-doc-click.d.ts +1 -0
  69. package/lib/modules/header-bar/utils/profile-menu/index.d.ts +1 -0
  70. package/lib/modules/header-bar/utils/profile-menu/profile-header.d.ts +16 -0
  71. package/lib/modules/header-bar/utils/profile-menu/profile-menu.d.ts +13 -0
  72. package/lib/modules/header-bar/utils/profile-menu/update-notification.d.ts +14 -0
  73. package/lib/modules/header-bar/utils/profile.d.ts +18 -0
  74. package/lib/modules/header-bar/utils/title.d.ts +15 -0
  75. package/lib/ng-dhis2-ui.module.d.ts +7 -0
  76. package/lib/shared/components/index.d.ts +3 -0
  77. package/lib/shared/components/react-component-wrapper/react-wrapper.component.d.ts +19 -0
  78. package/lib/shared/shared.module.d.ts +7 -0
  79. package/package.json +38 -0
@@ -0,0 +1,1239 @@
1
+ import * as i0 from '@angular/core';
2
+ import { NgModule, Directive, Input, ContentChild, Component, EventEmitter, Output } from '@angular/core';
3
+ import * as i2 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import parse from 'html-react-parser';
6
+ import * as React from 'react';
7
+ import React__default, { useState, useCallback, useRef, useEffect, createContext, useContext, useMemo } from 'react';
8
+ import * as ReactDOM from 'react-dom/client';
9
+ import * as i1 from '@iapps/ngx-dhis2-http-client';
10
+ import { catchError, of, map, zip, firstValueFrom } from 'rxjs';
11
+ import { useDataQuery, useConfig, useAlert, clearSensitiveCaches, DataProvider } from '@dhis2/app-runtime';
12
+ import i18n from '@dhis2/d2-i18n';
13
+ import { colors, theme, spacers } from '@dhis2/ui-constants';
14
+ import PropTypes from 'prop-types';
15
+ import { Card } from '@dhis2-ui/card';
16
+ import { InputField } from '@dhis2-ui/input';
17
+ import { IconSettings24, IconApps24, IconMessages24, IconMail24, IconUser24, IconQuestion24, IconInfo24, IconLogOut24 } from '@dhis2/ui-icons';
18
+ import { LogoIconWhite } from '@dhis2-ui/logo';
19
+ import { UserAvatar } from '@dhis2-ui/user-avatar';
20
+ import { ButtonStrip, Button } from '@dhis2-ui/button';
21
+ import { Modal, ModalTitle, ModalContent, ModalActions } from '@dhis2-ui/modal';
22
+ import { Center } from '@dhis2-ui/center';
23
+ import { Divider } from '@dhis2-ui/divider';
24
+ import { Layer } from '@dhis2-ui/layer';
25
+ import { CircularLoader } from '@dhis2-ui/loader';
26
+ import { MenuItem, MenuDivider } from '@dhis2-ui/menu';
27
+ import { Button as Button$1, Layer as Layer$1, layers, CenteredContent, CircularLoader as CircularLoader$1 } from '@dhis2/ui';
28
+ import AppAdapter from '@dhis2/app-adapter';
29
+
30
+ class NgDhis2UiModule {
31
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NgDhis2UiModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
32
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: NgDhis2UiModule, imports: [CommonModule] }); }
33
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NgDhis2UiModule, imports: [CommonModule] }); }
34
+ }
35
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: NgDhis2UiModule, decorators: [{
36
+ type: NgModule,
37
+ args: [{
38
+ imports: [CommonModule],
39
+ }]
40
+ }] });
41
+
42
+ window.React = React;
43
+ class ReactWrapperComponent {
44
+ constructor(elementRef) {
45
+ this.elementRef = elementRef;
46
+ }
47
+ ngAfterViewInit() {
48
+ if (!this.elementRef)
49
+ throw new Error('No element ref');
50
+ this.reactDomRoot = ReactDOM.createRoot(this.elementRef.nativeElement);
51
+ this.render();
52
+ }
53
+ ngOnChanges() {
54
+ if (this.elementRef)
55
+ this.render();
56
+ }
57
+ ngOnDestroy() {
58
+ this.reactDomRoot?.unmount();
59
+ }
60
+ render() {
61
+ if (!this.component)
62
+ throw new Error('React component must be supplied');
63
+ if (!this.reactDomRoot)
64
+ return;
65
+ const jsxContent = this.content?.nativeElement?.outerHTML
66
+ ? parse(this.content?.nativeElement?.outerHTML)
67
+ : undefined;
68
+ this.reactDomRoot.render(React.createElement(this.component, { ...this.props },
69
+ " ",
70
+ jsxContent || '',
71
+ " "));
72
+ }
73
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ReactWrapperComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive }); }
74
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: ReactWrapperComponent, selector: "ng-dhis2-ui-wrapper", inputs: { props: "props", component: "component" }, queries: [{ propertyName: "content", first: true, predicate: ["ngDhis2UiContent"], descendants: true }], usesOnChanges: true, ngImport: i0 }); }
75
+ }
76
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ReactWrapperComponent, decorators: [{
77
+ type: Directive,
78
+ args: [{
79
+ selector: 'ng-dhis2-ui-wrapper',
80
+ }]
81
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { props: [{
82
+ type: Input
83
+ }], component: [{
84
+ type: Input
85
+ }], content: [{
86
+ type: ContentChild,
87
+ args: ['ngDhis2UiContent']
88
+ }] } });
89
+
90
+ // Copyright 2023 UDSM DHIS2 Lab. All rights reserved.
91
+ // Use of this source code is governed by a BSD-style
92
+ // license that can be found in the LICENSE file.
93
+ const sharedComponents = [];
94
+
95
+ class SharedDhis2UiModule {
96
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SharedDhis2UiModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
97
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: SharedDhis2UiModule, imports: [CommonModule] }); }
98
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SharedDhis2UiModule, imports: [CommonModule] }); }
99
+ }
100
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: SharedDhis2UiModule, decorators: [{
101
+ type: NgModule,
102
+ args: [{
103
+ imports: [CommonModule],
104
+ declarations: [...sharedComponents],
105
+ exports: [...sharedComponents],
106
+ }]
107
+ }] });
108
+
109
+ // Copyright 2023 UDSM DHIS2 Lab. All rights reserved.
110
+ // Use of this source code is governed by a BSD-style
111
+ // license that can be found in the LICENSE file.
112
+ class HeaderBarApp {
113
+ constructor(module) {
114
+ this.name = module['name'];
115
+ this.namespace = module['namespace'];
116
+ this.defaultAction = this.getPath(module['defaultAction']);
117
+ this.icon = this.getPath(module['icon']);
118
+ this.displayName = module['displayName'];
119
+ this.description = module['description'];
120
+ }
121
+ getPath(item) {
122
+ if (item?.startsWith('../')) {
123
+ return '../../' + item;
124
+ }
125
+ return item;
126
+ }
127
+ }
128
+
129
+ // Copyright 2023 UDSM DHIS2 Lab. All rights reserved.
130
+ // Use of this source code is governed by a BSD-style
131
+ // license that can be found in the LICENSE file.
132
+ class HeaderBarNotification {
133
+ constructor(notification) {
134
+ this.unreadInterpretations = notification['unreadInterpretations'];
135
+ this.unreadMessageConversations =
136
+ notification['unreadMessageConversations'];
137
+ }
138
+ }
139
+
140
+ // Copyright 2023 UDSM DHIS2 Lab. All rights reserved.
141
+ // Use of this source code is governed by a BSD-style
142
+ // license that can be found in the LICENSE file.
143
+ class HeaderBarData {
144
+ constructor(httpClient) {
145
+ this.httpClient = httpClient;
146
+ }
147
+ getApps() {
148
+ return this.httpClient
149
+ .get('dhis-web-commons/menu/getModules.action', {
150
+ useRootUrl: true,
151
+ })
152
+ .pipe(catchError(() => of({
153
+ modules: [],
154
+ })), map((result) => {
155
+ return (result?.modules || [])
156
+ .map((module) => new HeaderBarApp(module))
157
+ .filter((headerBarApp) => headerBarApp);
158
+ }));
159
+ }
160
+ getNotifications() {
161
+ return this.httpClient
162
+ .get('me/dashboard')
163
+ .pipe(map((notifications) => notifications ? new HeaderBarNotification(notifications) : null));
164
+ }
165
+ get() {
166
+ return zip(this.getApps(), this.httpClient.get('systemSettings/helpPageLink'), this.httpClient.get('me/dashboard'), this.httpClient.get('systemSettings/applicationTitle'), this.httpClient.get('me.json?fields=authorities,avatar,email,name,settings')).pipe(map((response) => {
167
+ if (response) {
168
+ const [apps, help, notifications, title, user] = response;
169
+ this.apps = apps;
170
+ this.help = help;
171
+ this.notifications = notifications;
172
+ this.title = title;
173
+ this.user = user;
174
+ return this;
175
+ }
176
+ return null;
177
+ }), catchError(() => {
178
+ return of(null);
179
+ }));
180
+ }
181
+ }
182
+
183
+ // Copyright 2023 UDSM DHIS2 Lab. All rights reserved.
184
+ // Use of this source code is governed by a BSD-style
185
+ // license that can be found in the LICENSE file.
186
+
187
+ const joinPath = (...parts) => {
188
+ const realParts = parts.filter((part) => !!part);
189
+ return realParts.map((part) => part.replace(/^\/+|\/+$/g, '')).join('/');
190
+ };
191
+
192
+ /**
193
+ * Copied from here:
194
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
195
+ */
196
+ function escapeRegExpCharacters(text) {
197
+ return text.replace(/[/.*+?^${}()|[\]\\]/g, '\\$&');
198
+ }
199
+ function Search({ value, onChange, baseUrl }) {
200
+ return (React__default.createElement("div", { className: "headerbar-apps-search" },
201
+ React__default.createElement("span", { className: "headerbar-apps-search-input" },
202
+ React__default.createElement(InputField, { value: value, name: "filter", placeholder: i18n.t('Search apps'), onChange: onChange, initialFocus: true })),
203
+ React__default.createElement("span", { className: "headerbar-apps-search-icon" },
204
+ React__default.createElement("a", { href: joinPath(baseUrl, 'dhis-web-menu-management') },
205
+ React__default.createElement(IconSettings24, { color: colors.grey700 }))),
206
+ React__default.createElement("style", { jsx: true }, `
207
+ .headerbar-apps-search {
208
+ display: flex;
209
+ flex-direction: row;
210
+ flex-wrap: nowrap;
211
+ height: 52px;
212
+ margin: 8px;
213
+ }
214
+
215
+ .headerbar-apps-search-input {
216
+ flex: 1 100%;
217
+ }
218
+
219
+ .headerbar-apps-search-icon {
220
+ flex: 1 auto;
221
+ margin: 8px;
222
+ }
223
+ `)));
224
+ }
225
+ Search.propTypes = {
226
+ value: PropTypes.string.isRequired,
227
+ onChange: PropTypes.func.isRequired,
228
+ baseUrl: PropTypes.string,
229
+ };
230
+ function Item({ name, path, img }) {
231
+ return (React__default.createElement("a", { className: "headerbar-app-item", href: path },
232
+ React__default.createElement("img", { className: "headerbar-app-item-logo", src: img, alt: "app logo" }),
233
+ React__default.createElement("div", { className: "headerbar-app-item-title" }, name),
234
+ React__default.createElement("style", { jsx: true }, `
235
+ .headerbar-app-item {
236
+ display: flex;
237
+ flex-direction: column;
238
+ -webkit-box-align: center;
239
+ align-items: center;
240
+ -webkit-box-pack: center;
241
+ justify-content: center;
242
+ width: 96px;
243
+ margin: 8px;
244
+ border-radius: 12px;
245
+ text-decoration: none;
246
+ cursor: pointer;
247
+ }
248
+ .headerbar-app-item-logo {
249
+ display: inline-block;
250
+ display: flex;
251
+ flex-direction: column;
252
+ align-items: center;
253
+ justify-content: center;
254
+ width: 96px;
255
+ margin: 8px;
256
+ border-radius: 12px;
257
+ text-decoration: none;
258
+ cursor: pointer;
259
+ }
260
+
261
+ .headerbar-app-item-logo:hover,
262
+ .headerbar-app-item-logo:focus {
263
+ background-color: ${theme.primary050};
264
+ cursor: pointer;
265
+ }
266
+
267
+ .headerbar-app-item-logo:hover > div {
268
+ font-weight: 500;
269
+ cursor: pointer;
270
+ }
271
+
272
+ .headerbar-app-item-logo {
273
+ width: 48px;
274
+ height: 48px;
275
+ margin: 8px;
276
+ cursor: pointer;
277
+ }
278
+
279
+ .headerbar-app-item-title {
280
+ overflow-wrap: anywhere;
281
+ margin-top: 14px;
282
+ color: rgba(0, 0, 0, 0.87);
283
+ font-size: 12px;
284
+ letter-spacing: 0.01em;
285
+ line-height: 14px;
286
+ text-align: center;
287
+ cursor: pointer;
288
+ }
289
+ `)));
290
+ }
291
+ Item.propTypes = {
292
+ img: PropTypes.string,
293
+ name: PropTypes.string,
294
+ path: PropTypes.string,
295
+ };
296
+ function List({ apps, filter }) {
297
+ return (React__default.createElement("div", { className: "headerbar-apps-menu-list", "data-test": "headerbar-apps-menu-list" },
298
+ apps
299
+ .filter(({ displayName, name }) => {
300
+ const appName = displayName || name;
301
+ const formattedAppName = appName.toLowerCase();
302
+ const formattedFilter = escapeRegExpCharacters(filter).toLowerCase();
303
+ return filter.length > 0
304
+ ? formattedAppName.match(formattedFilter)
305
+ : true;
306
+ })
307
+ .map(({ displayName, name, defaultAction, icon }, idx) => (React__default.createElement(Item, { key: `app-${name}-${idx}`, name: displayName || name, path: defaultAction, img: icon }))),
308
+ React__default.createElement("style", { jsx: true }, `
309
+ .headerbar-apps-menu-list {
310
+ display: flex;
311
+ flex-direction: row;
312
+ flex-wrap: wrap;
313
+ align-content: flex-start;
314
+ align-items: flex-start;
315
+ justify-content: flex-start;
316
+ width: 30vw;
317
+ min-width: 300px;
318
+ max-width: 560px;
319
+
320
+ min-height: 200px;
321
+ max-height: 465px;
322
+ margin: 0 8px 8px 8px;
323
+
324
+ overflow: auto;
325
+ overflow-x: hidden;
326
+ }
327
+ `)));
328
+ }
329
+ List.propTypes = {
330
+ apps: PropTypes.array,
331
+ filter: PropTypes.string,
332
+ };
333
+ const AppMenu = ({ apps, filter, onFilterChange, baseUrl }) => (React__default.createElement("div", { className: "headerbar-apps-menu", "data-test": "headerbar-apps-menu" },
334
+ React__default.createElement(Card, null,
335
+ React__default.createElement(Search, { value: filter, onChange: onFilterChange, baseUrl: baseUrl }),
336
+ React__default.createElement(List, { apps: apps, filter: filter })),
337
+ React__default.createElement("style", { jsx: true }, `
338
+ .headerbar-apps-menu {
339
+ z-index: 10000;
340
+ position: absolute;
341
+ right: -4px;
342
+ }
343
+ `)));
344
+ AppMenu.propTypes = {
345
+ apps: PropTypes.array.isRequired,
346
+ onFilterChange: PropTypes.func.isRequired,
347
+ filter: PropTypes.string,
348
+ baseUrl: PropTypes.string,
349
+ };
350
+ const Apps = ({ apps, baseUrl }) => {
351
+ const [show, setShow] = useState(false);
352
+ const [filter, setFilter] = useState('');
353
+ const handleVisibilityToggle = useCallback(() => setShow(!show), [show]);
354
+ const handleFilterChange = useCallback(({ value }) => setFilter(value), []);
355
+ const containerEl = useRef(null);
356
+ const onDocClick = useCallback((evt) => {
357
+ if (containerEl.current &&
358
+ !containerEl.current.contains(evt.target)) {
359
+ setShow(false);
360
+ }
361
+ }, []);
362
+ useEffect(() => {
363
+ document.addEventListener('click', onDocClick);
364
+ return () => document.removeEventListener('click', onDocClick);
365
+ }, [onDocClick]);
366
+ return (React__default.createElement("div", { className: "headerbar-apps", ref: containerEl, "data-test": "headerbar-apps" },
367
+ React__default.createElement("button", { onClick: handleVisibilityToggle, "data-test": "headerbar-apps-icon" },
368
+ React__default.createElement(IconApps24, { color: colors.white })),
369
+ show ? (React__default.createElement(AppMenu, { apps: apps, filter: filter, baseUrl: baseUrl, onFilterChange: handleFilterChange })) : null,
370
+ React__default.createElement("style", { jsx: true }, `
371
+ .headerbar-apps button {
372
+ display: block;
373
+ background: transparent;
374
+ padding: ${spacers.dp4} ${spacers.dp12} 0;
375
+ border: 0;
376
+ cursor: pointer;
377
+ height: 100%;
378
+ }
379
+ .headerbar-apps button:focus {
380
+ outline: 2px solid white;
381
+ outline-offset: -2px;
382
+ }
383
+ .headerbar-apps button:focus:not(:focus-visible) {
384
+ outline: none;
385
+ }
386
+ .headerbar-apps button:hover {
387
+ background: #1a557f;
388
+ }
389
+ .headerbar-apps button:active {
390
+ background: #104067;
391
+ }
392
+
393
+ .headerbar-apps {
394
+ position: relative;
395
+ height: 100%;
396
+ }
397
+ `)));
398
+ };
399
+ Apps.propTypes = {
400
+ apps: PropTypes.array.isRequired,
401
+ baseUrl: PropTypes.string,
402
+ };
403
+
404
+ const query = {
405
+ customLogo: {
406
+ resource: 'staticContent/logo_banner',
407
+ },
408
+ };
409
+ const pathExists = (data) => data &&
410
+ data.customLogo &&
411
+ data.customLogo.images &&
412
+ data.customLogo.images.png;
413
+ const LogoImage = () => {
414
+ const { loading, error, data } = useDataQuery(query);
415
+ if (loading) {
416
+ return null;
417
+ }
418
+ let Logo;
419
+ if (!error && pathExists(data)) {
420
+ Logo = (React__default.createElement("img", { alt: "Headerbar Logo", src: data.customLogo.images.png }));
421
+ }
422
+ else {
423
+ Logo = React__default.createElement(LogoIconWhite, null);
424
+ }
425
+ return (React__default.createElement("div", { className: "header-bar-logo" },
426
+ Logo,
427
+ React__default.createElement("style", { jsx: true }, `
428
+ .header-bar-logo {
429
+ min-width: 48px;
430
+ max-width: 250px;
431
+ height: 48px;
432
+ display: flex;
433
+ -webkit-box-pack: center;
434
+ justify-content: center;
435
+ -webkit-box-align: center;
436
+ align-items: center;
437
+ overflow: hidden;
438
+ }
439
+
440
+ .header-bar-logo svg {
441
+ height: 25px;
442
+ width: 27px;
443
+ }
444
+
445
+ .header-bar-logo img {
446
+ max-height: 100%;
447
+ min-height: auto;
448
+ width: auto;
449
+ }
450
+ `)));
451
+ };
452
+
453
+ const Logo = ({ baseUrl }) => {
454
+ return (React__default.createElement("div", { className: "headerbar-logo", "data-test": "headerbar-logo" },
455
+ React__default.createElement("a", { href: baseUrl },
456
+ React__default.createElement(LogoImage, null)),
457
+ React__default.createElement("style", { jsx: true }, `
458
+ .headerbar-logo {
459
+ box-sizing: border-box;
460
+ min-width: 49px;
461
+ max-height: 48px;
462
+ margin: 0 12px 0 0;
463
+ border-right: 1px solid rgba(32, 32, 32, 0.15);
464
+ }
465
+ .headerbar-logo:hover {
466
+ background-color: #1a557f;
467
+ }
468
+
469
+ .headerbar-logo a,
470
+ .headerbar-logo a:hover,
471
+ .headerbar-logo a:focus,
472
+ .headerbar-logo a:active,
473
+ .headerbar-logo a:visited {
474
+ user-select: none;
475
+ }
476
+ .headerbar-logo a:focus {
477
+ outline: 2px solid white;
478
+ outline-offset: -2px;
479
+ }
480
+ .headerbar-logo a:focus:not(:focus-visible) {
481
+ outline: none;
482
+ }
483
+ `)));
484
+ };
485
+
486
+ function icon(kind) {
487
+ if (kind === 'message') {
488
+ return React__default.createElement(IconMessages24, { color: colors.white });
489
+ }
490
+ else {
491
+ return React__default.createElement(IconMail24, { color: colors.white });
492
+ }
493
+ }
494
+ const NotificationIcon = (params) => {
495
+ const { count, href, kind, dataTestId } = params;
496
+ const notificationClassName = "headerbar-notification-icon " + kind;
497
+ return (React__default.createElement("a", { href: href, className: notificationClassName, "data-test": dataTestId },
498
+ icon(kind),
499
+ count > 0 && React__default.createElement("span", { "data-test": `${dataTestId}-count` }, count),
500
+ React__default.createElement("style", { jsx: true }, `
501
+ .headerbar-notification-icon {
502
+ position: relative;
503
+ margin: 0;
504
+ cursor: pointer;
505
+ padding: 0 ${spacers.dp12};
506
+ height: 100%;
507
+ display: flex;
508
+ align-items: center;
509
+ }
510
+ .headerbar-notification-icon:focus {
511
+ outline: 2px solid white;
512
+ outline-offset: -2px;
513
+ }
514
+ .headerbar-notification-icon:focus:not(:focus-visible) {
515
+ outline: none;
516
+ }
517
+ .headerbar-notification-icon:hover {
518
+ background: #1a557f;
519
+ }
520
+ .headerbar-notification-icon:active {
521
+ background: #104067;
522
+ }
523
+ .headerbar-notification-icon span {
524
+ display: flex;
525
+ justify-content: center;
526
+ align-items: center;
527
+ z-index: 1;
528
+ position: absolute;
529
+ top: 3px;
530
+ right: 2px;
531
+ min-width: 18px;
532
+ min-height: 18px;
533
+ border-radius: ${spacers.dp12};
534
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1),
535
+ 0 1px 2px 0 rgba(0, 0, 0, 0.06);
536
+ background-color: ${theme.secondary500};
537
+ color: ${colors.white};
538
+ font-size: 13px;
539
+ font-weight: 600;
540
+ line-height: 15px;
541
+ text-align: center;
542
+ cursor: inherit;
543
+ padding: 0 ${spacers.dp4};
544
+ }
545
+ `)));
546
+ };
547
+ NotificationIcon.defaultProps = {
548
+ count: 0,
549
+ };
550
+ NotificationIcon.propTypes = {
551
+ href: PropTypes.string.isRequired,
552
+ count: PropTypes.number,
553
+ dataTestId: PropTypes.string,
554
+ message: PropTypes.string,
555
+ kind: PropTypes.oneOf(['interpretation', 'message']),
556
+ };
557
+
558
+ /*
559
+ AUTHORITIES:
560
+ - ALL: superuser
561
+ - M_dhis-web-interpretation: access to interpretations app
562
+ - M_dhis-web-messaging: access to messaging app
563
+ */
564
+ const hasAuthority = (userAuthorities, authId) => Array.isArray(userAuthorities) &&
565
+ userAuthorities.some((userAuthId) => userAuthId === 'ALL' || userAuthId === authId);
566
+ const Notifications = (params) => {
567
+ const { interpretations, messages, userAuthorities, } = params;
568
+ const { baseUrl } = useConfig();
569
+ return (React__default.createElement("div", { className: "headerbar-notifications", "data-test": "headerbar-notifications" },
570
+ hasAuthority(userAuthorities, 'M_dhis-web-interpretation') && (React__default.createElement(NotificationIcon, { count: interpretations, href: joinPath(baseUrl, 'dhis-web-interpretation'), kind: "message", dataTestId: "headerbar-interpretations" })),
571
+ hasAuthority(userAuthorities, 'M_dhis-web-messaging') && (React__default.createElement(NotificationIcon, { message: "email", count: messages, href: joinPath(baseUrl, 'dhis-web-messaging'), kind: "interpretation", dataTestId: "headerbar-messages" })),
572
+ React__default.createElement("style", { jsx: true }, `
573
+ .headerbar-notifications {
574
+ user-select: none;
575
+ display: flex;
576
+ flex-direction: row;
577
+ align-items: center;
578
+ height: 100%;
579
+ }
580
+ `)));
581
+ };
582
+ Notifications.propTypes = {
583
+ interpretations: PropTypes.number,
584
+ messages: PropTypes.number,
585
+ userAuthorities: PropTypes.arrayOf(PropTypes.string),
586
+ };
587
+
588
+ const useDebugInfo = () => {
589
+ const { appName, appVersion, systemInfo } = useConfig();
590
+ return {
591
+ app_name: appName || null,
592
+ app_version: appVersion?.full || null,
593
+ dhis2_version: systemInfo?.version || null,
594
+ dhis2_revision: systemInfo?.revision || null,
595
+ };
596
+ };
597
+ const useFormattedDebugInfo = () => JSON.stringify(useDebugInfo(), undefined, 2);
598
+
599
+ const formatDebugInfoKey = (key) => {
600
+ const tokens = key.split('_');
601
+ return tokens
602
+ .map((token) => {
603
+ if (token.toLowerCase() === 'dhis2') {
604
+ return 'DHIS2';
605
+ }
606
+ else {
607
+ return token[0].toUpperCase() + token.substr(1).toLowerCase();
608
+ }
609
+ })
610
+ .join(' ');
611
+ };
612
+ function DebugInfoTable() {
613
+ const debugInfo = useDebugInfo();
614
+ return (React__default.createElement("table", { 'data-test': 'dhis2-ui-headerbar-debuginfotable' },
615
+ React__default.createElement("tbody", null, Object.keys(debugInfo).map((key) => (React__default.createElement("tr", { key: key },
616
+ React__default.createElement("td", { className: "debug-info-key" }, formatDebugInfoKey(key)),
617
+ React__default.createElement("td", null, debugInfo[key]))))),
618
+ React__default.createElement("style", { jsx: true }, `
619
+ table {
620
+ white-space: pre-wrap;
621
+ font-size: 14px;
622
+ line-height: 1.2;
623
+ color: ${colors.grey700};
624
+ font-famile: Menlo, Courier, monospace !important;
625
+ }
626
+ td {
627
+ padding: 3px 16px 3px 0;
628
+ }
629
+ .debug-info-key {
630
+ font-weight: bold;
631
+ }
632
+ `)));
633
+ }
634
+
635
+ function DebugInfoModal(params) {
636
+ const { onClose } = params;
637
+ const debugInfo = useFormattedDebugInfo();
638
+ const { show: showClipboardAlert } = useAlert('Debug information copied to clipboard', { duration: 3000 });
639
+ const copyDebugInfo = () => {
640
+ navigator.clipboard.writeText(debugInfo);
641
+ onClose();
642
+ showClipboardAlert();
643
+ };
644
+ return (React__default.createElement(Modal, { position: "middle", dataTest: "dhis2-ui-headerbar-debuginfomodal" },
645
+ React__default.createElement(ModalTitle, null, i18n.t('Debug info')),
646
+ React__default.createElement(ModalContent, null,
647
+ React__default.createElement(DebugInfoTable, null)),
648
+ React__default.createElement(ModalActions, null,
649
+ React__default.createElement(ButtonStrip, { end: true },
650
+ React__default.createElement(Button, { onClick: () => onClose() }, i18n.t('Close')),
651
+ React__default.createElement(Button, { primary: true, onClick: copyDebugInfo, dataTest: "dhis2-ui-headerbar-debuginfomodal-copybutton" }, i18n.t('Copy debug info'))))));
652
+ }
653
+ DebugInfoModal.propTypes = {
654
+ onClose: PropTypes.func.isRequired,
655
+ };
656
+
657
+ const DebugInfoMenuItem = (params) => {
658
+ const { hideProfileMenu, showDebugInfoModal } = params;
659
+ const debugInfo = useDebugInfo();
660
+ const openDebugModal = () => {
661
+ hideProfileMenu();
662
+ showDebugInfoModal();
663
+ };
664
+ const debugInfoLabel = (React__default.createElement("div", { className: "root" },
665
+ React__default.createElement("div", { className: "instance-info version", "data-test": "dhis2-ui-headerbar-instanceinfo" }, debugInfo.dhis2_version
666
+ ? i18n.t('DHIS2 {{dhis2Version}}', {
667
+ dhis2Version: debugInfo.dhis2_version,
668
+ })
669
+ : i18n.t('DHIS2 version unknown')),
670
+ React__default.createElement("div", { className: "version", "data-test": "dhis2-ui-headerbar-appinfo" }, debugInfo.app_name
671
+ ? debugInfo.app_version
672
+ ? `${debugInfo.app_name} ${debugInfo.app_version}`
673
+ : i18n.t('{{appName}} version unknown', {
674
+ appName: debugInfo.app_name,
675
+ })
676
+ : debugInfo.app_version
677
+ ? i18n.t('App {{appVersion}}', {
678
+ appVersion: debugInfo.app_version,
679
+ })
680
+ : i18n.t('App version unknown')),
681
+ React__default.createElement("style", { jsx: true }, `
682
+ .root {
683
+ color: ${colors.grey700};
684
+ font-style: italic;
685
+ font-size: 14px;
686
+ line-height: 17px;
687
+ }
688
+ .instance-info {
689
+ margin-bottom: 4px;
690
+ }
691
+ .version {
692
+ white-space: no-wrap;
693
+ }
694
+ `)));
695
+ return (React__default.createElement(MenuItem, { dense: true, onClick: openDebugModal, label: debugInfoLabel, dataTest: "dhis2-ui-headerbar-debuginfo" }));
696
+ };
697
+ DebugInfoMenuItem.propTypes = {
698
+ hideProfileMenu: PropTypes.func.isRequired,
699
+ showDebugInfoModal: PropTypes.func.isRequired,
700
+ };
701
+
702
+ const ProfileName = (params) => {
703
+ const { children } = params;
704
+ return (React__default.createElement("div", { className: "headerbar-profile-username", "data-test": "headerbar-profile-username" },
705
+ children,
706
+ React__default.createElement("style", { jsx: true }, `
707
+ .headerbar-profile-username {
708
+ margin-bottom: 3px;
709
+ font-size: 16px;
710
+ line-height: 19px;
711
+ }
712
+ `)));
713
+ };
714
+ ProfileName.propTypes = {
715
+ children: PropTypes.string,
716
+ };
717
+ const ProfileEmail = (params) => {
718
+ const { children } = params;
719
+ return (React__default.createElement("div", { className: "headerbar-profile-user-email", "data-test": "headerbar-profile-user-email" },
720
+ children,
721
+ React__default.createElement("style", { jsx: true }, `
722
+ .headerbar-profile-user-email {
723
+ margin-bottom: 6px;
724
+ font-size: 14px;
725
+ line-height: 16px;
726
+ }
727
+ `)));
728
+ };
729
+ ProfileEmail.propTypes = {
730
+ children: PropTypes.string,
731
+ };
732
+ const ProfileEdit = (params) => {
733
+ const { children } = params;
734
+ const { baseUrl } = useConfig();
735
+ return (React__default.createElement("a", { className: "headerbar-profile-edit-profile-link", href: joinPath(baseUrl, 'dhis-web-user-profile/#/profile'), "data-test": "headerbar-profile-edit-profile-link" },
736
+ children,
737
+ React__default.createElement("style", { jsx: true }, `
738
+ .headerbar-profile-edit-profile-link {
739
+ color: rgba(0, 0, 0, 0.87);
740
+ font-size: 12px;
741
+ line-height: 14px;
742
+ text-decoration: underline !important;
743
+ cursor: pointer;
744
+ }
745
+ `)));
746
+ };
747
+ ProfileEdit.propTypes = {
748
+ children: PropTypes.string,
749
+ };
750
+ const ProfileDetails = (params) => {
751
+ const { name, email } = params;
752
+ return (React__default.createElement("div", { className: "headerbar-profile-details" },
753
+ React__default.createElement(ProfileName, null, name),
754
+ React__default.createElement(ProfileEmail, null, email),
755
+ React__default.createElement(ProfileEdit, null, i18n.t('Edit profile')),
756
+ React__default.createElement("style", { jsx: true }, `
757
+ .headerbar-profile-details {
758
+ display: flex;
759
+ flex-direction: column;
760
+ margin-left: 20px;
761
+ color: #000;
762
+ font-size: 14px;
763
+ font-weight: 400;
764
+ }
765
+ `)));
766
+ };
767
+ ProfileDetails.propTypes = {
768
+ email: PropTypes.string,
769
+ name: PropTypes.string,
770
+ };
771
+ const ProfileHeader = (params) => {
772
+ const { name, email, avatarId } = params;
773
+ return (React__default.createElement("div", { className: "headerbar-profile-header" },
774
+ React__default.createElement(UserAvatar, { avatarId: avatarId, name: name, dataTest: "headerbar-profile-menu-icon", large: true }),
775
+ React__default.createElement(ProfileDetails, { name: name, email: email }),
776
+ React__default.createElement("style", { jsx: true }, `
777
+ .headerbar-profile-header {
778
+ display: flex;
779
+ flex-direction: row;
780
+ margin-left: 24px;
781
+ padding-top: 20px;
782
+ }
783
+ `)));
784
+ };
785
+ ProfileHeader.propTypes = {
786
+ avatarId: PropTypes.string,
787
+ email: PropTypes.string,
788
+ name: PropTypes.string,
789
+ };
790
+
791
+ const headerBarContext = createContext({
792
+ updateAvailable: false,
793
+ onApplyAvailableUpdate: () => { },
794
+ });
795
+ const HeaderBarContextProvider = (params) => {
796
+ const { updateAvailable, onApplyAvailableUpdate, children, } = params;
797
+ return (React__default.createElement(headerBarContext.Provider, { value: { updateAvailable, onApplyAvailableUpdate } }, children));
798
+ };
799
+ HeaderBarContextProvider.propTypes = {
800
+ children: PropTypes.node,
801
+ updateAvailable: PropTypes.bool,
802
+ onApplyAvailableUpdate: PropTypes.func,
803
+ };
804
+ const useHeaderBarContext = () => useContext(headerBarContext);
805
+
806
+ function UpdateNotification(params) {
807
+ const { hideProfileMenu } = params;
808
+ const { appName } = useConfig();
809
+ const { updateAvailable, onApplyAvailableUpdate } = useHeaderBarContext();
810
+ const onClick = () => {
811
+ hideProfileMenu();
812
+ onApplyAvailableUpdate?.();
813
+ };
814
+ const updateNotificationLabel = (React__default.createElement("div", { className: "root" },
815
+ React__default.createElement("div", { className: "badge" }),
816
+ React__default.createElement("div", { className: "spacer" }),
817
+ React__default.createElement("div", { className: "message" },
818
+ appName
819
+ ? i18n.t('New {{appName}} version available', { appName })
820
+ : i18n.t('New app version available'),
821
+ React__default.createElement("br", null),
822
+ i18n.t('Click to reload')),
823
+ React__default.createElement("style", { jsx: true }, `
824
+ .root {
825
+ display: flex;
826
+ flex-direction: row;
827
+ align-items: center;
828
+ font-size: 14px;
829
+ line-height: 17px;
830
+ }
831
+ .badge {
832
+ display: inline-block;
833
+ width: 12px;
834
+ height: 12px;
835
+ margin: 0 8px;
836
+ border-radius: 6px;
837
+ background-color: ${colors.blue600};
838
+ }
839
+ .spacer {
840
+ display: inline-block;
841
+ width: 8px;
842
+ }
843
+ .message {
844
+ display: inline-block;
845
+ }
846
+ `)));
847
+ return updateAvailable ? (React__default.createElement(MenuItem, { dense: true, onClick: onClick, label: updateNotificationLabel, dataTest: "dhis2-ui-headerbar-updatenotification" })) : null;
848
+ }
849
+ UpdateNotification.propTypes = {
850
+ hideProfileMenu: PropTypes.func.isRequired,
851
+ };
852
+
853
+ const LoadingMask = () => (React__default.createElement(Layer, { translucent: true, disablePortal: true, dataTest: "headerbar-profile-menu-loading-mask" },
854
+ React__default.createElement(Center, null,
855
+ React__default.createElement(CircularLoader, null))));
856
+ const ProfileContents = (params) => {
857
+ const { name, email, avatarId, helpUrl, hideProfileMenu, showDebugInfoModal, } = params;
858
+ const { baseUrl } = useConfig();
859
+ const [loading, setLoading] = useState(false);
860
+ return (React__default.createElement(Card, null,
861
+ React__default.createElement("div", { className: "headerbar-profile-contents" },
862
+ React__default.createElement(ProfileHeader, { name: name, email: email, avatarId: avatarId }),
863
+ React__default.createElement(Divider, { margin: "13px 0 7px 0" }),
864
+ React__default.createElement("ul", { "data-test": "headerbar-profile-menu" },
865
+ React__default.createElement(MenuItem, { href: joinPath(baseUrl, 'dhis-web-user-profile/#/settings'), label: i18n.t('Settings'), value: "settings", icon: React__default.createElement(IconSettings24, { color: colors.grey700 }) }),
866
+ React__default.createElement(MenuItem, { href: joinPath(baseUrl, 'dhis-web-user-profile/#/account'), label: i18n.t('Account'), value: "account", icon: React__default.createElement(IconUser24, { color: colors.grey700 }) }),
867
+ helpUrl && (React__default.createElement(MenuItem, { href: helpUrl, label: i18n.t('Help'), value: "help", icon: React__default.createElement(IconQuestion24, { color: colors.grey700 }) })),
868
+ React__default.createElement(MenuItem, { href: joinPath(baseUrl, 'dhis-web-user-profile/#/aboutPage'), label: i18n.t('About DHIS2'), value: "about", icon: React__default.createElement(IconInfo24, { color: colors.grey700 }) }),
869
+ React__default.createElement(MenuItem, { href: joinPath(baseUrl, 'dhis-web-commons-security/logout.action'),
870
+ // NB: By MenuItem implementation, this callback
871
+ // overwrites default navigation behavior but maintains
872
+ // the href attribute
873
+ onClick: async () => {
874
+ setLoading(true);
875
+ await clearSensitiveCaches();
876
+ setLoading(false);
877
+ window.location.assign(joinPath(baseUrl, 'dhis-web-commons-security/logout.action'));
878
+ }, label: i18n.t('Logout'), value: "logout", icon: React__default.createElement(IconLogOut24, { color: colors.grey700 }) }),
879
+ React__default.createElement(MenuDivider, { dense: true }),
880
+ React__default.createElement(DebugInfoMenuItem, { hideProfileMenu: hideProfileMenu, showDebugInfoModal: showDebugInfoModal }),
881
+ React__default.createElement(UpdateNotification, { hideProfileMenu: hideProfileMenu }))),
882
+ loading && React__default.createElement(LoadingMask, null),
883
+ React__default.createElement("style", { jsx: true }, `
884
+ .headerbar-profile-contents {
885
+ width: 100%;
886
+ padding: 0;
887
+ }
888
+
889
+ .headerbar-profile-contents ul {
890
+ padding: 0;
891
+ margin: 0;
892
+ }
893
+
894
+ .headerbar-profile-contents a {
895
+ display: inline-flex !important;
896
+ }
897
+
898
+ .headerbar-profile-contents a,
899
+ .headerbar-profile-contents a:hover,
900
+ .headerbar-profile-contents a:focus,
901
+ .headerbar-profile-contents a:active,
902
+ .headerbar-profile-contents a:visited {
903
+ text-decoration: none;
904
+ display: block;
905
+ }
906
+ `)));
907
+ };
908
+ ProfileContents.propTypes = {
909
+ hideProfileMenu: PropTypes.func.isRequired,
910
+ showDebugInfoModal: PropTypes.func.isRequired,
911
+ avatarId: PropTypes.string,
912
+ email: PropTypes.string,
913
+ helpUrl: PropTypes.string,
914
+ name: PropTypes.string,
915
+ };
916
+ const ProfileMenu = (params) => {
917
+ const { ...props } = params;
918
+ return (React__default.createElement("div", { className: "headerbar-profile-menu", "data-test": "headerbar-profile-menu" },
919
+ React__default.createElement(ProfileContents, { ...props }),
920
+ React__default.createElement("style", { jsx: true }, `
921
+ .headerbar-profile-menu {
922
+ z-index: 10000;
923
+ position: absolute;
924
+ right: 4px;
925
+ width: 320px;
926
+ }
927
+ `)));
928
+ };
929
+ ProfileMenu.propTypes = {
930
+ hideProfileMenu: PropTypes.func.isRequired,
931
+ showDebugInfoModal: PropTypes.func.isRequired,
932
+ avatarId: PropTypes.string,
933
+ email: PropTypes.string,
934
+ helpUrl: PropTypes.string,
935
+ name: PropTypes.string,
936
+ };
937
+
938
+ const useOnDocClick = (containerRef, hide) => {
939
+ const onDocClick = useMemo(() => {
940
+ return (evt) => {
941
+ if (!containerRef.current) {
942
+ return null;
943
+ }
944
+ if (!containerRef.current.contains(evt.target)) {
945
+ hide();
946
+ }
947
+ return null;
948
+ };
949
+ }, [containerRef, hide]);
950
+ useEffect(() => {
951
+ document.addEventListener('click', onDocClick);
952
+ return () => {
953
+ document.removeEventListener('click', onDocClick);
954
+ };
955
+ }, [onDocClick]);
956
+ };
957
+
958
+ const Profile = (params) => {
959
+ const { name, email, avatarId, helpUrl } = params;
960
+ const [showProfileMenu, setShowProfileMenu] = useState(false);
961
+ const [showDebugInfoModal, setShowDebugInfoModal] = useState(false);
962
+ const hideProfileMenu = useCallback(() => setShowProfileMenu(false), [setShowProfileMenu]);
963
+ const toggleProfileMenu = useCallback(() => setShowProfileMenu((show) => !show), [setShowProfileMenu]);
964
+ const containerRef = useRef(null);
965
+ useOnDocClick(containerRef, hideProfileMenu);
966
+ return (React__default.createElement("div", { ref: containerRef, "data-test": "headerbar-profile", className: "headerbar-profile" },
967
+ React__default.createElement("button", { className: "headerbar-profile-btn", onClick: toggleProfileMenu },
968
+ React__default.createElement(UserAvatar, { avatarId: avatarId, name: name, dataTest: "headerbar-profile-icon", medium: true })),
969
+ showProfileMenu && (React__default.createElement(ProfileMenu, { avatarId: avatarId, name: name, email: email, helpUrl: helpUrl, hideProfileMenu: hideProfileMenu, showDebugInfoModal: () => {
970
+ setShowDebugInfoModal(true);
971
+ } })),
972
+ showDebugInfoModal && (React__default.createElement(DebugInfoModal, { onClose: () => {
973
+ setShowDebugInfoModal(false);
974
+ } })),
975
+ React__default.createElement("style", { jsx: true }, `
976
+ .headerbar-profile {
977
+ position: relative;
978
+ height: 100%;
979
+ }
980
+
981
+ .headerbar-profile-btn {
982
+ background: transparent;
983
+ padding: 6px;
984
+ border: 0;
985
+ cursor: pointer;
986
+ }
987
+ .headerbar-profile-btn:focus {
988
+ outline: 2px solid white;
989
+ outline-offset: -2px;
990
+ }
991
+ .headerbar-profile-btn:focus:not(:focus-visible) {
992
+ outline: none;
993
+ }
994
+ .headerbar-profile-btn:hover {
995
+ background: #1a557f;
996
+ }
997
+ .headerbar-profile-btn:active {
998
+ background: #104067;
999
+ }
1000
+ `)));
1001
+ };
1002
+ Profile.propTypes = {
1003
+ name: PropTypes.string.isRequired,
1004
+ avatarId: PropTypes.string,
1005
+ email: PropTypes.string,
1006
+ helpUrl: PropTypes.string,
1007
+ };
1008
+
1009
+ const Title = (params) => {
1010
+ const { app, instance } = params;
1011
+ return (React__default.createElement("div", { className: "headerbar-title", "data-test": "headerbar-title" },
1012
+ app ? `${instance} - ${app}` : `${instance}`,
1013
+ React__default.createElement("style", { jsx: true }, `
1014
+ .headerbar-title {
1015
+ overflow: hidden;
1016
+ text-overflow: ellipsis;
1017
+ font-size: 14px;
1018
+ font-weight: 500;
1019
+ letter-spacing: 0.01em;
1020
+ white-space: nowrap;
1021
+ }
1022
+ `)));
1023
+ };
1024
+ Title.propTypes = {
1025
+ app: PropTypes.string,
1026
+ instance: PropTypes.string,
1027
+ };
1028
+
1029
+ const HeaderBar = ({ appName, data, className }) => {
1030
+ const loading = false;
1031
+ const error = null;
1032
+ // See https://jira.dhis2.org/browse/LIBS-180
1033
+ if (!loading && !error) {
1034
+ // TODO: This will run every render which is probably wrong!
1035
+ // Also, setting the global locale shouldn't be done in the headerbar
1036
+ const locale = data.user['settings']['keyUiLocale'] || 'en';
1037
+ i18n.setDefaultNamespace('default');
1038
+ i18n.changeLanguage(locale);
1039
+ }
1040
+ {
1041
+ /* TODO: Find better way to attach base URL */
1042
+ }
1043
+ const baseUrl = '../../../';
1044
+ return (React__default.createElement("header", { className: className },
1045
+ React__default.createElement("div", { className: "main" }, !loading && !error && (React__default.createElement(React__default.Fragment, null,
1046
+ React__default.createElement(Logo, { baseUrl: baseUrl }),
1047
+ React__default.createElement(Title, { app: appName, instance: data.title['applicationTitle'] }),
1048
+ React__default.createElement("div", { className: "right-control-spacer" }),
1049
+ React__default.createElement(Notifications, { interpretations: data.notifications.unreadInterpretations, messages: data.notifications.unreadMessageConversations, userAuthorities: data.user['authorities'] }),
1050
+ React__default.createElement(Apps, { apps: data.apps, baseUrl: baseUrl }),
1051
+ React__default.createElement(Profile, { name: data.user['name'], email: data.user['email'], avatarId: (data.user['avatar'] || {})['id'], helpUrl: data.help.helpPageLink })))),
1052
+ React__default.createElement("style", { jsx: true }, `
1053
+ .main {
1054
+ display: flex;
1055
+ flex-direction: row;
1056
+ align-items: center;
1057
+ justify-content: space-between;
1058
+ background-color: #2c6693;
1059
+ color: ${colors.white};
1060
+ height: 48px;
1061
+ }
1062
+ .right-control-spacer {
1063
+ margin-left: auto;
1064
+ }
1065
+ `)));
1066
+ };
1067
+ HeaderBar.propTypes = {
1068
+ appName: PropTypes.string,
1069
+ data: HeaderBarData,
1070
+ className: PropTypes.string,
1071
+ updateAvailable: PropTypes.bool,
1072
+ onApplyAvailableUpdate: PropTypes.func,
1073
+ };
1074
+
1075
+ class HeaderBarComponent extends ReactWrapperComponent {
1076
+ constructor(elementRef, httpClient) {
1077
+ super(elementRef);
1078
+ this.httpClient = httpClient;
1079
+ }
1080
+ async ngAfterViewInit() {
1081
+ if (!this.elementRef)
1082
+ throw new Error('No element ref');
1083
+ this.reactDomRoot = ReactDOM.createRoot(this.elementRef.nativeElement);
1084
+ const manifest = await firstValueFrom(this.httpClient.manifest());
1085
+ const data = await firstValueFrom(new HeaderBarData(this.httpClient).get());
1086
+ this.component = data
1087
+ ? () => (React__default.createElement(DataProvider, null, React__default.createElement(HeaderBar, { appName: manifest?.name, data: data })))
1088
+ : () => React__default.createElement("div", null, "Something went wrong");
1089
+ this.render();
1090
+ }
1091
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HeaderBarComponent, deps: [{ token: i0.ElementRef }, { token: i1.NgxDhis2HttpClientService }], target: i0.ɵɵFactoryTarget.Component }); }
1092
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: HeaderBarComponent, selector: "ng-dhis2-ui-header-bar", usesInheritance: true, ngImport: i0, template: `<ng-container *ngIf="data$ | async as data"> </ng-container>`, isInline: true, dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }] }); }
1093
+ }
1094
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HeaderBarComponent, decorators: [{
1095
+ type: Component,
1096
+ args: [{
1097
+ selector: 'ng-dhis2-ui-header-bar',
1098
+ template: `<ng-container *ngIf="data$ | async as data"> </ng-container>`,
1099
+ }]
1100
+ }], ctorParameters: function () { return [{ type: i0.ElementRef }, { type: i1.NgxDhis2HttpClientService }]; } });
1101
+
1102
+ class HeaderBarModule {
1103
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HeaderBarModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1104
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: HeaderBarModule, declarations: [HeaderBarComponent], imports: [CommonModule, SharedDhis2UiModule], exports: [HeaderBarComponent] }); }
1105
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HeaderBarModule, imports: [CommonModule, SharedDhis2UiModule] }); }
1106
+ }
1107
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HeaderBarModule, decorators: [{
1108
+ type: NgModule,
1109
+ args: [{
1110
+ imports: [CommonModule, SharedDhis2UiModule],
1111
+ declarations: [HeaderBarComponent],
1112
+ exports: [HeaderBarComponent],
1113
+ }]
1114
+ }] });
1115
+
1116
+ // Copyright 2023 UDSM DHIS2 Lab. All rights reserved.
1117
+ // Use of this source code is governed by a BSD-style
1118
+ // license that can be found in the LICENSE file.
1119
+
1120
+ class ButtonComponent extends ReactWrapperComponent {
1121
+ constructor() {
1122
+ super(...arguments);
1123
+ this.buttonClick = new EventEmitter();
1124
+ this.props = {
1125
+ onClick: (e) => {
1126
+ this.onButtonClick(e);
1127
+ },
1128
+ };
1129
+ this.component = Button$1;
1130
+ }
1131
+ ngOnInit() {
1132
+ this.props = {
1133
+ ...this.props,
1134
+ primary: this.primary,
1135
+ secondary: this.secondary,
1136
+ destructive: this.destructive,
1137
+ small: this.small,
1138
+ large: this.large,
1139
+ };
1140
+ }
1141
+ onButtonClick(event) {
1142
+ this.buttonClick.emit(event);
1143
+ }
1144
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ButtonComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1145
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: ButtonComponent, selector: "ng-dhis2-ui-button", inputs: { label: "label", primary: "primary", secondary: "secondary", destructive: "destructive", disabled: "disabled", small: "small", large: "large" }, outputs: { buttonClick: "buttonClick" }, usesInheritance: true, ngImport: i0, template: '<ng-content></ng-content>', isInline: true }); }
1146
+ }
1147
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ButtonComponent, decorators: [{
1148
+ type: Component,
1149
+ args: [{
1150
+ selector: 'ng-dhis2-ui-button',
1151
+ template: '<ng-content></ng-content>',
1152
+ }]
1153
+ }], propDecorators: { label: [{
1154
+ type: Input
1155
+ }], primary: [{
1156
+ type: Input
1157
+ }], secondary: [{
1158
+ type: Input
1159
+ }], destructive: [{
1160
+ type: Input
1161
+ }], disabled: [{
1162
+ type: Input
1163
+ }], small: [{
1164
+ type: Input
1165
+ }], large: [{
1166
+ type: Input
1167
+ }], buttonClick: [{
1168
+ type: Output
1169
+ }] } });
1170
+
1171
+ class ButtonModule {
1172
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ButtonModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1173
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: ButtonModule, declarations: [ButtonComponent], imports: [CommonModule, SharedDhis2UiModule], exports: [ButtonComponent] }); }
1174
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ButtonModule, imports: [CommonModule, SharedDhis2UiModule] }); }
1175
+ }
1176
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ButtonModule, decorators: [{
1177
+ type: NgModule,
1178
+ args: [{
1179
+ declarations: [ButtonComponent],
1180
+ exports: [ButtonComponent],
1181
+ imports: [CommonModule, SharedDhis2UiModule],
1182
+ }]
1183
+ }] });
1184
+
1185
+ // Copyright 2023 UDSM DHIS2 Lab. All rights reserved.
1186
+ // Use of this source code is governed by a BSD-style
1187
+ // license that can be found in the LICENSE file.
1188
+
1189
+ class AppShellComponent extends ReactWrapperComponent {
1190
+ constructor() {
1191
+ super(...arguments);
1192
+ this.appConfig = {
1193
+ // url: process.env.REACT_APP_DHIS2_BASE_URL,
1194
+ // appName: process.env.REACT_APP_DHIS2_APP_NAME || '',
1195
+ // appVersion: process.env.REACT_APP_DHIS2_APP_VERSION || '',
1196
+ // apiVersion: parseInt(process.env.REACT_APP_DHIS2_API_VERSION),
1197
+ // pwaEnabled: process.env.REACT_APP_DHIS2_APP_PWA_ENABLED === 'true',
1198
+ // plugin: process.env.REACT_APP_DHIS2_APP_PLUGIN === 'true',
1199
+ };
1200
+ this.component = React__default.createElement(AppAdapter, { ...this.appConfig },
1201
+ React__default.createElement(React__default.Suspense, { fallback: React__default.createElement(Layer$1, { level: layers.alert },
1202
+ React__default.createElement(CenteredContent, null,
1203
+ React__default.createElement(CircularLoader$1, null))) }));
1204
+ }
1205
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AppShellComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
1206
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: AppShellComponent, selector: "ng-dhis2-ui-app-shell", usesInheritance: true, ngImport: i0, template: '<ng-content></ng-content>', isInline: true }); }
1207
+ }
1208
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AppShellComponent, decorators: [{
1209
+ type: Component,
1210
+ args: [{
1211
+ selector: 'ng-dhis2-ui-app-shell',
1212
+ template: '<ng-content></ng-content>',
1213
+ }]
1214
+ }] });
1215
+
1216
+ class AppShellModule {
1217
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AppShellModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1218
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: AppShellModule, declarations: [AppShellComponent], imports: [CommonModule, SharedDhis2UiModule], exports: [AppShellComponent] }); }
1219
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AppShellModule, imports: [CommonModule, SharedDhis2UiModule] }); }
1220
+ }
1221
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: AppShellModule, decorators: [{
1222
+ type: NgModule,
1223
+ args: [{
1224
+ declarations: [AppShellComponent],
1225
+ imports: [CommonModule, SharedDhis2UiModule],
1226
+ exports: [AppShellComponent],
1227
+ }]
1228
+ }] });
1229
+
1230
+ // Copyright 2023 UDSM DHIS2 Lab. All rights reserved.
1231
+ // Use of this source code is governed by a BSD-style
1232
+ // license that can be found in the LICENSE file.
1233
+
1234
+ /**
1235
+ * Generated bundle index. Do not edit.
1236
+ */
1237
+
1238
+ export { AppShellComponent, AppShellModule, ButtonComponent, ButtonModule, HeaderBarComponent, HeaderBarModule, NgDhis2UiModule };
1239
+ //# sourceMappingURL=iapps-ng-dhis2-ui.mjs.map