@@ -18,7 +18,6 @@ | |||
"vue-datetime": "^1.0.0-beta.11", | |||
"vue-router": "^3.0.6", | |||
"vue-select": "^3.4.0", | |||
"vue-sidebar-menu": "^4.4.3", | |||
"vuex": "^3.1.1" | |||
}, | |||
"devDependencies": { | |||
@@ -1,13 +1,13 @@ | |||
import Vue from 'vue'; | |||
import Router from 'vue-router'; | |||
import HomePage from '../account/HomePage' | |||
import ActivationPage from '../auth/ActivationPage' | |||
import ModelSetupPage from '../admin/ModelSetupPage' | |||
import RegisterPage from '../auth/RegisterPage' | |||
import LoginPage from '../auth/LoginPage' | |||
import LogoutPage from '../auth/LogoutPage' | |||
import MultifactorAuthPage from '../auth/MultifactorAuthPage' | |||
import DashboardPage from '../account/DashboardPage' | |||
import ProfilePage from '../account/profile/ProfilePage' | |||
import ActionPage from '../account/profile/ActionPage' | |||
import PolicyPage from '../account/profile/PolicyPage' | |||
@@ -57,50 +57,39 @@ const newNetworkChildren = [ | |||
export const router = new Router({ | |||
mode: 'history', | |||
routes: [ | |||
{ path: '', component: DashboardPage }, | |||
{ path: '/', component: DashboardPage }, | |||
{ path: '/me', component: ProfilePage }, | |||
{ path: '/me/policy', component: PolicyPage }, | |||
{ path: '/me/action', component: ActionPage }, | |||
{ path: '/me/changePassword', component: ChangePasswordPage }, | |||
{ path: '/me/setPassword/:code', component: SetPasswordPage }, | |||
{ path: '/me/keys', component: SshKeysPage }, | |||
{ path: '/me/bills', component: BillsPage }, | |||
{ path: '/me/payment', component: PaymentMethodsPage, children: paymentMethodsChildren }, | |||
{ path: '/devices', component: DevicesPage }, | |||
{ path: '/apps', component: AppsPage }, | |||
{ path: '/app/:app', component: AppPage }, | |||
{ path: '/app/:app/config/:view', component: AppConfigPage }, | |||
{ path: '/app/:app/config/:view/:item', component: AppConfigPage }, | |||
{ path: '/app/:app/view/:view', component: AppDataViewPage }, | |||
{ path: '/app/:app/site/:site', component: AppSitePage }, | |||
{ path: '/app/:app/site/:site/view/:view', component: AppDataViewPage }, | |||
{ path: '/notifications', component: NotificationsPage }, | |||
{ | |||
path: '/', component: HomePage, | |||
path: '/bubbles', component: NetworksPage , | |||
children: [ | |||
{ | |||
path: '', component: NetworksPage, | |||
children: [ | |||
{ | |||
path: '', component: NewNetworkPage, | |||
children: newNetworkChildren | |||
}, | |||
] | |||
}, | |||
{ path: '/me', component: ProfilePage }, | |||
{ path: '/me/policy', component: PolicyPage }, | |||
{ path: '/me/action', component: ActionPage }, | |||
{ path: '/me/changePassword', component: ChangePasswordPage }, | |||
{ path: '/me/setPassword/:code', component: SetPasswordPage }, | |||
{ path: '/me/keys', component: SshKeysPage }, | |||
{ path: '/me/bills', component: BillsPage }, | |||
{ path: '/me/payment', component: PaymentMethodsPage, children: paymentMethodsChildren }, | |||
{ path: '/devices', component: DevicesPage }, | |||
{ path: '/apps', component: AppsPage }, | |||
{ path: '/app/:app', component: AppPage }, | |||
{ path: '/app/:app/config/:view', component: AppConfigPage }, | |||
{ path: '/app/:app/config/:view/:item', component: AppConfigPage }, | |||
{ path: '/app/:app/view/:view', component: AppDataViewPage }, | |||
{ path: '/app/:app/site/:site', component: AppSitePage }, | |||
{ path: '/app/:app/site/:site/view/:view', component: AppDataViewPage }, | |||
{ path: '/notifications', component: NotificationsPage }, | |||
{ | |||
path: '/bubbles', component: NetworksPage , | |||
children: [ | |||
{ | |||
path: '', component: NewNetworkPage, | |||
children: newNetworkChildren | |||
}, | |||
] | |||
}, | |||
{ path: '/new_bubble', component: NewNetworkPage, | |||
path: '', component: NewNetworkPage, | |||
children: newNetworkChildren | |||
}, | |||
{ path: '/bubble/:id', component: NetworkPage } | |||
] | |||
}, | |||
{ path: '/new_bubble', component: NewNetworkPage, | |||
children: newNetworkChildren | |||
}, | |||
{ path: '/bubble/:id', component: NetworkPage }, | |||
{ path: '/action', component: ActionPage }, | |||
{ path: '/activate', component: ActivationPage }, | |||
@@ -119,7 +108,7 @@ export const router = new Router({ | |||
{ path: '/admin/accounts/:id/payment', component: PaymentMethodsPage, children: paymentMethodsChildren }, | |||
{ path: '/admin/model', component: ModelSetupPage }, | |||
// otherwise redirect to home | |||
// otherwise redirect to dashboard | |||
{ path: '*', redirect: '/' } | |||
] | |||
}); | |||
@@ -8,6 +8,7 @@ const state = { | |||
allowRegistration: false, | |||
paymentsEnabled: false, | |||
sageLauncher: false, | |||
bubbleNode: null, | |||
entityClasses: [], | |||
locales: ['en_US'], | |||
cloudConfigs: {}, | |||
@@ -142,84 +143,93 @@ const actions = { | |||
}; | |||
const getters = { | |||
menu: function () { | |||
dashboardApps: function () { | |||
const configs = state.configs; | |||
const messages = state.messages; | |||
const menu = [{ | |||
href: '/', | |||
title: messages.label_menu_dashboard, | |||
icon: messages.label_menu_dashboard_icon | |||
}, { | |||
const isAdmin = account.state.user.admin === true; | |||
const dashApps = []; | |||
dashApps.push({ | |||
href: '/me', | |||
title: messages.label_menu_account, | |||
icon: messages.label_menu_account_icon | |||
// }, { | |||
// href: '/notifications', | |||
// title: messages.label_menu_notifications, | |||
// icon: messages.label_menu_notifications_icon | |||
}]; | |||
icon: messages.label_menu_account_icon, | |||
index: 0 | |||
}); | |||
if (configs.sageLauncher) { | |||
menu.splice(2, 0, { | |||
href: '/me/bubbles', | |||
dashApps.push({ | |||
href: '/bubbles', | |||
title: messages.label_menu_networks, | |||
icon: messages.label_menu_networks_icon | |||
icon: messages.label_menu_networks_icon, | |||
index: 1 | |||
}); | |||
if (account.state.user.admin === true) { | |||
menu.splice(3, 0, { | |||
href: '/apps', | |||
title: messages.label_menu_apps, | |||
icon: messages.label_menu_apps_icon | |||
}); | |||
} | |||
} else { | |||
menu.splice(2, 0, { | |||
dashApps.push({ | |||
href: '/devices', | |||
title: messages.label_menu_devices, | |||
icon: messages.label_menu_devices_icon | |||
icon: messages.label_menu_devices_icon, | |||
index: 2 | |||
}); | |||
menu.splice(3, 0, { | |||
dashApps.push({ | |||
href: '/apps', | |||
title: messages.label_menu_apps, | |||
icon: messages.label_menu_apps_icon | |||
icon: messages.label_menu_apps_icon, | |||
index: 3 | |||
}); | |||
} | |||
if (account.state.user.admin === true) { | |||
const admin_menu = { | |||
href: '/admin', | |||
title: messages.label_menu_admin, | |||
icon: messages.label_menu_admin_icon, | |||
child: [{ | |||
href: '/admin/accounts', | |||
title: messages.label_menu_admin_users, | |||
icon: messages.label_menu_admin_users_icon | |||
}, { | |||
href: '/admin/model', | |||
title: messages.label_menu_admin_model, | |||
icon: messages.label_menu_admin_model_icon | |||
}] | |||
}; | |||
menu.splice(1, 0, admin_menu); | |||
if (configs.sageLauncher) { | |||
admin_menu.child.push({ | |||
href: '/admin/bubbles', | |||
title: messages.label_menu_admin_networks, | |||
icon: messages.label_menu_admin_networks_icon | |||
}); | |||
} | |||
if (isAdmin && configs.sageLauncher) { | |||
const adminApps = [{ | |||
href: '/', | |||
title: messages.label_menu_dashboard, | |||
icon: messages.label_menu_dashboard_icon, | |||
index: 0 | |||
}]; | |||
adminApps.push({ | |||
href: '/admin/accounts', | |||
title: messages.label_menu_admin_users, | |||
icon: messages.label_menu_admin_users_icon, | |||
index: 1 | |||
}); | |||
adminApps.push({ | |||
href: '/admin/model', | |||
title: messages.label_menu_admin_model, | |||
icon: messages.label_menu_admin_model_icon, | |||
index: 2 | |||
}); | |||
adminApps.push({ | |||
href: '/admin/bubbles', | |||
title: messages.label_menu_admin_networks, | |||
icon: messages.label_menu_admin_networks_icon, | |||
index: 3 | |||
}); | |||
if (configs.paymentsEnabled) { | |||
admin_menu.child.push({ | |||
adminApps.push({ | |||
href: '/admin/bills', | |||
title: messages.label_menu_admin_bills, | |||
icon: messages.label_menu_admin_bills_icon | |||
icon: messages.label_menu_admin_bills_icon, | |||
index: 4 | |||
}); | |||
} | |||
dashApps.push({ | |||
href: '/?app=admin', | |||
title: messages.label_menu_admin, | |||
icon: messages.label_menu_admin_icon, | |||
index: 4, | |||
apps: adminApps | |||
}); | |||
} else if (isAdmin) { | |||
dashApps.push({ | |||
href: '/admin/accounts', | |||
title: messages.label_menu_admin_users, | |||
icon: messages.label_menu_admin_users_icon, | |||
index: 4 | |||
}); | |||
} | |||
menu.push({ | |||
dashApps.push({ | |||
href: '/logout', | |||
title: messages.label_menu_logout, | |||
icon: messages.label_menu_logout_icon | |||
icon: messages.label_menu_logout_icon, | |||
index: 1000 | |||
}); | |||
return menu; | |||
return dashApps; | |||
}, | |||
promoCodesEnabled: function () { return state.promoCodePolicy === 'required' || state.promoCodePolicy === 'optional'; }, | |||
promoCodeRequired: function () { return state.promoCodePolicy === 'required'; } | |||
@@ -0,0 +1,57 @@ | |||
<template> | |||
<table width="100%"> | |||
<tr> | |||
<td width="100%" v-if="dashApps && dashApps.length > 0"> | |||
<router-link :to="app.href" v-for="app in dashApps" class="icon-dash-cell"> | |||
<div class="icon-dash-title"><i aria-hidden="true" :class="'icon-dash-app '+app.icon" :title="app.title"></i><br/>{{app.title}}</div> | |||
</router-link> | |||
</td> | |||
</tr> | |||
</table> | |||
</template> | |||
<script> | |||
import { mapState, mapActions, mapGetters } from 'vuex'; | |||
export default { | |||
computed: { | |||
...mapState({ | |||
account: state => state.account, | |||
users: state => state.users.all | |||
}), | |||
...mapState('account', ['locale']), | |||
...mapState('system', ['messages', 'detectedTimezone', 'detectedLocale']), | |||
...mapGetters('system', ['dashboardApps']), | |||
queryApp () { | |||
if (typeof this.$route.query.app !== 'undefined' && this.$route.query.app !== null && this.$route.query.app !== '') { | |||
return this.$route.query.app; | |||
} | |||
return null; | |||
}, | |||
dashApps () { | |||
let appView = this.dashboardApps; | |||
const qApp = this.queryApp; | |||
if (qApp !== null) { | |||
const appPath = this.$route.query.app; | |||
for (let i=0; i<appView.length; i++) { | |||
const app = appView[i]; | |||
if (app.href === '/?app='+appPath) { | |||
appView = app.apps; | |||
break; | |||
} | |||
} | |||
} | |||
return appView; | |||
} | |||
}, | |||
methods: { | |||
...mapActions('system', ['loadMessages', 'loadTimezones', 'detectTimezone', 'detectLocale']) | |||
}, | |||
created () { | |||
this.loadMessages('post_auth', this.locale); | |||
this.loadMessages('apps', this.locale); | |||
this.detectLocale(); | |||
this.detectTimezone(); | |||
} | |||
}; | |||
</script> |
@@ -1,14 +1,15 @@ | |||
<template> | |||
<div> | |||
<h1>{{messages.label_homepage_hello && messages.label_homepage_hello.parseMessage(this)}}</h1> | |||
<hr/> | |||
<!-- <h1>{{messages.label_homepage_hello && messages.label_homepage_hello.parseMessage(this)}}</h1>--> | |||
<!-- <hr/>--> | |||
<router-view></router-view> | |||
</div> | |||
</template> | |||
<script> | |||
import { mapState, mapActions } from 'vuex' | |||
import { mapState, mapActions } from 'vuex'; | |||
// todo: this can all move | |||
export default { | |||
computed: { | |||
...mapState({ | |||
@@ -1,10 +1,14 @@ | |||
<template> | |||
<div class="jumbotron"> | |||
<totp-modal/> | |||
<sidebar-menu :hide-toggle="true" :menu="menu" v-if="status.loggedIn && activated"/> | |||
<router-link v-if="status.loggedIn && activated && path && path !== '' && path !== '/'" to="/" class="icon-dash-cell"> | |||
<div class="icon-dash-title"><i aria-hidden="true" :class="'icon-dash-app '+messages.label_menu_dashboard_icon" :title="messages.label_menu_dashboard"></i><br/>{{messages.label_menu_dashboard}}</div> | |||
</router-link> | |||
<div class="container"> | |||
<div class="row"> | |||
<div class="col-sm-6 offset-sm-3"> | |||
<div class="col-sm-6 offset-sm-2"> | |||
<div v-if="alert.message" :class="`alert ${alert.type}`">{{alert.message}}</div> | |||
<router-view></router-view> | |||
</div> | |||
@@ -41,12 +45,12 @@ export default { | |||
}, | |||
computed: { | |||
...mapState('account', ['status', 'user', 'locale', 'registrationError']), | |||
...mapState('system', ['activated', 'configs', 'messages', 'messageGroupsLoaded', 'menu']), | |||
...mapGetters('system', ['menu']), | |||
...mapState('system', ['activated', 'configs', 'messages', 'messageGroupsLoaded']), | |||
...mapState({ | |||
alert: state => state.alert | |||
}), | |||
locales () { return this.configs.locales; } | |||
locales () { return this.configs.locales; }, | |||
path () { return this.$route.path; } | |||
}, | |||
methods: { | |||
...mapActions({ clearAlert: 'alert/clear' }), | |||
@@ -96,7 +100,7 @@ export default { | |||
const user = util.currentUser(); | |||
this.selectedLocale = (user !== null && typeof user.locale !== 'undefined' && user.locale !== null ? user.locale : 'detect'); | |||
this.loadIsActivated(); | |||
this.loadSystemConfigs(); // determine if we can show the registration link | |||
this.loadSystemConfigs(); | |||
this.loadTimezones(); | |||
this.loadMessages('pre_auth', this.selectedLocale); | |||
@@ -56,6 +56,20 @@ | |||
right: 0; | |||
margin: auto; | |||
} | |||
.icon-dash-title { | |||
text-align: center; | |||
font-size: large; | |||
float: left; | |||
margin-right: 24pt; | |||
margin-bottom: 24pt; | |||
} | |||
.icon-dash-cell { | |||
font-size: xx-large; | |||
} | |||
.icon-dash-app { | |||
font-size: xx-large; | |||
} | |||
</style> | |||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> | |||
@@ -1,6 +1,5 @@ | |||
import Vue from 'vue'; | |||
import VeeValidate from 'vee-validate'; | |||
import VueSidebarMenu from 'vue-sidebar-menu' | |||
import vSelect from 'vue-select' | |||
import { Datetime } from 'vue-datetime'; | |||
@@ -21,7 +20,6 @@ import FormField from "./_components/FormField"; | |||
import App from './app/App'; | |||
Vue.use(VeeValidate); | |||
Vue.use(VueSidebarMenu); | |||
Vue.component('v-select', vSelect); | |||
Vue.component('datetime', Datetime); | |||
Vue.component('totp-modal', TotpModal); | |||