mirror of
https://github.com/nasa/openmct.git
synced 2025-02-20 01:16:46 +00:00
Role selection for operator status roles (#6706)
* Add additional test roles to example user * Add session storage and role to user indicator * Update example user provider * Added selection dialog to overlays and implemented for operator status * Display role in user indicator * Updates to broadcast channel lifecycle * Update comment * Comment width * UserAPI role updates and UserIndicator improvement * Moved prompt to UserIndicator * Reconnect channel on error and UserIndicator updates * Updates to status api canPRovideStatusForRole * Cleanup * Store status roles in an array instead of a singular value * Added success notification and cleanup * Lint * Removed unused role param from status api call * Remove default status role from example user plugin * Removed status.getStatusRoleForCurrentUser * Cleanup * Cleanup * Moved roleChannel to private field * Separated input value from active role value * More flight like status role names and parameter names * Update statusRole parameter name * Update default selection for roles if input is not chosen * Update OperatorStatusIndicator install to hide if an observer * console.log * Return null instead of undefined * Remove unneccesary filter on allRoles * refactor: format with prettier * Undid merge error * Merge conflict extra line * Copyright statement * RoleChannelProvider to RoleChannel * Throw error on no provider * Change RoleChannel to ActiveRoleSynchronizer and update method calls to match * iconClass to alert * Add role selection step to beforeEach * example-role to flight * Dismiss overlay from exampleUser plugin which affected menu api positioning --------- Co-authored-by: Scott Bell <scott@traclabs.com>
This commit is contained in:
parent
92329b3d8e
commit
32529ff6b2
@ -47,6 +47,11 @@ test.describe('Operator Status', () => {
|
||||
path: path.join(__dirname, '../../../../helper/', 'addInitOperatorStatus.js')
|
||||
});
|
||||
await page.goto('./', { waitUntil: 'domcontentloaded' });
|
||||
await expect(page.getByText('Select Role')).toBeVisible();
|
||||
// set role
|
||||
await page.getByRole('button', { name: 'Select' }).click();
|
||||
// dismiss role confirmation popup
|
||||
await page.getByRole('button', { name: 'Dismiss' }).click();
|
||||
});
|
||||
|
||||
// verify that operator status is visible
|
||||
|
@ -63,16 +63,24 @@ const STATUSES = [
|
||||
* @implements {StatusUserProvider}
|
||||
*/
|
||||
export default class ExampleUserProvider extends EventEmitter {
|
||||
constructor(openmct, { defaultStatusRole } = { defaultStatusRole: undefined }) {
|
||||
constructor(
|
||||
openmct,
|
||||
{ statusRoles } = {
|
||||
statusRoles: []
|
||||
}
|
||||
) {
|
||||
super();
|
||||
|
||||
this.openmct = openmct;
|
||||
this.user = undefined;
|
||||
this.loggedIn = false;
|
||||
this.autoLoginUser = undefined;
|
||||
this.status = STATUSES[0];
|
||||
this.statusRoleValues = statusRoles.map((role) => ({
|
||||
role: role,
|
||||
status: STATUSES[0]
|
||||
}));
|
||||
this.pollQuestion = undefined;
|
||||
this.defaultStatusRole = defaultStatusRole;
|
||||
this.statusRoles = statusRoles;
|
||||
|
||||
this.ExampleUser = createExampleUser(this.openmct.user.User);
|
||||
this.loginPromise = undefined;
|
||||
@ -94,14 +102,13 @@ export default class ExampleUserProvider extends EventEmitter {
|
||||
return this.loginPromise;
|
||||
}
|
||||
|
||||
canProvideStatusForRole() {
|
||||
return Promise.resolve(true);
|
||||
canProvideStatusForRole(role) {
|
||||
return this.statusRoles.includes(role);
|
||||
}
|
||||
|
||||
canSetPollQuestion() {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
hasRole(roleId) {
|
||||
if (!this.loggedIn) {
|
||||
Promise.resolve(undefined);
|
||||
@ -110,16 +117,18 @@ export default class ExampleUserProvider extends EventEmitter {
|
||||
return Promise.resolve(this.user.getRoles().includes(roleId));
|
||||
}
|
||||
|
||||
getStatusRoleForCurrentUser() {
|
||||
return Promise.resolve(this.defaultStatusRole);
|
||||
getPossibleRoles() {
|
||||
return this.user.getRoles();
|
||||
}
|
||||
|
||||
getAllStatusRoles() {
|
||||
return Promise.resolve([this.defaultStatusRole]);
|
||||
return Promise.resolve(this.statusRoles);
|
||||
}
|
||||
|
||||
getStatusForRole(role) {
|
||||
return Promise.resolve(this.status);
|
||||
const statusForRole = this.statusRoleValues.find((statusRole) => statusRole.role === role);
|
||||
|
||||
return Promise.resolve(statusForRole?.status);
|
||||
}
|
||||
|
||||
async getDefaultStatusForRole(role) {
|
||||
@ -130,7 +139,8 @@ export default class ExampleUserProvider extends EventEmitter {
|
||||
|
||||
setStatusForRole(role, status) {
|
||||
status.timestamp = Date.now();
|
||||
this.status = status;
|
||||
const matchingIndex = this.statusRoleValues.findIndex((statusRole) => statusRole.role === role);
|
||||
this.statusRoleValues[matchingIndex].status = status;
|
||||
this.emit('statusChange', {
|
||||
role,
|
||||
status
|
||||
@ -175,7 +185,7 @@ export default class ExampleUserProvider extends EventEmitter {
|
||||
// for testing purposes, this will skip the form, this wouldn't be used in
|
||||
// a normal authentication process
|
||||
if (this.autoLoginUser) {
|
||||
this.user = new this.ExampleUser(id, this.autoLoginUser, ['example-role']);
|
||||
this.user = new this.ExampleUser(id, this.autoLoginUser, ['flight', 'driver', 'observer']);
|
||||
this.loggedIn = true;
|
||||
|
||||
return Promise.resolve();
|
||||
|
@ -21,16 +21,18 @@
|
||||
*****************************************************************************/
|
||||
|
||||
import ExampleUserProvider from './ExampleUserProvider';
|
||||
const AUTO_LOGIN_USER = 'mct-user';
|
||||
const STATUS_ROLES = ['flight', 'driver'];
|
||||
|
||||
export default function ExampleUserPlugin(
|
||||
{ autoLoginUser, defaultStatusRole } = {
|
||||
autoLoginUser: 'guest',
|
||||
defaultStatusRole: 'test-role'
|
||||
{ autoLoginUser, statusRoles } = {
|
||||
autoLoginUser: AUTO_LOGIN_USER,
|
||||
statusRoles: STATUS_ROLES
|
||||
}
|
||||
) {
|
||||
return function install(openmct) {
|
||||
const userProvider = new ExampleUserProvider(openmct, {
|
||||
defaultStatusRole
|
||||
statusRoles
|
||||
});
|
||||
|
||||
if (autoLoginUser !== undefined) {
|
||||
|
@ -1,6 +1,29 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2023, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import Overlay from './Overlay';
|
||||
import Dialog from './Dialog';
|
||||
import ProgressDialog from './ProgressDialog';
|
||||
import Selection from './Selection';
|
||||
|
||||
/**
|
||||
* The OverlayAPI is responsible for pre-pending templates to
|
||||
@ -130,6 +153,13 @@ class OverlayAPI {
|
||||
|
||||
return progressDialog;
|
||||
}
|
||||
|
||||
selection(options) {
|
||||
let selection = new Selection(options);
|
||||
this.showOverlay(selection);
|
||||
|
||||
return selection;
|
||||
}
|
||||
}
|
||||
|
||||
export default OverlayAPI;
|
||||
|
67
src/api/overlays/Selection.js
Normal file
67
src/api/overlays/Selection.js
Normal file
@ -0,0 +1,67 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2023, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import SelectionComponent from './components/SelectionComponent.vue';
|
||||
import Overlay from './Overlay';
|
||||
import Vue from 'vue';
|
||||
|
||||
class Selection extends Overlay {
|
||||
constructor({
|
||||
iconClass,
|
||||
title,
|
||||
message,
|
||||
selectionOptions,
|
||||
onChange,
|
||||
currentSelection,
|
||||
...options
|
||||
}) {
|
||||
let component = new Vue({
|
||||
components: {
|
||||
SelectionComponent: SelectionComponent
|
||||
},
|
||||
provide: {
|
||||
iconClass,
|
||||
title,
|
||||
message,
|
||||
selectionOptions,
|
||||
onChange,
|
||||
currentSelection
|
||||
},
|
||||
template: '<selection-component></selection-component>'
|
||||
}).$mount();
|
||||
|
||||
super({
|
||||
element: component.$el,
|
||||
size: 'fit',
|
||||
dismissable: false,
|
||||
onChange,
|
||||
currentSelection,
|
||||
...options
|
||||
});
|
||||
|
||||
this.once('destroy', () => {
|
||||
component.$destroy();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default Selection;
|
34
src/api/overlays/components/SelectionComponent.vue
Normal file
34
src/api/overlays/components/SelectionComponent.vue
Normal file
@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="c-message">
|
||||
<!--Uses flex-row -->
|
||||
<div class="c-message__icon" :class="['u-icon-bg-color-' + iconClass]"></div>
|
||||
<div class="c-message__text">
|
||||
<!-- Uses flex-column -->
|
||||
<div v-if="title" class="c-message__title">
|
||||
{{ title }}
|
||||
</div>
|
||||
|
||||
<div v-if="message" class="c-message__action-text">
|
||||
{{ message }}
|
||||
</div>
|
||||
<select @change="onChange">
|
||||
<option
|
||||
v-for="option in selectionOptions"
|
||||
:key="option.key"
|
||||
:value="option.key"
|
||||
:selected="option.key === currentSelection"
|
||||
>
|
||||
{{ option.name }}
|
||||
</option>
|
||||
</select>
|
||||
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inject: ['iconClass', 'title', 'message', 'selectionOptions', 'currentSelection', 'onChange']
|
||||
};
|
||||
</script>
|
37
src/api/user/ActiveRoleSynchronizer.js
Normal file
37
src/api/user/ActiveRoleSynchronizer.js
Normal file
@ -0,0 +1,37 @@
|
||||
import { ACTIVE_ROLE_BROADCAST_CHANNEL_NAME } from './constants';
|
||||
|
||||
class ActiveRoleSynchronizer {
|
||||
#roleChannel;
|
||||
|
||||
constructor(openmct) {
|
||||
this.openmct = openmct;
|
||||
this.#roleChannel = new BroadcastChannel(ACTIVE_ROLE_BROADCAST_CHANNEL_NAME);
|
||||
this.setActiveRoleFromChannelMessage = this.setActiveRoleFromChannelMessage.bind(this);
|
||||
|
||||
this.subscribeToRoleChanges(this.setActiveRoleFromChannelMessage);
|
||||
}
|
||||
subscribeToRoleChanges(callback) {
|
||||
this.#roleChannel.addEventListener('message', callback);
|
||||
}
|
||||
unsubscribeFromRoleChanges(callback) {
|
||||
this.#roleChannel.removeEventListener('message', callback);
|
||||
}
|
||||
|
||||
setActiveRoleFromChannelMessage(event) {
|
||||
const role = event.data;
|
||||
this.openmct.user.setActiveRole(role);
|
||||
}
|
||||
broadcastNewRole(role) {
|
||||
if (!this.#roleChannel.name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.#roleChannel.postMessage(role);
|
||||
}
|
||||
destroy() {
|
||||
this.unsubscribeFromRoleChanges(this.setActiveRoleFromChannelMessage);
|
||||
this.#roleChannel.close();
|
||||
}
|
||||
}
|
||||
|
||||
export default ActiveRoleSynchronizer;
|
@ -140,9 +140,9 @@ export default class StatusAPI extends EventEmitter {
|
||||
const provider = this.#userAPI.getProvider();
|
||||
|
||||
if (provider.canProvideStatusForRole) {
|
||||
return provider.canProvideStatusForRole(role);
|
||||
return Promise.resolve(provider.canProvideStatusForRole(role));
|
||||
} else {
|
||||
return false;
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,11 +151,16 @@ export default class StatusAPI extends EventEmitter {
|
||||
* @param {Status} status The status to set for the provided role
|
||||
* @returns {Promise<Boolean>} true if operation was successful, otherwise false.
|
||||
*/
|
||||
setStatusForRole(role, status) {
|
||||
setStatusForRole(status) {
|
||||
const provider = this.#userAPI.getProvider();
|
||||
|
||||
if (provider.setStatusForRole) {
|
||||
return provider.setStatusForRole(role, status);
|
||||
const activeRole = this.#userAPI.getActiveRole();
|
||||
if (!provider.canProvideStatusForRole(activeRole)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return provider.setStatusForRole(activeRole, status);
|
||||
} else {
|
||||
this.#userAPI.error('User provider does not support setting role status');
|
||||
}
|
||||
@ -216,21 +221,6 @@ export default class StatusAPI extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The status role of the current user. A user may have multiple roles, but will only have one role
|
||||
* that provides status at any time.
|
||||
* @returns {Promise<import("./UserAPI").Role>} the role for which the current user can provide status.
|
||||
*/
|
||||
getStatusRoleForCurrentUser() {
|
||||
const provider = this.#userAPI.getProvider();
|
||||
|
||||
if (provider.getStatusRoleForCurrentUser) {
|
||||
return provider.getStatusRoleForCurrentUser();
|
||||
} else {
|
||||
this.#userAPI.error('User provider cannot provide role status for this user');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<Boolean>} true if the configured UserProvider can provide status for the currently logged in user, false otherwise.
|
||||
* @see StatusUserProvider
|
||||
@ -238,14 +228,13 @@ export default class StatusAPI extends EventEmitter {
|
||||
async canProvideStatusForCurrentUser() {
|
||||
const provider = this.#userAPI.getProvider();
|
||||
|
||||
if (provider.getStatusRoleForCurrentUser) {
|
||||
const activeStatusRole = await this.#userAPI.getProvider().getStatusRoleForCurrentUser();
|
||||
const canProvideStatus = await this.canProvideStatusForRole(activeStatusRole);
|
||||
|
||||
return canProvideStatus;
|
||||
} else {
|
||||
if (!provider) {
|
||||
return false;
|
||||
}
|
||||
const activeStatusRole = await this.#userAPI.getActiveRole();
|
||||
const canProvideStatus = await this.canProvideStatusForRole(activeStatusRole);
|
||||
|
||||
return canProvideStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,5 +77,4 @@ export default class StatusUserProvider extends UserProvider {
|
||||
/**
|
||||
* @returns {Promise<import("./UserAPI").Role>} the active status role for the currently logged in user
|
||||
*/
|
||||
async getStatusRoleForCurrentUser() {}
|
||||
}
|
||||
|
37
src/api/user/StoragePersistance.js
Normal file
37
src/api/user/StoragePersistance.js
Normal file
@ -0,0 +1,37 @@
|
||||
/*****************************************************************************
|
||||
* Open MCT, Copyright (c) 2014-2023, United States Government
|
||||
* as represented by the Administrator of the National Aeronautics and Space
|
||||
* Administration. All rights reserved.
|
||||
*
|
||||
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0.
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
* Open MCT includes source code licensed under additional open source
|
||||
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
||||
* this source code distribution or the Licensing information page available
|
||||
* at runtime from the About dialog for additional information.
|
||||
*****************************************************************************/
|
||||
|
||||
import { ACTIVE_ROLE_LOCAL_STORAGE_KEY } from './constants';
|
||||
|
||||
class StoragePersistance {
|
||||
getActiveRole() {
|
||||
return localStorage.getItem(ACTIVE_ROLE_LOCAL_STORAGE_KEY);
|
||||
}
|
||||
setActiveRole(role) {
|
||||
return localStorage.setItem(ACTIVE_ROLE_LOCAL_STORAGE_KEY, role);
|
||||
}
|
||||
clearActiveRole() {
|
||||
return localStorage.removeItem(ACTIVE_ROLE_LOCAL_STORAGE_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
export default new StoragePersistance();
|
@ -24,6 +24,7 @@ import EventEmitter from 'EventEmitter';
|
||||
import { MULTIPLE_PROVIDER_ERROR, NO_PROVIDER_ERROR } from './constants';
|
||||
import StatusAPI from './StatusAPI';
|
||||
import User from './User';
|
||||
import StoragePersistance from './StoragePersistance';
|
||||
|
||||
class UserAPI extends EventEmitter {
|
||||
/**
|
||||
@ -86,6 +87,58 @@ class UserAPI extends EventEmitter {
|
||||
return this._provider.getCurrentUser();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* If a user provider is set, it will return an array of possible roles
|
||||
* that can be selected by the current user
|
||||
* @memberof module:openmct.UserAPI#
|
||||
* @returns {Array}
|
||||
* @throws Will throw an error if no user provider is set
|
||||
*/
|
||||
|
||||
getPossibleRoles() {
|
||||
if (!this.hasProvider()) {
|
||||
this.error(NO_PROVIDER_ERROR);
|
||||
}
|
||||
return this._provider.getPossibleRoles();
|
||||
}
|
||||
/**
|
||||
* If a user provider is set, it will return the active role or null
|
||||
* @memberof module:openmct.UserAPI#
|
||||
* @returns {string|null}
|
||||
*/
|
||||
getActiveRole() {
|
||||
if (!this.hasProvider()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// get from session storage
|
||||
const sessionStorageValue = StoragePersistance.getActiveRole();
|
||||
|
||||
return sessionStorageValue;
|
||||
}
|
||||
/**
|
||||
* Set the active role in session storage
|
||||
* @memberof module:openmct.UserAPI#
|
||||
* @returns {undefined}
|
||||
*/
|
||||
setActiveRole(role) {
|
||||
StoragePersistance.setActiveRole(role);
|
||||
this.emit('roleChanged', role);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return if a role can provide a operator status response
|
||||
* @memberof module:openmct.UserApi#
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
canProvideStatusForRole() {
|
||||
if (!this.hasProvider()) {
|
||||
return null;
|
||||
}
|
||||
const activeRole = this.getActiveRole();
|
||||
|
||||
return this._provider.canProvideStatusForRole?.(activeRole);
|
||||
}
|
||||
|
||||
/**
|
||||
* If a user provider is set, it will return the user provider's
|
||||
|
@ -25,7 +25,7 @@ import { MULTIPLE_PROVIDER_ERROR } from './constants';
|
||||
import ExampleUserProvider from '../../../example/exampleUser/ExampleUserProvider';
|
||||
|
||||
const USERNAME = 'Test User';
|
||||
const EXAMPLE_ROLE = 'example-role';
|
||||
const EXAMPLE_ROLE = 'flight';
|
||||
|
||||
describe('The User API', () => {
|
||||
let openmct;
|
||||
|
@ -22,3 +22,6 @@
|
||||
|
||||
export const MULTIPLE_PROVIDER_ERROR = 'Only one user provider may be set at a time.';
|
||||
export const NO_PROVIDER_ERROR = 'No user provider has been set.';
|
||||
|
||||
export const ACTIVE_ROLE_LOCAL_STORAGE_KEY = 'ACTIVE_USER_ROLE';
|
||||
export const ACTIVE_ROLE_BROADCAST_CHANNEL_NAME = 'ActiveRoleChannel';
|
||||
|
@ -48,7 +48,6 @@
|
||||
|
||||
<script>
|
||||
const DEFAULT_POLL_QUESTION = 'NO POLL QUESTION';
|
||||
|
||||
export default {
|
||||
inject: ['openmct', 'indicator', 'configuration'],
|
||||
props: {
|
||||
@ -63,7 +62,6 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
allRoles: [],
|
||||
role: '--',
|
||||
pollQuestionUpdated: '--',
|
||||
currentPollQuestion: DEFAULT_POLL_QUESTION,
|
||||
@ -78,26 +76,27 @@ export default {
|
||||
left: `${this.positionX}px`,
|
||||
top: `${this.positionY}px`
|
||||
};
|
||||
},
|
||||
canProvideStatusForRole() {
|
||||
return this.openmct.user.canProvideStatusForRole(this.role);
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.openmct.user.status.off('statusChange', this.setStatus);
|
||||
this.openmct.user.status.off('pollQuestionChange', this.setPollQuestion);
|
||||
this.openmct.user.off('roleChanged', this.fetchMyStatus);
|
||||
},
|
||||
async mounted() {
|
||||
this.unsubscribe = [];
|
||||
await this.fetchUser();
|
||||
await this.findFirstApplicableRole();
|
||||
this.fetchPossibleStatusesForUser();
|
||||
this.fetchCurrentPoll();
|
||||
this.fetchMyStatus();
|
||||
await this.fetchMyStatus();
|
||||
this.subscribeToMyStatus();
|
||||
this.subscribeToPollQuestion();
|
||||
this.subscribeToRoleChange();
|
||||
},
|
||||
methods: {
|
||||
async findFirstApplicableRole() {
|
||||
this.role = await this.openmct.user.status.getStatusRoleForCurrentUser();
|
||||
},
|
||||
async fetchUser() {
|
||||
this.user = await this.openmct.user.getCurrentUser();
|
||||
},
|
||||
@ -117,9 +116,22 @@ export default {
|
||||
this.indicator.text(pollQuestion?.question || '');
|
||||
},
|
||||
async fetchMyStatus() {
|
||||
const activeStatusRole = await this.openmct.user.status.getStatusRoleForCurrentUser();
|
||||
const status = await this.openmct.user.status.getStatusForRole(activeStatusRole);
|
||||
// hide indicator for observer
|
||||
const isStatusCapable = await this.openmct.user.canProvideStatusForRole();
|
||||
if (!isStatusCapable) {
|
||||
this.indicator.text('');
|
||||
this.indicator.statusClass('hidden');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const activeRole = await this.openmct.user.getActiveRole();
|
||||
if (!activeRole) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.role = activeRole;
|
||||
const status = await this.openmct.user.status.getStatusForRole(activeRole);
|
||||
if (status !== undefined) {
|
||||
this.setStatus({ status });
|
||||
}
|
||||
@ -130,7 +142,10 @@ export default {
|
||||
subscribeToPollQuestion() {
|
||||
this.openmct.user.status.on('pollQuestionChange', this.setPollQuestion);
|
||||
},
|
||||
setStatus({ role, status }) {
|
||||
subscribeToRoleChange() {
|
||||
this.openmct.user.on('roleChanged', this.fetchMyStatus);
|
||||
},
|
||||
setStatus({ status }) {
|
||||
status = this.applyStyling(status);
|
||||
this.selectedStatus = status.key;
|
||||
this.indicator.iconClass(status.iconClassPoll);
|
||||
@ -148,11 +163,16 @@ export default {
|
||||
return this.allStatuses.find((possibleMatch) => possibleMatch.key === statusKey);
|
||||
},
|
||||
async changeStatus() {
|
||||
if (!this.openmct.user.canProvideStatusForRole()) {
|
||||
this.openmct.notifications.error('Selected role is ineligible to provide operator status');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.selectedStatus !== undefined) {
|
||||
const statusObject = this.findStatusByKey(this.selectedStatus);
|
||||
|
||||
const result = await this.openmct.user.status.setStatusForRole(this.role, statusObject);
|
||||
|
||||
const result = await this.openmct.user.status.setStatusForRole(statusObject);
|
||||
if (result === true) {
|
||||
this.openmct.notifications.info('Successfully set operator status');
|
||||
} else {
|
||||
|
@ -29,13 +29,8 @@ import PollQuestionIndicator from './pollQuestion/PollQuestionIndicator';
|
||||
export default function operatorStatusPlugin(configuration) {
|
||||
return function install(openmct) {
|
||||
if (openmct.user.hasProvider()) {
|
||||
openmct.user.status.canProvideStatusForCurrentUser().then((canProvideStatus) => {
|
||||
if (canProvideStatus) {
|
||||
const operatorStatusIndicator = new OperatorStatusIndicator(openmct, configuration);
|
||||
|
||||
operatorStatusIndicator.install();
|
||||
}
|
||||
});
|
||||
const operatorStatusIndicator = new OperatorStatusIndicator(openmct, configuration);
|
||||
operatorStatusIndicator.install();
|
||||
|
||||
openmct.user.status.canSetPollQuestion().then((canSetPollQuestion) => {
|
||||
if (canSetPollQuestion) {
|
||||
|
@ -23,30 +23,107 @@
|
||||
<template>
|
||||
<div class="c-indicator icon-person c-indicator--clickable">
|
||||
<span class="label c-indicator__label">
|
||||
{{ userName }}
|
||||
{{ role ? `${userName}: ${role}` : userName }}
|
||||
<button @click="promptForRoleSelection">Change Role</button>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ActiveRoleSynchronizer from '../../../api/user/ActiveRoleSynchronizer';
|
||||
export default {
|
||||
inject: ['openmct'],
|
||||
data() {
|
||||
return {
|
||||
userName: undefined,
|
||||
loggedIn: false
|
||||
role: undefined,
|
||||
loggedIn: false,
|
||||
roleChannel: undefined,
|
||||
inputRoleSelection: undefined,
|
||||
roleSelectionDialog: undefined
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.getUserInfo();
|
||||
async mounted() {
|
||||
await this.getUserInfo();
|
||||
this.roleChannel = new ActiveRoleSynchronizer(this.openmct);
|
||||
this.roleChannel.subscribeToRoleChanges(this.onRoleChange);
|
||||
await this.fetchOrPromptForRole();
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.roleChannel.unsubscribeFromRoleChanges(this.onRoleChange);
|
||||
},
|
||||
methods: {
|
||||
getUserInfo() {
|
||||
this.openmct.user.getCurrentUser().then((user) => {
|
||||
this.userName = user.getName();
|
||||
this.loggedIn = this.openmct.user.isLoggedIn();
|
||||
async getUserInfo() {
|
||||
const user = await this.openmct.user.getCurrentUser();
|
||||
this.userName = user.getName();
|
||||
this.role = this.openmct.user.getActiveRole();
|
||||
this.loggedIn = this.openmct.user.isLoggedIn();
|
||||
},
|
||||
async fetchOrPromptForRole() {
|
||||
const UserAPI = this.openmct.user;
|
||||
const activeRole = UserAPI.getActiveRole();
|
||||
this.role = activeRole;
|
||||
if (!activeRole) {
|
||||
this.promptForRoleSelection();
|
||||
} else {
|
||||
// only notify the user if they have more than one role available
|
||||
const allRoles = await this.openmct.user.getPossibleRoles();
|
||||
if (allRoles.length > 1) {
|
||||
this.openmct.notifications.info(`You're logged in as role ${activeRole}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
async promptForRoleSelection() {
|
||||
const allRoles = await this.openmct.user.getPossibleRoles();
|
||||
const selectionOptions = allRoles.map((role) => ({
|
||||
key: role,
|
||||
name: role
|
||||
}));
|
||||
// automatically select only role option
|
||||
if (selectionOptions.length === 1) {
|
||||
this.updateRole(selectionOptions[0].key);
|
||||
return;
|
||||
}
|
||||
|
||||
this.roleSelectionDialog = this.openmct.overlays.selection({
|
||||
selectionOptions,
|
||||
iconClass: 'alert',
|
||||
title: 'Select Role',
|
||||
message: 'Please select your role for operator status.',
|
||||
currentSelection: this.role,
|
||||
onChange: (event) => {
|
||||
this.inputRoleSelection = event.target.value;
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
label: 'Select',
|
||||
emphasis: true,
|
||||
callback: () => {
|
||||
this.roleSelectionDialog.dismiss();
|
||||
this.roleSelectionDialog = undefined;
|
||||
const inputValueOrDefault = this.inputRoleSelection || selectionOptions[0].key;
|
||||
this.updateRole(inputValueOrDefault);
|
||||
this.openmct.notifications.info(`Successfully set new role to ${this.role}`);
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
},
|
||||
onRoleChange(event) {
|
||||
const role = event.data;
|
||||
this.roleSelectionDialog?.dismiss();
|
||||
this.setRoleSelection(role);
|
||||
},
|
||||
setRoleSelection(role) {
|
||||
this.role = role;
|
||||
},
|
||||
|
||||
updateRole(role) {
|
||||
this.setRoleSelection(role);
|
||||
this.openmct.user.setActiveRole(role);
|
||||
// update other tabs through broadcast channel
|
||||
this.roleChannel.broadcastNewRole(role);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -113,6 +113,10 @@ export default {
|
||||
mounted() {
|
||||
this.openmct.notifications.on('notification', this.showNotification);
|
||||
this.openmct.notifications.on('dismiss-all', this.clearModel);
|
||||
if (this.openmct.notifications.activeNotification) {
|
||||
activeNotification = this.openmct.notifications.activeNotification;
|
||||
this.showNotification(activeNotification);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showNotification(notification) {
|
||||
|
@ -25,6 +25,7 @@ import MCT from 'MCT';
|
||||
let nativeFunctions = [];
|
||||
let mockObjects = setMockObjects();
|
||||
|
||||
const EXAMPLE_ROLE = 'flight';
|
||||
const DEFAULT_TIME_OPTIONS = {
|
||||
timeSystemKey: 'utc',
|
||||
bounds: {
|
||||
@ -38,6 +39,7 @@ export function createOpenMct(timeSystemOptions = DEFAULT_TIME_OPTIONS) {
|
||||
openmct.install(openmct.plugins.LocalStorage());
|
||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||
openmct.setAssetPath('/base');
|
||||
openmct.user.setActiveRole(EXAMPLE_ROLE);
|
||||
|
||||
const timeSystemKey = timeSystemOptions.timeSystemKey;
|
||||
const start = timeSystemOptions.bounds.start;
|
||||
|
Loading…
x
Reference in New Issue
Block a user