ultravisor 1.0.25 → 1.0.26

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.
@@ -22,6 +22,7 @@
22
22
  "pict-section-markdowneditor": "^1.0.7",
23
23
  "pict-section-modal": "^0.0.1",
24
24
  "pict-section-objecteditor": "^1.0.1",
25
+ "pict-section-usermanagement": "^0.0.1",
25
26
  "pict-view": "^1.0.67"
26
27
  },
27
28
  "devDependencies": {
@@ -31,7 +31,10 @@ const libViewBeaconList = require('./views/PictView-Ultravisor-BeaconList.js');
31
31
  const libViewReachabilityMap = require('./views/PictView-Ultravisor-ReachabilityMap.js');
32
32
  const libViewOperationDescriptionEditor = require('./views/PictView-Ultravisor-OperationDescriptionEditor.js');
33
33
  const libViewFleet = require('./views/PictView-Ultravisor-Fleet.js');
34
+ const libViewLogin = require('./views/PictView-Ultravisor-Login.js');
35
+ const libViewUserManagement = require('./views/PictView-Ultravisor-UserManagement.js');
34
36
  const libPictSectionModal = require('pict-section-modal');
37
+ const libPictSectionUserManagement = require('pict-section-usermanagement');
35
38
 
36
39
  class UltravisorApplication extends libPictApplication
37
40
  {
@@ -68,9 +71,36 @@ class UltravisorApplication extends libPictApplication
68
71
  this.pict.addView('Ultravisor-ReachabilityMap', libViewReachabilityMap.default_configuration, libViewReachabilityMap);
69
72
  this.pict.addView('Ultravisor-OperationDescriptionEditor', libViewOperationDescriptionEditor.default_configuration, libViewOperationDescriptionEditor);
70
73
  this.pict.addView('Ultravisor-Fleet', libViewFleet.default_configuration, libViewFleet);
71
-
72
- // Modal/toast notification system (replaces browser alert/confirm)
74
+ this.pict.addView('Ultravisor-Login', libViewLogin.default_configuration, libViewLogin);
75
+ this.pict.addView('Ultravisor-UserManagement', libViewUserManagement.default_configuration, libViewUserManagement);
76
+
77
+ // Modal/toast notification system (replaces browser alert/confirm).
78
+ // Two registrations: 'Modal' is the legacy hash existing call sites
79
+ // use; 'Pict-Section-Modal' is what pict-section-usermanagement's
80
+ // confirm/toast hooks look up. Both addView calls produce
81
+ // independent instances of the same section, which is fine because
82
+ // the modal section is stateless across constructions.
73
83
  this.pict.addView('Modal', {}, libPictSectionModal);
84
+ this.pict.addView('Pict-Section-Modal', {}, libPictSectionModal);
85
+
86
+ // pict-section-usermanagement — install() registers the provider
87
+ // + 5 views (Login, CurrentUser, UserList, UserEdit,
88
+ // PasswordChange). Default browser fetch() is the transport,
89
+ // hitting orator-authentication's /1.0/Authenticate + the
90
+ // auth-beacon /Users routes mounted by ultravisor's API server.
91
+ // OnLogin / OnLogout hooks let us re-render the topbar and
92
+ // reroute on session-state flips without each view subscribing
93
+ // to AppData.
94
+ let tmpSelf = this;
95
+ libPictSectionUserManagement.install(this.pict,
96
+ {
97
+ ProviderOptions: { BaseURL: '/1.0/' },
98
+ ViewOptions:
99
+ {
100
+ Login: { OnLogin: () => tmpSelf._afterLogin() },
101
+ CurrentUser: { OnLogout: () => tmpSelf._afterLogout() }
102
+ }
103
+ });
74
104
 
75
105
  // Register pict-section-form service types so Form panels can use them
76
106
  this.pict.addServiceType('PictFormMetacontroller', libPictSectionForm.PictFormMetacontroller);
@@ -139,6 +169,31 @@ class UltravisorApplication extends libPictApplication
139
169
  this.pict.providers.PictRouter.navigate(pRoute);
140
170
  }
141
171
 
172
+ /**
173
+ * Hook fired by pict-section-usermanagement's Login view after a
174
+ * successful sign-in. Re-render the TopBar so the CurrentUser badge
175
+ * appears, then bounce to the dashboard so the user lands on a
176
+ * useful page rather than the login form's success message.
177
+ */
178
+ _afterLogin()
179
+ {
180
+ let tmpTopBar = this.pict.views['Ultravisor-TopBar'];
181
+ if (tmpTopBar) tmpTopBar.render();
182
+ this.navigateTo('/Home');
183
+ }
184
+
185
+ /**
186
+ * Hook fired by pict-section-usermanagement's CurrentUser view
187
+ * after a logout. Drop back to /Login so it's obvious the session
188
+ * is gone (vs. seeing a stale dashboard with no badge).
189
+ */
190
+ _afterLogout()
191
+ {
192
+ let tmpTopBar = this.pict.views['Ultravisor-TopBar'];
193
+ if (tmpTopBar) tmpTopBar.render();
194
+ this.navigateTo('/Login');
195
+ }
196
+
142
197
  showView(pViewIdentifier)
143
198
  {
144
199
  if (pViewIdentifier in this.pict.views)
@@ -61,6 +61,14 @@
61
61
  {
62
62
  "path": "/Docs",
63
63
  "template": "{~LV:Pict.PictApplication.showView(`Ultravisor-Documentation`)~}"
64
+ },
65
+ {
66
+ "path": "/Login",
67
+ "template": "{~LV:Pict.PictApplication.showView(`Ultravisor-Login`)~}"
68
+ },
69
+ {
70
+ "path": "/Users",
71
+ "template": "{~LV:Pict.PictApplication.showView(`Ultravisor-UserManagement`)~}"
64
72
  }
65
73
  ]
66
74
  }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * PictView-Ultravisor-Login
