mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
add YnhConsole component to display history
This commit is contained in:
parent
23aa8bba64
commit
a86e90e49a
4 changed files with 223 additions and 32 deletions
|
@ -3,7 +3,10 @@
|
|||
<!-- HEADER -->
|
||||
<header>
|
||||
<b-navbar>
|
||||
<b-navbar-brand :to="{ name: 'home' }" exact exact-active-class="active">
|
||||
<b-navbar-brand
|
||||
:to="{ name: 'home' }" :disabled="waiting"
|
||||
exact exact-active-class="active"
|
||||
>
|
||||
<img alt="Yunohost logo" src="./assets/logo.png">
|
||||
</b-navbar-brand>
|
||||
|
||||
|
@ -40,8 +43,12 @@
|
|||
<router-view v-else class="static" />
|
||||
</main>
|
||||
</api-wait-overlay>
|
||||
|
||||
<!-- CONSOLE/HISTORY -->
|
||||
<ynh-console @height-changed="consoleHeight = $event" class="mt-auto" />
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer>
|
||||
<footer :style="'padding-bottom: ' + consoleHeight + 'px;'">
|
||||
<nav>
|
||||
<b-nav class="justify-content-center">
|
||||
<b-nav-item href="https://yunohost.org/docs" target="_blank" link-classes="text-secondary">
|
||||
|
@ -79,22 +86,27 @@
|
|||
import { mapGetters } from 'vuex'
|
||||
|
||||
import ApiWaitOverlay from '@/components/ApiWaitOverlay'
|
||||
import YnhConsole from '@/components/YnhConsole'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
|
||||
data: () => ({
|
||||
transitionName: null
|
||||
}),
|
||||
data () {
|
||||
return {
|
||||
transitionName: null,
|
||||
// Value used to add padding to the footer so the opened console never hides content
|
||||
consoleHeight: 0
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapGetters(['connected', 'yunohost', 'transitions'])
|
||||
...mapGetters(['connected', 'yunohost', 'transitions', 'waiting'])
|
||||
},
|
||||
|
||||
watch: {
|
||||
// Set the css class to animate the components transition
|
||||
'$route' (to, from) {
|
||||
if (!this.transitions) return
|
||||
if (!this.transitions || from.name === null) return
|
||||
// Use the breadcrumb array length as a direction indicator
|
||||
const toDepth = to.meta.breadcrumb.length
|
||||
const fromDepth = from.meta.breadcrumb.length
|
||||
|
@ -109,7 +121,8 @@ export default {
|
|||
},
|
||||
|
||||
components: {
|
||||
ApiWaitOverlay
|
||||
ApiWaitOverlay,
|
||||
YnhConsole
|
||||
},
|
||||
|
||||
// This hook is only triggered at page first load
|
||||
|
@ -126,9 +139,19 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
// Global import of Bootstrap and custom styles
|
||||
@import '@/scss/main.scss';
|
||||
</style>
|
||||
|
||||
#app > header {
|
||||
<style lang="scss" scoped>
|
||||
|
||||
::v-deep#app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
header {
|
||||
border-bottom: $thin-border;
|
||||
padding-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
|
@ -146,28 +169,6 @@ export default {
|
|||
li {
|
||||
margin: .2rem 0;
|
||||
}
|
||||
icon {
|
||||
margin-left: .5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#app > footer {
|
||||
padding: 1rem 0;
|
||||
border-top: 1px solid #eee;
|
||||
font-size: 0.875rem;
|
||||
margin-top: 2rem;
|
||||
|
||||
.nav-item {
|
||||
& + .nav-item a::before {
|
||||
content: "•";
|
||||
width: 1rem;
|
||||
display: inline-block;
|
||||
margin-left: -1.15rem;
|
||||
}
|
||||
&:first-child {
|
||||
margin-left: -1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -197,4 +198,33 @@ main {
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#console {
|
||||
// Allows the console to be tabbed before the footer links while remaining visually
|
||||
// the last element of the page
|
||||
order: 3;
|
||||
}
|
||||
|
||||
footer {
|
||||
border-top: 1px solid #eee;
|
||||
font-size: $font-size-sm;
|
||||
margin-top: 2rem;
|
||||
padding-bottom: 3rem;
|
||||
|
||||
.nav {
|
||||
padding: 1rem 0 3rem 0;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
& + .nav-item a::before {
|
||||
content: "•";
|
||||
width: 1rem;
|
||||
display: inline-block;
|
||||
margin-left: -1.15rem;
|
||||
}
|
||||
&:first-child {
|
||||
margin-left: -1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
145
app/src/components/YnhConsole.vue
Normal file
145
app/src/components/YnhConsole.vue
Normal file
|
@ -0,0 +1,145 @@
|
|||
<template>
|
||||
<div id="console">
|
||||
<b-list-group>
|
||||
<!-- HISTORY BAR -->
|
||||
<b-list-group-item class="d-flex align-items-center" :class="{ 'bg-best text-white': open }">
|
||||
<h6 class="m-0">
|
||||
<icon iname="history" /> {{ $t('history.title') }}
|
||||
</h6>
|
||||
|
||||
<div class="ml-auto">
|
||||
<!-- LAST ACTION -->
|
||||
<small v-if="lastAction">
|
||||
<u v-t="'history.last_action'" />
|
||||
{{ lastAction.uri | readableUri }} ({{ $t('history.methods.' + lastAction.method) }})
|
||||
</small>
|
||||
|
||||
<b-button
|
||||
v-b-toggle:collapse
|
||||
class="ml-2 px-1 py-0" size="sm" :variant="open ? 'light' : 'outline-dark'"
|
||||
>
|
||||
<icon iname="chevron-right" /><span class="sr-only">{{ $t('words.collapse') }}</span>
|
||||
</b-button>
|
||||
</div>
|
||||
</b-list-group-item>
|
||||
|
||||
<!-- ACTION LIST -->
|
||||
<b-collapse id="collapse" v-model="open">
|
||||
<b-list-group-item class="p-0" id="history" ref="history">
|
||||
<!-- ACTION -->
|
||||
<b-list-group v-for="(action, i) in history" :key="i" flush>
|
||||
<!-- ACTION DESC -->
|
||||
<b-list-group-item class="sticky-top d-flex align-items-center" variant="dark">
|
||||
<div>
|
||||
<strong>{{ $t('action') }}:</strong>
|
||||
{{ action.uri | readableUri }}
|
||||
<small>({{ $t('history.methods.' + action.method) }})</small>
|
||||
</div>
|
||||
|
||||
<time :datetime="action.date | hour" class="ml-auto">{{ action.date | hour }}</time>
|
||||
</b-list-group-item>
|
||||
|
||||
<!-- ACTION MESSAGE -->
|
||||
<b-list-group-item v-for="({ type, text }, j) in action.messages" :key="j">
|
||||
<icon iname="comment" :class="'text-' + type" /> <span v-html="text" />
|
||||
</b-list-group-item>
|
||||
</b-list-group>
|
||||
</b-list-group-item>
|
||||
</b-collapse>
|
||||
</b-list-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'YnhConsole',
|
||||
|
||||
props: {
|
||||
value: { type: Boolean, default: false },
|
||||
height: { type: [Number, String], default: 30 }
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
open: false
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
async open (value) {
|
||||
// In case it is needed.
|
||||
this.$emit('toggle', value)
|
||||
if (!value) {
|
||||
// Reset footer padding.
|
||||
this.$emit('height-changed', 0)
|
||||
} else {
|
||||
// Wait for DOM update.
|
||||
await this.$nextTick()
|
||||
// Send history's elem height so the footer can update its padding.
|
||||
this.$emit('height-changed', this.$refs.history.clientHeight)
|
||||
// Scroll to the last action.
|
||||
const lastActionItem = document.querySelector('#history > .list-group:last-of-type')
|
||||
if (lastActionItem) {
|
||||
lastActionItem.scrollIntoView()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'lastAction.messages' (a, b) {
|
||||
if (!this.open) return
|
||||
this.$nextTick(() => {
|
||||
document.querySelector('#history > .list-group:last-of-type').scrollIntoView()
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
computed: mapGetters(['history', 'lastAction']),
|
||||
|
||||
filters: {
|
||||
readableUri (uri) {
|
||||
return uri.split('?')[0].replace('/', ' > ')
|
||||
},
|
||||
|
||||
hour (date) {
|
||||
return new Date(date).toLocaleTimeString()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
#console {
|
||||
position: sticky;
|
||||
z-index: 10;
|
||||
bottom: 0;
|
||||
|
||||
margin-left: -1.5rem;
|
||||
width: calc(100% + 3rem);
|
||||
|
||||
@include media-breakpoint-down(xs) {
|
||||
margin-left: -15px;
|
||||
width: calc(100% + 30px);
|
||||
|
||||
& > .list-group {
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#history {
|
||||
overflow-y: auto;
|
||||
max-height: 30vh;
|
||||
}
|
||||
|
||||
#collapse {
|
||||
// disable collapse animation
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
font-size: $font-size-sm;
|
||||
padding: $tooltip-padding-y $tooltip-padding-x;
|
||||
}
|
||||
</style>
|
|
@ -7,6 +7,7 @@
|
|||
"all": "All",
|
||||
"all_apps": "All apps",
|
||||
"api_not_responding": "The YunoHost API is not responding. Maybe 'yunohost-api' is down or got restarted?",
|
||||
"api_waiting": "Waiting for the server's response...",
|
||||
"app_actions": "Actions",
|
||||
"app_actions_label": "Perform actions",
|
||||
"app_change_label": "Change Label",
|
||||
|
@ -166,6 +167,16 @@
|
|||
"groups_and_permissions": "Groups and permissions",
|
||||
"groups_and_permissions_manage": "Manage groups and permissions",
|
||||
"permissions": "Permissions",
|
||||
"history": {
|
||||
"title": "History",
|
||||
"last_action": "Last action:",
|
||||
"action": "Action:",
|
||||
"methods": {
|
||||
"POST": "create/execute",
|
||||
"PUT": "modify",
|
||||
"DELETE": "delete"
|
||||
}
|
||||
},
|
||||
"home": "Home",
|
||||
"hook_adminjs_group_configuration": "System configurations",
|
||||
"hook_conf_cron": "Automatic tasks",
|
||||
|
@ -245,6 +256,7 @@
|
|||
"passwords_dont_match": "Passwords don't match",
|
||||
"passwords_too_short": "Password is too short",
|
||||
"path": "Path",
|
||||
"perform": "Perform",
|
||||
"placeholder": {
|
||||
"username": "johndoe",
|
||||
"firstname": "John",
|
||||
|
@ -294,6 +306,7 @@
|
|||
"restore": "Restore",
|
||||
"restart": "Restart",
|
||||
"run": "Run",
|
||||
"running": "Running",
|
||||
"save": "Save",
|
||||
"search": {
|
||||
"domain": "Search for domains...",
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
// Style overrides happens after dependencies imports
|
||||
|
||||
// Bootstrap overrides
|
||||
html, body {
|
||||
height: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: "Source Sans Pro", "Helvetica Neue", "Fira Sans", Helvetica, Arial, sans-serif;
|
||||
|
@ -104,7 +108,6 @@ body {
|
|||
top: 2px;
|
||||
}
|
||||
|
||||
|
||||
// Fork-awesome overrides
|
||||
.fa-fw {
|
||||
width: 1.25em !important;
|
||||
|
|
Loading…
Reference in a new issue