Use bootstrap on admin panel

This commit is contained in:
Evert Prants 2018-02-13 22:12:45 +02:00
parent 60e9b00a74
commit 67f303b382
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
16 changed files with 225 additions and 274 deletions

View File

@ -4,24 +4,24 @@
.noactive.stamp(title='Expired', v-if='expired')
i.fa.fa-fw.fa-ban
.info
.section
span.key User
span.value {{ user.display_name }}
.section
span.key Admin
span.value {{ admin.display_name }}
.section
span.key Reason
span.value {{ reason }}
.section
span.key Placed
span.value {{ new Date(created_at).toString() }}
.section
span.key Expires
span.value(v-if='expires_at') {{ new Date(expires_at).toString() }}
span.value(v-else='v-else')
.row
.col-3 User
.col {{ user.display_name }}
.row
.col-3 Admin
.col {{ admin.display_name }}
.row
.col-3 Reason
.col {{ reason }}
.row
.col-3 Placed
.col {{ new Date(created_at).toString() }}
.row
.col-3 Expires
.col(v-if='expires_at') {{ new Date(expires_at).toString() }}
.col(v-else='v-else')
b This ban is permanent.
.button.remove(@click='$parent.$emit("pardon", id)')
.btn.btn-success.remove.mt-3(@click='$parent.$emit("pardon", id)')
i.fa.fa-fw.fa-check
| Pardon
</template>

View File

@ -1,7 +1,7 @@
<template lang="pug">
#banlist
h3 Bans ({{pagination.total}})
.message.error(v-if='error') {{ error }}
.alert.alert-danger(v-if='error') {{ error }}
.entry(v-else)
pagination(:page="pagination.page" :pages="pagination.pages" v-on:page="getBans")
.list.bans

View File

@ -2,18 +2,18 @@
modal(:show='show', @close='close')
.modal-header
h3 Ban user
.modal-body.aligned-form
.message.error(v-if='error') {{ error }}
.modal-body
.alert.alert-danger(v-if='error') {{ error }}
input(type='hidden', name='user_id', :value='id')
.cell
.form-group
label(for='reason') Reason
input#reason(type='text', name='reason', v-model='reason')
.cell
input.form-control#reason(type='text', name='reason', v-model='reason')
.form-group
label(for='expires_at') Expires
input#expires_at(type='date', name='expires_at', v-model='expires_at')
.modal-footer.text-align
button(@click='submit') Ban
button(@click='close') Cancel
input.form-control#expires_at(type='date', name='expires_at', v-model='expires_at')
.modal-footer.text-right
button.btn.btn-primary(@click='submit') Ban
button.btn.btn-secondary(@click='close') Cancel
</template>
<script type="text/javascript">

View File

@ -4,29 +4,29 @@
h3(v-if="id > 0") Edit Client
h3(v-else) New Client
.modal-body.aligned-form
.message.error(v-if='error') {{ error }}
.cell
.alert.alert-danger(v-if='error') {{ error }}
.form-group
label(for="title") Title
input(type="text" id="title" name="title" v-model="title")
.cell
input.form-control(type="text" id="title" name="title" v-model="title")
.form-group
label(for="description") Description
input(type="text" id="description" name="description" v-model="description")
.cell
input.form-control(type="text" id="description" name="description" v-model="description")
.form-group
label(for="url") URL
input(type="text" id="url" name="url" v-model="url")
.cell
input.form-control(type="text" id="url" name="url" v-model="url")
.form-group
label(for="scope") Scope
input(type="text" id="scope" name="scope" v-model="scope")
.cell
input.form-control(type="text" id="scope" name="scope" v-model="scope")
.form-group
label(for="redirect_url") Redirect
input(type="text" id="redirect_url" name="redirect_url" v-model="redirect_url")
.cell
label(for="verified") Verified
input(type="checkbox" id="verified" name="verified" v-model="verified")
.modal-footer.text-align
button(@click='submit') Done
button(@click='newSecret' v-if="id > 0") New Secret
button(@click='close') Cancel
input.form-control(type="text" id="redirect_url" name="redirect_url" v-model="redirect_url")
.form-check
input.form-check-input(type="checkbox" id="verified" name="verified" v-model="verified")
label.form-check-label(for="verified") Verified
.modal-footer.text-right
button.btn.btn-primary(@click='submit') Done
button.btn.btn-danger(@click='newSecret' v-if="id > 0") New Secret
button.btn.btn-secondary(@click='close') Cancel
</template>
<script type="text/javascript">

