diff --git a/package-lock.json b/package-lock.json
index 512268b..e4f540a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6148,6 +6148,15 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-2.5.9.tgz",
"integrity": "sha512-9B9XBpCtj8y5eJFrspIcKxIWt+lG9FMdF8qgyOlUeOIvcS4xSAvcARygbzHA6Pi0KWFj4BvxjtWbuPVWRx/wuA=="
},
+ "vue-clickaway": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/vue-clickaway/-/vue-clickaway-2.1.0.tgz",
+ "integrity": "sha1-OS3nZHUfMc0geH6Ps6bjGYJfjU8=",
+ "dev": true,
+ "requires": {
+ "loose-envify": "1.3.1"
+ }
+ },
"vue-hot-reload-api": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.2.4.tgz",
diff --git a/package.json b/package.json
index 341b858..85e55b2 100644
--- a/package.json
+++ b/package.json
@@ -68,6 +68,7 @@
"mustache": "^2.3.0",
"standard": "^10.0.3",
"uglifyjs-webpack-plugin": "^0.4.6",
+ "vue-clickaway": "^2.1.0",
"vue-loader": "^13.5.0",
"vue-resource": "^1.3.4",
"vue-router": "^3.0.1",
diff --git a/src/script/component/Ban.vue b/src/script/component/Ban.vue
new file mode 100644
index 0000000..6c89bc6
--- /dev/null
+++ b/src/script/component/Ban.vue
@@ -0,0 +1,31 @@
+
+ .ban.list-item
+ .stamps
+ .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')
+ b This ban is permanent.
+ .button.remove(@click='$parent.$emit("pardon", id)') Pardon
+
+
+
diff --git a/src/script/component/BanList.vue b/src/script/component/BanList.vue
index 122aa77..d4ce697 100644
--- a/src/script/component/BanList.vue
+++ b/src/script/component/BanList.vue
@@ -2,39 +2,16 @@
#banlist
h3 Bans ({{pagination.total}})
.message.error(v-if='error') {{ error }}
- .entry(v-else='v-else')
- .pgn
- span.pagenum Page {{ pagination.page }} of {{ pagination.pages }}
- .button(v-if='pagination.page > 1', v-on:click='getBans(pagination.page - 1)') Previous
- .button(v-for='n in pagination.pages', v-on:click='getBans(n)', v-bind:class='{active: n == pagination.page}') {{ n }}
- .button(v-if='pagination.page < pagination.pages', v-on:click='getBans(pagination.page + 1)') Next
+ .entry(v-else)
+ pagination(v-bind="pagination" v-on:page="getBans")
.list.bans
- .ban.list-item(v-for='ban in bans')
- .stamps
- .noactive(title='Expired', v-if='ban.expired')
- i.fa.fa-fw.fa-ban
- .info
- .section
- span.key User
- span.value {{ ban.user.display_name }}
- .section
- span.key Admin
- span.value {{ ban.admin.display_name }}
- .section
- span.key Reason
- span.value {{ ban.reason }}
- .section
- span.key Placed
- span.value {{ new Date(ban.created_at).toString() }}
- .section
- span.key Expires
- span.value(v-if='ban.expires_at') {{ new Date(ban.expires_at).toString() }}
- span.value(v-else='v-else')
- b This ban is permanent.
- .button.remove(@click='pardon(ban.id)') Pardon
+ ban(v-for='ban in bans' v-bind="ban")
diff --git a/src/script/component/OAuthClient.vue b/src/script/component/OAuthClient.vue
new file mode 100644
index 0000000..91f316c
--- /dev/null
+++ b/src/script/component/OAuthClient.vue
@@ -0,0 +1,71 @@
+
+ .application.list-item
+ .picture
+ img(v-if="icon" :src="'/usercontent/images/' + icon")
+ .noicon(v-else)
+ i.fa.fa-fw.fa-gears
+ .info
+ .stamps
+ .verified.stamp(v-if="verified")
+ i.fa.fa-fw.fa-check
+
+ .dropdown-wrapper.stamp(@click="dropdown = !dropdown" v-on-clickaway='away')
+ i.fa.fa-fw.fa-ellipsis-v
+ transition(name="modal")
+ .dropdown(v-show="dropdown")
+ .title Actions
+ .action(@click="$parent.$emit('edit', id)")
+ i.fa.fa-fw.fa-pencil
+ | Edit
+ .action(@click="$parent.$emit('delete', id)")
+ i.fa.fa-fw.fa-trash
+ | Delete
+
+ .name {{ title }}
+ .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:
+ #showbutton(@click="secretShown = !secretShown")
+ span(v-show="!secretShown") Click here to reveal secret
+ #hiddensecret(v-show="secretShown") {{ secret }}
+ .section
+ span.key Created
+ span.value {{ new Date(created_at).toString() }}
+ .section
+ span.key Owner
+ span.value {{ user.display_name }}
+
+
+
diff --git a/src/script/component/OAuthClients.vue b/src/script/component/OAuthClients.vue
index 4f0a376..d596e98 100644
--- a/src/script/component/OAuthClients.vue
+++ b/src/script/component/OAuthClients.vue
@@ -1,38 +1,17 @@
#clientlist
button(@click="editing = -1") New Client
- .pgn
- span.pagenum Page {{ pagination.page }} of {{ pagination.pages }}
- .button(v-if='pagination.page > 1', v-on:click='getClients(pagination.page - 1)') Previous
- .button(v-for='n in pagination.pages', v-on:click='getClients(n)', v-bind:class='{active: n == pagination.page}') {{ n }}
- .button(v-if='pagination.page < pagination.pages', v-on:click='getClients(pagination.page + 1)') Next
- .list.client
- .message.error(v-if="error") {{ error }}
- .application.list-item(v-else v-for="client in clients")
- .picture
- img(v-if="client.icon" :src="'/usercontent/images/' + client.icon")
- .noicon(v-else)
- i.fa.fa-fw.fa-gears
- .info
- .stamps
- .verified(v-if="client.verified")
- i.fa.fa-fw.fa-check
- .name {{ client.title }}
- .description {{ client.description }}
- a.url(:href='client.url', target='_blank', rel='nofollow') {{ client.url }}
- .scope Scopes: {{ client.scope }}
- .redirect_url Redirect: {{ client.redirect_url }}
- .id Client ID: {{ client.id }}
- .secret
- | Client Secret:
- #showbutton Hover
- #hiddensecret {{ client.secret }}
- .button.edit(@click="editing = client.id") Edit
- .button.delete(@click="deleteClient(client.id)") Delete
- client-modal(:show="editing != 0" @close='editing = 0', :id='editing')
+ .message.error(v-if="error") {{ error }}
+ .entry(v-else)
+ pagination(v-bind="pagination" v-on:page="getClients")
+ .list.client
+ o-auth-client(v-for="client in clients" v-bind="client")
+ client-modal(:show="editing != 0" @close='editing = 0', :id='editing')
diff --git a/src/script/component/Pagination.vue b/src/script/component/Pagination.vue
new file mode 100644
index 0000000..f80ebf9
--- /dev/null
+++ b/src/script/component/Pagination.vue
@@ -0,0 +1,13 @@
+
+ .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
+
+
+
diff --git a/src/script/component/User.vue b/src/script/component/User.vue
new file mode 100644
index 0000000..11e64cb
--- /dev/null
+++ b/src/script/component/User.vue
@@ -0,0 +1,48 @@
+
+ .user.list-item
+ .avatar
+ img(v-if='avatar_file', v-bind:src="'/usercontent/images/' + avatar_file")
+ img(v-else='v-else', src='/static/image/avatar.png')
+ .info
+ .stamps
+ .noactive.stamp(v-if='activated == false', title='Not activated.')
+ i.fa.fa-fw.fa-envelope
+
+ .dropdown-wrapper.stamp(@click="dropdown = !dropdown" v-on-clickaway='away')
+ i.fa.fa-fw.fa-ellipsis-v
+ transition(name="modal")
+ .dropdown(v-show="dropdown")
+ .title Actions
+ .action(v-if='bannable' v-on:click='$parent.$emit("ban", id)')
+ i.fa.fa-fw.fa-ban
+ | Ban User
+
+ .display_name {{ display_name }}
+ .ame {{ id }} - {{ username }} ({{ uuid }})
+ .email {{ email }}
+ .privilege Privilege: level {{ nw_privilege }}
+ .timestamp {{ new Date(created_at).toString() }}
+ .external(v-if='!password')
+ b Used external login
+
+
+
diff --git a/src/script/component/UserList.vue b/src/script/component/UserList.vue
index fa3da91..7cf54fa 100644
--- a/src/script/component/UserList.vue
+++ b/src/script/component/UserList.vue
@@ -1,34 +1,15 @@
#userlist
h3 Registered Users ({{ pagination.total }})
- .pgn
- span.pagenum Page {{ pagination.page }} of {{ pagination.pages }}
- .button(v-if='pagination.page > 1', v-on:click='getUsers(pagination.page - 1)') Previous
- .button(v-for='n in pagination.pages', v-on:click='getUsers(n)', v-bind:class='{active: n == pagination.page}') {{ n }}
- .button(v-if='pagination.page < pagination.pages', v-on:click='getUsers(pagination.page + 1)') Next
+ pagination(v-bind="pagination" v-on:page="getUsers")
.list.users
- .user.list-item(v-for='user in users')
- .avatar
- img(v-if='user.avatar_file', v-bind:src="'/usercontent/images/' + user.avatar_file")
- img(v-else='v-else', src='/static/image/avatar.png')
- .info
- .stamps
- .noactive(v-if='user.activated == false', title='Not activated.')
- i.fa.fa-fw.fa-envelope
- .display_name {{ user.display_name }}
- .username {{ user.id }} - {{ user.username }} ({{ user.uuid }})
- .email {{ user.email }}
- .privilege Privilege: level {{ user.nw_privilege }}
- .timestamp {{ new Date(user.created_at).toString() }}
- .external(v-if='!user.password')
- b Used external login
- .button.ban(v-if='user.bannable', v-on:click='banning = user.id')
- i.fa.fa-fw.fa-ban
- | Ban User
- ban-modal(:show='banning', @close='banning = 0', :id='banning')
+ user(v-for='user in users' v-bind="user")
+ ban-modal(:show='banning' @close='banning = 0' :id='banning')
diff --git a/src/style/admin.styl b/src/style/admin.styl
index 3acd4ac..ee7ee3d 100644
--- a/src/style/admin.styl
+++ b/src/style/admin.styl
@@ -42,9 +42,13 @@ nav
float: right
.stamps
float: right
+ .stamp
+ padding: 5px
+ font-size: 120%
+ display: inline-block
.user
- min-height: 180px
+ min-height: 160px
.avatar
float: left
.info
@@ -74,24 +78,56 @@ nav
min-height: 200px
#hiddensecret
- display: none
color: #ff796f
background-color: #f1f1f1
- padding: 5px
min-width: 250px
#showbutton
font-style: italic
display: inline-block
cursor: pointer
- &:hover > #hiddensecret
- display: block
.link
color: green
cursor: pointer
display: inline-block
+.dropdown-wrapper
+ padding: 3px 0px
+ border-radius: 5px
+ position: relative
+ cursor: pointer
+
+ .dropdown
+ cursor: default
+ position: absolute
+ top: 25px
+ right: 0
+ background-color: #fff
+ border: 1px solid #ddd
+ border-radius: 5px
+ min-width: 180px
+ font-size: 120%
+
+ .title
+ padding: 5px
+ color: #a5a5a5
+
+ .action
+ transition: background-color .3s linear
+ cursor: pointer
+ padding: 5px
+
+ &:hover
+ background-color: #ececec
+
+ .separator
+ margin: 0 15px
+ margin-top: 10px
+ border-bottom: 1px solid #ddd
+ margin-bottom: 9px
+
+
.modal-mask
position: fixed
z-index: 9998