openmct/example/exampleUser/ExampleUserProvider.js
Jesse Mazzella 82fa4c1597
feat: Mission Status for Situational Awareness (#7418)
* refactor: `UserIndicator` use vue component directly

* style(WIP): filler styles for user-indicator

* feat(WIP): working on mission status indicators

* feat: support mission statuses

* feat(WIP): can display mission statuses now

* feat(WIP): add composables and dynamically calculate popup position

* feat(WIP): dismissible popup, use moar compositionAPI

* Closes #7420
- Styling and markup for mission status control panel.
- Tweaks and additions to some common style elements.

* feat: set/unset mission status for role

* refactor: rename some functions

* feat: more renaming, get mission role statuses working

* refactor: more method renaming

* fix: remove dupe method

* feat: hook up event listeners

* refactor: convert to CompositionAPI and listen to events

* fix: add that back in, woops

* test: fix some existing tests

* lint: words for the word god

* refactor: rename

* fix: setting mission statuses

* style: fix go styling

* style: add mission status button

* refactor: rename `MissionRole` -> `MissionAction`

* test: fix most existing tests

* test: remove integration tests already covered by e2e

- These tests are going to be wonky since they depend on the View. Any unit tests depending on Vue / the View will become increasingly volatile over time as we migrate more of the app into the main Vue app. Since these LOC are already covered by e2e, I'm going to remove them. We will need to move towards a more component / Vue-friendly testing framework to stabilize all of this.

* docs: add documentation

* refactor: rename

* fix: a comma

* refactor: a word

* fix: emit parameter format

* fix: correct emit for `missionStatusActionChange`

---------

Co-authored-by: Charles Hacskaylo <charles.f.hacskaylo@nasa.gov>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
2024-02-02 22:50:16 +00:00

240 lines
6.0 KiB
JavaScript

/*****************************************************************************
* Open MCT, Copyright (c) 2014-2024, 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 EventEmitter from 'EventEmitter';
import { v4 as uuid } from 'uuid';
import createExampleUser from './exampleUserCreator.js';
const STATUSES = [
{
key: 'NO_STATUS',
label: 'Not set',
iconClass: 'icon-question-mark',
iconClassPoll: 'icon-status-poll-question-mark'
},
{
key: 'GO',
label: 'Go',
iconClass: 'icon-check',
iconClassPoll: 'icon-status-poll-question-mark',
statusClass: 's-status-ok',
statusBgColor: '#33cc33',
statusFgColor: '#000'
},
{
key: 'MAYBE',
label: 'Maybe',
iconClass: 'icon-alert-triangle',
iconClassPoll: 'icon-status-poll-question-mark',
statusClass: 's-status-warning',
statusBgColor: '#ffb66c',
statusFgColor: '#000'
},
{
key: 'NO_GO',
label: 'No go',
iconClass: 'icon-circle-slash',
iconClassPoll: 'icon-status-poll-question-mark',
statusClass: 's-status-error',
statusBgColor: '#9900cc',
statusFgColor: '#fff'
}
];
/**
* @implements {StatusUserProvider}
*/
export default class ExampleUserProvider extends EventEmitter {
constructor(
openmct,
{ statusRoles } = {
statusRoles: []
}
) {
super();
this.openmct = openmct;
this.user = undefined;
this.loggedIn = false;
this.autoLoginUser = undefined;
this.statusRoleValues = statusRoles.map((role) => ({
role: role,
status: STATUSES[0]
}));
this.pollQuestion = undefined;
this.statusRoles = statusRoles;
this.ExampleUser = createExampleUser(this.openmct.user.User);
this.loginPromise = undefined;
}
isLoggedIn() {
return this.loggedIn;
}
autoLogin(username) {
this.autoLoginUser = username;
}
getCurrentUser() {
if (!this.loginPromise) {
this.loginPromise = this._login().then(() => this.user);
}
return this.loginPromise;
}
canProvideStatusForRole(role) {
return this.statusRoles.includes(role);
}
canSetPollQuestion() {
return Promise.resolve(true);
}
canSetMissionStatus() {
return Promise.resolve(false);
}
hasRole(roleId) {
if (!this.loggedIn) {
Promise.resolve(undefined);
}
return Promise.resolve(this.user.getRoles().includes(roleId));
}
getPossibleRoles() {
return this.user.getRoles();
}
getAllStatusRoles() {
return Promise.resolve(this.statusRoles);
}
getStatusForRole(role) {
const statusForRole = this.statusRoleValues.find((statusRole) => statusRole.role === role);
return Promise.resolve(statusForRole?.status);
}
async getDefaultStatusForRole(role) {
const allRoles = await this.getPossibleStatuses();
return allRoles?.[0];
}
setStatusForRole(role, status) {
status.timestamp = Date.now();
const matchingIndex = this.statusRoleValues.findIndex((statusRole) => statusRole.role === role);
this.statusRoleValues[matchingIndex].status = status;
this.emit('statusChange', {
role,
status
});
return true;
}
// eslint-disable-next-line require-await
async getPollQuestion() {
if (this.pollQuestion) {
return this.pollQuestion;
} else {
return undefined;
}
}
setPollQuestion(pollQuestion) {
if (!pollQuestion) {
// If the poll question is undefined, set it to a blank string.
// This behavior better reflects how other telemetry systems
// deal with undefined poll questions.
pollQuestion = '';
}
this.pollQuestion = {
question: pollQuestion,
timestamp: Date.now()
};
this.emit('pollQuestionChange', this.pollQuestion);
return true;
}
getPossibleStatuses() {
return Promise.resolve(STATUSES);
}
_login() {
const id = uuid();
// 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, ['flight', 'driver', 'observer']);
this.loggedIn = true;
return Promise.resolve();
}
const formStructure = {
title: 'Login',
sections: [
{
rows: [
{
key: 'username',
control: 'textfield',
name: 'Username',
pattern: '\\S+',
required: true,
cssClass: 'l-input-lg',
value: ''
}
]
}
],
buttons: {
submit: {
label: 'Login'
}
}
};
return this.openmct.forms.showForm(formStructure).then(
(info) => {
this.user = new this.ExampleUser(id, info.username, ['example-role']);
this.loggedIn = true;
},
() => {
// user canceled, setting a default username
this.user = new this.ExampleUser(id, 'Pat', ['example-role']);
this.loggedIn = true;
}
);
}
}
/**
* @typedef {import('@/api/user/StatusUserProvider').default} StatusUserProvider
*/