mirror of
https://github.com/fleetbase/fleetbase.git
synced 2026-01-07 06:50:14 +00:00
Compare commits
4 Commits
v0.3.10
...
deploy/sta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd65ee619b | ||
|
|
e790a0e123 | ||
|
|
723e3ca3d1 | ||
|
|
4eb706d33e |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -27,7 +27,6 @@ verdaccio/storage
|
||||
packages/billing
|
||||
packages/flespi
|
||||
packages/loconav
|
||||
packages/internals
|
||||
packages/projectargus-engine
|
||||
# wip
|
||||
packages/solid
|
||||
|
||||
@@ -7,11 +7,7 @@ routes:
|
||||
headers:
|
||||
Cache-Control: "max-age=600, no-transform, public"
|
||||
gzip: false
|
||||
- route: "^.+\\.(xml|json)$"
|
||||
headers:
|
||||
Cache-Control: "max-age=600, no-transform, public"
|
||||
gzip: true
|
||||
- route: "^.+\\.(html)$"
|
||||
- route: "^.+\\.(html|xml|json)$"
|
||||
headers:
|
||||
Cache-Control: "public, max-age=0, must-revalidate"
|
||||
gzip: true
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^7.3|^8.0",
|
||||
"fleetbase/core-api": "^1.3.13",
|
||||
"fleetbase/fleetops-api": "^0.4.4",
|
||||
"fleetbase/storefront-api": "^0.2.9",
|
||||
"fleetbase/core-api": "^1.3.5",
|
||||
"fleetbase/fleetops-api": "^0.3.7",
|
||||
"fleetbase/storefront-api": "^0.2.5",
|
||||
"fleetbase/billing-api": "^0.0.5",
|
||||
"fruitcake/laravel-cors": "^2.0",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"laravel/framework": "^8.75",
|
||||
@@ -32,6 +33,12 @@
|
||||
"nunomaduro/collision": "^5.10",
|
||||
"phpunit/phpunit": "^9.5.10"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "git@github.com:fleetbase/billing.git"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "app/",
|
||||
|
||||
1298
api/composer.lock
generated
1298
api/composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('FILESYSTEM_DRIVER', 'public'),
|
||||
'default' => env('FILESYSTEM_DRIVER', 'local'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
||||
@@ -13,7 +13,4 @@ php artisan migrate --force
|
||||
php artisan sandbox:migrate --force
|
||||
|
||||
# Seed database
|
||||
php artisan fleetbase:seed
|
||||
|
||||
# Restart queue
|
||||
php artisan queue:restart
|
||||
php artisan fleetbase:seed
|
||||
5622
api/pnpm-lock.yaml
generated
5622
api/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,7 @@ module.exports = {
|
||||
'ember/no-empty-glimmer-component-classes': 'off',
|
||||
'ember/no-get': 'off',
|
||||
'ember/classic-decorator-no-classic-methods': 'off',
|
||||
'n/no-unpublished-require': [
|
||||
'node/no-unpublished-require': [
|
||||
'error',
|
||||
{
|
||||
allowModules: [
|
||||
|
||||
1
console/.gitignore
vendored
1
console/.gitignore
vendored
@@ -4,7 +4,6 @@
|
||||
|
||||
# dependencies
|
||||
/node_modules/
|
||||
/scripts/node_modules/
|
||||
|
||||
# misc
|
||||
/.env*
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
extends: ['stylelint-config-standard', 'stylelint-prettier/recommended'],
|
||||
rules: {
|
||||
'import-notation': null,
|
||||
},
|
||||
extends: ['stylelint-config-standard', 'stylelint-prettier/recommended'],
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@ module.exports = {
|
||||
'no-bare-strings': 'off',
|
||||
'no-invalid-interactive': 'off',
|
||||
'no-yield-only': 'off',
|
||||
'no-down-event-binding': 'off',
|
||||
'table-groups': 'off',
|
||||
'link-href-attributes': 'off',
|
||||
'require-input-label': 'off',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# ---- Build Stage ----
|
||||
FROM node:18.15.0-alpine AS builder
|
||||
FROM node:16.20-alpine AS builder
|
||||
|
||||
# Set the working directory in the container to /app
|
||||
WORKDIR /app
|
||||
|
||||
@@ -14,7 +14,7 @@ export default class App extends Application {
|
||||
|
||||
async ready() {
|
||||
const extensions = await loadExtensions();
|
||||
|
||||
|
||||
this.extensions = extensions;
|
||||
this.engines = mapEngines(extensions);
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{{yield}}
|
||||
@@ -1,3 +0,0 @@
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
export default class Configure2faComponent extends Component {}
|
||||
@@ -1,30 +0,0 @@
|
||||
<div class="next-user-button" ...attributes>
|
||||
<BasicDropdown @defaultClass={{@wrapperClass}} @onOpen={{@onOpen}} @onClose={{@onClose}} @verticalPosition={{@verticalPosition}} @horizontalPosition={{@horizontalPosition}} @renderInPlace={{or @renderInPlace true}} @initiallyOpened={{@initiallyOpened}} as |dd|>
|
||||
<dd.Trigger class={{@triggerClass}}>
|
||||
<div class="next-org-button-trigger flex-shrink-0 {{if dd.isOpen 'is-open'}}">
|
||||
<FaIcon @icon="globe" @size="sm" />
|
||||
</div>
|
||||
</dd.Trigger>
|
||||
<dd.Content class={{@contentClass}}>
|
||||
<div class="next-dd-menu {{@dropdownMenuClass}} {{if dd.isOpen 'is-open'}}">
|
||||
{{#each-in this.availableLocales as |key country|}}
|
||||
<div class="px-1">
|
||||
<a href="javascript:;" class="next-dd-item" {{on "click" (fn this.changeLocale key)}}>
|
||||
<div class="flex flex-row items-center justify-between w-full">
|
||||
<div class="flex-1">
|
||||
<span class="mr-1">{{country.emoji}}</span>
|
||||
<span>{{country.language}}</span>
|
||||
</div>
|
||||
{{#if (eq this.currentLocale key)}}
|
||||
<div>
|
||||
<FaIcon @icon="check" class="text-green-400" />
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{{/each-in}}
|
||||
</div>
|
||||
</dd.Content>
|
||||
</BasicDropdown>
|
||||
</div>
|
||||
@@ -1,144 +0,0 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { task } from 'ember-concurrency-decorators';
|
||||
|
||||
export default class LocaleSelectorComponent extends Component {
|
||||
/**
|
||||
* Inject the intl service.
|
||||
*
|
||||
* @memberof LocaleSelectorComponent
|
||||
*/
|
||||
@service intl;
|
||||
|
||||
/**
|
||||
* Inject the intl service.
|
||||
*
|
||||
* @memberof LocaleSelectorComponent
|
||||
*/
|
||||
@service fetch;
|
||||
|
||||
/**
|
||||
* Tracks all the available locales.
|
||||
*
|
||||
* @memberof LocaleSelectorComponent
|
||||
*/
|
||||
@tracked locales = [];
|
||||
|
||||
/**
|
||||
* All available countries data.
|
||||
*
|
||||
* @memberof LocaleSelectorComponent
|
||||
*/
|
||||
@tracked countries = [];
|
||||
|
||||
/**
|
||||
* The current locale in use.
|
||||
*
|
||||
* @memberof LocaleSelectorComponent
|
||||
*/
|
||||
@tracked currentLocale;
|
||||
|
||||
/**
|
||||
* Creates an instance of LocaleSelectorComponent.
|
||||
* @memberof LocaleSelectorComponent
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
this.locales = this.intl.locales;
|
||||
this.currentLocale = this.intl.primaryLocale;
|
||||
this.loadAvailableCountries.perform();
|
||||
|
||||
// Check for locale change
|
||||
this.intl.onLocaleChanged(() => {
|
||||
this.currentLocale = this.intl.primaryLocale;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the change of locale.
|
||||
* @param {string} selectedLocale - The selected locale.
|
||||
* @returns {void}
|
||||
* @memberof LocaleSelectorComponent
|
||||
* @method changeLocale
|
||||
* @instance
|
||||
* @action
|
||||
*/
|
||||
@action changeLocale(selectedLocale) {
|
||||
this.currentLocale = selectedLocale;
|
||||
this.intl.setLocale(selectedLocale);
|
||||
// Persist to server
|
||||
this.saveUserLocale.perform(selectedLocale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads available countries asynchronously.
|
||||
* @returns {void}
|
||||
* @memberof LocaleSelectorComponent
|
||||
* @method loadAvailableCountries
|
||||
* @instance
|
||||
* @task
|
||||
* @generator
|
||||
*/
|
||||
@task *loadAvailableCountries() {
|
||||
this.countries = yield this.fetch.get('lookup/countries', { columns: ['name', 'cca2', 'flag', 'emoji', 'languages'] });
|
||||
this.availableLocales = this._createAvailableLocaleMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the user's selected locale to the server.
|
||||
* @param {string} locale - The user's selected locale.
|
||||
* @returns {void}
|
||||
* @memberof LocaleSelectorComponent
|
||||
* @method saveUserLocale
|
||||
* @instance
|
||||
* @task
|
||||
* @generator
|
||||
*/
|
||||
@task *saveUserLocale(locale) {
|
||||
yield this.fetch.post('users/locale', { locale });
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a map of available locales.
|
||||
* @private
|
||||
* @returns {Object} - The map of available locales.
|
||||
* @memberof LocaleSelectorComponent
|
||||
* @method _createAvailableLocaleMap
|
||||
* @instance
|
||||
*/
|
||||
_createAvailableLocaleMap() {
|
||||
const localeMap = {};
|
||||
|
||||
for (let i = 0; i < this.locales.length; i++) {
|
||||
const locale = this.locales.objectAt(i);
|
||||
|
||||
localeMap[locale] = this._findCountryDataForLocale(locale);
|
||||
}
|
||||
|
||||
return localeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds country data for a given locale.
|
||||
* @private
|
||||
* @param {string} locale - The locale to find country data for.
|
||||
* @returns {Object|null} - The country data or null if not found.
|
||||
* @memberof LocaleSelectorComponent
|
||||
* @method _findCountryDataForLocale
|
||||
* @instance
|
||||
*/
|
||||
_findCountryDataForLocale(locale) {
|
||||
const localeCountry = locale.split('-')[1];
|
||||
const country = this.countries.find((country) => country.cca2.toLowerCase() === localeCountry);
|
||||
|
||||
if (country) {
|
||||
// get the language
|
||||
country.language = Object.values(country.languages)[0];
|
||||
}
|
||||
|
||||
return country;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{{#if this.shouldRender}}
|
||||
<InfoBlock @icon="triangle-exclamation" class="two-fa-enforcement-alert bg-yellow-100 border-2 border-yellow-600 dark:border-yellow-500 rounded-lg py-3.5 px-5">
|
||||
<div class="flex flex-row justify-between">
|
||||
<div class="flex-1 pr-2">
|
||||
<p class="text-sm dark:text-yellow-900 mb-2">
|
||||
{{t "component.two-fa-enforcement-alert.message"}}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex-shrink-0">
|
||||
<Button id="two-fa-setup-button" @text={{t "component.two-fa-enforcement-alert.button-text"}} @icon="shield-halved" @type="warning" @buttonType="button" @onClick={{this.transitionToTwoFactorSettings}} />
|
||||
</div>
|
||||
</div>
|
||||
</InfoBlock>
|
||||
{{/if}}
|
||||
@@ -1,74 +0,0 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { action } from '@ember/object';
|
||||
import { task } from 'ember-concurrency-decorators';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
|
||||
/**
|
||||
* Glimmer component for handling notification enforcement.
|
||||
*
|
||||
* @class EnforceNotificationComponent
|
||||
* @extends Component
|
||||
*/
|
||||
export default class TwoFaEnforcementAlertComponent extends Component {
|
||||
/**
|
||||
* Flag to determine whether the component should render or not.
|
||||
*
|
||||
* @property {boolean} shouldRender
|
||||
* @default false
|
||||
* @tracked
|
||||
*/
|
||||
@tracked shouldRender = false;
|
||||
|
||||
/**
|
||||
* Ember Router service for transitioning between routes.
|
||||
*
|
||||
* @type {RouterService}
|
||||
*/
|
||||
@service router;
|
||||
|
||||
/**
|
||||
* Fetch service for making HTTP requests.
|
||||
*
|
||||
* @property {FetchService} fetch
|
||||
* @inject
|
||||
*/
|
||||
@service fetch;
|
||||
|
||||
/**
|
||||
* Constructor method for the ConsoleAccountAuthController.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.checkTwoFactorEnforcement.perform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Transition to the users auth page.
|
||||
*
|
||||
* @method transitionToTwoFa
|
||||
* @memberof ConsoleHomeController
|
||||
*/
|
||||
@action transitionToTwoFactorSettings() {
|
||||
this.router.transitionTo('console.account.auth');
|
||||
}
|
||||
|
||||
@task *checkTwoFactorEnforcement() {
|
||||
const shouldRender = yield this.fetch.get('two-fa/enforce').catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
});
|
||||
|
||||
/**
|
||||
* Task to check whether two-factor authentication enforcement is required.
|
||||
*
|
||||
* @method checkTwoFactorEnforcement
|
||||
* @memberof TwoFaEnforcementAlertComponent
|
||||
* @task
|
||||
*/
|
||||
if (shouldRender) {
|
||||
this.shouldRender = shouldRender.shouldEnforce;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<div class="flex items-center space-x-4">
|
||||
<label class="text-base font-medium">Enable Two-Factor Authentication</label>
|
||||
<Toggle @isToggled={{this.isTwoFaEnabled}} @onToggle={{this.onTwoFaToggled}} />
|
||||
</div>
|
||||
|
||||
{{#if this.isTwoFaEnabled}}
|
||||
<div class="mt-6">
|
||||
{{#if this.showEnforceOption}}
|
||||
<div class="flex items-center space-x-4 mb-6">
|
||||
<label class="text-base font-medium">Require Users to Set-Up 2FA</label>
|
||||
<Toggle @isToggled={{this.isTwoFaEnforced}} @onToggle={{this.onTwoFaEnforcedToggled}} />
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if this.showMethodSelection}}
|
||||
<label class="text-base font-medium">Choose an authentication method</label>
|
||||
<p class="text-sm text-gray-600 dark:text-gray-300 mt-1">In addition to your username and password, you'll have to enter a code (delivered via app or SMS) to sign in to your account</p>
|
||||
{{#each @twoFaMethods as |method|}}
|
||||
<div class="border rounded-lg px-4 py-3 mt-2 transition duration-300 {{if (eq this.selectedTwoFaMethod method.key) 'border-blue-500' 'border-gray-200 dark:border-gray-700'}}">
|
||||
<input type="radio" name="2fa-method" id="{{method.name}}" checked={{eq this.selectedTwoFaMethod method.key}} {{on "change" (fn this.onTwoFaSelected method.key)}} />
|
||||
<label for="{{method.name}}">
|
||||
{{method.name}}
|
||||
{{#if method.recommended}}
|
||||
<span class="bg-blue-500 rounded-xl text-white px-2 py-1 ml-2 text-xs font-semibold">Recommended</span>
|
||||
{{/if}}
|
||||
<p class="text-sm text-gray-600 dark:text-gray-300 mt-1">{{method.description}}</p>
|
||||
</label>
|
||||
</div>
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
@@ -1,169 +0,0 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { isArray } from '@ember/array';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
/**
|
||||
* Default Two-Factor Authentication method when not explicitly selected.
|
||||
*
|
||||
* @property {string} DEFAULT_2FA_METHOD
|
||||
* @private
|
||||
*/
|
||||
const DEFAULT_2FA_METHOD = 'email';
|
||||
|
||||
/**
|
||||
* Glimmer component for managing Two-Factor Authentication settings.
|
||||
*
|
||||
* @class TwoFaSettingsComponent
|
||||
* @extends Component
|
||||
*/
|
||||
export default class TwoFaSettingsComponent extends Component {
|
||||
/**
|
||||
* The fetch service for making HTTP requests.
|
||||
*
|
||||
* @property {Service} fetch
|
||||
* @public
|
||||
*/
|
||||
@service fetch;
|
||||
|
||||
/**
|
||||
* The notifications service for displaying user notifications.
|
||||
*
|
||||
* @property {Service} notifications
|
||||
* @public
|
||||
*/
|
||||
@service notifications;
|
||||
|
||||
/**
|
||||
* The currently selected Two-Factor Authentication method.
|
||||
*
|
||||
* @property {string} selectedTwoFaMethod
|
||||
* @public
|
||||
*/
|
||||
@tracked selectedTwoFaMethod;
|
||||
|
||||
/**
|
||||
* Indicates whether Two-Factor Authentication is currently enabled.
|
||||
*
|
||||
* @property {boolean} isTwoFaEnabled
|
||||
* @public
|
||||
*/
|
||||
@tracked isTwoFaEnabled;
|
||||
|
||||
/**
|
||||
* Indicates whether Two-Factor Authentication is required for all users.
|
||||
*
|
||||
* @property {boolean} isTwoFaEnforced
|
||||
* @public
|
||||
*/
|
||||
@tracked isTwoFaEnforced;
|
||||
|
||||
/**
|
||||
* Indicates whether the settings should render an option to `enforce`
|
||||
* Enforce is a flag that indicates that users either under a company or system must setup 2FA.
|
||||
*
|
||||
* @property {boolean} showEnforceOption
|
||||
* @public
|
||||
*/
|
||||
@tracked showEnforceOption;
|
||||
|
||||
/**
|
||||
* Indicates whether the settings should render an option to select 2fa `mn=ethod`
|
||||
* Method is a flag that indicates which method users can receive a 2FA code from.
|
||||
*
|
||||
* @property {boolean} showEnforceOption
|
||||
* @public
|
||||
*/
|
||||
@tracked showMethodSelection;
|
||||
|
||||
/**
|
||||
* Class constructor to initialize the component.
|
||||
*
|
||||
* @constructor
|
||||
* @param {Object} owner - The owner of the component.
|
||||
* @param {Object} options - Options passed during component instantiation.
|
||||
* @param {Object} options.twoFaSettings - The current Two-Factor Authentication settings.
|
||||
* @param {Array} options.twoFaMethods - Available Two-Factor Authentication methods.
|
||||
*/
|
||||
constructor(owner, { twoFaSettings, twoFaMethods, showEnforceOption, showMethodSelection = true }) {
|
||||
super(...arguments);
|
||||
|
||||
const userSelectedMethod = isArray(twoFaMethods) ? twoFaMethods.find(({ key }) => key === twoFaSettings.method) : null;
|
||||
|
||||
this.showMethodSelection = showMethodSelection === true;
|
||||
this.showEnforceOption = showEnforceOption === true;
|
||||
this.isTwoFaEnabled = twoFaSettings.enabled === true;
|
||||
this.isTwoFaEnforced = twoFaSettings.enforced === true;
|
||||
this.selectedTwoFaMethod = userSelectedMethod ? userSelectedMethod.key : DEFAULT_2FA_METHOD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Action handler for toggling Two-Factor Authentication.
|
||||
*
|
||||
* @method onTwoFaToggled
|
||||
* @param {boolean} isTwoFaEnabled - Indicates whether Two-Factor Authentication is enabled.
|
||||
* @return {void}
|
||||
* @public
|
||||
*/
|
||||
@action onTwoFaToggled(isTwoFaEnabled) {
|
||||
this.isTwoFaEnabled = isTwoFaEnabled;
|
||||
|
||||
if (isTwoFaEnabled) {
|
||||
const recommendedMethod = isArray(this.args.twoFaMethods) ? this.args.twoFaMethods.find((method) => method.recommended) : null;
|
||||
if (recommendedMethod) {
|
||||
this.selectedTwoFaMethod = recommendedMethod.key;
|
||||
}
|
||||
} else {
|
||||
this.selectedTwoFaMethod = null;
|
||||
}
|
||||
|
||||
if (typeof this.args.onTwoFaToggled === 'function') {
|
||||
this.args.onTwoFaToggled(...arguments);
|
||||
}
|
||||
|
||||
if (typeof this.args.onTwoFaMethodSelected === 'function') {
|
||||
this.args.onTwoFaMethodSelected(this.selectedTwoFaMethod);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action handler for toggling Two-Factor Authentication.
|
||||
*
|
||||
* @method onTwoFaEnforcedToggled
|
||||
* @param {boolean} isTwoFaEnforced - Indicates whether Two-Factor Authentication is enabled.
|
||||
* @return {void}
|
||||
* @public
|
||||
*/
|
||||
@action onTwoFaEnforcedToggled(isTwoFaEnforced) {
|
||||
this.isTwoFaEnforced = isTwoFaEnforced;
|
||||
|
||||
if (typeof this.args.onTwoFaEnforcedToggled === 'function') {
|
||||
this.args.onTwoFaEnforcedToggled(...arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action handler for selecting a Two-Factor Authentication method.
|
||||
*
|
||||
* @method onTwoFaSelected
|
||||
* @param {string} method - The selected Two-Factor Authentication method.
|
||||
* @return {void}
|
||||
* @public
|
||||
*/
|
||||
@action onTwoFaSelected(method) {
|
||||
this.selectedTwoFaMethod = method;
|
||||
|
||||
if (typeof this.args.onTwoFaMethodSelected === 'function') {
|
||||
this.args.onTwoFaMethodSelected(...arguments);
|
||||
}
|
||||
}
|
||||
|
||||
@action onRequireUsersToSetUpToggled(isTwoFaEnforced) {
|
||||
this.isTwoFaEnforced = isTwoFaEnforced;
|
||||
|
||||
if (typeof this.args.onTwoFaEnforcedToggled === 'function') {
|
||||
this.args.onTwoFaEnforcedToggled(isTwoFaEnforced);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,13 +18,6 @@ export default class AuthForgotPasswordController extends Controller {
|
||||
*/
|
||||
@service notifications;
|
||||
|
||||
/**
|
||||
* Inject the `intl` service
|
||||
*
|
||||
* @memberof AuthForgotPasswordController
|
||||
*/
|
||||
@service intl;
|
||||
|
||||
/**
|
||||
* The email variable
|
||||
*
|
||||
@@ -62,7 +55,7 @@ export default class AuthForgotPasswordController extends Controller {
|
||||
this.fetch
|
||||
.post('auth/get-magic-reset-link', { email })
|
||||
.then(() => {
|
||||
this.notifications.success(this.intl.t('auth.forgot-password.success-message'));
|
||||
this.notifications.success('Check your email to continue!');
|
||||
this.isSent = true;
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
@@ -33,6 +33,7 @@ export default class AuthLoginController extends Controller {
|
||||
*/
|
||||
@service session;
|
||||
|
||||
|
||||
/**
|
||||
* Inject the `router` service
|
||||
*
|
||||
@@ -40,20 +41,6 @@ export default class AuthLoginController extends Controller {
|
||||
*/
|
||||
@service router;
|
||||
|
||||
/**
|
||||
* Inject the `intl` service
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service intl;
|
||||
|
||||
/**
|
||||
* Inject the `fetch` service
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service fetch;
|
||||
|
||||
/**
|
||||
* Whether or not to remember the users session
|
||||
*
|
||||
@@ -110,62 +97,29 @@ export default class AuthLoginController extends Controller {
|
||||
*/
|
||||
@tracked failedAttempts = 0;
|
||||
|
||||
@tracked token;
|
||||
|
||||
/**
|
||||
* Authenticate the user
|
||||
*
|
||||
* @void
|
||||
*/
|
||||
@action async login(event) {
|
||||
// firefox patch
|
||||
event.preventDefault();
|
||||
|
||||
// get user credentials
|
||||
const { identity, password, rememberMe } = this;
|
||||
|
||||
// If no password error
|
||||
if (!identity) {
|
||||
return this.notifications.warning(this.intl.t('auth.login.no-identity-notification'));
|
||||
}
|
||||
|
||||
// If no password error
|
||||
if (!password) {
|
||||
return this.notifications.warning(this.intl.t('auth.login.no-identity-notification'));
|
||||
}
|
||||
const { email, password, rememberMe } = this;
|
||||
|
||||
// start loader
|
||||
this.set('isLoading', true);
|
||||
|
||||
// set where to redirect on login
|
||||
this.setRedirect();
|
||||
|
||||
// send request to check for 2fa
|
||||
try {
|
||||
let { twoFaSession, isTwoFaEnabled } = await this.session.checkForTwoFactor(identity);
|
||||
|
||||
if (isTwoFaEnabled) {
|
||||
return this.session.store
|
||||
.persist({ identity })
|
||||
.then(() => {
|
||||
return this.router.transitionTo('auth.two-fa', { queryParams: { token: twoFaSession } }).then(() => {
|
||||
this.reset('success');
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
this.reset('error');
|
||||
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
return this.notifications.serverError(error);
|
||||
}
|
||||
|
||||
try {
|
||||
await this.session.authenticate('authenticator:fleetbase', { identity, password }, rememberMe);
|
||||
await this.session.authenticate('authenticator:fleetbase', { email, password }, rememberMe);
|
||||
} catch (error) {
|
||||
this.failedAttempts++;
|
||||
|
||||
// Handle unverified user
|
||||
if (error.toString().includes('not verified')) {
|
||||
return this.sendUserForEmailVerification(identity);
|
||||
}
|
||||
|
||||
return this.failure(error);
|
||||
}
|
||||
|
||||
@@ -192,30 +146,6 @@ export default class AuthLoginController extends Controller {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an email verification session and transitions user to verification route.
|
||||
*
|
||||
* @param {String} email
|
||||
* @return {Promise<Transition>}
|
||||
* @memberof AuthLoginController
|
||||
*/
|
||||
sendUserForEmailVerification(email) {
|
||||
return this.fetch.post('auth/create-verification-session', { email, send: true }).then(({ token }) => {
|
||||
return this.session.store.persist({ email }).then(() => {
|
||||
this.notifications.warning(this.intl.t('auth.login.unverified-notification'));
|
||||
return this.router
|
||||
.transitionTo('auth.verification', {
|
||||
queryParams: {
|
||||
token,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.reset('error');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets correct route to send user to after login.
|
||||
*
|
||||
@@ -255,7 +185,7 @@ export default class AuthLoginController extends Controller {
|
||||
* @void
|
||||
*/
|
||||
slowConnection() {
|
||||
this.notifications.error(this.intl.t('auth.login.slow-connection-message'));
|
||||
this.notifications.error('Experiencing connectivity issues.');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,13 +25,6 @@ export default class AuthResetPasswordController extends Controller {
|
||||
*/
|
||||
@service router;
|
||||
|
||||
/**
|
||||
* Inject the `intl` service
|
||||
*
|
||||
* @memberof AuthResetPasswordController
|
||||
*/
|
||||
@service intl;
|
||||
|
||||
/**
|
||||
* The code param.
|
||||
*
|
||||
@@ -77,7 +70,7 @@ export default class AuthResetPasswordController extends Controller {
|
||||
this.fetch
|
||||
.post('auth/reset-password', { link: id, code, password, password_confirmation })
|
||||
.then(() => {
|
||||
this.notifications.success(this.intl.t('auth.reset-password.success-message'));
|
||||
this.notifications.success('Your password has been reset! Login to continue.');
|
||||
|
||||
return this.router.transitionTo('auth.login');
|
||||
})
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
/**
|
||||
* Controller responsible for handling two-factor authentication.
|
||||
* @class AuthTwoFaController
|
||||
* @extends Controller
|
||||
*/
|
||||
export default class AuthTwoFaController extends Controller {
|
||||
/**
|
||||
* Router service.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service router;
|
||||
|
||||
/**
|
||||
* Fetch service for making HTTP requests.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service fetch;
|
||||
|
||||
/**
|
||||
* Notifications service for handling notifications.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service notifications;
|
||||
|
||||
/**
|
||||
* Session service for managing user sessions.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service session;
|
||||
|
||||
/**
|
||||
* Internationalization service.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service intl;
|
||||
|
||||
/**
|
||||
* Tracked property for storing the verification token.
|
||||
*
|
||||
* @property {string} token
|
||||
* @tracked
|
||||
*/
|
||||
@tracked token;
|
||||
|
||||
/**
|
||||
* The current 2FA identity in memory
|
||||
*/
|
||||
@tracked identity;
|
||||
|
||||
/**
|
||||
* Tracked property representing the client token from the validated 2fa session.
|
||||
*
|
||||
* @property {number} clientToken
|
||||
* @tracked
|
||||
* @default null
|
||||
*/
|
||||
@tracked clientToken;
|
||||
|
||||
/**
|
||||
* Tracked property for storing the verification code.
|
||||
*
|
||||
* @property {string} verificationCode
|
||||
* @tracked
|
||||
*/
|
||||
@tracked verificationCode = '';
|
||||
|
||||
/**
|
||||
* Tracked property for storing the verification code.
|
||||
*
|
||||
* @property {string} verificationCode
|
||||
* @tracked
|
||||
*/
|
||||
@tracked otpValue = '';
|
||||
|
||||
/**
|
||||
* Tracked property representing the date the 2fa session will expire
|
||||
* @property {Date|null} twoFactorSessionExpiresAfter
|
||||
* @tracked
|
||||
* @default null
|
||||
*/
|
||||
@tracked twoFactorSessionExpiresAfter;
|
||||
|
||||
/**
|
||||
* Tracked property representing when the countdown is ready to start.
|
||||
*
|
||||
* @property {Boolean} countdownReady
|
||||
* @tracked
|
||||
* @default false
|
||||
*/
|
||||
@tracked countdownReady = false;
|
||||
|
||||
/**
|
||||
* Tracked property representing when verification code has expired.
|
||||
*
|
||||
* @property {Boolean} isCodeExpired
|
||||
* @tracked
|
||||
* @default false
|
||||
*/
|
||||
@tracked isCodeExpired = false;
|
||||
|
||||
/**
|
||||
* Query parameters for the controller.
|
||||
*
|
||||
* @property {Array} queryParams
|
||||
*/
|
||||
queryParams = ['token', 'clientToken'];
|
||||
|
||||
/**
|
||||
* Action method for verifying the entered verification code.
|
||||
*
|
||||
* @method verifyCode
|
||||
* @action
|
||||
*/
|
||||
@action async verifyCode(event) {
|
||||
// prevent form default behaviour
|
||||
if (event && typeof event.preventDefault === 'function') {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
try {
|
||||
const { token, verificationCode, clientToken, identity } = this;
|
||||
|
||||
if (!clientToken) {
|
||||
this.notifications.error(this.intl.t('auth.two-fa.verify-code.invalid-session-error-notification'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the backend API to verify the entered verification code
|
||||
const { authToken } = await this.fetch.post('two-fa/verify', {
|
||||
token,
|
||||
code: verificationCode,
|
||||
clientToken,
|
||||
identity,
|
||||
});
|
||||
|
||||
// If verification is successful, transition to the desired route
|
||||
this.notifications.success(this.intl.t('auth.two-fa.verify-code.verification-successful-notification'));
|
||||
|
||||
// authenticate user
|
||||
return this.session.authenticate('authenticator:fleetbase', { authToken }).then(() => {
|
||||
return this.router.transitionTo('console');
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.message.includes('Verification code has expired')) {
|
||||
this.notifications.info(this.intl.t('auth.two-fa.verify-code.verification-code-expired-notification'));
|
||||
} else {
|
||||
this.notifications.error(this.intl.t('auth.two-fa.verify-code.verification-code-failed-notification'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resends the verification code for Two-Factor Authentication.
|
||||
* Disables the countdown timer while processing and handles success or error notifications.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
* @action
|
||||
*/
|
||||
@action async resendCode() {
|
||||
// disable countdown timer
|
||||
this.countdownReady = false;
|
||||
|
||||
try {
|
||||
const { identity, token } = this;
|
||||
const { clientToken } = await this.fetch.post('two-fa/resend', {
|
||||
identity,
|
||||
token,
|
||||
});
|
||||
|
||||
if (clientToken) {
|
||||
this.clientToken = clientToken;
|
||||
this.twoFactorSessionExpiresAfter = this.getExpirationDateFromClientToken(clientToken);
|
||||
this.countdownReady = true;
|
||||
this.isCodeExpired = false;
|
||||
this.notifications.success(this.intl.t('auth.two-fa.resend-code.verification-code-resent-notification'));
|
||||
} else {
|
||||
this.notifications.error(this.intl.t('auth.two-fa.resend-code.verification-code-resent-error-notification'));
|
||||
}
|
||||
} catch (error) {
|
||||
// Handle errors, show error notifications, etc.
|
||||
this.notifications.error(this.intl.t('auth.two-fa.resend-code.verification-code-resent-error-notification'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the current Two-Fa session and redirects to login screen.
|
||||
*
|
||||
* @returns {Promise<Transition>}
|
||||
* @memberof AuthTwoFaController
|
||||
*/
|
||||
@action cancelTwoFactor() {
|
||||
return this.fetch
|
||||
.post('two-fa/invalidate', {
|
||||
identity: this.identity,
|
||||
token: this.token,
|
||||
})
|
||||
.then(() => {
|
||||
return this.router.transitionTo('auth.login');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set that the verification code has expired and allow user to resend.
|
||||
*
|
||||
* @memberof AuthTwoFaController
|
||||
*/
|
||||
@action handleCodeExpired() {
|
||||
this.isCodeExpired = true;
|
||||
this.countdownReady = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the input of the OTP (One-Time Password) and triggers the verification process.
|
||||
*
|
||||
* @param {string} otpValue - The OTP value entered by the user.
|
||||
* @returns {void}
|
||||
* @action
|
||||
*/
|
||||
@action handleOtpInput(otpValue) {
|
||||
this.verificationCode = otpValue;
|
||||
this.verifyCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a base64 encoded client token to a Date representing the expiration date.
|
||||
*
|
||||
* @method getExpirationDateFromClientToken
|
||||
* @param {string} clientToken - Base64 encoded client token.
|
||||
* @returns {Date|null} - Date representing the expiration date, or null if invalid.
|
||||
*/
|
||||
getExpirationDateFromClientToken(clientToken) {
|
||||
const decoder = new TextDecoder();
|
||||
const binString = atob(clientToken);
|
||||
const bytes = Uint8Array.from(binString, (m) => m.codePointAt(0));
|
||||
const decodedString = decoder.decode(bytes);
|
||||
|
||||
if (typeof decodedString === 'string' && decodedString.includes('|')) {
|
||||
const parts = decodedString.split('|');
|
||||
const expiresAt = this.convertUtcToClientTime(parts[0]);
|
||||
|
||||
if (expiresAt instanceof Date) {
|
||||
return expiresAt;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a UTC date-time string to client time zone.
|
||||
*
|
||||
* @method convertUtcToClientTime
|
||||
* @param {string} utcDateTimeString - UTC date-time string.
|
||||
* @returns {Date} - Date in client time zone.
|
||||
*/
|
||||
convertUtcToClientTime(utcDateTimeString) {
|
||||
const utcDate = new Date(utcDateTimeString);
|
||||
const clientTimezoneOffset = new Date().getTimezoneOffset();
|
||||
const clientDate = new Date(utcDate.getTime() - clientTimezoneOffset * 60 * 1000);
|
||||
return clientDate;
|
||||
}
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { later } from '@ember/runloop';
|
||||
import { not } from '@ember/object/computed';
|
||||
|
||||
export default class AuthVerificationController extends Controller {
|
||||
/**
|
||||
* Inject the `fetch` service
|
||||
*
|
||||
* @memberof OnboardIndexController
|
||||
*/
|
||||
@service fetch;
|
||||
|
||||
/**
|
||||
* Inject the `notifications` service
|
||||
*
|
||||
* @memberof OnboardIndexController
|
||||
*/
|
||||
@service notifications;
|
||||
|
||||
/**
|
||||
* Inject the `modalsManager` service
|
||||
*
|
||||
* @memberof OnboardIndexController
|
||||
*/
|
||||
@service modalsManager;
|
||||
|
||||
/**
|
||||
* Inject the `currentUser` service
|
||||
*
|
||||
* @memberof OnboardIndexController
|
||||
*/
|
||||
@service currentUser;
|
||||
|
||||
/**
|
||||
* Inject the `router` service
|
||||
*
|
||||
* @memberof OnboardIndexController
|
||||
*/
|
||||
@service router;
|
||||
|
||||
/**
|
||||
* Inject the `session` service
|
||||
*
|
||||
* @memberof OnboardIndexController
|
||||
*/
|
||||
@service session;
|
||||
|
||||
/**
|
||||
* The session paramerer.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@tracked hello;
|
||||
|
||||
/**
|
||||
* The token paramerer.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@tracked token;
|
||||
|
||||
/**
|
||||
* The loading state of the verification request.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@tracked isLoading = false;
|
||||
|
||||
/**
|
||||
* Validation state tracker.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@tracked isReadyToSubmit = false;
|
||||
|
||||
/**
|
||||
* The request timeout to trigger alternative verification options.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@tracked waitTimeout = 1000 * 60 * 1.25;
|
||||
|
||||
/**
|
||||
* Determines if Fleetbase is still awaiting verification after a certain time.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@tracked stillWaiting = false;
|
||||
|
||||
/**
|
||||
* the input code.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@tracked code;
|
||||
|
||||
/**
|
||||
* The query param for the session token.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@tracked queryParams = ['hello', 'token'];
|
||||
|
||||
/**
|
||||
* The boolean opposite of `isReadyToSubmit`
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@not('isReadyToSubmit') isNotReadyToSubmit;
|
||||
|
||||
/**
|
||||
* Creates an instance of OnboardVerifyEmailController.
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
later(
|
||||
this,
|
||||
() => {
|
||||
this.stillWaiting = true;
|
||||
},
|
||||
this.waitTimeout
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow user to manually trigger no code received prompt.
|
||||
*
|
||||
* @memberof AuthVerificationController
|
||||
*/
|
||||
@action onDidntReceiveCode() {
|
||||
this.stillWaiting = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the input
|
||||
*
|
||||
* @param {InputEvent} { target: { value } }
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@action validateInput({ target: { value } }) {
|
||||
if (value.length > 5) {
|
||||
this.isReadyToSubmit = true;
|
||||
} else {
|
||||
this.isReadyToSubmit = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits to verify code.
|
||||
*
|
||||
* @return {Promise}
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@action verifyCode() {
|
||||
const { token, code, email } = this;
|
||||
|
||||
this.isLoading = true;
|
||||
|
||||
return this.fetch
|
||||
.post('auth/verify-email', { token, code, email, authenticate: true })
|
||||
.then(({ status, token }) => {
|
||||
if (status === 'ok') {
|
||||
this.notifications.success('Email successfully verified!');
|
||||
|
||||
if (token) {
|
||||
this.notifications.info('Welcome to Fleetbase!');
|
||||
this.session.manuallyAuthenticate(token);
|
||||
|
||||
return this.router.transitionTo('console');
|
||||
}
|
||||
|
||||
return this.router.transitionTo('auth.login');
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to resend verification code by SMS.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@action resendBySms() {
|
||||
this.modalsManager.show('modals/verify-by-sms', {
|
||||
title: 'Verify Account by Phone',
|
||||
acceptButtonText: 'Send',
|
||||
phone: this.currentUser.phone,
|
||||
confirm: (modal) => {
|
||||
modal.startLoading();
|
||||
const phone = modal.getOption('phone');
|
||||
|
||||
return this.fetch
|
||||
.post('onboard/send-verification-sms', { phone, session: this.hello })
|
||||
.then(() => {
|
||||
this.notifications.success('Verification code SMS sent!');
|
||||
})
|
||||
.catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to resend verification code by email.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@action resendEmail() {
|
||||
this.modalsManager.show('modals/resend-verification-email', {
|
||||
title: 'Resend Verification Code',
|
||||
acceptButtonText: 'Send',
|
||||
email: this.currentUser.email,
|
||||
confirm: (modal) => {
|
||||
modal.startLoading();
|
||||
const email = modal.getOption('email');
|
||||
|
||||
return this.fetch
|
||||
.post('onboard/send-verification-email', { email, session: this.hello })
|
||||
.then(() => {
|
||||
this.notifications.success('Verification code email sent!');
|
||||
})
|
||||
.catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -115,21 +115,22 @@ export default class ConsoleController extends Controller {
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
this.router.on('routeDidChange', (transition) => {
|
||||
if (this.sidebarContext) {
|
||||
// Determine if the new route should hide the sidebar
|
||||
const shouldHideSidebar = this.hiddenSidebarRoutes.includes(transition.to.name);
|
||||
|
||||
|
||||
// Check if the sidebar was manually toggled and is currently closed
|
||||
const isSidebarManuallyClosed = this.sidebarToggleState.clicked && !this.sidebarToggleState.isOpen;
|
||||
|
||||
|
||||
// Hide the sidebar if the current route is in hiddenSidebarRoutes
|
||||
if (shouldHideSidebar) {
|
||||
this.sidebarContext.hideNow();
|
||||
this.sidebarToggleEnabled = false;
|
||||
return; // Exit early as no further action is required
|
||||
}
|
||||
|
||||
|
||||
// If the sidebar was manually closed and not on a hidden route, keep it closed
|
||||
if (isSidebarManuallyClosed) {
|
||||
this.sidebarContext.hideNow();
|
||||
@@ -137,7 +138,7 @@ export default class ConsoleController extends Controller {
|
||||
// Otherwise, show the sidebar
|
||||
this.sidebarContext.show();
|
||||
}
|
||||
|
||||
|
||||
// Ensure toggle is enabled unless on a hidden route
|
||||
this.sidebarToggleEnabled = !shouldHideSidebar;
|
||||
}
|
||||
@@ -154,7 +155,7 @@ export default class ConsoleController extends Controller {
|
||||
@action onSidebarToggle(sidebar, isOpen) {
|
||||
this.sidebarToggleState = {
|
||||
clicked: true,
|
||||
isOpen,
|
||||
isOpen
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,248 +0,0 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { task } from 'ember-concurrency-decorators';
|
||||
import getTwoFaMethods from '@fleetbase/console/utils/get-two-fa-methods';
|
||||
|
||||
/**
|
||||
* Controller for managing user authentication and password-related actions in the console.
|
||||
*
|
||||
* @class ConsoleAccountAuthController
|
||||
* @extends Controller
|
||||
*/
|
||||
export default class ConsoleAccountAuthController extends Controller {
|
||||
/**
|
||||
* Service for handling data fetching.
|
||||
*
|
||||
* @type {fetch}
|
||||
*/
|
||||
@service fetch;
|
||||
|
||||
/**
|
||||
* Service for displaying notifications.
|
||||
*
|
||||
* @type {notifications}
|
||||
*/
|
||||
@service notifications;
|
||||
|
||||
/**
|
||||
* Service for managing application routing.
|
||||
*
|
||||
* @type {router}
|
||||
*/
|
||||
@service router;
|
||||
|
||||
/**
|
||||
* The user's current password.
|
||||
* @type {string}
|
||||
*/
|
||||
@tracked password;
|
||||
|
||||
/**
|
||||
* The user's confirmation of the new password.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
@tracked confirmPassword;
|
||||
|
||||
/**
|
||||
* The new password the user intends to set.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
@tracked newPassword;
|
||||
|
||||
/**
|
||||
* The user's confirmation of the new password.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
@tracked newConfirmPassword;
|
||||
|
||||
/**
|
||||
* Flag indicating whether the current password has been validated.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
@tracked isPasswordValidated = false;
|
||||
|
||||
/**
|
||||
* System-wide two-factor authentication configuration.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
@tracked twoFaConfig = {};
|
||||
|
||||
/**
|
||||
* User-specific two-factor authentication settings.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
@tracked twoFaSettings = {};
|
||||
|
||||
/**
|
||||
* Flag indicating whether system-wide two-factor authentication is enabled.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
@tracked isSystemTwoFaEnabled = false;
|
||||
|
||||
/**
|
||||
* Available two-factor authentication methods.
|
||||
*
|
||||
* @type {Array}
|
||||
*/
|
||||
@tracked methods = getTwoFaMethods();
|
||||
|
||||
/**
|
||||
* Constructor method for the ConsoleAccountAuthController.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadSystemTwoFaConfig.perform();
|
||||
this.loadUserTwoFaSettings.perform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the user's current password.
|
||||
*
|
||||
* @method validatePassword
|
||||
* @param {Event} event - The event object triggering the action.
|
||||
*/
|
||||
@action validatePassword(event) {
|
||||
event.preventDefault();
|
||||
this.validatePasswordTask.perform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the task to change the user's password asynchronously.
|
||||
*
|
||||
* @method changeUserPasswordTask
|
||||
* @param {Event} event - The event object triggering the action.
|
||||
*/
|
||||
@action changeUserPassword(event) {
|
||||
event.preventDefault();
|
||||
this.changeUserPasswordTask.perform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the event when two-factor authentication is toggled.
|
||||
*
|
||||
* @method onTwoFaToggled
|
||||
* @param {boolean} enabled - Whether two-factor authentication is enabled or not.
|
||||
*/
|
||||
@action onTwoFaToggled(enabled) {
|
||||
this.twoFaSettings = {
|
||||
...this.twoFaSettings,
|
||||
enabled,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the event when a two-factor authentication method is selected.
|
||||
*
|
||||
* @method onTwoFaMethodSelected
|
||||
* @param {string} method - The selected two-factor authentication method.
|
||||
*/
|
||||
@action onTwoFaMethodSelected(method) {
|
||||
this.twoFaSettings = {
|
||||
...this.twoFaSettings,
|
||||
method,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the task to save user-specific two-factor authentication settings asynchronously.
|
||||
*
|
||||
* @method saveTwoFactorAuthSettings
|
||||
*/
|
||||
@action saveTwoFactorAuthSettings() {
|
||||
this.saveUserTwoFaSettings.perform(this.twoFaSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the task to save user-specific two-factor authentication settings asynchronously.
|
||||
*
|
||||
* @method saveUserTwoFaSettings
|
||||
* @param {Object} twoFaSettings - User-specific two-factor authentication settings.
|
||||
*/
|
||||
@task *saveUserTwoFaSettings(twoFaSettings = {}) {
|
||||
yield this.fetch
|
||||
.post('users/two-fa', { twoFaSettings })
|
||||
.then(() => {
|
||||
this.notifications.success('2FA Settings saved successfully.');
|
||||
})
|
||||
.catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the task to load user-specific two-factor authentication settings asynchronously.
|
||||
*
|
||||
* @method loadUserTwoFaSettings
|
||||
*/
|
||||
@task *loadUserTwoFaSettings() {
|
||||
const twoFaSettings = yield this.fetch.get('users/two-fa');
|
||||
|
||||
if (twoFaSettings) {
|
||||
this.isUserTwoFaEnabled = twoFaSettings.enabled;
|
||||
this.twoFaSettings = twoFaSettings;
|
||||
}
|
||||
return twoFaSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the task to load system-wide two-factor authentication configuration asynchronously.
|
||||
*
|
||||
* @method loadSystemTwoFaConfig
|
||||
*/
|
||||
@task *loadSystemTwoFaConfig() {
|
||||
const twoFaConfig = yield this.fetch.get('two-fa/config');
|
||||
|
||||
if (twoFaConfig) {
|
||||
this.isSystemTwoFaEnabled = twoFaConfig.enabled;
|
||||
this.twoFaConfig = twoFaConfig;
|
||||
}
|
||||
return twoFaConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the task to validate the user's current password asynchronously.
|
||||
*
|
||||
* @method validatePasswordTask
|
||||
*/
|
||||
@task *validatePasswordTask() {
|
||||
try {
|
||||
yield this.fetch.post('users/validate-password', {
|
||||
password: this.password,
|
||||
password_confirmation: this.confirmPassword,
|
||||
});
|
||||
|
||||
this.isPasswordValidated = true;
|
||||
} catch (error) {
|
||||
this.notifications.serverError(error, 'Invalid current password.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the task to change the user's password asynchronously.
|
||||
*
|
||||
* @method changeUserPasswordTask
|
||||
*/
|
||||
@task *changeUserPasswordTask() {
|
||||
try {
|
||||
yield this.fetch.post('users/change-password', {
|
||||
password: this.newPassword,
|
||||
password_confirmation: this.newConfirmPassword,
|
||||
});
|
||||
|
||||
this.notifications.success('Password change successfully.');
|
||||
} catch (error) {
|
||||
this.notifications.error('Failed to change password');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { task } from 'ember-concurrency-decorators';
|
||||
import getTwoFaMethods from '@fleetbase/console/utils/get-two-fa-methods';
|
||||
|
||||
/**
|
||||
* Controller responsible for handling Two-Factor Authentication settings in the admin console.
|
||||
*
|
||||
* @class ConsoleAdminTwoFaSettingsController
|
||||
* @extends Controller
|
||||
*/
|
||||
export default class ConsoleAdminTwoFaSettingsController extends Controller {
|
||||
/**
|
||||
* Service for handling data fetching.
|
||||
*
|
||||
* @type {fetch}
|
||||
*/
|
||||
@service fetch;
|
||||
|
||||
/**
|
||||
* Service for displaying notifications.
|
||||
*
|
||||
* @type {notifications}
|
||||
*/
|
||||
@service notifications;
|
||||
|
||||
/**
|
||||
* System-wide two-factor authentication configuration.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
@tracked twoFaConfig = {};
|
||||
|
||||
/**
|
||||
* User-specific two-factor authentication settings.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
@tracked twoFaSettings = {};
|
||||
|
||||
/**
|
||||
* Flag indicating whether system-wide two-factor authentication is enabled.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
@tracked isSystemTwoFaEnabled = false;
|
||||
|
||||
/**
|
||||
* Flag indicating whether 2FA enforcement is required.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
@tracked isTwoFaEnforced = false;
|
||||
|
||||
/**
|
||||
* Available two-factor authentication methods.
|
||||
*
|
||||
* @type {Array}
|
||||
*/
|
||||
@tracked methods = getTwoFaMethods();
|
||||
|
||||
/**
|
||||
* Tracked property for the loading state
|
||||
*
|
||||
* @memberof ConsoleAdminTwoFaSettingsController
|
||||
* @var {Boolean}
|
||||
*/
|
||||
@tracked isLoading = false;
|
||||
|
||||
/**
|
||||
* Constructor method for the ConsoleAccountAuthController.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadSystemTwoFaConfig.perform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the event when two-factor authentication is toggled.
|
||||
*
|
||||
* @method onTwoFaToggled
|
||||
* @param {boolean} enabled - Whether two-factor authentication is enabled or not.
|
||||
*/
|
||||
@action onTwoFaToggled(enabled) {
|
||||
this.twoFaSettings = {
|
||||
...this.twoFaSettings,
|
||||
enabled,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the event when a two-factor authentication method is selected.
|
||||
*
|
||||
* @method onTwoFaMethodSelected
|
||||
* @param {string} method - The selected two-factor authentication method.
|
||||
*/
|
||||
@action onTwoFaMethodSelected(method) {
|
||||
this.twoFaSettings = {
|
||||
...this.twoFaSettings,
|
||||
method,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the event when two-factor authentication is toggled.
|
||||
*
|
||||
* @method onTwoFaToggled
|
||||
* @param {boolean} enabled - Whether two-factor authentication is enforced or not.
|
||||
*/
|
||||
@action onTwoFaEnforceToggled(enforced) {
|
||||
this.twoFaSettings = {
|
||||
...this.twoFaSettings,
|
||||
enforced,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the event when 2FA enforcement is toggled.
|
||||
*
|
||||
* @method onTwoFaEnforceToggled
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initiates the task to save user-specific two-factor authentication settings asynchronously.
|
||||
*
|
||||
* @method saveTwoFactorAuthSettings
|
||||
*/
|
||||
@action saveSettings() {
|
||||
this.saveTwoFactorSettingsForAdmin.perform(this.twoFaSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the task to load system-wide two-factor authentication configuration asynchronously.
|
||||
*
|
||||
* @method loadSystemTwoFaConfig
|
||||
*/
|
||||
|
||||
@task *loadSystemTwoFaConfig() {
|
||||
const twoFaSettings = yield this.fetch.get('two-fa/config').catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
});
|
||||
if (twoFaSettings) {
|
||||
this.twoFaSettings = twoFaSettings;
|
||||
}
|
||||
|
||||
return twoFaSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the task to save user-specific two-factor authentication settings asynchronously.
|
||||
*
|
||||
* @method saveTwoFactorSettingsForAdmin
|
||||
* @param {Object} twoFaSettings - User-specific two-factor authentication settings.
|
||||
*/
|
||||
@task *saveTwoFactorSettingsForAdmin(twoFaSettings = {}) {
|
||||
yield this.fetch
|
||||
.post('two-fa/config', { twoFaSettings })
|
||||
.then(() => {
|
||||
this.notifications.success('2FA Settings saved for admin successfully.');
|
||||
})
|
||||
.catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import Controller from '@ember/controller';
|
||||
|
||||
export default class ConsoleSettingsAuthController extends Controller {}
|
||||
@@ -1,184 +0,0 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { task } from 'ember-concurrency-decorators';
|
||||
import getTwoFaMethods from '@fleetbase/console/utils/get-two-fa-methods';
|
||||
|
||||
export default class ConsoleSettingsTwoFaController extends Controller {
|
||||
/**
|
||||
* Service for handling data fetching.
|
||||
*
|
||||
* @type {fetch}
|
||||
*/
|
||||
@service fetch;
|
||||
|
||||
/**
|
||||
* Service for displaying notifications.
|
||||
*
|
||||
* @type {notifications}
|
||||
*/
|
||||
@service notifications;
|
||||
|
||||
/**
|
||||
* System-wide two-factor authentication configuration.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
@tracked twoFaConfig = {};
|
||||
|
||||
/**
|
||||
* User-specific two-factor authentication settings.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
@tracked twoFaSettings = {};
|
||||
|
||||
/**
|
||||
* Flag indicating whether system-wide two-factor authentication is enabled.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
@tracked isSystemTwoFaEnabled = false;
|
||||
|
||||
/**
|
||||
* Flag indicating whether system-wide two-factor authentication is enabled.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
@tracked isUserTwoFaEnabled = false;
|
||||
|
||||
/**
|
||||
* Flag indicating whether 2FA enforcement is required.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
@tracked isTwoFaEnforced = false;
|
||||
|
||||
/**
|
||||
* Available two-factor authentication methods.
|
||||
*
|
||||
* @type {Array}
|
||||
*/
|
||||
@tracked methods = getTwoFaMethods();
|
||||
|
||||
/**
|
||||
* Constructor method for the ConsoleAccountAuthController.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadSystemTwoFaConfig.perform();
|
||||
this.loadCompanyTwoFaSettings.perform();
|
||||
this.loadUserTwoFaSettings.perform();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the event when two-factor authentication is toggled.
|
||||
*
|
||||
* @method onTwoFaToggled
|
||||
* @param {boolean} enabled - Whether two-factor authentication is enabled or not.
|
||||
*/
|
||||
@action onTwoFaToggled(enabled) {
|
||||
this.twoFaSettings = {
|
||||
...this.twoFaSettings,
|
||||
enabled,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the event when a two-factor authentication method is selected.
|
||||
*
|
||||
* @method onTwoFaMethodSelected
|
||||
* @param {string} method - The selected two-factor authentication method.
|
||||
*/
|
||||
@action onTwoFaMethodSelected(method) {
|
||||
this.twoFaSettings = {
|
||||
...this.twoFaSettings,
|
||||
method,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the event when two-factor authentication is toggled.
|
||||
*
|
||||
* @method onTwoFaToggled
|
||||
* @param {boolean} enabled - Whether two-factor authentication is enforced or not.
|
||||
*/
|
||||
@action onTwoFaEnforceToggled(enforced) {
|
||||
this.twoFaSettings = {
|
||||
...this.twoFaSettings,
|
||||
enforced,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the task to save user-specific two-factor authentication settings asynchronously.
|
||||
*
|
||||
* @method saveTwoFactor
|
||||
*/
|
||||
@action saveTwoFactor() {
|
||||
this.saveTwoFactorSettingsForCompany.perform(this.twoFaSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the task to load user-specific two-factor authentication settings asynchronously.
|
||||
*
|
||||
* @method loadUserTwoFaSettings
|
||||
*/
|
||||
@task *loadCompanyTwoFaSettings() {
|
||||
const twoFaSettings = yield this.fetch.get('companies/two-fa');
|
||||
if (twoFaSettings) {
|
||||
this.twoFaSettings = twoFaSettings;
|
||||
this.isTwoFaEnforced = twoFaSettings.enforced;
|
||||
}
|
||||
return twoFaSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the task to load system-wide two-factor authentication configuration asynchronously.
|
||||
*
|
||||
* @method loadSystemTwoFaConfig
|
||||
*/
|
||||
@task *loadSystemTwoFaConfig() {
|
||||
const twoFaConfig = yield this.fetch.get('two-fa/config');
|
||||
|
||||
if (twoFaConfig) {
|
||||
this.isSystemTwoFaEnabled = twoFaConfig.enabled;
|
||||
this.twoFaConfig = twoFaConfig;
|
||||
}
|
||||
return twoFaConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the task to load user-specific two-factor authentication settings asynchronously.
|
||||
*
|
||||
* @method loadUserTwoFaSettings
|
||||
*/
|
||||
@task *loadUserTwoFaSettings() {
|
||||
const twoFaSettings = yield this.fetch.get('users/two-fa');
|
||||
if (twoFaSettings) {
|
||||
this.isUserTwoFaEnabled = twoFaSettings.enabled;
|
||||
this.twoFaSettings = twoFaSettings;
|
||||
}
|
||||
return twoFaSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the task to save user-specific two-factor authentication settings for the company asynchronously.
|
||||
*
|
||||
* @method saveTwoFactorSettingsForCompany
|
||||
* @param {Object} twoFaSettings - User-specific two-factor authentication settings.
|
||||
*/
|
||||
@task *saveTwoFactorSettingsForCompany(twoFaSettings = {}) {
|
||||
yield this.fetch
|
||||
.post('companies/two-fa', { twoFaSettings })
|
||||
.then(() => {
|
||||
this.notifications.success('2FA Settings saved for organization successfully.');
|
||||
})
|
||||
.catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -124,18 +124,17 @@ export default class OnboardIndexController extends Controller {
|
||||
|
||||
return this.fetch
|
||||
.post('onboard/create-account', input)
|
||||
.then(({ status, skipVerification, token, session }) => {
|
||||
if (status === 'success') {
|
||||
if (skipVerification === true && token) {
|
||||
// only manually authenticate if skip verification
|
||||
this.session.isOnboarding().manuallyAuthenticate(token);
|
||||
.then((response) => {
|
||||
if (response.status === 'success') {
|
||||
this.session.isOnboarding().manuallyAuthenticate(response.token);
|
||||
|
||||
if (response.skipVerification === true) {
|
||||
return this.router.transitionTo('console').then(() => {
|
||||
this.notifications.success('Welcome to Fleetbase!');
|
||||
});
|
||||
}
|
||||
|
||||
return this.router.transitionTo('onboard.verify-email', { queryParams: { hello: session } });
|
||||
return this.router.transitionTo('onboard.verify-email', { queryParams: { hello: response.session } });
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
@@ -1,7 +1,133 @@
|
||||
import AuthVerificationController from '../auth/verification';
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { later } from '@ember/runloop';
|
||||
import { not } from '@ember/object/computed';
|
||||
|
||||
export default class OnboardVerifyEmailController extends Controller {
|
||||
/**
|
||||
* Inject the `fetch` service
|
||||
*
|
||||
* @memberof OnboardIndexController
|
||||
*/
|
||||
@service fetch;
|
||||
|
||||
/**
|
||||
* Inject the `notifications` service
|
||||
*
|
||||
* @memberof OnboardIndexController
|
||||
*/
|
||||
@service notifications;
|
||||
|
||||
/**
|
||||
* Inject the `modalsManager` service
|
||||
*
|
||||
* @memberof OnboardIndexController
|
||||
*/
|
||||
@service modalsManager;
|
||||
|
||||
/**
|
||||
* Inject the `currentUser` service
|
||||
*
|
||||
* @memberof OnboardIndexController
|
||||
*/
|
||||
@service currentUser;
|
||||
|
||||
|
||||
/**
|
||||
* Inject the `router` service
|
||||
*
|
||||
* @memberof OnboardIndexController
|
||||
*/
|
||||
@service router;
|
||||
|
||||
/**
|
||||
* The session paramerer.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@tracked hello;
|
||||
|
||||
/**
|
||||
* The loading state of the verification request.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@tracked isLoading = false;
|
||||
|
||||
/**
|
||||
* Validation state tracker.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@tracked isReadyToSubmit = false;
|
||||
|
||||
/**
|
||||
* The request timeout to trigger alternative verification options.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@tracked waitTimeout = 800 * 60 * 2;
|
||||
|
||||
/**
|
||||
* Determines if Fleetbase is still awaiting verification after a certain time.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@tracked stillWaiting = false;
|
||||
|
||||
/**
|
||||
* the input code.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@tracked code;
|
||||
|
||||
/**
|
||||
* The query param for the session token.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@tracked queryParams = ['hello'];
|
||||
|
||||
/**
|
||||
* The boolean opposite of `isReadyToSubmit`
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@not('isReadyToSubmit') isNotReadyToSubmit;
|
||||
|
||||
/**
|
||||
* Creates an instance of OnboardVerifyEmailController.
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
later(
|
||||
this,
|
||||
() => {
|
||||
this.stillWaiting = true;
|
||||
},
|
||||
this.waitTimeout
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the input
|
||||
*
|
||||
* @param {InputEvent} { target: { value } }
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@action validateInput({ target: { value } }) {
|
||||
if (value.length > 5) {
|
||||
this.isReadyToSubmit = true;
|
||||
} else {
|
||||
this.isReadyToSubmit = false;
|
||||
}
|
||||
}
|
||||
|
||||
export default class OnboardVerifyEmailController extends AuthVerificationController {
|
||||
/**
|
||||
* Submits to verify code.
|
||||
*
|
||||
@@ -9,24 +135,19 @@ export default class OnboardVerifyEmailController extends AuthVerificationContro
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@action verifyCode() {
|
||||
const { hello, code } = this;
|
||||
const session = this.hello;
|
||||
const code = this.code;
|
||||
|
||||
this.isLoading = true;
|
||||
|
||||
return this.fetch
|
||||
.post('onboard/verify-email', { session: hello, code })
|
||||
.then(({ status, token }) => {
|
||||
if (status === 'ok') {
|
||||
.post('onboard/verify-email', { session, code })
|
||||
.then((response) => {
|
||||
if (response.status === 'success') {
|
||||
this.notifications.success('Email successfully verified!');
|
||||
this.notifications.info('Welcome to Fleetbase!');
|
||||
|
||||
if (token) {
|
||||
this.notifications.info('Welcome to Fleetbase!');
|
||||
this.session.manuallyAuthenticate(token);
|
||||
|
||||
return this.router.transitionTo('console');
|
||||
}
|
||||
|
||||
return this.router.transitionTo('auth.login');
|
||||
return this.router.transitionTo('console');
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -36,4 +157,46 @@ export default class OnboardVerifyEmailController extends AuthVerificationContro
|
||||
this.isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to resend verification code by SMS.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@action resendBySms() {
|
||||
this.modalsManager.show('modals/verify-by-sms', {
|
||||
title: 'Verify Account by Phone',
|
||||
acceptButtonText: 'Send',
|
||||
phone: this.currentUser.phone,
|
||||
confirm: (modal) => {
|
||||
modal.startLoading();
|
||||
const phone = modal.getOption('phone');
|
||||
|
||||
return this.fetch.post('onboard/send-verification-sms', { phone }).then(() => {
|
||||
this.notifications.success('Verification code SMS sent!');
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to resend verification code by email.
|
||||
*
|
||||
* @memberof OnboardVerifyEmailController
|
||||
*/
|
||||
@action resendEmail() {
|
||||
this.modalsManager.show('modals/resend-verification-email', {
|
||||
title: 'Resend Verification Code',
|
||||
acceptButtonText: 'Send',
|
||||
email: this.currentUser.email,
|
||||
confirm: (modal) => {
|
||||
modal.startLoading();
|
||||
const email = modal.getOption('email');
|
||||
|
||||
return this.fetch.post('onboard/send-verification-email', { email }).then(() => {
|
||||
this.notifications.success('Verification code email sent!');
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import config from 'ember-get-config';
|
||||
|
||||
export function initialize(owner) {
|
||||
const universe = owner.lookup('service:universe');
|
||||
|
||||
if (universe) {
|
||||
universe.registerOrganizationMenuItem(`v${config.version}`, {
|
||||
index: 4,
|
||||
route: null,
|
||||
icon: 'code-branch',
|
||||
iconSize: 'xs',
|
||||
iconClass: 'mr-1.5',
|
||||
wrapperClass: 'app-version-in-nav',
|
||||
overwriteWrapperClass: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
initialize,
|
||||
};
|
||||
@@ -1,44 +0,0 @@
|
||||
import Model, { attr, belongsTo, hasMany } from '@ember-data/model';
|
||||
import { computed } from '@ember/object';
|
||||
import { format, formatDistanceToNow } from 'date-fns';
|
||||
|
||||
export default class CommentModel extends Model {
|
||||
/** @ids */
|
||||
@attr('string') company_uuid;
|
||||
@attr('string') parent_comment_uuid;
|
||||
@attr('string') subject_uuid;
|
||||
@attr('string') subject_type;
|
||||
|
||||
/** @relationships */
|
||||
@belongsTo('user') author;
|
||||
@belongsTo('comment', { inverse: 'replies' }) parent;
|
||||
@hasMany('comment', { inverse: 'parent' }) replies;
|
||||
|
||||
/** @attributes */
|
||||
@attr('string') content;
|
||||
@attr('boolean') editable;
|
||||
@attr('raw') tags;
|
||||
@attr('raw') meta;
|
||||
|
||||
/** @dates */
|
||||
@attr('date') created_at;
|
||||
@attr('date') updated_at;
|
||||
@attr('date') deleted_at;
|
||||
|
||||
/** @computed */
|
||||
@computed('created_at') get createdAgo() {
|
||||
return formatDistanceToNow(this.created_at);
|
||||
}
|
||||
|
||||
@computed('created_at') get createdAt() {
|
||||
return format(this.created_at, 'PPP p');
|
||||
}
|
||||
|
||||
@computed('updated_at') get updatedAgo() {
|
||||
return formatDistanceToNow(this.updated_at);
|
||||
}
|
||||
|
||||
@computed('updated_at') get updatedAt() {
|
||||
return format(this.updated_at, 'PPP p');
|
||||
}
|
||||
}
|
||||
@@ -79,11 +79,6 @@ export default class UserModel extends Model {
|
||||
@not('isEmailVerified') emailIsNotVerified;
|
||||
@not('isPhoneVerified') phoneIsNotVerified;
|
||||
|
||||
/** @computed */
|
||||
@computed('meta.two_factor_enabled') get isTwoFactorEnabled() {
|
||||
return this.meta && this.meta.two_factor_enabled;
|
||||
}
|
||||
|
||||
@computed('types') get typesList() {
|
||||
const types = Array.from(this.types);
|
||||
return types.join(', ');
|
||||
|
||||
@@ -10,9 +10,7 @@ Router.map(function () {
|
||||
this.route('auth', function () {
|
||||
this.route('login', { path: '/' });
|
||||
this.route('forgot-password');
|
||||
this.route('reset-password', { path: '/reset-password/:id' });
|
||||
this.route('two-fa');
|
||||
this.route('verification');
|
||||
this.route('reset-password');
|
||||
});
|
||||
this.route('onboard', function () {
|
||||
this.route('verify-email');
|
||||
@@ -27,11 +25,9 @@ Router.map(function () {
|
||||
this.route('notifications');
|
||||
this.route('account', function () {
|
||||
this.route('virtual', { path: '/:slug/:view' });
|
||||
this.route('auth');
|
||||
});
|
||||
this.route('settings', function () {
|
||||
this.route('virtual', { path: '/:slug/:view' });
|
||||
this.route('two-fa');
|
||||
});
|
||||
this.route('virtual', { path: '/:slug/:view' });
|
||||
this.route('admin', function () {
|
||||
@@ -47,9 +43,33 @@ Router.map(function () {
|
||||
});
|
||||
this.route('branding');
|
||||
this.route('notifications');
|
||||
this.route('two-fa-settings');
|
||||
this.route('virtual', { path: '/:slug/:view' });
|
||||
});
|
||||
|
||||
this.mount('@fleetbase/billing-engine', {
|
||||
as: 'billing',
|
||||
path: 'billing'
|
||||
});
|
||||
|
||||
this.mount('@fleetbase/dev-engine', {
|
||||
as: 'developers',
|
||||
path: 'developers'
|
||||
});
|
||||
|
||||
this.mount('@fleetbase/fleetops-engine', {
|
||||
as: 'fleet-ops',
|
||||
path: 'fleet-ops'
|
||||
});
|
||||
|
||||
this.mount('@fleetbase/iam-engine', {
|
||||
as: 'iam',
|
||||
path: 'iam'
|
||||
});
|
||||
|
||||
this.mount('@fleetbase/storefront-engine', {
|
||||
as: 'storefront',
|
||||
path: 'storefront'
|
||||
});
|
||||
});
|
||||
this.route('install');
|
||||
});
|
||||
|
||||
@@ -4,14 +4,7 @@ import { inject as service } from '@ember/service';
|
||||
export default class AuthResetPasswordRoute extends Route {
|
||||
@service store;
|
||||
|
||||
async model(params) {
|
||||
return params;
|
||||
}
|
||||
|
||||
async setupController(controller) {
|
||||
super.setupController(...arguments);
|
||||
|
||||
// set brand to controller
|
||||
this.brand = await this.store.findRecord('brand', 1);
|
||||
model() {
|
||||
return this.store.findRecord('brand', 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class AuthTwoFaRoute extends Route {
|
||||
/**
|
||||
* Fetch service for making HTTP requests.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service fetch;
|
||||
|
||||
/**
|
||||
* Notifications service for handling notifications.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service notifications;
|
||||
|
||||
/**
|
||||
* Router service.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service router;
|
||||
|
||||
/**
|
||||
* Session service for managing user sessions.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service session;
|
||||
|
||||
/**
|
||||
* Query parameters for the route.
|
||||
*
|
||||
* @var {Object}
|
||||
*/
|
||||
queryParams = {
|
||||
token: {
|
||||
refreshModel: false,
|
||||
replace: true,
|
||||
},
|
||||
clientToken: {
|
||||
refreshModel: false,
|
||||
replace: true,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Executes before the model is loaded, used for validating 2FA session with the server.
|
||||
*
|
||||
* @param {Object} transition - The transition object representing the route transition.
|
||||
* @return {Promise} A promise that resolves if the 2FA session is valid, and rejects with an error otherwise.
|
||||
*/
|
||||
beforeModel(transition) {
|
||||
// validate 2fa session with server
|
||||
let { token, clientToken } = transition.to.queryParams;
|
||||
|
||||
return this.session.store.restore().then(({ identity }) => {
|
||||
if (!identity) {
|
||||
this.notifications.error('2FA failed to initialize.');
|
||||
return this.router.transitionTo('auth.login');
|
||||
}
|
||||
|
||||
return this.fetch
|
||||
.post('two-fa/validate', { token, identity, clientToken })
|
||||
.then(({ clientToken, expired }) => {
|
||||
// handle when code expired
|
||||
if (expired === true) {
|
||||
return this.invalidateTwoFaSession(token, identity);
|
||||
}
|
||||
|
||||
// clear session data after validated 2fa session
|
||||
this.session.store.persist({
|
||||
identity,
|
||||
token,
|
||||
clientToken,
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
return this.router.transitionTo('auth.login');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the controller, including client token and session expiration details.
|
||||
*
|
||||
* @param {Object} controller - The controller for the route.
|
||||
*/
|
||||
setupController(controller) {
|
||||
super.setupController(...arguments);
|
||||
|
||||
this.session.store.restore().then(({ clientToken, identity }) => {
|
||||
controller.clientToken = clientToken;
|
||||
controller.identity = identity;
|
||||
controller.twoFactorSessionExpiresAfter = controller.getExpirationDateFromClientToken(clientToken);
|
||||
controller.countdownReady = true;
|
||||
});
|
||||
}
|
||||
|
||||
invalidateTwoFaSession(token, identity) {
|
||||
this.notifications.error('2FA authentication session has expired.');
|
||||
return this.fetch
|
||||
.post('two-fa/invalidate', {
|
||||
token,
|
||||
identity,
|
||||
})
|
||||
.then(() => {
|
||||
return this.router.transitionTo('auth.login');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class AuthVerificationRoute extends Route {
|
||||
@service session;
|
||||
@service fetch;
|
||||
@service router;
|
||||
|
||||
queryParams = {
|
||||
token: {
|
||||
refreshModel: false,
|
||||
replace: true,
|
||||
},
|
||||
};
|
||||
|
||||
beforeModel(transition) {
|
||||
let { token } = transition.to.queryParams;
|
||||
|
||||
return this.session.store.restore().then(({ email }) => {
|
||||
return this.fetch.post('auth/validate-verification-session', { email, token }).then(({ valid }) => {
|
||||
if (!valid) {
|
||||
return this.router.transitionTo('auth.login');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async setupController(controller) {
|
||||
super.setupController(...arguments);
|
||||
let { email } = await this.session.store.restore();
|
||||
controller.email = email;
|
||||
}
|
||||
}
|
||||
@@ -25,13 +25,6 @@ export default class ConsoleRoute extends Route {
|
||||
*/
|
||||
@service session;
|
||||
|
||||
/**
|
||||
* Inject the `intl` service
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service intl;
|
||||
|
||||
/**
|
||||
* Inject the `currentUser` service
|
||||
*
|
||||
@@ -79,12 +72,6 @@ export default class ConsoleRoute extends Route {
|
||||
@action setupController(controller, model) {
|
||||
super.setupController(controller, model);
|
||||
|
||||
// Get and set user locale
|
||||
this.fetch.get('users/locale').then(({ locale }) => {
|
||||
this.intl.setLocale(locale);
|
||||
});
|
||||
|
||||
// Get user organizations
|
||||
this.fetch.get('auth/organizations').then((organizations) => {
|
||||
this.currentUser.setOption('organizations', organizations);
|
||||
controller.organizations = organizations;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default class ConsoleAccountAuthRoute extends Route {}
|
||||
@@ -1,3 +0,0 @@
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default class ConsoleAdminTwoFaSettingsRoute extends Route {}
|
||||
@@ -1,3 +0,0 @@
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default class ConsoleSettingsAuthRoute extends Route {}
|
||||
@@ -1,3 +0,0 @@
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default class ConsoleSettingsTwoFaRoute extends Route {}
|
||||
@@ -1,38 +0,0 @@
|
||||
import ApplicationSerializer from '@fleetbase/ember-core/serializers/application';
|
||||
import { EmbeddedRecordsMixin } from '@ember-data/serializer/rest';
|
||||
|
||||
export default class CommentSerializer extends ApplicationSerializer.extend(EmbeddedRecordsMixin) {
|
||||
/**
|
||||
* Embedded relationship attributes
|
||||
*
|
||||
* @var {Object}
|
||||
*/
|
||||
get attrs() {
|
||||
return {
|
||||
author: { embedded: 'always' },
|
||||
parent: { embedded: 'always' },
|
||||
replies: { embedded: 'always' },
|
||||
};
|
||||
}
|
||||
|
||||
serializeAttribute(snapshot, json, key, attributes) {
|
||||
if (key === 'editable') {
|
||||
return;
|
||||
}
|
||||
|
||||
super.serializeAttribute(...arguments);
|
||||
}
|
||||
|
||||
serializeHasMany(snapshot, json, relationship) {
|
||||
let key = relationship.key;
|
||||
if (key === 'replies') {
|
||||
return;
|
||||
} else {
|
||||
super.serializeHasMany(...arguments);
|
||||
}
|
||||
}
|
||||
|
||||
serializeBelongsTo() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
import ApplicationSerializer from '@fleetbase/ember-core/serializers/application';
|
||||
import { EmbeddedRecordsMixin } from '@ember-data/serializer/rest';
|
||||
|
||||
export default class TwoFaSettingsSerializer extends ApplicationSerializer.extend(EmbeddedRecordsMixin) {}
|
||||
@@ -2,4 +2,3 @@
|
||||
@import 'tailwindcss/components';
|
||||
@import 'tailwindcss/utilities';
|
||||
@import 'inter-ui/inter.css';
|
||||
@import 'console.css';
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
.two-fa-enforcement-alert svg.fa-triangle-exclamation {
|
||||
font-size: 2.25rem;
|
||||
padding-right: 0.5rem;
|
||||
color: rgb(202 138 4);
|
||||
}
|
||||
|
||||
body[data-theme='dark'] .btn.btn-warning-alert.btn-warning,
|
||||
.btn.btn-warning-alert.btn-warning,
|
||||
.two-fa-enforcement-alert button#two-fa-setup-button.btn.btn-warning,
|
||||
body[data-theme='dark'] .two-fa-enforcement-alert button#two-fa-setup-button.btn.btn-warning {
|
||||
background-color: rgb(202 138 4);
|
||||
border-color: rgb(161 98 7);
|
||||
color: rgb(254 249 195);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.app-version-in-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
|
||||
font-size: 0.8rem;
|
||||
line-height: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-top: 0.2rem;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="mb-4">
|
||||
<Image src={{@model.logo_url}} @fallbackSrc="/images/fleetbase-logo-svg.svg" alt={{t "app.name"}} width="160" height="56" class="w-40 h-14 mx-auto" />
|
||||
<h2 class="text-center text-lg font-extrabold text-gray-900 dark:text-white truncate">
|
||||
{{if this.isSent (t "auth.forgot-password.is-sent.title") (t "auth.forgot-password.not-sent.title")}}
|
||||
{{if this.isSent "Almost done!" "Forgot your password?"}}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<FaIcon @icon="check-circle" @size="lg" class="text-green-900 mr-4" />
|
||||
</div>
|
||||
<p class="flex-1 text-sm text-green-900 dark:text-green-900">
|
||||
{{t "auth.forgot-password.is-sent.message" htmlSafe=true}}
|
||||
<strong>Check your email!</strong><br> We've sent you a magic link to your email which will allow you to reset your password. The link expires in 15 minutes.
|
||||
</p>
|
||||
</div>
|
||||
{{else}}
|
||||
@@ -21,23 +21,23 @@
|
||||
<FaIcon @icon="info-circle" @size="lg" class="text-blue-900 mr-4" />
|
||||
</div>
|
||||
<p class="flex-1 text-sm text-blue-900 dark:text-blue-900">
|
||||
{{t "auth.forgot-password.not-sent.message" htmlSafe=true appName=(t "app.name")}}
|
||||
<strong>Don't worry, we've got your back.</strong><br> Enter the email you use to login to {{t "app.name"}} and we will send you a secure link to reset your password.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form class="space-y-6" {{on "submit" this.sendSecureLink}}>
|
||||
<div>
|
||||
<label for="email" class="block text-sm font-medium text-gray-700 dark:text-gray-50">
|
||||
{{t "auth.forgot-password.form.email-label"}}
|
||||
Your email address
|
||||
</label>
|
||||
<div class="mt-2">
|
||||
<Input @value={{this.email}} @type="email" id="email" name="email" required class="form-input form-input-lg w-full" placeholder={{t "auth.forgot-password.form.email-label"}} />
|
||||
<Input @value={{this.email}} @type="email" id="email" name="email" required class="form-input form-input-lg w-full" placeholder="Your email" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row space-x-2">
|
||||
<Button @icon="magic" @type="primary" @buttonType="submit" @text={{t "auth.forgot-password.form.submit-button"}} @onClick={{this.sendSecureLink}} @isLoading={{this.isLoading}} />
|
||||
<Button @buttonType="button" @text={{t "auth.forgot-password.form.nevermind-button"}} @onClick={{fn (transition-to "auth.login")}} @disabled={{this.isLoading}} />
|
||||
<Button @icon="magic" @type="primary" @buttonType="submit" @text="OK, Send me a magic link!" @onClick={{this.sendSecureLink}} @isLoading={{this.isLoading}} />
|
||||
<Button @buttonType="button" @text="Nevermind" @onClick={{fn (transition-to "auth.login")}} @disabled={{this.isLoading}} />
|
||||
</div>
|
||||
</form>
|
||||
{{/if}}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<LogoIcon @url={{@brand.icon_url}} @size="12" class="mx-auto" />
|
||||
</div>
|
||||
<h2 class="mt-6 mb-3 text-3xl font-extrabold leading-9 text-center text-gray-900 dark:text-gray-100">
|
||||
{{t "auth.login.title"}}
|
||||
Sign in to your account
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
<FaIcon @icon="exclamation-triangle" @size="lg" class="text-yellow-900 mr-4" />
|
||||
</div>
|
||||
<p class="flex-1 text-sm text-yellow-900 dark:yellow-red-900">
|
||||
{{t "auth.login.failed-attempt.message" htmlSafe=true}}
|
||||
<strong>Forgot your password?</strong><br> Click the button below to reset your password.
|
||||
</p>
|
||||
</div>
|
||||
<Button @text={{t "auth.login.failed-attempt.button-text"}} @type="warning" @onClick={{this.forgotPassword}} />
|
||||
<Button @text="Ok, help me reset!" @type="warning" @onClick={{this.forgotPassword}} />
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
@@ -25,30 +25,10 @@
|
||||
<input type="hidden" name="remember" value="true" />
|
||||
<div class="rounded-md shadow-sm">
|
||||
<div>
|
||||
<Input
|
||||
@value={{this.identity}}
|
||||
aria-label={{t "auth.login.form.email-label"}}
|
||||
name="email"
|
||||
@type="email"
|
||||
autocomplete="username"
|
||||
required
|
||||
class="relative block w-full px-3 py-2 text-gray-900 placeholder-gray-500 border border-gray-300 rounded-none appearance-none rounded-t-md focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm sm:leading-5 dark:text-white dark:bg-gray-700 dark:border-gray-900"
|
||||
placeholder={{t "auth.login.form.email-label"}}
|
||||
disabled={{this.isLoading}}
|
||||
/>
|
||||
<Input @value={{this.email}} aria-label="Email address" name="email" @type="email" autocomplete="username" required class="relative block w-full px-3 py-2 text-gray-900 placeholder-gray-500 border border-gray-300 rounded-none appearance-none rounded-t-md focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm sm:leading-5 dark:text-white dark:bg-gray-700 dark:border-gray-900" placeholder="Email address" disabled={{this.isLoading}} />
|
||||
</div>
|
||||
<div class="-mt-px">
|
||||
<Input
|
||||
@value={{this.password}}
|
||||
aria-label={{t "auth.login.form.password-label"}}
|
||||
name="password"
|
||||
@type="password"
|
||||
autocomplete="current-password"
|
||||
required
|
||||
class="relative block w-full px-3 py-2 text-gray-900 placeholder-gray-500 border border-gray-300 rounded-none appearance-none rounded-b-md focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm sm:leading-5 dark:text-white dark:bg-gray-700 dark:border-gray-900"
|
||||
placeholder={{t "auth.login.form.password-label"}}
|
||||
disabled={{this.isLoading}}
|
||||
/>
|
||||
<Input @value={{this.password}} aria-label="Password" name="password" @type="password" autocomplete="current-password" required class="relative block w-full px-3 py-2 text-gray-900 placeholder-gray-500 border border-gray-300 rounded-none appearance-none rounded-b-md focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm sm:leading-5 dark:text-white dark:bg-gray-700 dark:border-gray-900" placeholder="Password" disabled={{this.isLoading}} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -56,27 +36,22 @@
|
||||
<div class="flex items-center">
|
||||
<Input id="rememberMe" @type="checkbox" @checked={{this.rememberMe}} disabled={{this.isLoading}} class="w-4 h-4 transition duration-150 ease-in-out form-checkbox text-sky-500" />
|
||||
<label for="rememberMe" class="block ml-2 text-sm leading-5 text-gray-900 dark:text-gray-100">
|
||||
{{t "auth.login.form.remember-me-label"}}
|
||||
Remember me
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="text-sm leading-5">
|
||||
<a
|
||||
href="javascript:;"
|
||||
{{on "click" this.forgotPassword}}
|
||||
disabled={{this.isLoading}}
|
||||
class="font-medium transition duration-150 ease-in-out text-sky-600 hover:text-sky-500 focus:outline-none focus:underline"
|
||||
>
|
||||
{{t "auth.login.form.forgot-password-label"}}
|
||||
<a href="javascript:;" {{on "click" this.forgotPassword}} disabled={{this.isLoading}} class="font-medium transition duration-150 ease-in-out text-sky-600 hover:text-sky-500 focus:outline-none focus:underline">
|
||||
Forgot your password?
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
<Button @buttonType="submit" @type="primary" @text={{t "auth.login.form.sign-in-button"}} @icon="lock" @wrapperClass="btn-block" @isLoading={{this.isLoading}} @onClick={{this.login}} />
|
||||
<Button @buttonType="submit" @type="primary" @text="Sign In" @icon="lock" @wrapperClass="btn-block" @isLoading={{this.isLoading}} @onClick={{this.login}} />
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<Button @text={{t "auth.login.form.create-account-button"}} @wrapperClass="btn-block" @disabled={{this.isLoading}} @onClick={{fn (transition-to "onboard")}} />
|
||||
<Button @text="Create a new Account" @wrapperClass="btn-block" @disabled={{this.isLoading}} @onClick={{fn (transition-to "onboard")}} />
|
||||
</div>
|
||||
</form>
|
||||
@@ -1,19 +1,19 @@
|
||||
<div class="bg-white dark:bg-gray-800 py-8 px-4 shadow rounded-lg">
|
||||
<div class="mb-4">
|
||||
<Image src={{this.brand.logo_url}} @fallbackSrc="/images/fleetbase-logo-svg.svg" alt={{t "app.name"}} width="160" height="56" class="w-40 h-14 mx-auto" />
|
||||
<Image src={{@model.logo_url}} @fallbackSrc="/images/fleetbase-logo-svg.svg" alt={{t "app.name"}} width="160" height="56" class="w-40 h-14 mx-auto" />
|
||||
<h2 class="text-center text-lg font-extrabold text-gray-900 dark:text-white truncate">
|
||||
{{t "auth.reset-password.title"}}
|
||||
Reset your password
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<form class="space-y-6" {{on "submit" this.resetPassword}}>
|
||||
<InputGroup @name={{t "auth.reset-password.form.code.label"}} @value={{this.code}} @inputClass="form-input-lg" @helpText={{t "auth.reset-password.form.code.help-text"}} />
|
||||
<InputGroup @name={{t "auth.reset-password.form.password.label"}} @value={{this.password}} @type="password" @inputClass="form-input-lg" @helpText={{t "auth.reset-password.form.password.help-text"}} />
|
||||
<InputGroup @name={{t "auth.reset-password.form.confirm-password.label"}} @value={{this.password_confirmation}} @type="password" @inputClass="form-input-lg" @helpText={{t "auth.reset-password.form.confirm-password.help-text"}} />
|
||||
<InputGroup @name="Your reset code" @value={{this.code}} @inputClass="form-input-lg" @helpText="The verification code you received in your email." />
|
||||
<InputGroup @name="New Password" @value={{this.password}} @type="password" @inputClass="form-input-lg" @helpText="Enter a password at-least 6 characters to continue." />
|
||||
<InputGroup @name="Confirm new Password" @value={{this.password_confirmation}} @type="password" @inputClass="form-input-lg" @helpText="Enter a password at-least 6 characters to continue." />
|
||||
|
||||
<div class="flex space-x-2">
|
||||
<Button @icon="check" @size="lg" @type="primary" @buttonType="submit" @text={{t "auth.reset-password.form.submit-button"}} @onClick={{this.resetPassword}} @isLoading={{this.isLoading}} />
|
||||
<Button @size="lg" @buttonType="button" @text={{t "auth.reset-password.form.back-button"}} @onClick={{fn (transition-to "auth.login")}} @disabled={{this.isLoading}} />
|
||||
<Button @icon="check" @size="lg" @type="primary" @buttonType="submit" @text="Reset Password" @onClick={{this.resetPassword}} @isLoading={{this.isLoading}} />
|
||||
<Button @size="lg" @buttonType="button" @text="Back" @onClick={{fn (transition-to "auth.login")}} @disabled={{this.isLoading}} />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,47 +0,0 @@
|
||||
<div class="mb-8 text-center">
|
||||
<Image src={{@model.logo_url}} @fallbackSrc="/images/fleetbase-logo-svg.svg" alt={{t "app.name"}} width="160" height="56" class="w-40 h-14 mx-auto" />
|
||||
<h2 class="text-lg font-extrabold text-gray-900 dark:text-white truncate">
|
||||
{{if this.isSent "Verification Code"}}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="flex px-3 py-2 mb-4 rounded-md shadow-sm bg-green-200">
|
||||
<div>
|
||||
<FaIcon @icon="check-circle" @size="lg" class="text-green-900 mr-4" />
|
||||
</div>
|
||||
<p class="flex-1 text-sm text-green-900 dark:text-green-900">
|
||||
<strong>Check your {{this.selectedMethod}}</strong><br />
|
||||
We've sent you a verification code. Enter the code below to complete the login process.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form class="mt-8" {{on "submit" this.verifyCode}}>
|
||||
<div class="flex items-center justify-between my-6">
|
||||
<OtpInput @onInputCompleted={{this.handleOtpInput}} />
|
||||
</div>
|
||||
|
||||
<div id="otp-countdown-container" class="otp-countdown-container flex items-center justify-center min-h-12">
|
||||
{{#if this.countdownReady}}
|
||||
<Countdown @expiry={{this.twoFactorSessionExpiresAfter}} @countdownClass="text-lg" @onCountdownEnd={{this.handleCodeExpired}} />
|
||||
{{/if}}
|
||||
{{#if this.isCodeExpired}}
|
||||
<InfoBlock>
|
||||
<div>Your 2FA authentication code has expired. You can request another code if you need more time.</div>
|
||||
<Button @type="primary" @wrapperClass="mt-2" @text="Resend Code" @icon="arrow-rotate-right" @onClick={{this.resendCode}} />
|
||||
</InfoBlock>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<Button @buttonType="submit" @type="primary" @text="Verify Code" @icon="check-circle" @wrapperClass="btn-block" @isLoading={{this.isLoading}} />
|
||||
</div>
|
||||
|
||||
<div class="text-center flex flex-row items-center justify-center space-x-4 mt-3.5">
|
||||
<a href="#" class="text-sm text-blue-500 hover:underline inline-block" {{on "click" this.resendCode}}>
|
||||
Resend Code
|
||||
</a>
|
||||
<a href="#" class="text-sm text-danger hover:underline inline-block" {{on "click" this.cancelTwoFactor}}>
|
||||
Cancel Two-Factor
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
@@ -1,48 +0,0 @@
|
||||
{{page-title (t "auth.verification.header-title")}}
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 py-8 px-4 shadow rounded-lg w-full">
|
||||
<div class="mb-8">
|
||||
<img class="mx-auto h-12 w-auto " src="/images/fleetbase-logo-svg.svg" alt={{t "app.name"}}>
|
||||
<h2 class="mt-6 text-center text-lg font-extrabold text-gray-900 dark:text-white truncate">
|
||||
{{t "auth.verification.title"}}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="flex px-3 py-2 mb-6 rounded-md shadow-sm bg-blue-200">
|
||||
<div>
|
||||
<FaIcon @icon="shield-check" @size="lg" class="text-blue-900 mr-4" />
|
||||
</div>
|
||||
<p class="flex-1 text-sm text-blue-900 dark:text-blue-900">
|
||||
{{t "auth.verification.message-text" htmlSafe=true}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form class="mt-8 space-y-6" {{on "submit" this.verifyCode}}>
|
||||
<InputGroup @type="tel" @name={{t "auth.verification.verification-input-label"}} @value={{this.code}} @helpText={{t "auth.verification.verification-code-text"}} @inputClass="input-lg" {{on "input" this.validateInput}} />
|
||||
|
||||
<div class="flex flex-row items-center space-x-4">
|
||||
<Button @icon="check" @iconPrefix="fas" @buttonType="submit" @type="primary" @size="lg" @text="Verify & Continue" @isLoading={{this.isLoading}} @disabled={{this.isNotReadyToSubmit}} @onClick={{this.verifyCode}} />
|
||||
<a href="#" {{on "click" this.onDidntReceiveCode}} class="text-sm text-blue-400 hover:text-blue-300">{{t "auth.verification.didnt-receive-a-code"}}</a>
|
||||
</div>
|
||||
|
||||
{{#if this.stillWaiting}}
|
||||
<div class="bg-yellow-50 rounded shadow-sm border-l-4 border-yellow-400 px-4 py-2">
|
||||
<div class="flex">
|
||||
<div class="flex-shrink-0">
|
||||
<FaIcon @icon="exclamation-triangle" @size="lg" class="text-yellow-400" />
|
||||
</div>
|
||||
<div class="ml-3 flex items-center">
|
||||
<span class="text-lg font-extrabold text-yellow-800">{{t "auth.verification.didnt-receive-a-code"}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="py-1">
|
||||
<p class="text-yellow-700 text-sm">{{t "auth.verification.not-sent.alternative-choice"}}</p>
|
||||
<div class="flex items-center mt-3">
|
||||
<Button @buttonType="button" @type="warning" @wrapperClass="mr-2" @onClick={{this.resendEmail}} class="btn-warning-alert">{{t "auth.verification.not-sent.resend-email"}}</Button>
|
||||
<Button @buttonType="button" @type="warning" @onClick={{this.resendBySms}} class="btn-warning-alert">{{t "auth.verification.not-sent.send-by-sms"}}</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,11 +1,10 @@
|
||||
{{page-title (t "app.name")}}
|
||||
{{page-title "Console"}}
|
||||
<Layout::Container>
|
||||
<Layout::Header @brand={{@model}} @user={{this.user}} @organizations={{this.organizations}} @menuItems={{this.universe.headerMenuItems}} @extensions={{this.extensions}} @onAction={{this.onAction}} @showSidebarToggle={{true}} @sidebarToggleEnabled={{this.sidebarToggleEnabled}} @onSidebarToggle={{this.onSidebarToggle}} />
|
||||
<Layout::Main>
|
||||
<Layout::Sidebar @onSetup={{this.setSidebarContext}}>
|
||||
<div class="next-sidebar-content-inner">
|
||||
<div role="menu" id="sidebar-menu-items">
|
||||
</div>
|
||||
<div role="menu" id="sidebar-menu-items"></div>
|
||||
</div>
|
||||
</Layout::Sidebar>
|
||||
<Layout::Section>
|
||||
@@ -13,9 +12,4 @@
|
||||
</Layout::Section>
|
||||
</Layout::Main>
|
||||
<Layout::MobileNavbar @brand={{@model}} @user={{this.user}} @organizations={{this.organizations}} @menuItems={{this.universe.headerMenuItems}} @extensions={{this.extensions}} @onAction={{this.onAction}} />
|
||||
</Layout::Container>
|
||||
|
||||
{{!-- Add Locale Selector to Header --}}
|
||||
<EmberWormhole @to="view-header-actions">
|
||||
<LocaleSelector class="mr-0.5" />
|
||||
</EmberWormhole>
|
||||
</Layout::Container>
|
||||
@@ -1,8 +1,7 @@
|
||||
{{page-title "Account"}}
|
||||
<EmberWormhole @to="sidebar-menu-items">
|
||||
<Layout::Sidebar::Panel @open={{true}} @title={{t "common.account"}}>
|
||||
<Layout::Sidebar::Panel @open={{true}} @title="Account">
|
||||
<Layout::Sidebar::Item @route="console.account.index" @icon="user">Profile</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.account.auth" @icon="key">Auth</Layout::Sidebar::Item>
|
||||
{{#each this.universe.accountMenuItems as |menuItem|}}
|
||||
<Layout::Sidebar::Item @onClick={{fn this.universe.transitionMenuItem "console.account.virtual" menuItem}} @item={{menuItem}} @icon={{menuItem.icon}}>{{menuItem.title}}</Layout::Sidebar::Item>
|
||||
{{/each}}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
{{page-title "Account Auth"}}
|
||||
<Layout::Section::Header @title="Account Auth" />
|
||||
|
||||
<Layout::Section::Body class="overflow-y-scroll h-full">
|
||||
<div class="container mx-auto h-screen" {{increase-height-by 500}}>
|
||||
<div class="max-w-3xl my-10 mx-auto space-y-6">
|
||||
<ContentPanel @title="Change Password" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
{{#if this.isPasswordValidated}}
|
||||
<form id="change-password-form" aria-label="change-password" {{on "submit" this.changeUserPassword}}>
|
||||
<legend class="mb-3">Change Password</legend>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<InputGroup @name="Enter new Password" @type="password" @value={{this.newPassword}} />
|
||||
<InputGroup @name="Confirm Password" @type="password" @value={{this.newConfirmPassword}} />
|
||||
</div>
|
||||
<Button @type="primary" @buttonType="submit" @text="Confirm & Change Password" @icon="save" {{on "click" this.changeUserPassword}} />
|
||||
</form>
|
||||
{{else}}
|
||||
<form id="validate-password-form" aria-label="validate-password" {{on "submit" this.validatePassword}}>
|
||||
<legend class="mb-3">Validate Current Password</legend>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<InputGroup @name="Password" @type="password" @value={{this.password}} />
|
||||
<InputGroup @name="Confirm Password" @type="password" @value={{this.confirmPassword}} />
|
||||
</div>
|
||||
<Button @type="primary" @buttonType="submit" @text="Continue" @icon="check" {{on "click" this.validatePassword}} />
|
||||
</form>
|
||||
{{/if}}
|
||||
</ContentPanel>
|
||||
|
||||
{{#if this.isSystemTwoFaEnabled}}
|
||||
<ContentPanel @title="2FA Settings" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<div class="mb-3">
|
||||
{{#if this.loadUserTwoFaSettings.isIdle}}
|
||||
<TwoFaSettings
|
||||
@twoFaMethods={{this.methods}}
|
||||
@twoFaSettings={{this.twoFaSettings}}
|
||||
@onTwoFaToggled={{this.onTwoFaToggled}}
|
||||
@onTwoFaMethodSelected={{this.onTwoFaMethodSelected}}
|
||||
/>
|
||||
{{else}}
|
||||
<div class="flex items-center justify-center p-4">
|
||||
<Spinner @loadingMessage="Loading User 2FA Settings..." @wrapperClass="flex flex-row" @iconClass="mr-2" />
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<Button
|
||||
@type="primary"
|
||||
@buttonType="submit"
|
||||
@text="Save 2FA Settings"
|
||||
@icon="save"
|
||||
@onClick={{this.saveTwoFactorAuthSettings}}
|
||||
@isLoading={{this.saveUserTwoFaSettings.isRunning}}
|
||||
/>
|
||||
</ContentPanel>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</Layout::Section::Body>
|
||||
@@ -1,25 +1,25 @@
|
||||
<Layout::Section::Header @title={{t "common.profile"}} />
|
||||
<Layout::Section::Header @title="Profile" />
|
||||
|
||||
<Layout::Section::Body class="overflow-y-scroll h-full">
|
||||
<div class="container mx-auto h-screen" {{increase-height-by 500}}>
|
||||
<div class="max-w-3xl my-10 mx-auto">
|
||||
<ContentPanel @title={{t "common.your-profile"}} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<ContentPanel @title="Your Profile" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<form class="flex flex-col md:flex-row" {{on "submit" this.saveProfile}}>
|
||||
<div class="w-32 flex flex-col justify-center mb-6 mr-6">
|
||||
<Image src={{this.user.avatar_url}} @fallbackSrc={{config "defaultValues.userImage"}} alt={{this.user.name}} class="w-32 h-32 rounded-md" />
|
||||
<FileUpload @name={{t "console.account.index.photos"}} @accept="image/*" @onFileAdded={{this.uploadNewPhoto}} @labelClass="flex flex-row items-center justify-center" as |queue|>
|
||||
<FileUpload @name="photos" @accept="image/*" @onFileAdded={{this.uploadNewPhoto}} @labelClass="flex flex-row items-center justify-center" as |queue|>
|
||||
<a tabindex={{0}} class="flex items-center px-0 mt-2 text-xs no-underline truncate btn btn-sm btn-default" disabled={{queue.files.length}}>
|
||||
{{#if queue.files.length}}
|
||||
<div class="mr-1.5">
|
||||
<Spinner />
|
||||
</div>
|
||||
<span>
|
||||
{{t "common.uploading"}}
|
||||
Uploading...
|
||||
</span>
|
||||
{{else}}
|
||||
<FaIcon @icon="image" class="mr-1.5" />
|
||||
<span>
|
||||
{{t "console.account.index.upload-new"}}
|
||||
<span>
|
||||
Upload new
|
||||
</span>
|
||||
{{/if}}
|
||||
</a>
|
||||
@@ -27,15 +27,15 @@
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-2 text-xs dark:text-gray-100">
|
||||
<InputGroup @name={{t "common.name"}} @value={{this.user.name}} />
|
||||
<InputGroup @name={{t "common.email"}} @type="email" @value={{this.user.email}} />
|
||||
<InputGroup @name={{t "common.phone"}} @helpText={{t "console.account.index.phone"}}>
|
||||
<InputGroup @name="Name" @value={{this.user.name}} />
|
||||
<InputGroup @name="Email" @type="email" @value={{this.user.email}} />
|
||||
<InputGroup @name="Your phone number" @helpText="Your phone number.">
|
||||
<PhoneInput @value={{this.user.phone}} @onInput={{fn (mut this.user.phone)}} class="form-input input-lg w-full" />
|
||||
</InputGroup>
|
||||
<InputGroup @name={{t "common.date-of-birth"}} @type="date" @value={{this.user.date_of_birth}} />
|
||||
<InputGroup @name="Date of Birth" @type="date" @value={{this.user.date_of_birth}} />
|
||||
</div>
|
||||
<div class="mt-3 flex items-center justify-end">
|
||||
<Button @buttonType="submit" @type="primary" @size="lg" @icon="save" @text={{t "common.save-button-text"}} @onClick={{this.saveProfile}} @isLoading={{this.isLoading}} />
|
||||
<Button @buttonType="submit" @type="primary" @size="lg" @icon="save" @text="Save Changes" @onClick={{this.saveProfile}} @isLoading={{this.isLoading}} />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,35 +1,26 @@
|
||||
{{page-title "Admin"}}
|
||||
|
||||
<EmberWormhole @to="sidebar-menu-items">
|
||||
<Layout::Sidebar::Item @route="console.admin.index" @icon="rectangle-list">{{t "common.overview"}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.branding" @icon="palette">{{t "common.branding"}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.notifications" @icon="bell">{{t "common.notifications"}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.two-fa-settings" @icon="shield-halved">{{t "common.2fa-config"}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.index" @icon="rectangle-list">Overview</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.branding" @icon="palette">Branding</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.notifications" @icon="bell">Notifications</Layout::Sidebar::Item>
|
||||
{{#each this.universe.adminMenuItems as |menuItem|}}
|
||||
<Layout::Sidebar::Item
|
||||
@onClick={{fn this.universe.transitionMenuItem "console.admin.virtual" menuItem}}
|
||||
@item={{menuItem}}
|
||||
@icon={{menuItem.icon}}
|
||||
>{{menuItem.title}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @onClick={{fn this.universe.transitionMenuItem "console.admin.virtual" menuItem}} @item={{menuItem}} @icon={{menuItem.icon}}>{{menuItem.title}}</Layout::Sidebar::Item>
|
||||
{{/each}}
|
||||
{{#each this.universe.adminMenuPanels as |menuPanel|}}
|
||||
<Layout::Sidebar::Panel @open={{menuPanel.open}} @title={{menuPanel.title}}>
|
||||
{{#each menuPanel.items as |menuItem|}}
|
||||
<Layout::Sidebar::Item
|
||||
@onClick={{fn this.universe.transitionMenuItem "console.admin.virtual" menuItem}}
|
||||
@item={{menuItem}}
|
||||
@icon={{menuItem.icon}}
|
||||
>{{menuItem.title}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @onClick={{fn this.universe.transitionMenuItem "console.admin.virtual" menuItem}} @item={{menuItem}} @icon={{menuItem.icon}}>{{menuItem.title}}</Layout::Sidebar::Item>
|
||||
{{/each}}
|
||||
</Layout::Sidebar::Panel>
|
||||
{{/each}}
|
||||
<Layout::Sidebar::Panel @open={{true}} @title="System Config">
|
||||
<Layout::Sidebar::Item @route="console.admin.config.services" @icon="bell-concierge">{{t "common.services"}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.config.mail" @icon="envelope">{{t "common.mail"}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.config.filesystem" @icon="hard-drive">{{t "common.filesystem"}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.config.queue" @icon="layer-group">{{t "common.queue"}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.config.socket" @icon="plug">{{t "common.socket"}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.config.notification-channels" @icon="tower-broadcast">{{t "common.notification-channels"}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.config.services" @icon="bell-concierge">Services</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.config.mail" @icon="envelope">Mail</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.config.filesystem" @icon="hard-drive">Filesystem</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.config.queue" @icon="layer-group">Queue</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.config.socket" @icon="plug">Socket</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.config.notification-channels" @icon="tower-broadcast">Notification Channels</Layout::Sidebar::Item>
|
||||
</Layout::Sidebar::Panel>
|
||||
</EmberWormhole>
|
||||
|
||||
|
||||
@@ -1,62 +1,62 @@
|
||||
{{page-title (t "console.admin.branding.title")}}
|
||||
<Layout::Section::Header @title={{t "console.admin.branding.title"}}>
|
||||
<Button @type="primary" @size="sm" @icon="save" @text={{t "common.save-button-text"}} @onClick={{this.save}} @disabled={{this.isLoading}} @isLoading={{this.isLoading}} />
|
||||
{{page-title "Branding"}}
|
||||
<Layout::Section::Header @title="Branding">
|
||||
<Button @type="primary" @size="sm" @icon="save" @text="Save Changes" @onClick={{this.save}} @disabled={{this.isLoading}} @isLoading={{this.isLoading}} />
|
||||
</Layout::Section::Header>
|
||||
|
||||
<Layout::Section::Body class="overflow-y-scroll h-full">
|
||||
<div class="container mx-auto h-screen" {{increase-height-by 300}}>
|
||||
<div class="max-w-3xl my-10 mx-auto space-y-6">
|
||||
<ContentPanel @title={{t "console.admin.branding.title"}} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<div class="max-w-3xl my-10 mx-auto space-y-">
|
||||
<ContentPanel @title="Branding" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<form class="flex flex-col" {{on "submit" this.save}}>
|
||||
<div class="input-group">
|
||||
<label>{{t "console.admin.branding.icon-text"}}</label>
|
||||
<label>Icon</label>
|
||||
<div class="flex flex-row items-center space-x-2">
|
||||
<Image src={{@model.icon_url}} @fallbackSrc="/images/icon.png" alt={{t "app.name"}} width="32" height="32" class="w-8 h-8 shadow-sm" />
|
||||
<FileUpload @name={{t "console.admin.branding.icon-text"}} @accept="image/*" @onFileAdded={{this.uploadIcon}} @labelClass="flex flex-row items-center justify-center mb-0i" as |queue|>
|
||||
<FileUpload @name="icon" @accept="image/*" @onFileAdded={{this.uploadIcon}} @labelClass="flex flex-row items-center justify-center mb-0i" as |queue|>
|
||||
<a tabindex={{0}} class="flex items-center px-0 text-xs no-underline truncate btn btn-sm btn-default" disabled={{queue.files.length}}>
|
||||
{{#if queue.files.length}}
|
||||
<div class="mr-1.5">
|
||||
<Spinner />
|
||||
</div>
|
||||
<span>
|
||||
{{t "common.uploading"}}
|
||||
Uploading...
|
||||
</span>
|
||||
{{else}}
|
||||
<FaIcon @icon="image" class="mr-1.5" />
|
||||
<span>
|
||||
{{t "console.admin.branding.upload-new"}}
|
||||
Upload new
|
||||
</span>
|
||||
{{/if}}
|
||||
</a>
|
||||
</FileUpload>
|
||||
</div>
|
||||
<a href="javascript:;" class="text-danger text-xs mt-1" {{on "click" this.unsetIcon}}>{{t "console.admin.branding.reset-default"}}</a>
|
||||
<a href="javascript:;" class="text-danger text-xs mt-1" {{on "click" this.unsetIcon}}>Reset to default</a>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label>{{t "console.admin.branding.logo-text"}}</label>
|
||||
<label>Logo</label>
|
||||
<div class="flex flex-row items-center space-x-2">
|
||||
<Image src={{@model.logo_url}} @fallbackSrc="/images/fleetbase-logo-svg.svg" alt={{t "app.name"}} width="160" height="56" class="w-40 h-14" />
|
||||
<FileUpload @name={{t "console.admin.branding.logo-text"}} @accept="image/*" @onFileAdded={{this.uploadLogo}} @labelClass="flex flex-row items-center justify-center mb-0i" as |queue|>
|
||||
<FileUpload @name="logo" @accept="image/*" @onFileAdded={{this.uploadLogo}} @labelClass="flex flex-row items-center justify-center mb-0i" as |queue|>
|
||||
<a tabindex={{0}} class="flex items-center px-0 text-xs no-underline truncate btn btn-sm btn-default" disabled={{queue.files.length}}>
|
||||
{{#if queue.files.length}}
|
||||
<div class="mr-1.5">
|
||||
<Spinner />
|
||||
</div>
|
||||
<span>
|
||||
{{t "common.uploading"}}
|
||||
Uploading...
|
||||
</span>
|
||||
{{else}}
|
||||
<FaIcon @icon="image" class="mr-1.5" />
|
||||
<span>
|
||||
{{t "console.admin.branding.upload-new"}}
|
||||
Upload new
|
||||
</span>
|
||||
{{/if}}
|
||||
</a>
|
||||
</FileUpload>
|
||||
</div>
|
||||
<a href="javascript:;" class="text-danger text-xs mt-1" {{on "click" this.unsetLogo}}>{{t "console.admin.branding.reset-default"}}</a>
|
||||
<a href="javascript:;" class="text-danger text-xs mt-1" {{on "click" this.unsetLogo}}>Reset to default</a>
|
||||
</div>
|
||||
<InputGroup @name={{t "console.admin.branding.theme"}}>
|
||||
<InputGroup @name="Default Theme">
|
||||
<Select @value={{@model.default_theme}} @onSelect={{this.setTheme}} @options={{this.themeOptions}} />
|
||||
</InputGroup>
|
||||
</form>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{page-title "Database Configuration"}}
|
||||
<Layout::Section::Header @title={{t "console.admin.config.database.title"}} />
|
||||
<Layout::Section::Header @title="Database Configuration" />
|
||||
|
||||
<Layout::Section::Body class="overflow-y-scroll h-full">
|
||||
<div class="container mx-auto h-screen" {{increase-height-by 800}}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{page-title "Filesystem Configuration"}}
|
||||
<Layout::Section::Header @title={{t "console.admin.config.filesystem.title"}} />
|
||||
<Layout::Section::Header @title="Filesystem Configuration" />
|
||||
|
||||
<Layout::Section::Body class="overflow-y-scroll h-full">
|
||||
<div class="container mx-auto h-screen" {{increase-height-by 800}}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{page-title "Mail Configuration"}}
|
||||
<Layout::Section::Header @title={{t "console.admin.config.mail.title"}} />
|
||||
<Layout::Section::Header @title="Mail Configuration" />
|
||||
|
||||
<Layout::Section::Body class="overflow-y-scroll h-full">
|
||||
<div class="container mx-auto h-screen" {{increase-height-by 800}}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{page-title "Notification Channels Configuration"}}
|
||||
<Layout::Section::Header @title={{t "console.admin.config.notification-channels.title"}} />
|
||||
<Layout::Section::Header @title="Notification Channels Configuration" />
|
||||
|
||||
<Layout::Section::Body class="overflow-y-scroll h-full">
|
||||
<div class="container mx-auto h-screen" {{increase-height-by 800}}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{page-title "Queue Configuration"}}
|
||||
<Layout::Section::Header @title={{t "console.admin.config.queue.title"}} />
|
||||
<Layout::Section::Header @title="Queue Configuration" />
|
||||
|
||||
<Layout::Section::Body class="overflow-y-scroll h-full">
|
||||
<div class="container mx-auto h-screen" {{increase-height-by 800}}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{page-title "Services Configuration"}}
|
||||
<Layout::Section::Header @title={{t "console.admin.config.services.title"}} />
|
||||
<Layout::Section::Header @title="Services Configuration" />
|
||||
|
||||
<Layout::Section::Body class="overflow-y-scroll h-full">
|
||||
<div class="container mx-auto h-screen" {{increase-height-by 900}}>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{page-title "Socket Configuration"}}
|
||||
<Layout::Section::Header @title={{t "console.admin.config.socket.title"}} />
|
||||
<Layout::Section::Header @title="Socket Configuration" />
|
||||
|
||||
<Layout::Section::Body class="overflow-y-scroll h-full">
|
||||
<div class="container mx-auto h-screen" {{increase-height-by 900}}>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{{page-title "Overview"}}
|
||||
<Layout::Section::Header @title={{t "common.overview"}} />
|
||||
<Layout::Section::Header @title="Overview" />
|
||||
|
||||
<Layout::Section::Body class="overflow-y-scroll h-full">
|
||||
<div class="container mx-auto h-screen" {{increase-height-by 800}}>
|
||||
<div class="max-w-3xl my-10 mx-auto space-y-6">
|
||||
<div class="grid grid-cols-3 xs:grid-cols-1 gap-4">
|
||||
<StatWidget @title={{t "console.admin.index.total-users"}} @value={{@model.total_users}} />
|
||||
<StatWidget @title={{t "console.admin.index.total-organizations"}} @value={{@model.total_organizations}} />
|
||||
<StatWidget @title={{t "console.admin.index.total-transactions"}} @value={{@model.total_transactions}} />
|
||||
<StatWidget @title="Total Users" @value={{@model.total_users}} />
|
||||
<StatWidget @title="Total Organizations" @value={{@model.total_organizations}} />
|
||||
<StatWidget @title="Total Transactions" @value={{@model.total_transactions}} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{{page-title "Notifications"}}
|
||||
<Layout::Section::Header @title={{t "console.admin.notifications.title"}}>
|
||||
<Button @type="primary" @size="sm" @icon="save" @text={{t "common.save-button-text"}} @onClick={{this.saveSettings}} @disabled={{this.isLoading}} @isLoading={{this.isLoading}} />
|
||||
<Layout::Section::Header @title="Notifications">
|
||||
<Button @type="primary" @size="sm" @icon="save" @text="Save Changes" @onClick={{this.saveSettings}} @disabled={{this.isLoading}} @isLoading={{this.isLoading}} />
|
||||
</Layout::Section::Header>
|
||||
|
||||
<Layout::Section::Body class="overflow-y-scroll h-full">
|
||||
<div class="container mx-auto h-screen" {{increase-height-by 1200}}>
|
||||
<div class="max-w-3xl my-10 mx-auto space-y-4">
|
||||
{{#each-in this.groupedNotifications as |groupName notifications|}}
|
||||
<ContentPanel @title={{concat (smart-humanize groupName) (t "console.admin.notifications.notification-settings") }} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<ContentPanel @title={{concat (smart-humanize groupName) " Notification Settings"}} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
{{#each notifications as |notification|}}
|
||||
<InputGroup @name={{notification.name}} @helpText={{notification.description}}>
|
||||
<div class="fleetbase-model-select fleetbase-power-select ember-model-select">
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
{{page-title "2FA Config"}}
|
||||
<Layout::Section::Header @title="2FA Config">
|
||||
<Button @type="primary" @size="sm" @icon="save" @text="Save Changes" @onClick={{this.saveSettings}} @disabled={{this.isLoading}} @isLoading={{this.isLoading}} />
|
||||
</Layout::Section::Header>
|
||||
|
||||
<Layout::Section::Body class="overflow-y-scroll h-full">
|
||||
<div class="container mx-auto h-screen" {{increase-height-by 1200}}>
|
||||
<div class="max-w-3xl my-10 mx-auto space-y-4">
|
||||
<ContentPanel @title="2FA Config" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
{{#if this.loadSystemTwoFaConfig.isIdle}}
|
||||
<TwoFaSettings
|
||||
@showEnforceOption={{true}}
|
||||
@twoFaMethods={{this.methods}}
|
||||
@twoFaSettings={{this.twoFaSettings}}
|
||||
@onTwoFaToggled={{this.onTwoFaToggled}}
|
||||
@onTwoFaMethodSelected={{this.onTwoFaMethodSelected}}
|
||||
@onTwoFaEnforcedToggled={{this.onTwoFaEnforceToggled}}
|
||||
@isLoading={{this.isLoading}}
|
||||
/>
|
||||
{{else}}
|
||||
<div class="flex items-center justify-center p-4">
|
||||
<Spinner @loadingMessage="Loading 2FA Config..." @wrapperClass="flex flex-row" @iconClass="mr-2" />
|
||||
</div>
|
||||
{{/if}}
|
||||
</ContentPanel>
|
||||
</div>
|
||||
</div>
|
||||
</Layout::Section::Body>
|
||||
@@ -3,9 +3,9 @@
|
||||
<div class="container mx-auto h-screen space-y-4">
|
||||
<div class="flex flex-col items-center justify-center pt-14 px-40">
|
||||
<FaIcon @icon="shapes" @size="4x" class="mb-6 text-blue-500" />
|
||||
<h1 class="dark:text-gray-100 text-black text-4xl font-bold mb-4">{{t "console.extensions.title"}}</h1>
|
||||
<h1 class="dark:text-gray-100 text-black text-4xl font-bold mb-4">Extensions are coming soon!</h1>
|
||||
<p class="dark:text-gray-300 text-black text-lg">
|
||||
{{t "console.extensions.message"}}
|
||||
Please check back in the upcoming versions as we prepare to launch the Extensions repository and marketplace.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{{page-title "Dashboard"}}
|
||||
<Layout::Section::Body class="overflow-y-scroll h-full pt-6">
|
||||
<div class="console-home-container mx-auto h-screen space-y-4" {{increase-height-by 300}}>
|
||||
<TwoFaEnforcementAlert />
|
||||
<div class="grid grid-cols-1 md:grid-cols-12 gap-4">
|
||||
<GithubCard class="lg:col-span-4" />
|
||||
<FleetbaseBlog class="lg:col-span-8" />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Layout::Section::Header @title={{t "common.notifications"}}>
|
||||
<Button @icon="check-square" @type="default" @text={{t "console.notifications.select-all"}} {{on "click" this.selectAll}} class="mr-2" />
|
||||
<Button @icon="envelope" @type="primary" @text={{t "console.notifications.mark-as-read"}} {{on "click" this.read}} class="mr-2" />
|
||||
<Button @icon="trash" @type="danger" @text={{t "common.delete"}} {{on "click" this.delete}} />
|
||||
<Layout::Section::Header @title="Notifications">
|
||||
<Button @icon="check-square" @type="default" @text="Select All" {{on "click" this.selectAll}} class="mr-2" />
|
||||
<Button @icon="envelope" @type="primary" @text="Mark as Read" {{on "click" this.read}} class="mr-2" />
|
||||
<Button @icon="trash" @type="danger" @text="Delete" {{on "click" this.delete}} />
|
||||
</Layout::Section::Header>
|
||||
|
||||
<Layout::Section::Body class="h-full w-full">
|
||||
@@ -26,7 +26,7 @@
|
||||
<h1 class="text-sm font-semibold antialiased leading-4">{{notification.data.subject}}</h1>
|
||||
<div class="text-xs antialiased text-gray-900 dark:text-gray-200">- {{notification.data.message}}</div>
|
||||
</div>
|
||||
<div class="text-gray-300 text-xs antialiased mt-1">{{t "console.notifications.received"}} {{notification.createdAgo}}</div>
|
||||
<div class="text-gray-300 text-xs antialiased mt-1">Received: {{notification.createdAgo}}</div>
|
||||
</div>
|
||||
</a>
|
||||
<div>
|
||||
@@ -37,7 +37,7 @@
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="flex items-center justify-center h-full w-full">
|
||||
<p class="text-base text-gray-800 dark:text-gray-300 italic">{{t "console.notifications.message"}}</p>
|
||||
<p class="text-base text-gray-800 dark:text-gray-300 italic">No notifications to display.</p>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
@@ -1,28 +1,18 @@
|
||||
{{page-title (t "common.settings")}}
|
||||
{{page-title "Settings"}}
|
||||
<EmberWormhole @to="sidebar-menu-items">
|
||||
<Layout::Sidebar::Panel @open={{true}} @title={{t "common.settings"}}>
|
||||
<Layout::Sidebar::Item @route="console.settings.index" @icon="cog">{{t "common.organization"}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.settings.two-fa" @icon="shield-halved">{{t "common.two-factor"}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Panel @open={{true}} @title="Settings">
|
||||
<Layout::Sidebar::Item @route="console.settings.index" @icon="cog">Organization</Layout::Sidebar::Item>
|
||||
{{#each this.universe.settingsMenuItems as |menuItem|}}
|
||||
<Layout::Sidebar::Item
|
||||
@onClick={{fn this.universe.transitionMenuItem "console.settings.virtual" menuItem}}
|
||||
@item={{menuItem}}
|
||||
@icon={{menuItem.icon}}
|
||||
>{{menuItem.title}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @onClick={{fn this.universe.transitionMenuItem "console.settings.virtual" menuItem}} @item={{menuItem}} @icon={{menuItem.icon}}>{{menuItem.title}}</Layout::Sidebar::Item>
|
||||
{{/each}}
|
||||
</Layout::Sidebar::Panel>
|
||||
{{#each this.universe.settingsMenuPanels as |menuPanel|}}
|
||||
<Layout::Sidebar::Panel @open={{menuPanel.open}} @title={{menuPanel.title}}>
|
||||
{{#each menuPanel.items as |menuItem|}}
|
||||
<Layout::Sidebar::Item
|
||||
@onClick={{fn this.universe.transitionMenuItem "console.settings.virtual" menuItem}}
|
||||
@item={{menuItem}}
|
||||
@icon={{menuItem.icon}}
|
||||
>{{menuItem.title}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @onClick={{fn this.universe.transitionMenuItem "console.settings.virtual" menuItem}} @item={{menuItem}} @icon={{menuItem.icon}}>{{menuItem.title}}</Layout::Sidebar::Item>
|
||||
{{/each}}
|
||||
</Layout::Sidebar::Panel>
|
||||
{{/each}}
|
||||
|
||||
</EmberWormhole>
|
||||
|
||||
{{outlet}}
|
||||
@@ -1,40 +1,40 @@
|
||||
<Layout::Section::Header @title={{t "console.settings.index.title"}} />
|
||||
<Layout::Section::Header @title="Organization Settings" />
|
||||
|
||||
<Layout::Section::Body class="overflow-y-scroll h-full">
|
||||
<div class="container mx-auto h-screen" {{increase-height-by 500}}>
|
||||
<div class="max-w-3xl my-10 mx-auto space-y-6">
|
||||
<ContentPanel @title={{t "console.settings.index.title"}} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<ContentPanel @title="Organization Settings" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<form {{on "submit" this.saveSettings}}>
|
||||
<InputGroup @name={{t "console.settings.index.organization-name"}} @value={{@model.name}} />
|
||||
<InputGroup @name={{t "console.settings.index.organization-description"}} @value={{@model.description}} />
|
||||
<InputGroup @name={{t "console.settings.index.organization-phone"}}>
|
||||
<InputGroup @name="Organization name" @value={{@model.name}} />
|
||||
<InputGroup @name="Organization description" @value={{@model.description}} />
|
||||
<InputGroup @name="Organization phone number">
|
||||
<PhoneInput @value={{@model.phone}} @onInput={{fn (mut @model.phone)}} class="form-input w-full" />
|
||||
</InputGroup>
|
||||
<InputGroup @name={{t "console.settings.index.organization-currency"}}>
|
||||
<InputGroup @name="Organization currency">
|
||||
<CurrencySelect @value={{@model.currency}} @onSelect={{fn (mut @model.currency)}} @triggerClass="w-full form-select" />
|
||||
</InputGroup>
|
||||
<InputGroup @name={{t "console.settings.index.organization-id"}} @value={{@model.public_id}} @disabled={{true}} />
|
||||
<InputGroup @name="Organization ID" @value={{@model.public_id}} @disabled={{true}} />
|
||||
<div class="mt-3 flex items-center justify-end">
|
||||
<Button @buttonType="submit" @type="primary" @size="lg" @icon="save" @text="{{t "common.save-button-text"}}" @isLoading={{this.isLoading}} />
|
||||
<Button @buttonType="submit" @type="primary" @size="lg" @icon="save" @text="Save Changes" @isLoading={{this.isLoading}} />
|
||||
</div>
|
||||
</form>
|
||||
</ContentPanel>
|
||||
|
||||
<ContentPanel @title={{t "console.settings.index.organization-branding"}} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<InputGroup @name={{t "console.settings.index.logo"}} @helpText={{t "console.settings.index.logo-help-text"}}>
|
||||
<ContentPanel @title="Organization Branding" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<InputGroup @name="Logo" @helpText="Logo for your organization.">
|
||||
<div class="flex flex-row items-center">
|
||||
<Image src={{@model.logo_url}} @fallbackSrc={{config "defaultValues.placeholderImage"}} alt={{concat @model.name " logo"}} class="h-20 w-64 border dark:border-gray-900 rounded-md mr-4" />
|
||||
<FileUpload @name={{t "console.settings.index.logo"}} @accept="image/*" @onFileAdded={{fn this.uploadFile "logo"}} as |queue|>
|
||||
<FileUpload @name="logo" @accept="image/*" @onFileAdded={{fn this.uploadFile "logo"}} as |queue|>
|
||||
<a tabindex={{0}} class="flex items-center px-0 mt-2 text-xs no-underline truncate btn btn-sm btn-default">
|
||||
{{#if queue.files.length}}
|
||||
<Spinner class="mr-1" />
|
||||
<span>
|
||||
{{t "common.uploading"}}
|
||||
Uploading...
|
||||
</span>
|
||||
{{else}}
|
||||
<FaIcon @icon="image" class="mr-1" />
|
||||
<span>
|
||||
{{t "console.settings.index.upload-new-logo"}}
|
||||
Upload new logo
|
||||
</span>
|
||||
{{/if}}
|
||||
</a>
|
||||
@@ -42,20 +42,20 @@
|
||||
</div>
|
||||
</InputGroup>
|
||||
|
||||
<InputGroup @name={{t "console.settings.index.backdrop"}} @helpText={{t "console.settings.index.backdrop-help-text"}}>
|
||||
<InputGroup @name="Backdrop" @helpText="Optional banner or background image for your organization.">
|
||||
<div class="flex flex-row items-center">
|
||||
<Image src={{@model.backdrop_url}} @fallbackSrc={{config "defaultValues.placeholderImage"}} alt={{concat @model.name " backdrop"}} class="h-20 w-64 border dark:border-gray-900 rounded-md mr-4" />
|
||||
<FileUpload @name={{t "console.settings.index.backdrop"}} @accept="image/*" @onFileAdded={{fn this.uploadFile "backdrop"}} as |queue|>
|
||||
<FileUpload @name="backdrop" @accept="image/*" @onFileAdded={{fn this.uploadFile "backdrop"}} as |queue|>
|
||||
<a tabindex={{0}} class="flex items-center px-0 mt-2 text-xs no-underline truncate btn btn-sm btn-default">
|
||||
{{#if queue.files.length}}
|
||||
<Spinner class="mr-1" />
|
||||
<span>
|
||||
{{t "common.uploading"}}
|
||||
Uploading...
|
||||
</span>
|
||||
{{else}}
|
||||
<FaIcon @icon="image" class="mr-1" />
|
||||
<span>
|
||||
{{t "console.settings.index.upload-new-backdrop"}}
|
||||
Upload new backdrop
|
||||
</span>
|
||||
{{/if}}
|
||||
</a>
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
{{page-title "TwoFa"}}
|
||||
<Layout::Section::Header @title="2FA" />
|
||||
|
||||
<Layout::Section::Body class="overflow-y-scroll h-full">
|
||||
<div class="container mx-auto h-screen" {{increase-height-by 500}}>
|
||||
<div class="max-w-3xl my-10 mx-auto space-y-6">
|
||||
<ContentPanel @title="2FA Settings" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<div class="mb-3">
|
||||
{{#if this.loadCompanyTwoFaSettings.isIdle}}
|
||||
<TwoFaSettings
|
||||
@showEnforceOption={{true}}
|
||||
@showMethodSelection={{false}}
|
||||
@twoFaMethods={{this.methods}}
|
||||
@twoFaSettings={{this.twoFaSettings}}
|
||||
@onTwoFaToggled={{this.onTwoFaToggled}}
|
||||
@onTwoFaMethodSelected={{this.onTwoFaMethodSelected}}
|
||||
@onTwoFaEnforcedToggled={{this.onTwoFaEnforceToggled}}
|
||||
/>
|
||||
{{else}}
|
||||
<div class="flex items-center justify-center p-4">
|
||||
<Spinner @loadingMessage="Loading User 2FA Settings..." @wrapperClass="flex flex-row" @iconClass="mr-2" />
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<Button
|
||||
@type="primary"
|
||||
@buttonType="submit"
|
||||
@text="Save 2FA Settings"
|
||||
@icon="save"
|
||||
@onClick={{this.saveTwoFactor}}
|
||||
@isLoading={{this.enforceTwoFaForCompanyUsers.isRunning}}
|
||||
/>
|
||||
</ContentPanel>
|
||||
</div>
|
||||
</div>
|
||||
</Layout::Section::Body>
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="border border-black bg-gray-900 shadow-md rounded-md p-5 mt-12">
|
||||
<div class="w-full flex-col items-center justify-center text-center mb-6">
|
||||
<img src="/images/icon.png" alt={{concat (t "app.name") " Install"}} class="w-12 h-12 mx-auto" width="48" height="48" />
|
||||
<h3 class="mt-2 text-gray-50 font-bold">{{t "app.name"}} {{t "install.installer-header"}}</h3>
|
||||
<h3 class="mt-2 text-gray-50 font-bold">{{t "app.name"}} Installer</h3>
|
||||
</div>
|
||||
<div class="space-y-4 mb-5">
|
||||
{{#each this.steps as |step|}}
|
||||
@@ -24,10 +24,10 @@
|
||||
{{#if this.error}}
|
||||
<div class="flex items-center border border-red-900 bg-red-800 text-red-100 px-4 py-1.5 rounded-lg mb-3 shadow-md">
|
||||
<FaIcon @icon="triangle-exclamation" class="text-red-100 mr-2" />
|
||||
<span>{{t "install.failed-message-sent"}}</span>
|
||||
<span>The install failed! Click the button below to retry the install.</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<Button @type="primary" @icon="play" @size="lg" @text={{if this.error (t "install.retry-install") (t "install.start-install") }} @wrapperClass="flex-1" class="w-full" @onClick={{this.startInstall}} @isLoading={{this.install.isRunning}} @disabled={{this.install.isRunning}} />
|
||||
<Button @type="primary" @icon="play" @size="lg" @text={{if this.error "Retry Install" "Start Install"}} @wrapperClass="flex-1" class="w-full" @onClick={{this.startInstall}} @isLoading={{this.install.isRunning}} @disabled={{this.install.isRunning}} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="mb-8">
|
||||
<img class="mx-auto h-12 w-auto" src={{@brand.icon_url}} alt={{t "app.name"}}>
|
||||
<h2 class="mt-6 text-center text-lg font-extrabold text-gray-900 dark:text-white truncate">
|
||||
{{t "invite.for-users.invitation-message" companyName=@model.name}}
|
||||
You've been invited to join {{@model.name}}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@@ -13,22 +13,22 @@
|
||||
<FaIcon @icon="info-circle" class="text-blue-900 mr-4" />
|
||||
</div>
|
||||
<p class="flex-1 text-sm text-blue-900 dark:text-blue-900">
|
||||
{{t "invite.invitation-sent-message" htnmlSafe=true companyName=@model.name appName=(t "app.name")}}
|
||||
You've been invited to join the {{@model.name}} organization on {{t "app.name"}}. To accept this invitation, input your invitation code received by email and click continue.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form class="space-y-6" {{on "submit" this.acceptInvite}}>
|
||||
<div>
|
||||
<label for="code" class="block text-sm font-medium text-gray-700 dark:text-gray-50">
|
||||
{{t "invite.for-users.invitation-code-sent-text"}}
|
||||
Your invitiation code
|
||||
</label>
|
||||
<div class="mt-2">
|
||||
<Input @value={{this.code}} id="code" name="code" @type="code" required class="form-input form-input-lg w-full" placeholder={{t "invite.for-users.invitation-code-sent-text"}} />
|
||||
<Input @value={{this.code}} id="code" name="code" @type="code" required class="form-input form-input-lg w-full" placeholder="Your invitiation code" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button @icon="check" @size="lg" @type="primary" @buttonType="submit" @text={{t "invite.for-users.accept-invitation-text"}} @onClick={{this.acceptInvite}} @isLoading={{this.isLoading}} />
|
||||
<Button @icon="check" @size="lg" @type="primary" @buttonType="submit" @text="Accept Invitation" @onClick={{this.acceptInvite}} @isLoading={{this.isLoading}} />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="mb-4">
|
||||
<Image src={{@model.logo_url}} @fallbackSrc="/images/fleetbase-logo-svg.svg" alt={{t "app.name"}} width="160" height="56" class="w-40 h-14 mx-auto" />
|
||||
<h2 class="text-center text-lg font-extrabold text-gray-900 dark:text-white truncate">
|
||||
{{t "onboard.index.title"}}
|
||||
Create your account
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
<FaIcon @icon="hand-spock" @size="lg" class="text-blue-900 mr-4" />
|
||||
</div>
|
||||
<p class="flex-1 text-sm text-blue-900 dark:text-blue-900">
|
||||
{{t "onboard.index.welcome-title" htmlSafe=true companyName=(t "app.name")}}
|
||||
{{t "onboard.index.welcome-text"}}
|
||||
<strong>Welcome to {{t "app.name"}}!</strong><br />
|
||||
Complete the details required below to get started.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -20,17 +20,17 @@
|
||||
{{#if this.error}}
|
||||
<InfoBlock @icon="exclamation-triangle" @text={{this.error}} class="mb-6 px-3 py-2 bg-red-300 text-red-900" @textClass="text-red-900" />
|
||||
{{/if}}
|
||||
<InputGroup @name={{t "onboard.index.full-name"}} @value={{this.name}} @helpText={{t "onboard.index.full-name-help-text"}} @inputClass="input-lg" />
|
||||
<InputGroup @name={{t "onboard.index.your-email"}} @type="email" @value={{this.email}} @helpText={{t "onboard.index.your-email-help-text"}} @inputClass="input-lg" />
|
||||
<InputGroup @name={{t "onboard.index.phone"}} @helpText={{t "onboard.index.phone-help-text"}}>
|
||||
<InputGroup @name="Your full name" @value={{this.name}} @helpText="Your full name." @inputClass="input-lg" />
|
||||
<InputGroup @name="Your email address" @type="email" @value={{this.email}} @helpText="Your email address." @inputClass="input-lg" />
|
||||
<InputGroup @name="Your phone number" @helpText="Your phone number.">
|
||||
<PhoneInput @onInput={{fn (mut this.phone)}} class="form-input input-lg w-full" />
|
||||
</InputGroup>
|
||||
<InputGroup @name={{t "onboard.index.organization-name"}} @value={{this.organization_name}} @helpText={{t "onboard.index.organization-help-text"}} @inputClass="input-lg" />
|
||||
<InputGroup @name={{t "onboard.index.password"}} @value={{this.password}} @type="password" @helpText={{t "onboard.index.password-help-text"}} @inputClass="input-lg" />
|
||||
<InputGroup @name={{t "onboard.index.confirm-password"}} @value={{this.password_confirmation}} @type="password" @helpText={{t "onboard.index.confirm-password-help-text"}} @inputClass="input-lg" />
|
||||
<InputGroup @name="Organization name" @value={{this.organization_name}} @helpText="Your organization name, all your services and resources will be managed under this organization, later you can create as many organizations as you want or need." @inputClass="input-lg" />
|
||||
<InputGroup @name="Enter a password" @value={{this.password}} @type="password" @helpText="Your password, make sure it's a good one." @inputClass="input-lg" />
|
||||
<InputGroup @name="Confirm your password" @value={{this.password_confirmation}} @type="password" @helpText="Just to confirm the password you entered above." @inputClass="input-lg" />
|
||||
|
||||
<div class="flex items-center justify-end mt-5">
|
||||
<Button @icon="check" @iconPrefix="fas" @type="primary" @size="lg" @text={{t "onboard.index.continue-button-text"}} @isLoading={{this.isLoading}} @disabled={{this.readyToSubmit}} @onClick={{this.startOnboard}} />
|
||||
<Button @icon="check" @iconPrefix="fas" @type="primary" @size="lg" @text="Continue" @isLoading={{this.isLoading}} @disabled={{this.readyToSubmit}} @onClick={{this.startOnboard}} />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,10 +1,8 @@
|
||||
{{page-title (t "onboard.verify-email.header-title")}}
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 py-8 px-4 shadow rounded-lg w-full">
|
||||
<div class="mb-8">
|
||||
<img class="mx-auto h-12 w-auto " src="/images/fleetbase-logo-svg.svg" alt={{t "app.name"}}>
|
||||
<h2 class="mt-6 text-center text-lg font-extrabold text-gray-900 dark:text-white truncate">
|
||||
{{t "onboard.verify-email.title"}}
|
||||
Verify your email address
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@@ -13,16 +11,15 @@
|
||||
<FaIcon @icon="shield-check" @size="lg" class="text-blue-900 mr-4" />
|
||||
</div>
|
||||
<p class="flex-1 text-sm text-blue-900 dark:text-blue-900">
|
||||
{{t "onboard.verify-email.message-text" htmlSafe=true}}
|
||||
<strong>Almost done!</strong><br> Check your email for a verification code.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form class="mt-8 space-y-6" {{on "submit" this.verifyCode}}>
|
||||
<InputGroup @type="tel" @name={{t "onboard.verify-email.verification-input-label"}} @value={{this.code}} @helpText={{t "onboard.verify-email.verification-code-text"}} @inputClass="input-lg" {{on "input" this.validateInput}} />
|
||||
<InputGroup @type="tel" @name="Verification Code" @value={{this.code}} @helpText="Enter the verification code you received via email." @inputClass="input-lg" {{on "input" this.validateInput}} />
|
||||
|
||||
<div class="flex flex-row items-center space-x-4">
|
||||
<div>
|
||||
<Button @icon="check" @iconPrefix="fas" @buttonType="submit" @type="primary" @size="lg" @text="Verify & Continue" @isLoading={{this.isLoading}} @disabled={{this.isNotReadyToSubmit}} @onClick={{this.verifyCode}} />
|
||||
<a href="#" {{on "click" this.onDidntReceiveCode}} class="text-sm text-blue-400 hover:text-blue-300">{{t "onboard.verify-email.didnt-receive-a-code"}}</a>
|
||||
</div>
|
||||
|
||||
{{#if this.stillWaiting}}
|
||||
@@ -32,14 +29,14 @@
|
||||
<FaIcon @icon="exclamation-triangle" @size="lg" class="text-yellow-400" />
|
||||
</div>
|
||||
<div class="ml-3 flex items-center">
|
||||
<span class="text-lg font-extrabold text-yellow-800">{{t "onboard.verify-email.didnt-receive-a-code"}}</span>
|
||||
<span class="text-lg font-extrabold text-yellow-800">Didn't receive an email yet?</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="py-1">
|
||||
<p class="text-yellow-700 text-sm">{{t "onboard.verify-email.not-sent.alternative-choice"}}</p>
|
||||
<div class="py-3">
|
||||
<p class="text-yellow-700">Use alternaitve options below to verify your account.</p>
|
||||
<div class="flex items-center mt-3">
|
||||
<Button @buttonType="button" @type="warning" @wrapperClass="mr-2" @onClick={{this.resendEmail}} class="btn-warning-alert">{{t "onboard.verify-email.not-sent.resend-email"}}</Button>
|
||||
<Button @buttonType="button" @type="warning" @onClick={{this.resendBySms}} class="btn-warning-alert">{{t "onboard.verify-email.not-sent.send-by-sms"}}</Button>
|
||||
<Button @type="default" class="mr-2" @onClick={{this.resendEmail}}>Resend Email</Button>
|
||||
<Button @type="default" @onClick={{this.resendBySms}}>Send by SMS</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
export default function getTwoFaMethods() {
|
||||
return [
|
||||
// {
|
||||
// key: 'authenticator_app',
|
||||
// name: 'Authenticator App',
|
||||
// description: 'Get codes from an app like Authy, 1Password, Microsoft Authenticator, or Google Authenticator',
|
||||
// recommended: true,
|
||||
// },
|
||||
{ key: 'sms', name: 'SMS', description: 'Receive a unique code via SMS' },
|
||||
{ key: 'email', name: 'Email', description: 'Receive a unique code via Email' },
|
||||
];
|
||||
}
|
||||
@@ -14,7 +14,7 @@ module.exports = function (/* environment */) {
|
||||
* @type {String?}
|
||||
* @default "null"
|
||||
*/
|
||||
fallbackLocale: 'en-us',
|
||||
fallbackLocale: null,
|
||||
|
||||
/**
|
||||
* Path where translations are stored. This is relative to the project root.
|
||||
|
||||
@@ -2,12 +2,10 @@
|
||||
const toBoolean = require('./utils/to-boolean');
|
||||
const getenv = require('./utils/getenv');
|
||||
const fixApiHost = require('./utils/fix-api-host');
|
||||
const { version } = require('../package');
|
||||
|
||||
module.exports = function (environment) {
|
||||
const ENV = {
|
||||
modulePrefix: '@fleetbase/console',
|
||||
version,
|
||||
environment,
|
||||
rootURL: '/',
|
||||
locationType: 'history',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@fleetbase/console",
|
||||
"version": "0.3.10",
|
||||
"version": "0.3.3",
|
||||
"private": true,
|
||||
"description": "Fleetbase Console",
|
||||
"repository": "https://github.com/fleetbase/fleetbase",
|
||||
@@ -21,19 +21,19 @@
|
||||
"lint:hbs:fix": "ember-template-lint . --fix",
|
||||
"lint:js": "eslint . --cache",
|
||||
"lint:js:fix": "eslint . --fix",
|
||||
"lint:intl": "fleetbase-intl-lint",
|
||||
"start": "pnpm run prebuild && ember serve",
|
||||
"test": "concurrently \"npm:lint\" \"npm:test:*\" --names \"lint,test:\"",
|
||||
"test:ember": "ember test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fleetbase/ember-core": "^0.2.1",
|
||||
"@fleetbase/ember-ui": "^0.2.10",
|
||||
"@fleetbase/storefront-engine": "^0.2.9",
|
||||
"@fleetbase/fleetops-engine": "^0.4.4",
|
||||
"@fleetbase/fleetops-data": "^0.1.8",
|
||||
"@fleetbase/dev-engine": "^0.2.1",
|
||||
"@fleetbase/iam-engine": "^0.0.9",
|
||||
"@fleetbase/ember-core": "^0.1.9",
|
||||
"@fleetbase/ember-ui": "^0.2.8",
|
||||
"@fleetbase/storefront-engine": "^0.2.5",
|
||||
"@fleetbase/fleetops-engine": "^0.3.7",
|
||||
"@fleetbase/fleetops-data": "^0.1.6",
|
||||
"@fleetbase/dev-engine": "^0.2.0",
|
||||
"@fleetbase/iam-engine": "^0.0.8",
|
||||
"@fleetbase/billing-engine": "^0.0.5",
|
||||
"@fleetbase/leaflet-routing-machine": "^3.2.16",
|
||||
"@ember/legacy-built-in-components": "^0.4.1",
|
||||
"@fortawesome/ember-fontawesome": "^0.4.1",
|
||||
@@ -54,7 +54,6 @@
|
||||
"postcss-nth-list": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fleetbase/intl-lint": "^0.0.1",
|
||||
"@babel/core": "^7.23.2",
|
||||
"@babel/eslint-parser": "^7.22.15",
|
||||
"@babel/plugin-proposal-decorators": "^7.23.2",
|
||||
@@ -137,9 +136,9 @@
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"@fleetbase/fleetops-data": "^0.1.8",
|
||||
"@fleetbase/ember-core": "^0.2.1",
|
||||
"@fleetbase/ember-ui": "^0.2.10"
|
||||
"@fleetbase/fleetops-data": "^0.1.6",
|
||||
"@fleetbase/ember-core": "^0.1.9",
|
||||
"@fleetbase/ember-ui": "^0.2.8"
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
|
||||
1360
console/pnpm-lock.yaml
generated
1360
console/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -10,9 +10,7 @@ Router.map(function () {
|
||||
this.route('auth', function () {
|
||||
this.route('login', { path: '/' });
|
||||
this.route('forgot-password');
|
||||
this.route('reset-password', { path: '/reset-password/:id' });
|
||||
this.route('two-fa');
|
||||
this.route('verification');
|
||||
this.route('reset-password');
|
||||
});
|
||||
this.route('onboard', function () {
|
||||
this.route('verify-email');
|
||||
@@ -27,11 +25,9 @@ Router.map(function () {
|
||||
this.route('notifications');
|
||||
this.route('account', function () {
|
||||
this.route('virtual', { path: '/:slug/:view' });
|
||||
this.route('auth');
|
||||
});
|
||||
this.route('settings', function () {
|
||||
this.route('virtual', { path: '/:slug/:view' });
|
||||
this.route('two-fa');
|
||||
});
|
||||
this.route('virtual', { path: '/:slug/:view' });
|
||||
this.route('admin', function () {
|
||||
@@ -47,7 +43,6 @@ Router.map(function () {
|
||||
});
|
||||
this.route('branding');
|
||||
this.route('notifications');
|
||||
this.route('two-fa-settings');
|
||||
this.route('virtual', { path: '/:slug/:view' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from '@fleetbase/console/tests/helpers';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Component | configure/2fa', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.set('myAction', function(val) { ... });
|
||||
|
||||
await render(hbs`<Configure::2fa />`);
|
||||
|
||||
assert.dom(this.element).hasText('');
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
<Configure::2fa>
|
||||
template block text
|
||||
</Configure::2fa>
|
||||
`);
|
||||
|
||||
assert.dom(this.element).hasText('template block text');
|
||||
});
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from '@fleetbase/console/tests/helpers';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Component | locale-selector', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.set('myAction', function(val) { ... });
|
||||
|
||||
await render(hbs`<LocaleSelector />`);
|
||||
|
||||
assert.dom().hasText('');
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
<LocaleSelector>
|
||||
template block text
|
||||
</LocaleSelector>
|
||||
`);
|
||||
|
||||
assert.dom().hasText('template block text');
|
||||
});
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from '@fleetbase/console/tests/helpers';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Component | two-fa-enforcement-alert', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.set('myAction', function(val) { ... });
|
||||
|
||||
await render(hbs`<TwoFaEnforcementAlert />`);
|
||||
|
||||
assert.dom().hasText('');
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
<TwoFaEnforcementAlert>
|
||||
template block text
|
||||
</TwoFaEnforcementAlert>
|
||||
`);
|
||||
|
||||
assert.dom().hasText('template block text');
|
||||
});
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from '@fleetbase/console/tests/helpers';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Component | two-fa-settings', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.set('myAction', function(val) { ... });
|
||||
|
||||
await render(hbs`<TwoFaSettings />`);
|
||||
|
||||
assert.dom(this.element).hasText('');
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
<TwoFaSettings>
|
||||
template block text
|
||||
</TwoFaSettings>
|
||||
`);
|
||||
|
||||
assert.dom(this.element).hasText('template block text');
|
||||
});
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from '@fleetbase/console/tests/helpers';
|
||||
|
||||
module('Unit | Controller | auth/two-fa', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// TODO: Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let controller = this.owner.lookup('controller:auth/two-fa');
|
||||
assert.ok(controller);
|
||||
});
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from '@fleetbase/console/tests/helpers';
|
||||
|
||||
module('Unit | Controller | auth/verification', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// TODO: Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let controller = this.owner.lookup('controller:auth/verification');
|
||||
assert.ok(controller);
|
||||
});
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from '@fleetbase/console/tests/helpers';
|
||||
|
||||
module('Unit | Controller | console/account/auth', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// TODO: Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let controller = this.owner.lookup('controller:console/account/auth');
|
||||
assert.ok(controller);
|
||||
});
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from '@fleetbase/console/tests/helpers';
|
||||
|
||||
module('Unit | Controller | console/admin/two-fa-settings', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// TODO: Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let controller = this.owner.lookup('controller:console/admin/two-fa-settings');
|
||||
assert.ok(controller);
|
||||
});
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from '@fleetbase/console/tests/helpers';
|
||||
|
||||
module('Unit | Controller | console/settings/auth', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// TODO: Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let controller = this.owner.lookup('controller:console/settings/auth');
|
||||
assert.ok(controller);
|
||||
});
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from '@fleetbase/console/tests/helpers';
|
||||
|
||||
module('Unit | Controller | console/settings/two-fa', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// TODO: Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let controller = this.owner.lookup('controller:console/settings/two-fa');
|
||||
assert.ok(controller);
|
||||
});
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
import Application from '@ember/application';
|
||||
|
||||
import config from '@fleetbase/console/config/environment';
|
||||
import { initialize } from '@fleetbase/console/instance-initializers/register-app-version';
|
||||
import { module, test } from 'qunit';
|
||||
import Resolver from 'ember-resolver';
|
||||
import { run } from '@ember/runloop';
|
||||
|
||||
module('Unit | Instance Initializer | register-app-version', function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
this.TestApplication = class TestApplication extends Application {
|
||||
modulePrefix = config.modulePrefix;
|
||||
podModulePrefix = config.podModulePrefix;
|
||||
Resolver = Resolver;
|
||||
};
|
||||
|
||||
this.TestApplication.instanceInitializer({
|
||||
name: 'initializer under test',
|
||||
initialize,
|
||||
});
|
||||
|
||||
this.application = this.TestApplication.create({
|
||||
autoboot: false,
|
||||
});
|
||||
|
||||
this.instance = this.application.buildInstance();
|
||||
});
|
||||
hooks.afterEach(function () {
|
||||
run(this.instance, 'destroy');
|
||||
run(this.application, 'destroy');
|
||||
});
|
||||
|
||||
// TODO: Replace this with your real tests.
|
||||
test('it works', async function (assert) {
|
||||
await this.instance.boot();
|
||||
|
||||
assert.ok(true);
|
||||
});
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
import { setupTest } from '@fleetbase/console/tests/helpers';
|
||||
|
||||
module('Unit | Model | comment', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function (assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let model = store.createRecord('comment', {});
|
||||
assert.ok(model);
|
||||
});
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from '@fleetbase/console/tests/helpers';
|
||||
|
||||
module('Unit | Route | auth/two-fa', function (hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exists', function (assert) {
|
||||
let route = this.owner.lookup('route:auth/two-fa');
|
||||
assert.ok(route);
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user