3
+ *
4
+ * Thin host wrapper around `pict-section-usermanagement`'s Login
5
+ * view. The section's Login renders into `#PictUM-Login`; this view
6
+ * just provides that mount point inside ultravisor's main content
7
+ * panel and delegates to the section view's render.
8
+ *
9
+ * The section's Login carries its own theme-neutral CSS, so this
10
+ * view doesn't need to restyle the form — but does add a small
11
+ * "Ultravisor — sign in" page heading so the page doesn't feel
12
+ * floating.
13
+ */
14
+
15
+ const libPictView = require('pict-view');
16
+
17
+ const _ViewConfiguration =
18
+ {
19
+ ViewIdentifier: 'Ultravisor-Login',
20
+ AutoInitialize: true,
21
+ AutoRender: false,
22
+
23
+ DefaultRenderable: 'Ultravisor-Login-Content',
24
+ DefaultDestinationAddress: '#Ultravisor-Content-Container',
25
+
26
+ Templates:
27
+ [
28
+ {
29
+ Hash: 'Ultravisor-Login-Template',
30
+ Template: /*html*/`
31
+ <div class="ultravisor-login-page">
32
+ <div id="PictUM-Login"></div>
33
+ </div>`
34
+ }
35
+ ],
36
+
37
+ Renderables:
38
+ [
39
+ {
40
+ RenderableHash: 'Ultravisor-Login-Content',
41
+ TemplateHash: 'Ultravisor-Login-Template',
42
+ DestinationAddress: '#Ultravisor-Content-Container',
43
+ RenderMethod: 'replace'
44
+ }
45
+ ],
46
+
47
+ CSS: /*css*/`
48
+ .ultravisor-login-page {
49
+ min-height: calc(100vh - 56px);
50
+ display: flex; align-items: center; justify-content: center;
51
+ padding: 32px 16px;
52
+ }
53
+ `
54
+ };
55
+
56
+ class UltravisorLoginView extends libPictView
57
+ {
58
+ onAfterRender(pRenderable, pAddress, pRecord, pContent)
59
+ {
60
+ // Render the section's Login view into the mount point we just
61
+ // painted. The section view tracks its own destination via
62
+ // DefaultDestinationAddress: '#PictUM-Login', so a plain render()
63
+ // call is enough.
64
+ let tmpInner = this.pict && this.pict.views && this.pict.views['PictUM-Login'];
65
+ if (tmpInner) tmpInner.render();
66
+ this.pict.CSSMap.injectCSS();
67
+ return super.onAfterRender
68
+ ? super.onAfterRender(pRenderable, pAddress, pRecord, pContent)
69
+ : undefined;
70
+ }
71
+ }
72
+
73
+ module.exports = UltravisorLoginView;
74
+ module.exports.default_configuration = _ViewConfiguration;
@@ -219,9 +219,15 @@ const _ViewConfiguration =
219
219
  <a onclick="{~P~}.PictApplication.navigateTo('/Manifests')">Manifests</a>
220
220
  <a onclick="{~P~}.PictApplication.navigateTo('/Beacons')">Beacons</a>
221
221
  <a onclick="{~P~}.PictApplication.navigateTo('/Fleet')">Fleet</a>
222
+ <a onclick="{~P~}.PictApplication.navigateTo('/Users')">Users</a>
222
223
  <a onclick="{~P~}.PictApplication.navigateTo('/Docs')">Docs</a>