View File

@ -1,10 +1,11 @@
<template lang="pug">
.application.list-item
.application.list-item.row
.col-2
.picture
img(v-if="icon" :src="'/usercontent/images/' + icon")
.noicon(v-else)
i.fa.fa-fw.fa-gears
.info
.info.col
.stamps
.verified.stamp(v-if="verified")
i.fa.fa-fw.fa-check
@ -25,30 +26,30 @@
.description {{ description }}
a.url(:href='url', target='_blank', rel='nofollow') {{ url }}
.section
span.key Scopes
span.value {{ scope }}
.section
span.key Redirect
span.value {{ redirect_url }}
.section
span.key Client ID
span.value {{ id }}
.section
span.key Secret
span.value Client Secret:
.row
.col-2 Scopes
.col {{ scope }}
.row
.col-2 Redirect
.col {{ redirect_url }}
.row
.col-2 Client ID
.col {{ id }}
.row
.col-2 Secret
.col Client Secret:
#showbutton(@click="secretShown = !secretShown")
span(v-show="!secretShown") Click here to reveal secret
#hiddensecret(v-show="secretShown") {{ secret }}
.section
span.key Grants
span.value {{ grants }}
.section
span.key Owner
span.value {{ user.display_name }}
.section
span.key Created
span.value {{ new Date(created_at).toString() }}
.row
.col-2 Grants
.col {{ grants }}
.row
.col-2 Owner
.col {{ user.display_name }}
.row
.col-2 Created
.col {{ new Date(created_at).toString() }}
</template>
<script type="text/javascript">

View File

@ -1,6 +1,6 @@
<template lang="pug">
#clientlist
button(@click="editing = -1") New Client
button.btn.btn-primary.mt-1(@click="editing = -1") New Client
.message.error(v-if="error") {{ error }}
.entry(v-else)
pagination(:page="pagination.page" :pages="pagination.pages" v-on:page="getClients")

View File

@ -1,9 +1,11 @@
<template lang="pug">
.pgn
span.pagenum Page {{ page }} of {{ pages }}
.button(v-if='page > 1' v-on:click='$emit("page", page - 1)') Previous
.button(v-for='n in pages' v-on:click='$emit("page", n)' v-bind:class='{active: n == page}') {{ n }}
.button(v-if='page < pages' v-on:click='$emit("page", page + 1)') Next
ul.pagination.mt-4
li.page-item(v-bind:class='{disabled: page == 1}')
a.page-link(href="#" v-on:click='$emit("page", page - 1)') Previous
li.page-item(v-for='n in pages' v-bind:class='{active: n == page}')
a.page-link(href="#" v-on:click='$emit("page", n)') {{ n }}
li.page-item(v-bind:class='{disabled: page == pages}')
a.page-link(v-on:click='$emit("page", page + 1)') Next
</template>
<script type="text/javascript">

View File

