web-mojo 2.1.549 → 2.1.626
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.
- package/dist/admin.cjs.js +1 -1
- package/dist/admin.cjs.js.map +1 -1
- package/dist/admin.es.js +19 -10
- package/dist/admin.es.js.map +1 -1
- package/dist/auth.cjs.js +1 -1
- package/dist/auth.cjs.js.map +1 -1
- package/dist/auth.es.js +3 -3
- package/dist/auth.es.js.map +1 -1
- package/dist/charts.cjs.js +1 -1
- package/dist/charts.es.js +3 -3
- package/dist/chunks/{ChatView-S7z1axRn.js → ChatView-0e0k3QSK.js} +2 -2
- package/dist/chunks/{ChatView-S7z1axRn.js.map → ChatView-0e0k3QSK.js.map} +1 -1
- package/dist/chunks/{ChatView-DUmQtrvk.js → ChatView-BxbA5ob6.js} +6 -6
- package/dist/chunks/{ChatView-DUmQtrvk.js.map → ChatView-BxbA5ob6.js.map} +1 -1
- package/dist/chunks/{ContextMenu-DhbGpVSL.js → ContextMenu-ATInMbaI.js} +3 -3
- package/dist/chunks/{ContextMenu-DhbGpVSL.js.map → ContextMenu-ATInMbaI.js.map} +1 -1
- package/dist/chunks/{ContextMenu-BXCBFtAP.js → ContextMenu-Ckttp-rD.js} +10 -3
- package/dist/chunks/{ContextMenu-BXCBFtAP.js.map → ContextMenu-Ckttp-rD.js.map} +1 -1
- package/dist/chunks/{DataView-Ek4BYTEr.js → DataView-BQOKPprj.js} +2 -2
- package/dist/chunks/{DataView-Ek4BYTEr.js.map → DataView-BQOKPprj.js.map} +1 -1
- package/dist/chunks/{DataView-CbcfRwkD.js → DataView-BsWK0Ul7.js} +2 -2
- package/dist/chunks/{DataView-CbcfRwkD.js.map → DataView-BsWK0Ul7.js.map} +1 -1
- package/dist/chunks/{Dialog-B4CbgRxK.js → Dialog-aTTrQ6uW.js} +5 -5
- package/dist/chunks/{Dialog-B4CbgRxK.js.map → Dialog-aTTrQ6uW.js.map} +1 -1
- package/dist/chunks/{Dialog-sS4tcneL.js → Dialog-t4J3qOjC.js} +2 -2
- package/dist/chunks/{Dialog-sS4tcneL.js.map → Dialog-t4J3qOjC.js.map} +1 -1
- package/dist/chunks/{FormView-Nh9T4RYF.js → FormView-DjypPrEw.js} +2 -2
- package/dist/chunks/{FormView-Nh9T4RYF.js.map → FormView-DjypPrEw.js.map} +1 -1
- package/dist/chunks/{FormView-BochVM_E.js → FormView-DvKBq99H.js} +2 -2
- package/dist/chunks/{FormView-BochVM_E.js.map → FormView-DvKBq99H.js.map} +1 -1
- package/dist/chunks/{MetricsChart-BnAXZSYf.js → MetricsChart-BHB2dubV.js} +2 -2
- package/dist/chunks/{MetricsChart-BnAXZSYf.js.map → MetricsChart-BHB2dubV.js.map} +1 -1
- package/dist/chunks/{MetricsChart-DMAFQpiZ.js → MetricsChart-DJ_AMjGg.js} +3 -3
- package/dist/chunks/{MetricsChart-DMAFQpiZ.js.map → MetricsChart-DJ_AMjGg.js.map} +1 -1
- package/dist/chunks/{PDFViewer-UG4HgNRl.js → PDFViewer-DXoC6y5o.js} +2 -2
- package/dist/chunks/{PDFViewer-UG4HgNRl.js.map → PDFViewer-DXoC6y5o.js.map} +1 -1
- package/dist/chunks/{PDFViewer-DFGU5qEx.js → PDFViewer-kDd18mqo.js} +3 -3
- package/dist/chunks/{PDFViewer-DFGU5qEx.js.map → PDFViewer-kDd18mqo.js.map} +1 -1
- package/dist/chunks/{Page-3EI2SKd6.js → Page-BrWp1FsC.js} +2 -2
- package/dist/chunks/{Page-3EI2SKd6.js.map → Page-BrWp1FsC.js.map} +1 -1
- package/dist/chunks/{Page-x-paxTWR.js → Page-DgKaDD9p.js} +2 -2
- package/dist/chunks/{Page-x-paxTWR.js.map → Page-DgKaDD9p.js.map} +1 -1
- package/dist/chunks/TopNav-DGtRj5F4.js +1039 -0
- package/dist/chunks/TopNav-DGtRj5F4.js.map +1 -0
- package/dist/chunks/TopNav-DoAl9Rpd.js +2 -0
- package/dist/chunks/TopNav-DoAl9Rpd.js.map +1 -0
- package/dist/chunks/{WebApp-Co0le5eH.js → WebApp-DWIriU_w.js} +2 -2
- package/dist/chunks/{WebApp-Co0le5eH.js.map → WebApp-DWIriU_w.js.map} +1 -1
- package/dist/chunks/{WebApp-gPOds7Ie.js → WebApp-ciExPk0c.js} +30 -14
- package/dist/chunks/{WebApp-gPOds7Ie.js.map → WebApp-ciExPk0c.js.map} +1 -1
- package/dist/css/web-mojo.css +1 -1
- package/dist/docit.cjs.js +1 -1
- package/dist/docit.es.js +5 -5
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +237 -449
- package/dist/index.es.js.map +1 -1
- package/dist/lightbox.cjs.js +1 -1
- package/dist/lightbox.es.js +4 -4
- package/dist/portal.css +222 -0
- package/dist/table.css +0 -13
- package/package.json +1 -1
- package/dist/chunks/TopNav-BzW5Us7F.js +0 -2
- package/dist/chunks/TopNav-BzW5Us7F.js.map +0 -1
- package/dist/chunks/TopNav-cGCRUequ.js +0 -381
- package/dist/chunks/TopNav-cGCRUequ.js.map +0 -1
package/dist/index.es.js
CHANGED
|
@@ -1,442 +1,16 @@
|
|
|
1
|
-
import { V as View, W as WebApp, d as dataFormatter, M as Mustache } from "./chunks/WebApp-
|
|
2
|
-
import { B, D, g, E, h, r, R, b, a, c, e, f } from "./chunks/WebApp-
|
|
3
|
-
import { P as Page } from "./chunks/Page-
|
|
4
|
-
import { G as GroupList, T as ToastService, U as User, a as Group } from "./chunks/ContextMenu-
|
|
5
|
-
import { C, b as b2, c as c2, M, f as f2, g as g2, h as h2, i, j, e as e2, d } from "./chunks/ContextMenu-
|
|
6
|
-
import { M as Member } from "./chunks/ChatView-
|
|
7
|
-
import { f as f3, e as e3, C as C2, E as E2, k, j as j2, r as r2, t, s, x, z, y, u, w, v, F, g as g3, ap, aq, D as D2, I, B as B2, A, H, Q, R as R2, G, N, O, J, K, Y, Z, a2, a3, $, _, a0, a1, a5, a7, a6, a4, L, c as c3, a8, a9, l, n, m, ab, aa, ae, ac, ad, P, aj, an, ak, al, am, af, ag, ah, ao, ai, W, X, U, V, S, i as i2, h as h3, o, q, p, d as d2, b as b3, a as a10, T, ar, aw, av, as, at, au } from "./chunks/ChatView-
|
|
8
|
-
import { T as TopNav } from "./chunks/TopNav-
|
|
1
|
+
import { V as View, W as WebApp, d as dataFormatter, M as Mustache } from "./chunks/WebApp-ciExPk0c.js";
|
|
2
|
+
import { B, D, g, E, h, r, R, b, a, c, e, f } from "./chunks/WebApp-ciExPk0c.js";
|
|
3
|
+
import { P as Page } from "./chunks/Page-BrWp1FsC.js";
|
|
4
|
+
import { G as GroupList, T as ToastService, U as User, a as Group } from "./chunks/ContextMenu-Ckttp-rD.js";
|
|
5
|
+
import { C, b as b2, c as c2, M, f as f2, g as g2, h as h2, i, j, e as e2, d } from "./chunks/ContextMenu-Ckttp-rD.js";
|
|
6
|
+
import { M as Member } from "./chunks/ChatView-BxbA5ob6.js";
|
|
7
|
+
import { f as f3, e as e3, C as C2, E as E2, k, j as j2, r as r2, t, s, x, z, y, u, w, v, F, g as g3, ap, aq, D as D2, I, B as B2, A, H, Q, R as R2, G, N, O, J, K, Y, Z, a2, a3, $, _, a0, a1, a5, a7, a6, a4, L, c as c3, a8, a9, l, n, m, ab, aa, ae, ac, ad, P, aj, an, ak, al, am, af, ag, ah, ao, ai, W, X, U, V, S, i as i2, h as h3, o, q, p, d as d2, b as b3, a as a10, T, ar, aw, av, as, at, au } from "./chunks/ChatView-BxbA5ob6.js";
|
|
8
|
+
import { S as SimpleSearchView, T as TopNav } from "./chunks/TopNav-DGtRj5F4.js";
|
|
9
9
|
import { T as TokenManager } from "./chunks/TokenManager-Fjt083wv.js";
|
|
10
|
-
import Dialog from "./chunks/Dialog-
|
|
11
|
-
import { default as default2 } from "./chunks/DataView-
|
|
12
|
-
import { F as F2, a as a11 } from "./chunks/FormView-
|
|
10
|
+
import Dialog from "./chunks/Dialog-aTTrQ6uW.js";
|
|
11
|
+
import { default as default2 } from "./chunks/DataView-BQOKPprj.js";
|
|
12
|
+
import { F as F2, a as a11 } from "./chunks/FormView-DjypPrEw.js";
|
|
13
13
|
import { W as W2 } from "./chunks/WebSocketClient-B6ribe3B.js";
|
|
14
|
-
class ResultsView extends View {
|
|
15
|
-
constructor(options = {}) {
|
|
16
|
-
super({
|
|
17
|
-
className: "search-results-view flex-grow-1 overflow-auto d-flex flex-column",
|
|
18
|
-
template: `
|
|
19
|
-
<div class="flex-grow-1 overflow-auto">
|
|
20
|
-
{{#data.loading}}
|
|
21
|
-
<div class="text-center p-4">
|
|
22
|
-
<div class="spinner-border spinner-border-sm text-muted" role="status">
|
|
23
|
-
<span class="visually-hidden">Loading...</span>
|
|
24
|
-
</div>
|
|
25
|
-
<div class="mt-2 small text-muted">{{data.loadingText}}</div>
|
|
26
|
-
</div>
|
|
27
|
-
{{/data.loading}}
|
|
28
|
-
|
|
29
|
-
{{^data.loading}}
|
|
30
|
-
{{#data.items}}
|
|
31
|
-
<div class="simple-search-item position-relative"
|
|
32
|
-
data-action="select-item"
|
|
33
|
-
data-item-index="{{index}}">
|
|
34
|
-
{{{itemContent}}}
|
|
35
|
-
<i class="bi bi-chevron-right position-absolute end-0 top-50 translate-middle-y me-3 text-muted"></i>
|
|
36
|
-
</div>
|
|
37
|
-
{{/data.items}}
|
|
38
|
-
|
|
39
|
-
{{#data.showNoResults}}
|
|
40
|
-
<div class="text-center p-4">
|
|
41
|
-
<i class="bi bi-search text-muted mb-2" style="font-size: 1.5rem;"></i>
|
|
42
|
-
<div class="text-muted small">{{data.noResultsText}}</div>
|
|
43
|
-
<button type="button"
|
|
44
|
-
class="btn btn-link btn-sm mt-2 p-0"
|
|
45
|
-
data-action="clear-search">
|
|
46
|
-
Clear search
|
|
47
|
-
</button>
|
|
48
|
-
</div>
|
|
49
|
-
{{/data.showNoResults}}
|
|
50
|
-
|
|
51
|
-
{{#data.showEmpty}}
|
|
52
|
-
<div class="text-center p-4">
|
|
53
|
-
<i class="{{data.emptyIcon}} text-muted mb-2" style="font-size: 2rem;"></i>
|
|
54
|
-
<div class="text-muted small mb-2">{{data.emptyText}}</div>
|
|
55
|
-
{{#data.emptySubtext}}
|
|
56
|
-
<div class="text-muted" style="font-size: 0.75rem;">
|
|
57
|
-
{{data.emptySubtext}}
|
|
58
|
-
</div>
|
|
59
|
-
{{/data.emptySubtext}}
|
|
60
|
-
</div>
|
|
61
|
-
{{/data.showEmpty}}
|
|
62
|
-
{{/data.loading}}
|
|
63
|
-
</div>
|
|
64
|
-
|
|
65
|
-
{{#data.showResultsCount}}
|
|
66
|
-
<div class="border-top bg-light p-2 text-center">
|
|
67
|
-
<small class="text-muted">
|
|
68
|
-
{{data.filteredCount}} of {{data.totalCount}}
|
|
69
|
-
</small>
|
|
70
|
-
</div>
|
|
71
|
-
{{/data.showResultsCount}}
|
|
72
|
-
`,
|
|
73
|
-
...options
|
|
74
|
-
});
|
|
75
|
-
this.parentView = options.parentView;
|
|
76
|
-
}
|
|
77
|
-
async handleActionSelectItem(event, element) {
|
|
78
|
-
event.preventDefault();
|
|
79
|
-
const itemIndex = parseInt(element.getAttribute("data-item-index"));
|
|
80
|
-
if (this.parentView) {
|
|
81
|
-
this.parentView.handleItemSelection(itemIndex);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
async handleActionClearSearch(event, _element) {
|
|
85
|
-
event.preventDefault();
|
|
86
|
-
if (this.parentView) {
|
|
87
|
-
this.parentView.clearSearch();
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
class SimpleSearchView extends View {
|
|
92
|
-
constructor(options = {}) {
|
|
93
|
-
super({
|
|
94
|
-
className: "simple-search-view h-100 d-flex flex-column",
|
|
95
|
-
template: `
|
|
96
|
-
<div class="p-3 border-bottom bg-light">
|
|
97
|
-
<div class="d-flex justify-content-between align-items-start mb-3">
|
|
98
|
-
<h6 class="text-muted fw-semibold mb-0">
|
|
99
|
-
{{#data.headerIcon}}<i class="{{data.headerIcon}} me-2"></i>{{/data.headerIcon}}
|
|
100
|
-
{{{data.headerText}}}
|
|
101
|
-
</h6>
|
|
102
|
-
{{#data.showExitButton}}
|
|
103
|
-
<button class="btn btn-link p-0 text-muted simple-search-exit-btn"
|
|
104
|
-
type="button"
|
|
105
|
-
data-action="exit-view"
|
|
106
|
-
title="Exit"
|
|
107
|
-
aria-label="Exit view">
|
|
108
|
-
<i class="bi bi-x-lg" aria-hidden="true"></i>
|
|
109
|
-
</button>
|
|
110
|
-
{{/data.showExitButton}}
|
|
111
|
-
</div>
|
|
112
|
-
<div class="position-relative">
|
|
113
|
-
<input type="text"
|
|
114
|
-
class="form-control form-control-sm pe-5"
|
|
115
|
-
placeholder="{{data.searchPlaceholder}}"
|
|
116
|
-
value="{{data.searchValue}}"
|
|
117
|
-
data-filter="live-search"
|
|
118
|
-
data-filter-debounce="{{data.debounceMs}}"
|
|
119
|
-
data-change-action="search-items">
|
|
120
|
-
<button class="btn btn-link p-0 position-absolute top-50 end-0 translate-middle-y me-2 text-muted simple-search-clear-btn"
|
|
121
|
-
type="button"
|
|
122
|
-
data-action="clear-search"
|
|
123
|
-
title="Clear search"
|
|
124
|
-
aria-label="Clear search">
|
|
125
|
-
<i class="bi bi-x-circle-fill" aria-hidden="true"></i>
|
|
126
|
-
</button>
|
|
127
|
-
</div>
|
|
128
|
-
</div>
|
|
129
|
-
|
|
130
|
-
<div data-container="results"></div>
|
|
131
|
-
|
|
132
|
-
{{#data.showFooter}}
|
|
133
|
-
<div class="p-3 border-top bg-light">
|
|
134
|
-
<small class="text-muted">
|
|
135
|
-
<i class="{{data.footerIcon}} me-1"></i>
|
|
136
|
-
{{{data.footerContent}}}
|
|
137
|
-
</small>
|
|
138
|
-
</div>
|
|
139
|
-
{{/data.showFooter}}
|
|
140
|
-
`,
|
|
141
|
-
...options
|
|
142
|
-
});
|
|
143
|
-
this.Collection = options.Collection;
|
|
144
|
-
this.collection = options.collection;
|
|
145
|
-
this.itemTemplate = options.itemTemplate || this.getDefaultItemTemplate();
|
|
146
|
-
this.searchFields = options.searchFields || ["name"];
|
|
147
|
-
this.collectionParams = { size: 25, ...options.collectionParams };
|
|
148
|
-
this.headerText = options.headerText || "Select Item";
|
|
149
|
-
this.headerIcon = options.headerIcon || "bi bi-list";
|
|
150
|
-
this.searchPlaceholder = options.searchPlaceholder || "Search...";
|
|
151
|
-
this.loadingText = options.loadingText || "Loading items...";
|
|
152
|
-
this.noResultsText = options.noResultsText || "No items match your search";
|
|
153
|
-
this.emptyText = options.emptyText || "No items available";
|
|
154
|
-
this.emptySubtext = options.emptySubtext || null;
|
|
155
|
-
this.emptyIcon = options.emptyIcon || "bi bi-inbox";
|
|
156
|
-
this.footerContent = options.footerContent || null;
|
|
157
|
-
this.footerIcon = options.footerIcon || "bi bi-info-circle";
|
|
158
|
-
this.showExitButton = options.showExitButton || false;
|
|
159
|
-
this.searchValue = "";
|
|
160
|
-
this.filteredItems = [];
|
|
161
|
-
this.loading = false;
|
|
162
|
-
this.hasSearched = false;
|
|
163
|
-
this.searchTimer = null;
|
|
164
|
-
this.debounceMs = options.debounceMs || 800;
|
|
165
|
-
this.resultsView = new ResultsView({
|
|
166
|
-
parentView: this
|
|
167
|
-
});
|
|
168
|
-
if (!this.collection && this.Collection) {
|
|
169
|
-
this.collection = new this.Collection();
|
|
170
|
-
}
|
|
171
|
-
this.addChild(this.resultsView);
|
|
172
|
-
}
|
|
173
|
-
onInit() {
|
|
174
|
-
if (this.collection) {
|
|
175
|
-
this.setupCollection();
|
|
176
|
-
}
|
|
177
|
-
if (this.collection && this.options.autoLoad !== false) {
|
|
178
|
-
this.loadItems();
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
setupCollection() {
|
|
182
|
-
Object.assign(this.collection.params, this.collectionParams);
|
|
183
|
-
this.collection.on("fetch:success", () => {
|
|
184
|
-
this.loading = false;
|
|
185
|
-
this.updateFilteredItems();
|
|
186
|
-
});
|
|
187
|
-
this.collection.on("fetch:error", () => {
|
|
188
|
-
this.loading = false;
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
async loadItems() {
|
|
192
|
-
if (!this.collection) {
|
|
193
|
-
console.warn("SimpleSearchView: No collection provided");
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
this.loading = true;
|
|
197
|
-
this.updateResultsView();
|
|
198
|
-
try {
|
|
199
|
-
await this.collection.fetch();
|
|
200
|
-
this.updateFilteredItems();
|
|
201
|
-
} catch (error) {
|
|
202
|
-
console.error("Error loading items:", error);
|
|
203
|
-
const app = this.getApp();
|
|
204
|
-
app?.showError?.("Failed to load items. Please try again.");
|
|
205
|
-
} finally {
|
|
206
|
-
this.loading = false;
|
|
207
|
-
this.updateFilteredItems();
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
updateFilteredItems() {
|
|
211
|
-
if (!this.collection) {
|
|
212
|
-
this.filteredItems = [];
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
const items = this.collection.toJSON();
|
|
216
|
-
if (!this.searchValue || !this.searchValue.trim()) {
|
|
217
|
-
this.filteredItems = items;
|
|
218
|
-
} else {
|
|
219
|
-
const searchTerm = this.searchValue.toLowerCase().trim();
|
|
220
|
-
this.filteredItems = items.filter((item) => {
|
|
221
|
-
return this.searchFields.some((field) => {
|
|
222
|
-
const value = this.getNestedValue(item, field);
|
|
223
|
-
return value && value.toString().toLowerCase().includes(searchTerm);
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
this.updateResultsView();
|
|
228
|
-
}
|
|
229
|
-
getNestedValue(obj, path) {
|
|
230
|
-
return path.split(".").reduce((current, key) => current?.[key], obj);
|
|
231
|
-
}
|
|
232
|
-
async getViewData() {
|
|
233
|
-
return {
|
|
234
|
-
searchValue: this.searchValue,
|
|
235
|
-
showFooter: !!this.footerContent,
|
|
236
|
-
showExitButton: this.showExitButton,
|
|
237
|
-
debounceMs: this.debounceMs,
|
|
238
|
-
// UI text
|
|
239
|
-
headerText: this.headerText,
|
|
240
|
-
headerIcon: this.headerIcon,
|
|
241
|
-
searchPlaceholder: this.searchPlaceholder,
|
|
242
|
-
footerContent: this.footerContent,
|
|
243
|
-
footerIcon: this.footerIcon
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
updateResultsView() {
|
|
247
|
-
if (!this.resultsView) return;
|
|
248
|
-
const hasItems = this.collection && this.collection.length() > 0;
|
|
249
|
-
const hasFilteredItems = this.filteredItems.length > 0;
|
|
250
|
-
const hasSearchValue = this.searchValue.length > 0;
|
|
251
|
-
const processedItems = this.filteredItems.map((item, index2) => {
|
|
252
|
-
return {
|
|
253
|
-
...item,
|
|
254
|
-
index: index2,
|
|
255
|
-
itemContent: this.processItemTemplate(item)
|
|
256
|
-
};
|
|
257
|
-
});
|
|
258
|
-
this.resultsView.data = {
|
|
259
|
-
loading: this.loading,
|
|
260
|
-
items: processedItems,
|
|
261
|
-
showEmpty: !this.loading && !hasItems,
|
|
262
|
-
showNoResults: !this.loading && hasItems && !hasFilteredItems && hasSearchValue,
|
|
263
|
-
showResultsCount: !this.loading && hasItems,
|
|
264
|
-
filteredCount: this.filteredItems.length,
|
|
265
|
-
totalCount: this.collection?.restEnabled ? this.collection?.meta?.count || 0 : this.collection?.length() || 0,
|
|
266
|
-
// UI text
|
|
267
|
-
loadingText: this.loadingText,
|
|
268
|
-
noResultsText: this.noResultsText,
|
|
269
|
-
emptyText: this.emptyText,
|
|
270
|
-
emptySubtext: this.emptySubtext,
|
|
271
|
-
emptyIcon: this.emptyIcon
|
|
272
|
-
};
|
|
273
|
-
this.resultsView.render();
|
|
274
|
-
}
|
|
275
|
-
processItemTemplate(item) {
|
|
276
|
-
let template = this.itemTemplate;
|
|
277
|
-
template = template.replace(/\{\{(\w+)\}\}/g, (match, prop) => {
|
|
278
|
-
return this.getNestedValue(item, prop) || "";
|
|
279
|
-
});
|
|
280
|
-
return template;
|
|
281
|
-
}
|
|
282
|
-
getDefaultItemTemplate() {
|
|
283
|
-
return `
|
|
284
|
-
<div class="p-3 border-bottom">
|
|
285
|
-
<div class="fw-semibold text-dark">{{name}}</div>
|
|
286
|
-
<small class="text-muted">{{id}}</small>
|
|
287
|
-
</div>
|
|
288
|
-
`;
|
|
289
|
-
}
|
|
290
|
-
async onPassThruActionSearchItems(event, element) {
|
|
291
|
-
const searchValue = element.value || "";
|
|
292
|
-
console.log("search change...");
|
|
293
|
-
this.searchValue = searchValue;
|
|
294
|
-
this.hasSearched = true;
|
|
295
|
-
if (this.searchTimer) {
|
|
296
|
-
clearTimeout(this.searchTimer);
|
|
297
|
-
}
|
|
298
|
-
this.performSearch();
|
|
299
|
-
}
|
|
300
|
-
async performSearch() {
|
|
301
|
-
const searchParams = { ...this.collectionParams };
|
|
302
|
-
if (this.searchValue && this.searchValue.length > 1) {
|
|
303
|
-
searchParams.search = this.searchValue.trim();
|
|
304
|
-
}
|
|
305
|
-
this.collection.setParams(searchParams, true);
|
|
306
|
-
}
|
|
307
|
-
handleItemSelection(itemIndex) {
|
|
308
|
-
if (isNaN(itemIndex) || itemIndex < 0 || itemIndex >= this.filteredItems.length) {
|
|
309
|
-
console.error("Invalid item index:", itemIndex);
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
const item = this.filteredItems[itemIndex];
|
|
313
|
-
const model = this.collection ? this.collection.get(item.id) : null;
|
|
314
|
-
this.emit("item:selected", {
|
|
315
|
-
item,
|
|
316
|
-
model,
|
|
317
|
-
index: itemIndex
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
/**
|
|
321
|
-
* Set the collection for this search view
|
|
322
|
-
*/
|
|
323
|
-
setCollection(collection) {
|
|
324
|
-
this.collection = collection;
|
|
325
|
-
this.setupCollection();
|
|
326
|
-
return this;
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* Set the item template
|
|
330
|
-
*/
|
|
331
|
-
setItemTemplate(template) {
|
|
332
|
-
this.itemTemplate = template;
|
|
333
|
-
this.updateResultsView();
|
|
334
|
-
return this;
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* Set search fields
|
|
338
|
-
*/
|
|
339
|
-
setSearchFields(fields) {
|
|
340
|
-
this.searchFields = Array.isArray(fields) ? fields : [fields];
|
|
341
|
-
return this;
|
|
342
|
-
}
|
|
343
|
-
/**
|
|
344
|
-
* Refresh items list
|
|
345
|
-
*/
|
|
346
|
-
async refresh() {
|
|
347
|
-
await this.loadItems();
|
|
348
|
-
}
|
|
349
|
-
/**
|
|
350
|
-
* Focus the search input
|
|
351
|
-
*/
|
|
352
|
-
focusSearch() {
|
|
353
|
-
const searchInput = this.element?.querySelector('input[data-action="search-items"]');
|
|
354
|
-
if (searchInput) {
|
|
355
|
-
searchInput.focus();
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* Handle exit button click - emits event instead of closing
|
|
360
|
-
*/
|
|
361
|
-
async handleActionExitView(event, element) {
|
|
362
|
-
this.emit("exit", { view: this });
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Clear search and reset
|
|
366
|
-
*/
|
|
367
|
-
async handleActionClearSearch(event, element) {
|
|
368
|
-
this.clearSearch();
|
|
369
|
-
}
|
|
370
|
-
clearSearch() {
|
|
371
|
-
this.searchValue = "";
|
|
372
|
-
this.hasSearched = false;
|
|
373
|
-
const searchInput = this.element?.querySelector('input[data-change-action="search-items"]');
|
|
374
|
-
if (searchInput) {
|
|
375
|
-
searchInput.value = "";
|
|
376
|
-
searchInput.focus();
|
|
377
|
-
}
|
|
378
|
-
this.performSearch();
|
|
379
|
-
}
|
|
380
|
-
/**
|
|
381
|
-
* Get the number of available items
|
|
382
|
-
*/
|
|
383
|
-
getItemCount() {
|
|
384
|
-
return this.collection ? this.collection.length() : 0;
|
|
385
|
-
}
|
|
386
|
-
/**
|
|
387
|
-
* Get the number of filtered items
|
|
388
|
-
*/
|
|
389
|
-
getFilteredItemCount() {
|
|
390
|
-
return this.filteredItems.length;
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* Check if items are loaded
|
|
394
|
-
*/
|
|
395
|
-
hasItems() {
|
|
396
|
-
return this.getItemCount() > 0;
|
|
397
|
-
}
|
|
398
|
-
/**
|
|
399
|
-
* Get current search value
|
|
400
|
-
*/
|
|
401
|
-
getSearchValue() {
|
|
402
|
-
return this.searchValue;
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* Set search value programmatically
|
|
406
|
-
*/
|
|
407
|
-
setSearchValue(value) {
|
|
408
|
-
this.searchValue = value || "";
|
|
409
|
-
this.hasSearched = !!this.searchValue;
|
|
410
|
-
const searchInput = this.element?.querySelector('input[data-action="search-items"]');
|
|
411
|
-
if (searchInput) {
|
|
412
|
-
searchInput.value = this.searchValue;
|
|
413
|
-
}
|
|
414
|
-
this.performSearch();
|
|
415
|
-
return this;
|
|
416
|
-
}
|
|
417
|
-
async onAfterRender() {
|
|
418
|
-
await super.onAfterRender();
|
|
419
|
-
if (this.resultsView && !this.resultsView.isMounted()) {
|
|
420
|
-
const container = this.element?.querySelector('[data-container="results"]');
|
|
421
|
-
if (container) {
|
|
422
|
-
await this.resultsView.render(true, container);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
this.updateResultsView();
|
|
426
|
-
}
|
|
427
|
-
/**
|
|
428
|
-
* Cleanup on destroy
|
|
429
|
-
*/
|
|
430
|
-
async onBeforeDestroy() {
|
|
431
|
-
if (this.searchTimer) {
|
|
432
|
-
clearTimeout(this.searchTimer);
|
|
433
|
-
}
|
|
434
|
-
if (this.collection) {
|
|
435
|
-
this.collection.off("update");
|
|
436
|
-
}
|
|
437
|
-
await super.onBeforeDestroy();
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
14
|
class Sidebar extends View {
|
|
441
15
|
constructor(options = {}) {
|
|
442
16
|
super({
|
|
@@ -452,6 +26,7 @@ class Sidebar extends View {
|
|
|
452
26
|
this.isCollapsed = false;
|
|
453
27
|
this.sidebarTheme = options.theme || "sidebar-light";
|
|
454
28
|
this.customView = null;
|
|
29
|
+
if (this.options.groupHeader) this.groupHeader = this.options.groupHeader;
|
|
455
30
|
if (this.sidebarTheme) {
|
|
456
31
|
this.addClass(this.sidebarTheme);
|
|
457
32
|
}
|
|
@@ -461,6 +36,12 @@ class Sidebar extends View {
|
|
|
461
36
|
this.setupResponsiveBehavior();
|
|
462
37
|
}
|
|
463
38
|
}
|
|
39
|
+
groupHeader = `
|
|
40
|
+
<div class="sidebar-group-header py-3" data-action="show-group-search">
|
|
41
|
+
<div class='text-center fs-5 px-1 collapsed-hidden'>{{group.name}}</div>
|
|
42
|
+
<div class='text-center fs-6 collapsed-hidden'>kind: {{group.kind}}</div>
|
|
43
|
+
</div>
|
|
44
|
+
`;
|
|
464
45
|
/**
|
|
465
46
|
* Initialize sidebar and auto-switch to correct menu based on current route
|
|
466
47
|
*/
|
|
@@ -767,13 +348,7 @@ class Sidebar extends View {
|
|
|
767
348
|
};
|
|
768
349
|
}
|
|
769
350
|
getGroupHeader() {
|
|
770
|
-
return
|
|
771
|
-
<div class="sidebar-group-header py-3" data-action="show-group-search">
|
|
772
|
-
<div class='text-center text-muted fs-7 collapsed-hidden'>active group</div>
|
|
773
|
-
<div class='text-center fs-5 px-1 collapsed-hidden'>{{group.name}}</div>
|
|
774
|
-
<div class='text-center fs-6 collapsed-hidden'>kind: {{group.kind}}</div>
|
|
775
|
-
</div>
|
|
776
|
-
`;
|
|
351
|
+
return this.groupHeader;
|
|
777
352
|
}
|
|
778
353
|
/**
|
|
779
354
|
* Add a menu configuration
|
|
@@ -1491,6 +1066,182 @@ class Sidebar extends View {
|
|
|
1491
1066
|
return this;
|
|
1492
1067
|
}
|
|
1493
1068
|
}
|
|
1069
|
+
class PageHeader extends View {
|
|
1070
|
+
constructor(options = {}) {
|
|
1071
|
+
super({
|
|
1072
|
+
tagName: "div",
|
|
1073
|
+
className: "page-header",
|
|
1074
|
+
...options
|
|
1075
|
+
});
|
|
1076
|
+
this.style = options.style || "default";
|
|
1077
|
+
this.size = options.size || "md";
|
|
1078
|
+
this.showIcon = options.showIcon !== false;
|
|
1079
|
+
this.showDescription = options.showDescription !== false;
|
|
1080
|
+
this.showBreadcrumbs = options.showBreadcrumbs || false;
|
|
1081
|
+
this.currentPage = null;
|
|
1082
|
+
}
|
|
1083
|
+
async getTemplate() {
|
|
1084
|
+
if (this.style === "minimal") {
|
|
1085
|
+
return this.getMinimalTemplate();
|
|
1086
|
+
} else if (this.style === "breadcrumb") {
|
|
1087
|
+
return this.getBreadcrumbTemplate();
|
|
1088
|
+
}
|
|
1089
|
+
return this.getDefaultTemplate();
|
|
1090
|
+
}
|
|
1091
|
+
getDefaultTemplate() {
|
|
1092
|
+
return `
|
|
1093
|
+
{{#data.hasPage}}
|
|
1094
|
+
<div class="page-header-content page-header-{{data.size}}">
|
|
1095
|
+
<div class="page-header-main">
|
|
1096
|
+
<div class="page-header-info">
|
|
1097
|
+
{{#data.showIcon}}
|
|
1098
|
+
{{#data.pageIcon}}
|
|
1099
|
+
<div class="page-icon">
|
|
1100
|
+
<i class="{{data.pageIcon}}"></i>
|
|
1101
|
+
</div>
|
|
1102
|
+
{{/data.pageIcon}}
|
|
1103
|
+
{{/data.showIcon}}
|
|
1104
|
+
|
|
1105
|
+
<div class="page-title-group">
|
|
1106
|
+
<h1 class="page-title">{{data.pageTitle}}</h1>
|
|
1107
|
+
{{#data.showDescription}}
|
|
1108
|
+
{{#data.pageDescription}}
|
|
1109
|
+
<p class="page-description text-muted">{{data.pageDescription}}</p>
|
|
1110
|
+
{{/data.pageDescription}}
|
|
1111
|
+
{{/data.showDescription}}
|
|
1112
|
+
</div>
|
|
1113
|
+
</div>
|
|
1114
|
+
|
|
1115
|
+
{{#data.hasActions}}
|
|
1116
|
+
<div class="page-actions">
|
|
1117
|
+
{{#data.actions}}
|
|
1118
|
+
<button class="btn {{buttonClass}}"
|
|
1119
|
+
data-action="{{action}}"
|
|
1120
|
+
type="button">
|
|
1121
|
+
{{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}
|
|
1122
|
+
{{label}}
|
|
1123
|
+
</button>
|
|
1124
|
+
{{/data.actions}}
|
|
1125
|
+
</div>
|
|
1126
|
+
{{/data.hasActions}}
|
|
1127
|
+
</div>
|
|
1128
|
+
</div>
|
|
1129
|
+
{{/data.hasPage}}
|
|
1130
|
+
`;
|
|
1131
|
+
}
|
|
1132
|
+
getMinimalTemplate() {
|
|
1133
|
+
return `
|
|
1134
|
+
{{#data.hasPage}}
|
|
1135
|
+
<div class="page-header-content page-header-minimal">
|
|
1136
|
+
<h1 class="page-title">
|
|
1137
|
+
{{#data.showIcon}}
|
|
1138
|
+
{{#data.pageIcon}}<i class="{{data.pageIcon}} me-2"></i>{{/data.pageIcon}}
|
|
1139
|
+
{{/data.showIcon}}
|
|
1140
|
+
{{data.pageTitle}}
|
|
1141
|
+
</h1>
|
|
1142
|
+
</div>
|
|
1143
|
+
{{/data.hasPage}}
|
|
1144
|
+
`;
|
|
1145
|
+
}
|
|
1146
|
+
getBreadcrumbTemplate() {
|
|
1147
|
+
return `
|
|
1148
|
+
{{#data.hasPage}}
|
|
1149
|
+
<div class="page-header-content page-header-breadcrumb">
|
|
1150
|
+
{{#data.showBreadcrumbs}}
|
|
1151
|
+
<nav aria-label="breadcrumb">
|
|
1152
|
+
<ol class="breadcrumb mb-2">
|
|
1153
|
+
{{#data.breadcrumbs}}
|
|
1154
|
+
<li class="breadcrumb-item {{#active}}active{{/active}}">
|
|
1155
|
+
{{#href}}<a href="{{href}}">{{label}}</a>{{/href}}
|
|
1156
|
+
{{^href}}{{label}}{{/href}}
|
|
1157
|
+
</li>
|
|
1158
|
+
{{/data.breadcrumbs}}
|
|
1159
|
+
</ol>
|
|
1160
|
+
</nav>
|
|
1161
|
+
{{/data.showBreadcrumbs}}
|
|
1162
|
+
|
|
1163
|
+
<div class="d-flex justify-content-between align-items-start">
|
|
1164
|
+
<h1 class="page-title">
|
|
1165
|
+
{{#data.showIcon}}
|
|
1166
|
+
{{#data.pageIcon}}<i class="{{data.pageIcon}} me-2"></i>{{/data.pageIcon}}
|
|
1167
|
+
{{/data.showIcon}}
|
|
1168
|
+
{{data.pageTitle}}
|
|
1169
|
+
</h1>
|
|
1170
|
+
|
|
1171
|
+
{{#data.hasActions}}
|
|
1172
|
+
<div class="page-actions">
|
|
1173
|
+
{{#data.actions}}
|
|
1174
|
+
<button class="btn {{buttonClass}}"
|
|
1175
|
+
data-action="{{action}}"
|
|
1176
|
+
type="button">
|
|
1177
|
+
{{#icon}}<i class="{{icon}} me-1"></i>{{/icon}}
|
|
1178
|
+
{{label}}
|
|
1179
|
+
</button>
|
|
1180
|
+
{{/data.actions}}
|
|
1181
|
+
</div>
|
|
1182
|
+
{{/data.hasActions}}
|
|
1183
|
+
</div>
|
|
1184
|
+
|
|
1185
|
+
{{#data.showDescription}}
|
|
1186
|
+
{{#data.pageDescription}}
|
|
1187
|
+
<p class="page-description text-muted mt-2">{{data.pageDescription}}</p>
|
|
1188
|
+
{{/data.pageDescription}}
|
|
1189
|
+
{{/data.showDescription}}
|
|
1190
|
+
</div>
|
|
1191
|
+
{{/data.hasPage}}
|
|
1192
|
+
`;
|
|
1193
|
+
}
|
|
1194
|
+
async onBeforeRender() {
|
|
1195
|
+
await super.onBeforeRender();
|
|
1196
|
+
const page = this.currentPage;
|
|
1197
|
+
const hasPage = !!page;
|
|
1198
|
+
const headerActions = page?.options?.headerActions || page?.headerActions || page?.constructor?.prototype?.headerActions || [];
|
|
1199
|
+
this.data = {
|
|
1200
|
+
hasPage,
|
|
1201
|
+
pageTitle: page?.title || page?.name || "",
|
|
1202
|
+
pageIcon: page?.icon || page?.pageIcon || "",
|
|
1203
|
+
pageDescription: page?.description || "",
|
|
1204
|
+
showIcon: this.showIcon,
|
|
1205
|
+
showDescription: this.showDescription,
|
|
1206
|
+
showBreadcrumbs: this.showBreadcrumbs,
|
|
1207
|
+
breadcrumbs: page?.options?.breadcrumbs || page?.breadcrumbs || [],
|
|
1208
|
+
actions: headerActions,
|
|
1209
|
+
hasActions: headerActions.length > 0,
|
|
1210
|
+
size: this.size
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* Set the current page to display
|
|
1215
|
+
*/
|
|
1216
|
+
setPage(page) {
|
|
1217
|
+
this.currentPage = page;
|
|
1218
|
+
if (this.mounted) {
|
|
1219
|
+
this.render();
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
/**
|
|
1223
|
+
* Get the current page
|
|
1224
|
+
*/
|
|
1225
|
+
getPage() {
|
|
1226
|
+
return this.currentPage;
|
|
1227
|
+
}
|
|
1228
|
+
/**
|
|
1229
|
+
* Handle action clicks from page header buttons
|
|
1230
|
+
*/
|
|
1231
|
+
async onActionDefault(action, event, element) {
|
|
1232
|
+
if (this.currentPage && typeof this.currentPage.onHeaderAction === "function") {
|
|
1233
|
+
await this.currentPage.onHeaderAction(action, event, element);
|
|
1234
|
+
return true;
|
|
1235
|
+
}
|
|
1236
|
+
this.emit("action", {
|
|
1237
|
+
action,
|
|
1238
|
+
event,
|
|
1239
|
+
element,
|
|
1240
|
+
page: this.currentPage
|
|
1241
|
+
});
|
|
1242
|
+
return false;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1494
1245
|
class DeniedPage extends Page {
|
|
1495
1246
|
constructor(options = {}) {
|
|
1496
1247
|
super({
|
|
@@ -1826,9 +1577,12 @@ class PortalApp extends WebApp {
|
|
|
1826
1577
|
if (config.topnav && !config.topbar) {
|
|
1827
1578
|
this.topbarConfig = config.topnav;
|
|
1828
1579
|
}
|
|
1580
|
+
this.showPageHeader = config.showPageHeader || false;
|
|
1581
|
+
this.pageHeaderConfig = config.pageHeader || {};
|
|
1829
1582
|
this.sidebar = null;
|
|
1830
1583
|
this.topbar = null;
|
|
1831
1584
|
this.topnav = null;
|
|
1585
|
+
this.pageHeader = null;
|
|
1832
1586
|
this.tokenManager = new TokenManager();
|
|
1833
1587
|
this.activeGroup = null;
|
|
1834
1588
|
if (!this.isMobile()) {
|
|
@@ -1926,6 +1680,7 @@ class PortalApp extends WebApp {
|
|
|
1926
1680
|
this.activeUser.member = new Member();
|
|
1927
1681
|
await this.activeUser.member.fetchForGroup(group.id);
|
|
1928
1682
|
}
|
|
1683
|
+
this.events.emit("group:loaded", { group: this.activeGroup });
|
|
1929
1684
|
console.log("Loaded active group:", group.get("name"));
|
|
1930
1685
|
} catch (error) {
|
|
1931
1686
|
console.warn("Failed to load active group:", error);
|
|
@@ -1938,6 +1693,7 @@ class PortalApp extends WebApp {
|
|
|
1938
1693
|
const fallbackGroup = new Group({ id: storedGroupId });
|
|
1939
1694
|
await fallbackGroup.fetch();
|
|
1940
1695
|
this.activeGroup = fallbackGroup;
|
|
1696
|
+
this.events.emit("group:loaded", { group: this.activeGroup });
|
|
1941
1697
|
console.log("Fell back to stored active group:", fallbackGroup.get("name"));
|
|
1942
1698
|
} catch (fallbackError) {
|
|
1943
1699
|
console.warn("Fallback to stored group also failed:", fallbackError);
|
|
@@ -2061,14 +1817,24 @@ class PortalApp extends WebApp {
|
|
|
2061
1817
|
}
|
|
2062
1818
|
const showSidebar = this.sidebarConfig && Object.keys(this.sidebarConfig).length > 0;
|
|
2063
1819
|
const showTopbar = this.topbarConfig && Object.keys(this.topbarConfig).length > 0;
|
|
1820
|
+
const contentMarkup = this.showPageHeader ? `
|
|
1821
|
+
<div class="portal-content" id="portal-content">
|
|
1822
|
+
<div id="page-header"></div>
|
|
1823
|
+
<div id="page-container">
|
|
1824
|
+
<!-- Pages render here -->
|
|
1825
|
+
</div>
|
|
1826
|
+
</div>
|
|
1827
|
+
` : `
|
|
1828
|
+
<div class="portal-content" id="page-container">
|
|
1829
|
+
<!-- Pages render here -->
|
|
1830
|
+
</div>
|
|
1831
|
+
`;
|
|
2064
1832
|
container.innerHTML = `
|
|
2065
1833
|
<div class="portal-layout hide-sidebar">
|
|
2066
1834
|
${showSidebar ? '<div id="portal-sidebar"></div>' : ""}
|
|
2067
1835
|
<div class="portal-body">
|
|
2068
1836
|
${showTopbar ? '<div id="portal-topnav"></div>' : ""}
|
|
2069
|
-
|
|
2070
|
-
<!-- Pages render here -->
|
|
2071
|
-
</div>
|
|
1837
|
+
${contentMarkup}
|
|
2072
1838
|
</div>
|
|
2073
1839
|
</div>
|
|
2074
1840
|
`;
|
|
@@ -2083,6 +1849,7 @@ class PortalApp extends WebApp {
|
|
|
2083
1849
|
async setupPortalComponents() {
|
|
2084
1850
|
await this.setupSidebar();
|
|
2085
1851
|
await this.setupTopbar();
|
|
1852
|
+
await this.setupPageHeader();
|
|
2086
1853
|
this.setupPortalEvents();
|
|
2087
1854
|
}
|
|
2088
1855
|
/**
|
|
@@ -2115,6 +1882,24 @@ class PortalApp extends WebApp {
|
|
|
2115
1882
|
await this.topbar.render();
|
|
2116
1883
|
this.topnav = this.topbar;
|
|
2117
1884
|
}
|
|
1885
|
+
/**
|
|
1886
|
+
* Setup page header component
|
|
1887
|
+
*/
|
|
1888
|
+
async setupPageHeader() {
|
|
1889
|
+
if (!this.showPageHeader) return;
|
|
1890
|
+
this.pageHeader = new PageHeader({
|
|
1891
|
+
containerId: "page-header",
|
|
1892
|
+
style: this.pageHeaderConfig.style || "default",
|
|
1893
|
+
showIcon: this.pageHeaderConfig.showIcon !== false,
|
|
1894
|
+
showDescription: this.pageHeaderConfig.showDescription !== false,
|
|
1895
|
+
showBreadcrumbs: this.pageHeaderConfig.showBreadcrumbs || false,
|
|
1896
|
+
...this.pageHeaderConfig
|
|
1897
|
+
});
|
|
1898
|
+
const headerContainer = document.getElementById("page-header");
|
|
1899
|
+
if (headerContainer) {
|
|
1900
|
+
await this.pageHeader.render(true, headerContainer);
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
2118
1903
|
/**
|
|
2119
1904
|
* Setup portal event handling
|
|
2120
1905
|
*/
|
|
@@ -2190,8 +1975,8 @@ class PortalApp extends WebApp {
|
|
|
2190
1975
|
if (this.hasMobileLayout()) {
|
|
2191
1976
|
this.getPortalContainer().classList.add("hide-sidebar");
|
|
2192
1977
|
}
|
|
2193
|
-
if (
|
|
2194
|
-
this.updateNavigation(this.
|
|
1978
|
+
if (this.currentPage) {
|
|
1979
|
+
this.updateNavigation(this.currentPage);
|
|
2195
1980
|
}
|
|
2196
1981
|
return result;
|
|
2197
1982
|
}
|
|
@@ -2205,6 +1990,9 @@ class PortalApp extends WebApp {
|
|
|
2205
1990
|
if (this.topbar && this.topbar.setActivePage) {
|
|
2206
1991
|
this.topbar.setActivePage(page.route);
|
|
2207
1992
|
}
|
|
1993
|
+
if (this.pageHeader) {
|
|
1994
|
+
this.pageHeader.setPage(page);
|
|
1995
|
+
}
|
|
2208
1996
|
this.events.emit("portal:page-changed", { page });
|
|
2209
1997
|
}
|
|
2210
1998
|
/**
|