1
0
Fork 0

feat: Traefik Pilot WebUI

This commit is contained in:
Matthieu Hostache 2020-05-19 09:12:53 +02:00 committed by Traefiker Bot
parent 4a31544024
commit 58bf1a2ca5
16 changed files with 564 additions and 50 deletions

View file

@ -0,0 +1,20 @@
<template>
<q-btn color="accent" class="btn" @click="$emit('click', $event)" :bind="$attrs">{{label}}</q-btn>
</template>
<script>
export default {
name: 'ConnectButton',
props: ['label']
}
</script>
<style>
.btn {
font-family: 'Nunito', 'Roboto', sans-serif;
font-weight: bold;
font-size: 14px;
text-transform: inherit;
}
</style>

View file

@ -0,0 +1,70 @@
<template>
<div class="iframe-wrapper" v-if="isOnline">
<iframe
v-if="renderIrame"
id="platform-auth-state"
:src="AuthStateIFrameUrl"
@load="onAuthStateIFrameLoad"
height="64px"
frameBorder="0"
/>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import iFrameResize from 'iframe-resizer/js/iframeResizer'
import '../../_directives/resize'
export default {
name: 'PlatformPanel',
data () {
return {
renderIrame: true
}
},
computed: {
...mapGetters('platform', { isPlatformOpen: 'isOpen' }),
isOnline () {
return window.navigator.onLine
}
},
methods: {
...mapActions('platform', { openPlatform: 'open' }, { closePlatform: 'close' }),
onAuthStateIFrameLoad () {
iFrameResize({
log: false,
resize: true,
onMessage: ({ iframe, message }) => {
if (typeof message === 'string') {
if (message === 'open:profile') {
this.openPlatform()
}
if (message === 'logout') {
this.closePlatform()
}
}
}
}, '#platform-auth-state')
}
},
created () {
const authRedirectUrl = `${window.location.href.split('?')[0]}?platform=on`
const queryParams = `?authRedirectUrl=${encodeURIComponent(authRedirectUrl)}`
this.AuthStateIFrameUrl = `${this.platformUrl}/partials/auth-state${queryParams}`
},
watch: {
isPlatformOpen (isOpen, wasOpen) {
if (!isOpen && wasOpen) {
this.renderIrame = false
this.$nextTick().then(() => {
this.renderIrame = true
})
}
}
}
}
</script>

View file

@ -0,0 +1,90 @@
<template>
<transition name="slide" v-if="!instanceIsRegistered && isOnline">
<section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-lg">
<div class="platform-notification">
<p>
<q-avatar color="accent" text-color="white" class="icon">
<q-icon name="eva-alert-circle" />
</q-avatar>
This Traefik Instance is not registered in your Traefik Pilot account yet.
</p>
<platform-action-button label="Register Traefik instance" @click="openPlatform" />
</div>
</div>
</section>
</transition>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import PlatformActionButton from './PlatformActionButton'
export default {
name: 'PlatformNotification',
components: { PlatformActionButton },
created () {
this.getInstanceInfos()
},
computed: {
...mapGetters('platform', { isPlatformOpen: 'isOpen' }),
...mapGetters('core', { instanceInfos: 'version' }),
instanceIsRegistered () {
return !!(this.instanceInfos && this.instanceInfos.uuid && this.instanceInfos.uuid.length > 0)
},
isOnline () {
return window.navigator.onLine
}
},
methods: {
...mapActions('platform', { openPlatform: 'open' }),
...mapActions('core', { getInstanceInfos: 'getVersion' })
},
watch: {
isPlatformOpen (newValue, oldValue) {
const isClosed = !newValue && oldValue
if (isClosed) {
this.getInstanceInfos()
}
}
}
}
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";
.platform-notification {
min-height: 100px;
padding: 40px 36px;
display: flex;
justify-content: space-between;
align-items: center;
border-radius: 6px;
color: $accent;
background-color: rgba($accent, 0.1);
font-size: 16px;
}
p {
margin: 0;
}
.icon {
width: 32px;
height: 32px;
margin-right: 20px;
border-radius: 4px;
}
.slide-enter-active,
.slide-leave-active {
transition: transform 0.5s ease;
}
.slide-enter,
.slide-leave-to {
transform: translateX(100%);
transition: all 150ms ease-in 0s;
}
</style>

View file

@ -0,0 +1,106 @@
<template>
<side-panel :isOpen="isPlatformOpen" @onClose="closePlatform()" v-if="isOnline">
<div class="iframe-wrapper">
<iframe
id="platform-iframe"
:src="iFrameUrl"
style="position: relative; height: 100%; width: 100%;"
frameBorder="0"
scrolling="yes"
@load="onIFrameLoad"
/>
</div>
</side-panel>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import iFrameResize from 'iframe-resizer/js/iframeResizer'
import SidePanel from '../_commons/SidePanel'
import Helps from '../../_helpers/Helps'
import '../../_directives/resize'
export default {
name: 'PlatformPanel',
components: {
SidePanel
},
async created () {
this.getInstanceInfos()
},
computed: {
...mapGetters('platform', { isPlatformOpen: 'isOpen' }),
...mapGetters('core', { instanceInfos: 'version' }),
iFrameUrl () {
const instanceInfos = JSON.stringify(this.instanceInfos)
const authRedirectUrl = `${window.location.href.split('?')[0]}?platform=on`
const queryParams = `?authRedirectUrl=${encodeURIComponent(authRedirectUrl)}&instanceInfos=${encodeURIComponent(instanceInfos)}`
return `${this.platformUrl}/instances${queryParams}`
},
isOnline () {
return window.navigator.onLine
}
},
methods: {
...mapActions('platform', { openPlatform: 'open', closePlatform: 'close' }),
...mapActions('core', { getInstanceInfos: 'getVersion' }),
onIFrameLoad () {
iFrameResize({
log: false,
resize: false,
scrolling: true,
onMessage: ({ iframe, message }) => {
if (typeof message === 'string') {
switch (message) {
case 'open:profile':
this.openPlatform()
break
case 'logout':
this.closePlatform()
break
}
} else if (message.type) {
switch (message.type) {
case 'copy-to-clipboard':
navigator.clipboard.writeText(message.value)
break
}
} else if (message.isAuthenticated) {
this.isAuthenticated = message.isAuthenticated
}
}
}, '#platform-iframe')
}
},
watch: {
$route (to, from) {
const wasOpen = from.query && from.query.platform === 'on'
const isOpen = to.query && to.query.platform === 'on'
if (!wasOpen && isOpen) {
this.openPlatform()
}
},
isPlatformOpen (newValue, oldValue) {
if (newValue !== oldValue) {
document.querySelector('body').style.overflow = newValue ? 'hidden' : 'visible'
this.$router.push({
path: this.$route.path,
query: Helps.removeEmptyObjects({
...this.$route.query,
platform: newValue ? 'on' : undefined
})
})
}
}
}
}
</script>
<style scoped>
.iframe-wrapper {
height: 100%;
}
</style>