@ -1,9 +1,14 @@
<template lang="pug">
.user.list-item
.user.list-item.locked-account(v-if='locked')
h1 Locked account
.description This account has been locked
.id User ID: {{ id }}
.row.user.list-item(v-else='v-else')
.col-4
.avatar
img(v-if='avatar_file', v-bind:src="'/usercontent/images/' + avatar_file")
img(v-else='v-else', src='/static/image/avatar.png')
.info
.col.info
.stamps
.stamp(title="Used an external login" v-if="!password")
i.fa.fa-fw.fa-sign-out
@ -35,21 +40,25 @@
.action(v-on:click='resetPassword')
i.fa.fa-fw.fa-envelope
|&nbsp;Password Email
.separator
.action(v-on:click='$parent.$emit("lock", id)' v-if="id != 1 && nw_privilege < 2")
i.fa.fa-fw.fa-lock
|&nbsp;Lock Account
.display_name {{ display_name }}
.name {{ id }} - {{ username }} ({{ uuid }})
.section
span.key Email
span.value {{ email }}
.section
span.key Privilege
span.value {{ nw_privilege }}
.section
span.key Last IP
span.value {{ ip_address }}
.section
span.key Registered
span.value {{ new Date(created_at).toString() }}
.row
.col-4 Email
.col {{ email }}
.row
.col-4 Privilege
.col {{ nw_privilege }}
.row
.col-4 Last IP
.col {{ ip_address }}
.row
.col-4 Registered
.col {{ new Date(created_at).toString() }}
</template>
<script type="text/javascript">
@ -57,7 +66,7 @@
const csrfToken = document.querySelector('meta[name="csrf-token"]').content
export default {
props: ['avatar_file', 'activated', 'display_name', 'id', 'username', 'uuid', 'email', 'nw_privilege', 'created_at', 'password', 'bannable', 'ip_address', 'totp_enabled'],
props: ['avatar_file', 'activated', 'display_name', 'id', 'username', 'uuid', 'email', 'nw_privilege', 'created_at', 'password', 'bannable', 'ip_address', 'totp_enabled', 'locked'],
directives: {
onClickaway: onClickaway,
},

View File

@ -3,12 +3,13 @@
h3 Registered Users ({{ pagination.total }})
pagination(:page="pagination.page" :pages="pagination.pages" v-on:page="getUsers")
.list.users
.searchbox
input(v-model="search" placeholder="Begin typing a name or email address..")
.message.error(v-if="error") {{ error }}
.form-group
input.form-control(v-model="search" placeholder="Begin typing a name or email address..")
.alert.alert-danger(v-if="error") {{ error }}
user(v-else v-for='user in users' v-bind="user" :key="user.id")
ban-modal(:show='banning' @close='banning = 0' :id='banning')
user-modal(:show='editing' @close='editing = 0' :id='editing')
user-lock-modal(:show='locking' @close='locking = 0' :id='locking')
</template>
<script>
@ -16,6 +17,7 @@
import User from './User.vue'
import BanModal from './BanModal.vue'
import UserModal from './UserModal.vue'
import UserLockModal from './UserLockModal.vue'
import qs from 'querystring'
const csrfToken = document.querySelector('meta[name="csrf-token"]').content
@ -33,12 +35,13 @@
users: [],
banning: 0,
editing: 0,
locking: 0,
search: '',
error: ''
}
},
components: {
Pagination, User, BanModal, UserModal
Pagination, User, BanModal, UserModal, UserLockModal
},
methods: {
getUsers: function (page) {
@ -84,6 +87,10 @@
this.editing = id
})
this.$on('lock', function (id) {
this.locking = id
})
this.$root.$on('reload_users', () => {
this.getUsers(this.pagination.page)
})

View File

@ -0,0 +1,50 @@
<template lang="pug">
modal(:show='show', @close='close')
.modal-header
h3 Lock User
.modal-body.aligned-form
.alert.alert-danger(v-if='error') {{ error }}
p Are you sure you want to lock this user?
p This user will not be able to log in and will not be considered a registered user.
p This action cannot be reverted (user records will be overwritten).
.modal-footer.text-right
button.btn.btn-danger(@click='submit')
i.fa.fa-fw.fa-lock
|&nbsp;Yes
button.btn.btn-secondary(@click='close') No
</template>
<script type="text/javascript">
import Modal from './Modal.vue'
const csrfToken = document.querySelector('meta[name="csrf-token"]').content
export default {
props: ['show', 'id'],
data: function () {
return {
error: ''
}
},
components: {
Modal
},
methods: {
close: function () {
this.$emit('close')
this.error = ''
},
submit: function () {
this.$http.post('/admin/api/user/lock', {
user_id: this.id,
csrf: csrfToken
}).then(data => {
this.close()
this.$root.$emit('reload_users')
}).catch(err => {
console.error(err)
if (err.body && err.body.error) this.error = err.body.error
})
}
}
}
</script>

View File