223
224
  </div>
224
225
  <div class="ultravisor-topbar-right">
226
+ <!-- pict-section-usermanagement's CurrentUser badge renders here.
227
+ The view tracks #PictUM-CurrentUser as its destination;
228
+ onAfterRender() below calls its render() so the badge
229
+ appears alongside the status indicator. -->
230
+ <span id="PictUM-CurrentUser"></span>
225
231
  <div class="ultravisor-topbar-status" id="Ultravisor-TopBar-StatusArea"></div>
226
232
  <div class="ultravisor-settings-wrap">
227
233
  <button class="ultravisor-settings-gear" onclick="{~P~}.views['Ultravisor-TopBar'].toggleThemePanel()" title="Settings">
@@ -287,6 +293,25 @@ class UltravisorTopBarView extends libPictView
287
293
  // Render theme swatches
288
294
  this._renderThemeGrid();
289
295
 
296
+ // Render the user-management current-user badge into our slot.
297
+ // On first paint there's no session yet — the section view
298
+ // drives a CheckSession on its own first render. After that
299
+ // it just paints from AppData.UserManagement.CurrentUser.
300
+ let tmpCurrentUserView = this.pict.views['PictUM-CurrentUser'];
301
+ if (tmpCurrentUserView)
302
+ {
303
+ let tmpProvider = this.pict.providers['Pict-UserManagement-Provider'];
304
+ if (tmpProvider && !this._didInitialSessionCheck)
305
+ {
306
+ this._didInitialSessionCheck = true;
307
+ tmpProvider.checkSession(() => tmpCurrentUserView.render());
308
+ }
309
+ else
310
+ {
311
+ tmpCurrentUserView.render();
312
+ }
313
+ }
314
+
290
315
  // Set up click-outside-to-close handler
291
316
  if (this._boundCloseHandler)
