forked from platypush/platypush
Redesigned config panel UI.
This commit is contained in:
parent
d3fce6d922
commit
237e0c47cb
8 changed files with 233 additions and 48 deletions
File diff suppressed because one or more lines are too long
|
@ -5,7 +5,29 @@
|
|||
<span class="hostname" v-if="hostname" v-text="hostname" />
|
||||
</div>
|
||||
|
||||
<ul class="plugins">
|
||||
<ul class="plugins" v-if="selectedPanel === 'settings'">
|
||||
<li class="entry" title="Home" @click="onItemClick('entities')">
|
||||
<a href="/#">
|
||||
<i class="fas fa-home" />
|
||||
<span class="name" v-if="!collapsed">Home</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li v-for="config, name in configSections" :key="name" class="entry"
|
||||
:class="{selected: name === selectedConfigPanel}"
|
||||
:title="config.name" @click="$emit('select-config', name)">
|
||||
<a href="/#settings">
|
||||
<span class="icon">
|
||||
<i :class="config.icon['class']" v-if="config.icon?.['class']" />
|
||||
<img :src="config.icon?.imgUrl" v-else-if="config.icon?.imgUrl" alt="name"/>
|
||||
<i class="fas fa-puzzle-piece" v-else />
|
||||
</span>
|
||||
<span class="name" v-if="!collapsed" v-text="config.name" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="plugins" v-else>
|
||||
<li v-for="name in panelNames" :key="name" class="entry" :class="{selected: name === selectedPanel}"
|
||||
:title="name" @click="onItemClick(name)">
|
||||
<a :href="`/#${name}`">
|
||||
|
@ -21,7 +43,6 @@
|
|||
|
||||
<ul class="footer">
|
||||
<li :class="{selected: selectedPanel === 'settings'}" title="Settings" @click="onItemClick('settings')">
|
||||
<!--suppress HtmlUnknownAnchorTarget -->
|
||||
<a href="/#settings">
|
||||
<span class="icon">
|
||||
<i class="fa fa-cog" />
|
||||
|
@ -31,7 +52,6 @@
|
|||
</li>
|
||||
|
||||
<li title="Logout" @click="onItemClick('logout')">
|
||||
<!--suppress HtmlUnknownTarget -->
|
||||
<a href="/logout">
|
||||
<span class="icon">
|
||||
<i class="fas fa-sign-out-alt" />
|
||||
|
@ -46,10 +66,11 @@
|
|||
<script>
|
||||
import icons from '@/assets/icons.json'
|
||||
import Utils from "@/Utils";
|
||||
import configSections from '@/components/panels/Settings/sections.json';
|
||||
|
||||
export default {
|
||||
name: "Nav",
|
||||
emits: ['select'],
|
||||
emits: ['select', 'select-config'],
|
||||
mixins: [Utils],
|
||||
props: {
|
||||
panels: {
|
||||
|
@ -61,6 +82,10 @@ export default {
|
|||
type: String,
|
||||
},
|
||||
|
||||
selectedConfigPanel: {
|
||||
type: String,
|
||||
},
|
||||
|
||||
hostname: {
|
||||
type: String,
|
||||
},
|
||||
|
@ -94,6 +119,7 @@ export default {
|
|||
collapsed: true,
|
||||
icons: icons,
|
||||
host: null,
|
||||
configSections: configSections,
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -217,7 +243,6 @@ nav {
|
|||
.footer {
|
||||
height: $footer-expanded-height;
|
||||
background: $nav-footer-bg;
|
||||
box-shadow: $nav-footer-shadow;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<template>
|
||||
<div class="floating-btn" :class="className">
|
||||
<button type="button" class="btn btn-primary" :title="title" @click="$emit('click', $event)">
|
||||
<Icon :class="iconClass" :url="iconUrl" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Icon from "@/components/elements/Icon";
|
||||
|
||||
export default {
|
||||
name: "FloatingButton",
|
||||
components: {Icon},
|
||||
emits: ["click"],
|
||||
|
||||
props: {
|
||||
iconClass: {
|
||||
type: String,
|
||||
},
|
||||
iconUrl: {
|
||||
type: String,
|
||||
},
|
||||
class: {
|
||||
type: String,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
className() {
|
||||
return this.class
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.floating-btn {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
margin: auto 1em 1em auto;
|
||||
|
||||
button {
|
||||
background: $active-glow-bg-2 !important;
|
||||
color: $selected-fg !important;
|
||||
width: 4em;
|
||||
height: 4em;
|
||||
border-radius: 2em;
|
||||
border: none !important;
|
||||
padding: 0;
|
||||
box-shadow: $border-shadow-bottom-right;
|
||||
|
||||
&:hover {
|
||||
background: $hover-bg-2 !important;
|
||||
color: $hover-fg !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(button) {
|
||||
.icon-container {
|
||||
width: 4em;
|
||||
|
||||
.icon {
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,48 +1,32 @@
|
|||
<template>
|
||||
<div class="settings-container">
|
||||
<header>
|
||||
<div class="col-8">
|
||||
<Dropdown title="Select a category" icon-class="fa fa-ellipsis-h">
|
||||
<DropdownItem text="Users" icon-class="fa fa-user"
|
||||
:item-class="{selected: selectedView === 'users'}"
|
||||
@click="selectedView = 'users'" />
|
||||
<DropdownItem text="Generate a token" icon-class="fa fa-key"
|
||||
:item-class="{selected: selectedView === 'token'}"
|
||||
@click="selectedView = 'token'" />
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
<div class="col-4 pull-right">
|
||||
<button title="Add User" @click="$refs.usersView.$refs.addUserModal.show()" v-if="selectedView === 'users'">
|
||||
<i class="fa fa-plus" />
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<Users :session-token="sessionToken" :current-user="currentUser"
|
||||
v-if="selectedView === 'users'" ref="usersView" />
|
||||
v-if="selectedPanel === 'users' && currentUser" />
|
||||
<Token :session-token="sessionToken" :current-user="currentUser"
|
||||
v-else-if="selectedView === 'token'" ref="tokenView" />
|
||||
v-else-if="selectedPanel === 'tokens' && currentUser" />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Dropdown from "@/components/elements/Dropdown";
|
||||
import DropdownItem from "@/components/elements/DropdownItem";
|
||||
import Token from "@/components/panels/Settings/Token";
|
||||
import Users from "@/components/panels/Settings/Users";
|
||||
import Utils from "@/Utils";
|
||||
|
||||
export default {
|
||||
name: "Settings",
|
||||
components: {Dropdown, DropdownItem, Users, Token},
|
||||
components: {Users, Token},
|
||||
mixins: [Utils],
|
||||
|
||||
props: {
|
||||
selectedPanel: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
selectedView: 'users',
|
||||
currentUser: null,
|
||||
sessionToken: null,
|
||||
}
|
||||
|
|
|
@ -7,25 +7,66 @@
|
|||
<label>
|
||||
This is your generated token. Treat it carefully and do not share it with untrusted parties.<br/>
|
||||
Also, make sure to save it - it WILL NOT be displayed again.
|
||||
|
||||
<textarea class="token" v-text="token" @focus="onTokenSelect" />
|
||||
</label>
|
||||
|
||||
<textarea class="token" v-text="token" @focus="onTokenSelect" />
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<Modal ref="sessionTokenModal">
|
||||
<div class="token-container">
|
||||
<label>
|
||||
This is your current session token.
|
||||
It will be invalidated once you log out of the current session.
|
||||
</label>
|
||||
|
||||
<textarea class="token" v-text="sessionToken" @focus="onTokenSelect" />
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<div class="body">
|
||||
<div class="description">
|
||||
<p>Generate a JWT authentication token that can be used for API calls to the <tt>/execute</tt> endpoint.</p><br/>
|
||||
<p>
|
||||
Platypush provides two types of tokens:
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<b>JWT tokens</b> are bearer-only, and they contain encrypted
|
||||
authentication information.<br/>
|
||||
They can be used as permanent or time-based tokens to
|
||||
authenticate with the Platypush API.
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<b>Session tokens</b> are randomly generated tokens stored on the
|
||||
application database. A session token generated in this session
|
||||
will expire when you log out of it.
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p>Generate a JWT authentication token that can be used for API calls to the <code>/execute</code> endpoint.</p><br/>
|
||||
<p>You can include the token in your requests in any of the following ways:</p>
|
||||
|
||||
<ul>
|
||||
<li>Specify it on the <tt>Authorization: Bearer</tt> header;</li>
|
||||
<li>Specify it on the <tt>X-Token</tt> header;</li>
|
||||
<li>Specify it as a URL parameter: <tt>http://site:8008/execute?token=...</tt>;</li>
|
||||
<li>Specify it on the body of your JSON request: <tt>{"type":"request", "action", "...", "token":"..."}</tt>.</li>
|
||||
<li>Specify it on the <code>Authorization: Bearer</code> header;</li>
|
||||
<li>Specify it on the <code>X-Token</code> header;</li>
|
||||
<li>
|
||||
Specify it as a URL parameter: <code>http://site:8008/execute?token=...</code>
|
||||
for a JWT token and <code>...?session_token=...</code> for a
|
||||
session token;
|
||||
</li>
|
||||
<li>Specify it on the body of your JSON request:
|
||||
<code>{"type":"request", "action", "...", "token":"..."}</code> for
|
||||
a JWT token, or <code>"session_token"</code> for a session token.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
Confirm your credentials in order to generate a new token.
|
||||
<p>Confirm your credentials in order to generate a new JWT token.</p>
|
||||
<p>
|
||||
<i>Show session token</i> will instead show the token cookie associated
|
||||
to the current session.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="form-container">
|
||||
|
@ -50,13 +91,19 @@
|
|||
<input type="text" name="validityDays">
|
||||
</span>
|
||||
<span class="note">
|
||||
Decimal values are also supported (e.g. <i>0.5</i> to identify 6 hours). An empty or zero value means that
|
||||
the token has no expiry date.
|
||||
Decimal values are also supported - e.g. <i>0.5</i> means half a
|
||||
day (12 hours). An empty or zero value means that the token has
|
||||
no expiry date.
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="submit" class="btn btn-primary" value="Generate token">
|
||||
<input type="submit" class="btn btn-primary" value="Generate JWT token">
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="button" class="btn btn-default" value="Show session token"
|
||||
@click.stop="$refs.sessionTokenModal.show()">
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -80,6 +127,11 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
|
||||
sessionToken: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
|
@ -137,8 +189,13 @@ export default {
|
|||
.token-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: .15em;
|
||||
|
||||
label {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.body {
|
||||
background: $background-color;
|
||||
display: flex;
|
||||
|
@ -203,9 +260,13 @@ export default {
|
|||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 10em;
|
||||
margin-top: 1em;
|
||||
border-radius: 1em;
|
||||
border: none;
|
||||
background: $active-glow-bg-1;
|
||||
padding: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,15 +43,17 @@
|
|||
<li v-for="user in users" :key="user.user_id" class="item user" @click="selectedUser = user.username">
|
||||
<div class="name col-8" v-text="user.username" />
|
||||
<div class="actions pull-right col-4">
|
||||
<Dropdown title="User Actions" icon-class="fa fa-cog">
|
||||
<Dropdown title="User Actions" icon-class="fa fa-ellipsis">
|
||||
<DropdownItem text="Change Password" :disabled="commandRunning" icon-class="fa fa-key"
|
||||
@click="selectedUser = user.username; $refs.changePasswordModal.show()" />
|
||||
@click="showChangePasswordModal(user)" />
|
||||
<DropdownItem text="Delete User" :disabled="commandRunning" icon-class="fa fa-trash"
|
||||
@click="deleteUser(user)" />
|
||||
</Dropdown>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<FloatingButton icon-class="fa fa-plus" text="Add User" @click="showAddUserModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -61,10 +63,11 @@ import Modal from "@/components/Modal";
|
|||
import Loading from "@/components/Loading";
|
||||
import Utils from "@/Utils";
|
||||
import DropdownItem from "@/components/elements/DropdownItem";
|
||||
import FloatingButton from "@/components/elements/FloatingButton";
|
||||
|
||||
export default {
|
||||
name: "Users",
|
||||
components: {DropdownItem, Loading, Modal, Dropdown},
|
||||
components: {Dropdown, DropdownItem, FloatingButton, Loading, Modal},
|
||||
mixins: [Utils],
|
||||
|
||||
props: {
|
||||
|
@ -218,6 +221,22 @@ export default {
|
|||
|
||||
await this.refresh()
|
||||
},
|
||||
|
||||
showAddUserModal() {
|
||||
this.$refs.addUserModal.show()
|
||||
this.$nextTick(() => {
|
||||
this.$refs.addUserForm.reset()
|
||||
this.$refs.addUserForm.username.focus()
|
||||
})
|
||||
},
|
||||
|
||||
showChangePasswordModal(user) {
|
||||
this.$refs.changePasswordModal.show()
|
||||
this.$nextTick(() => {
|
||||
this.$refs.changePasswordForm.password.focus()
|
||||
this.selectedUser = user.username
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
|
@ -268,6 +287,7 @@ export default {
|
|||
justify-content: right;
|
||||
|
||||
button {
|
||||
background: none !important;
|
||||
width: min-content;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"users": {
|
||||
"name": "Users",
|
||||
"icon": {
|
||||
"class": "fas fa-user"
|
||||
}
|
||||
},
|
||||
|
||||
"tokens": {
|
||||
"name": "Tokens",
|
||||
"icon": {
|
||||
"class": "fas fa-key"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,18 @@
|
|||
<template>
|
||||
<main>
|
||||
<Loading v-if="loading" />
|
||||
<Nav :panels="components" :selected-panel="selectedPanel" :hostname="hostname"
|
||||
@select="selectedPanel = $event" v-else />
|
||||
<Nav :panels="components"
|
||||
:selected-panel="selectedPanel"
|
||||
:selected-config-panel="selectedConfigPanel"
|
||||
:hostname="hostname"
|
||||
@select="selectedPanel = $event"
|
||||
@select-config="selectedConfigPanel = $event"
|
||||
v-else
|
||||
/>
|
||||
|
||||
<div class="canvas" v-if="selectedPanel === 'settings'">
|
||||
<div class="panel">
|
||||
<Settings />
|
||||
<Settings :selected-panel="selectedConfigPanel" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -39,6 +45,7 @@ export default {
|
|||
components: {},
|
||||
hostname: undefined,
|
||||
selectedPanel: undefined,
|
||||
selectedConfigPanel: 'users',
|
||||
}
|
||||
},
|
||||
|
||||
|
|
Loading…
Reference in a new issue