@ -2,27 +2,27 @@
modal(:show='show', @close='close')
.modal-header
h3 Edit User
.modal-body.aligned-form
.message.error(v-if='error') {{ error }}
.cell
.modal-body
.alert.alert-danger(v-if='error') {{ error }}
.form-group
label(for="username") Username
input(type="text" id="username" name="username" v-model="username")
.cell
input.form-control(type="text" id="username" name="username" v-model="username")
.form-group
label(for="display_name") Display Name
input(type="text" id="display_name" name="display_name" v-model="display_name")
.cell
input.form-control(type="text" id="display_name" name="display_name" v-model="display_name")
.form-group
label(for="email") Email
input(type="email" id="email" name="email" v-model="email")
.cell
input.form-control(type="email" id="email" name="email" v-model="email")
.form-group
label(for="privilege") Privilege
input(type="range" min="0" max="5" step="1" id="privilege" name="privilege" v-model="nw_privilege")
span {{ nw_privilege }}
.cell
label(for="activated") Activated
input(type="checkbox" id="activated" name="activated" v-model="activated")
.modal-footer.text-align
button(@click='submit') Done
button(@click='close') Cancel
.form-check
input.form-check-input(type="checkbox" id="activated" name="activated" v-model="activated")
label.form-check-label(for="activated") Activated
.modal-footer.text-right
button.btn.btn-primary(@click='submit') Done
button.btn.btn-secondary(@click='close') Cancel
</template>
<script type="text/javascript">

View File

@ -1,9 +1,10 @@
<template lang="pug">
.root
h1 Welcome to the Admin Panel
.left
.row.mt-4
.col
user-list
.right
.col
ban-list
</template>

View File

@ -2,46 +2,7 @@ body
margin: 0
font-family: sans-serif
nav
display: block
height: 60px
background-color: #00BCD4
border-bottom: 5px solid #0096a9
ul
display: inline-block
margin: 0
padding: 0
list-style-type: none
&.right
float: right
li
height: 60px
display: inline-block
a
font-size: 180%
padding: 16px
line-height: 2
color: #fff
text-decoration: none
text-transform: uppercase
font-weight: bold
&:hover
background-color: #00c9e2
.wrapper
min-height: 100vh
overflow: hidden
.container
overflow: hidden
padding: 10px
background-color: #fff
min-height: 100vh
.left, .right
width: 48%
display: inline-block
.right
float: right
.stamps
.stamps
float: right
.stamp
color: #2196F3
@ -54,10 +15,7 @@ nav
.user
min-height: 160px
.avatar
float: left
.info
margin-left: 170px
.display_name
font-weight: bold
font-size: 120%
@ -80,7 +38,7 @@ nav
background-color: #fff
.application
min-height: 220px
height: auto
#hiddensecret
color: #ff796f
@ -97,6 +55,12 @@ nav
cursor: pointer
display: inline-block
.picture
float: none
.info
margin: 0
.dropdown-wrapper
border-radius: 5px
position: relative
@ -134,14 +98,6 @@ nav
border-bottom: 1px solid #ddd
margin-bottom: 9px
.searchbox
margin: 10px 0
input
font-size: 140%
display: block
width: 100%
.modal-mask
position: fixed
z-index: 9998
@ -181,9 +137,6 @@ nav
margin: 5px
display: inline-block
&.text-align
text-align: center
.fade-enter-active, .fade-leave-active
transition-property: opacity
transition-duration: .25s

View File

@ -1,8 +1,7 @@
extends layout.pug
block body
.container
.content
.container.mt-4
transition(name="fade")
router-view

View File