292
317
  {
@@ -0,0 +1,159 @@
1
+ /**
2
+ * PictView-Ultravisor-UserManagement
3
+ *
4
+ * Hosts the user-management surface inside ultravisor's main content
5
+ * panel. Composes three section views:
6
+ *
7
+ * - PictUM-UserList (admin: search/list/edit/delete users)
8
+ * - PictUM-UserEdit (admin: create/edit user form)
9
+ * - PictUM-PasswordChange (any session: self change-password)
10
+ *
11
+ * The section's CurrentUser badge is rendered in the TopBar, not here
12
+ * — the page itself doesn't need its own session indicator.
13
+ *
14
+ * Sub-tabs (Users | Change my password) keep the admin and self
15
+ * surfaces separate. A non-admin landing on /Users sees only the
16
+ * password tab; the admin tab self-hides via the section's 401/403
17
+ * error path.
18
+ */
19
+
20
+ const libPictView = require('pict-view');
21
+
22
+ const _ViewConfiguration =
23
+ {
24
+ ViewIdentifier: 'Ultravisor-UserManagement',
25
+ AutoInitialize: true,
26
+ AutoRender: false,
27
+
28
+ DefaultRenderable: 'Ultravisor-UserManagement-Content',
29
+ DefaultDestinationAddress: '#Ultravisor-Content-Container',
30
+
31
+ Templates:
32
+ [
33
+ {
34
+ Hash: 'Ultravisor-UserManagement-Template',
35
+ Template: /*html*/`
36
+ <div class="ultravisor-um-page">
37
+ <div class="ultravisor-um-tabs">
38
+ <button type="button" class="ultravisor-um-tab" id="UV-UM-Tab-Users"
39
+ onclick="{~P~}.views['Ultravisor-UserManagement'].switchTab('users')">Users</button>
40
+ <button type="button" class="ultravisor-um-tab" id="UV-UM-Tab-Pwd"
41
+ onclick="{~P~}.views['Ultravisor-UserManagement'].switchTab('change-password')">Change my password</button>
42
+ </div>
43
+ <div class="ultravisor-um-content">
44
+ <div id="UV-UM-Pane-Users">
45
+ <div id="PictUM-UserList"></div>
46
+ <div id="PictUM-UserEdit" style="margin-top: 16px;"></div>
47
+ </div>
48
+ <div id="UV-UM-Pane-Pwd" style="display:none;">
49
+ <div id="PictUM-PasswordChange"></div>
50
+ </div>
51
+ </div>
52
+ </div>`
53
+ }
54
+ ],
55
+
56
+ Renderables:
57
+ [
58
+ {
59
+ RenderableHash: 'Ultravisor-UserManagement-Content',
60
+ TemplateHash: 'Ultravisor-UserManagement-Template',
61
+ DestinationAddress: '#Ultravisor-Content-Container',
62
+ RenderMethod: 'replace'
63
+ }
64
+ ],
65
+
66
+ CSS: /*css*/`
67
+ .ultravisor-um-page { padding: 24px; }
68
+ .ultravisor-um-tabs {
69
+ display: flex; gap: 4px; margin-bottom: 16px;
70
+ border-bottom: 1px solid var(--uv-border, #2a2a2a);
71
+ }
72
+ .ultravisor-um-tab {
73
+ background: transparent; border: 0; cursor: pointer;
74
+ padding: 10px 18px; color: var(--uv-text-tertiary, #888);
75
+ font-size: 14px; font-weight: 500;
76
+ border-bottom: 2px solid transparent;
77
+ }
78
+ .ultravisor-um-tab:hover { color: var(--uv-text, #ddd); }
79
+ .ultravisor-um-tab-active {
80
+ color: var(--uv-text, #ddd);
81
+ border-bottom-color: var(--uv-brand, #2563eb);
82
+ }
83
+ `
84
+ };
85
+
86
+ class UltravisorUserManagementView extends libPictView
87
+ {
88
+ constructor(pFable, pOptions, pServiceHash)
89
+ {
90
+ super(pFable, pOptions, pServiceHash);
91
+ this._activeTab = 'users';
92
+ }
93
+
94
+ switchTab(pTab)
95
+ {
96
+ this._activeTab = pTab;
97
+ this._refreshTabState();
98
+ // Render the section view that's now visible. Other panes stay
99
+ // in the DOM (display:none) so their form state survives toggles.
100
+ let tmpProvider = this._provider();
101
+ if (pTab === 'users' && tmpProvider) { tmpProvider.loadUsers(); }
102
+ this._renderChildren();
103
+ }
104
+
105
+ onAfterRender(pRenderable, pAddress, pRecord, pContent)
106
+ {
107
+ this._refreshTabState();
108
+ // Kick a load so the list paints on first arrival; the section's
109
+ // UserList itself doesn't auto-fetch.
110
+ let tmpProvider = this._provider();
111
+ if (this._activeTab === 'users' && tmpProvider)
112
+ {
113
+ tmpProvider.loadUsers(() => this._renderChildren());
114
+ }
115
+ this._renderChildren();
116
+ this.pict.CSSMap.injectCSS();
117
+ return super.onAfterRender
118
+ ? super.onAfterRender(pRenderable, pAddress, pRecord, pContent)
119
+ : undefined;
120
+ }
121
+
122
+ _refreshTabState()
123
+ {
124
+ let tmpUsersTab = document.getElementById('UV-UM-Tab-Users');
125
+ let tmpPwdTab = document.getElementById('UV-UM-Tab-Pwd');
126
+ let tmpUsersPane = document.getElementById('UV-UM-Pane-Users');
127
+ let tmpPwdPane = document.getElementById('UV-UM-Pane-Pwd');
128
+ if (tmpUsersTab)
129
+ {
130
+ tmpUsersTab.className = 'ultravisor-um-tab' + (this._activeTab === 'users' ? ' ultravisor-um-tab-active' : '');
131
+ }
132
+ if (tmpPwdTab)
133
+ {
134
+ tmpPwdTab.className = 'ultravisor-um-tab' + (this._activeTab === 'change-password' ? ' ultravisor-um-tab-active' : '');
135
+ }
136
+ if (tmpUsersPane) tmpUsersPane.style.display = (this._activeTab === 'users') ? '' : 'none';
137
+ if (tmpPwdPane) tmpPwdPane.style.display = (this._activeTab === 'change-password') ? '' : 'none';
138
+ }
139
+
140
+ _renderChildren()
141
+ {
142
+ // Render section views into their mount points. Each view tracks
143
+ // its own destination — calling render() is enough.
144
+ let tmpList = this.pict.views['PictUM-UserList'];
145
+ let tmpEdit = this.pict.views['PictUM-UserEdit'];
146
+ let tmpPwd = this.pict.views['PictUM-PasswordChange'];
147
+ if (tmpList) tmpList.render();
148
+ if (tmpEdit) tmpEdit.render();
149
+ if (tmpPwd) tmpPwd.render();
150
+ }
151
+
152
+ _provider()
153
+ {
154
+ return this.pict.providers && this.pict.providers['Pict-UserManagement-Provider'];
155
+ }
156
+ }
157
+
158
+ module.exports = UltravisorUserManagementView;
159
+ module.exports.default_configuration = _ViewConfiguration;