@ -3,6 +3,7 @@ html
meta(charset="utf8")
meta(name="csrf-token", content=csrf)
block links
link(rel="stylesheet", type="text/css", href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css")
link(rel="stylesheet", type="text/css", href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css")
link(rel="stylesheet", type="text/css", href="/style/main.css")
link(rel="stylesheet", type="text/css", href="/style/admin.css")
@ -16,17 +17,23 @@ html
body
#app
block navigation
nav
ul
li
a.navlogo(href="/") Icy Network
li
router-link(to="/") Home
li
router-link(to="/oauth2") OAuth2
ul.right
li
a(href="/user/manage") #{user.display_name}
nav.navbar.navbar-expand-lg.navbar-light.bg-light.sticky-top
a.navbar-brand(href="/")
img(src="/static/image/icynet-icon.svg" width="30" heigth="30")
|Administration
button.navbar-toggler(type="button" data-toggle="collapse" data-target="#navCollapse" aria-controls="navCollapse" aria-expanded="false" aria-label="Toggle navigation")
span.navbar-toggler-icon
.navbar-collapse.collapse#navCollapse
ul.mr-auto.navbar-nav
router-link(tag="li" class="nav-item" active-class="active" to="/" exact)
a.nav-link Home
router-link(tag="li" class="nav-item" active-class="active" to="/oauth2")
a.nav-link OAuth2
ul.navbar-nav.my-2.my-lg-0
li.nav-item
a.nav-link(href="/user/manage") #{user.display_name}
.wrapper
block body

View File

@ -1,78 +0,0 @@
extends layout.pug
block body
.container
.content
h1 Manage OAuth2 Clients
.button(id="new") New Client
#clientlist
.templates
script(type="x-tmpl-mustache" id="client").
<div class="application" id="client-{{id}}">
<div class="picture">
{{#icon}}
<img src="/usercontent/images/{{icon}}">
{{/icon}}
{{^icon}}
<div class="noicon"><i class="fa fa-fw fa-gears"></i></div>
{{/icon}}
</div>
<div class="info">
<div class="stamps">
{{#verified}}
<div class="verified"><i class="fa fa-fw fa-check"></i></div>
{{/verified}}
</div>
<div class="name">{{title}}</div>
<div class="description">{{description}}</div>
<a class="url" href="{{url}}" target="_blank" rel="nofollow">{{url}}</a>
<div class="scope">Scopes: {{scope}}</div>
<div class="redirect_url">Redirect: {{redirect_url}}</div>
<div class="id">Client ID: {{id}}</div>
<div class="secret">Client Secret: <div id="hiddensecret">{{secret}}</div>
<div class="link" id="showbutton" onclick="$(this).parent().find('#hiddensecret').toggleClass('shown')">Show</div>
</div>
<div class="button edit" data-client="{{id}}">Edit</div>
<div class="button delete" data-client="{{id}}">Delete</div>
<div class="button newsecret" data-client="{{id}}">New Secret</div>
</div>
</div>
script(type="x-tmpl-mustache" id="clientEdit").
<form id="ffsubmit">
<div class="message error"></div>
<input type="hidden" name="id" value="{{id}}">
<input type="hidden" name="csrf" value="#{csrf}">
<label for="title">Title</label>
<input type="text" id="title" name="title" value="{{title}}">
<label for="description">Description</label>
<input type="text" id="description" name="description" value="{{description}}">
<label for="url">URL</label>
<input type="text" id="url" name="url" value="{{url}}">
<label for="scope">Scope</label>
<input type="text" id="scope" name="scope" value="{{scope}}">
<label for="redirect_url">Redirect</label>
<input type="text" id="redirect_url" name="redirect_url" value="{{redirect_url}}">
<label for="verified">Verified</label>
<input type="checkbox" id="verified" name="verified" {{#verified}}checked{{/verified}}>
<input type="submit" value="Edit">
</form>
script(type="x-tmpl-mustache" id="clientNew").
<form id="fnsubmit">
<div class="message error"></div>
<input type="hidden" name="csrf" value="#{csrf}">
<label for="title">Title</label>
<input type="text" id="title" name="title">
<label for="description">Description</label>
<input type="text" id="description" name="description">
<label for="url">URL</label>
<input type="text" id="url" name="url">
<label for="scope">Scope</label>
<input type="text" id="scope" name="scope">
<label for="redirect_url">Redirect</label>
<input type="text" id="redirect_url" name="redirect_url">
<input type="submit" value="Create">
</form>
script(type="x-tmpl-mustache" id="clientRemove").
<p>Are you sure?</p>
<div class="button" onclick="window.Dialog.close()">No</div>
<div class="button" id="fremove">Yes, I'm sure</div>