1
0
Fork 0

chore(webui): Migrate to Quasar 2.x and Vue.js 3.x

This commit is contained in:
Andi Sardina Ramos 2024-02-26 16:02:04 +02:00 committed by GitHub
parent 153765f99f
commit f7edb394f2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
65 changed files with 4374 additions and 7999 deletions

View file

@ -1,16 +1,32 @@
<template>
<q-avatar :color="state" text-color="white">
<q-icon v-if="state === 'positive'" name="eva-checkmark-circle-2" />
<q-icon v-if="state === 'warning'" name="eva-alert-circle" />
<q-icon v-if="state === 'negative'" name="eva-alert-triangle" />
<q-avatar
:color="state"
text-color="white"
>
<q-icon
v-if="state === 'positive'"
name="eva-checkmark-circle-2"
/>
<q-icon
v-if="state === 'warning'"
name="eva-alert-circle"
/>
<q-icon
v-if="state === 'negative'"
name="eva-alert-triangle"
/>
</q-avatar>
</template>
<script>
export default {
import { defineComponent } from 'vue'
export default defineComponent({
name: 'AvatarState',
props: ['state']
}
props: {
state: String
}
})
</script>
<style scoped lang="scss">

View file

@ -1,18 +1,36 @@
<template>
<div class="block-right-text">
<q-avatar :color="value ? 'positive' : 'negative'" text-color="white">
<q-icon v-if="value" name="eva-toggle-right" />
<q-icon v-if="!value" name="eva-toggle-left" />
<q-avatar
:color="value ? 'positive' : 'negative'"
text-color="white"
>
<q-icon
v-if="value"
name="eva-toggle-right"
/>
<q-icon
v-if="!value"
name="eva-toggle-left"
/>
</q-avatar>
<div v-bind:class="['block-right-text-label', `block-right-text-label-${!!value}`]">{{value ? 'True' : 'False'}}</div>
<div :class="['block-right-text-label', `block-right-text-label-${!!value}`]">
{{ value ? 'True' : 'False' }}
</div>
</div>
</template>
<script>
export default {
import { defineComponent } from 'vue'
export default defineComponent({
name: 'BooleanState',
props: ['value']
}
props: {
value: {
type: Boolean,
default: true
}
}
})
</script>
<style scoped lang="scss">

View file

@ -4,7 +4,8 @@
v-for="(chip, index) in list"
:key="index"
:dense="dense"
:class="classNames">
:class="classNames"
>
{{ chip }}
</q-chip>
</div>
@ -12,6 +13,10 @@
<script>
export default {
props: ['dense', 'classNames', 'list']
props: {
dense: Boolean,
classNames: Array[String],
list: Array[Object]
}
}
</script>

View file

@ -1,13 +1,18 @@
<template>
<div class="table-wrapper">
<q-infinite-scroll @load="handleLoadMore" :offset="250" ref="scroller">
<q-infinite-scroll
ref="scroller"
:offset="250"
@load="handleLoadMore"
>
<q-markup-table>
<thead>
<tr class="table-header">
<th
v-for="column in columns"
v-bind:class="`text-${column.align}`"
v-bind:key="column.name">
:key="column.name"
:class="`text-${column.align}`"
>
{{ column.label }}
</th>
</tr>
@ -15,16 +20,28 @@
<tfoot v-if="!data || !data.length">
<tr>
<td colspan="100%">
<q-icon name="warning" style="font-size: 1.5rem"/> No data available
<q-icon
name="warning"
style="font-size: 1.5rem"
/> No data available
</td>
</tr>
</tfoot>
<tbody>
<tr v-for="row in data" :key="row.name" class="cursor-pointer" @click="onRowClick(row)">
<tr
v-for="row in data"
:key="row.name"
class="cursor-pointer"
@click="onRowClick(row)"
>
<template v-for="column in columns">
<td :key="column.name" v-if="getColumn(column.name).component" v-bind:class="`text-${getColumn(column.name).align}`">
<td
v-if="getColumn(column.name).component"
:key="column.name"
:class="`text-${getColumn(column.name).align}`"
>
<component
v-bind:is="getColumn(column.name).component"
:is="getColumn(column.name).component"
v-bind="getColumn(column.name).fieldToProps(row)"
>
<template v-if="getColumn(column.name).content">
@ -33,27 +50,41 @@
</component>
</td>
<td
:key="column.name"
v-if="!getColumn(column.name).component"
v-bind:class="`text-${getColumn(column.name).align}`"
:key="column.name"
:class="`text-${getColumn(column.name).align}`"
v-bind="getColumn(column.name).fieldToProps(row)"
>
<span>
{{getColumn(column.name).content ? getColumn(column.name).content(row) : row[column.name]}}
<span>
{{ getColumn(column.name).content ? getColumn(column.name).content(row) : row[column.name] }}
</span>
</td>
</template>
</tr>
</tbody>
</q-markup-table>
<template v-slot:loading v-if="loading">
<template
v-if="loading"
#loading
>
<div class="row justify-center q-my-md">
<q-spinner-dots color="app-grey" size="40px" />
<q-spinner-dots
color="app-grey"
size="40px"
/>
</div>
</template>
</q-infinite-scroll>
<q-page-scroller position="bottom" :scroll-offset="150" class="back-to-top" v-if="endReached">
<q-btn color="primary" small>
<q-page-scroller
v-if="endReached"
position="bottom"
:scroll-offset="150"
class="back-to-top"
>
<q-btn
color="primary"
small
>
Back to top
</q-btn>
</q-page-scroller>
@ -61,28 +92,40 @@
</template>
<script>
import { defineComponent } from 'vue'
import { QMarkupTable, QInfiniteScroll, QSpinnerDots, QPageScroller } from 'quasar'
export default {
export default defineComponent({
name: 'MainTable',
props: ['data', 'columns', 'loading', 'onLoadMore', 'endReached', 'onRowClick'],
components: {
QMarkupTable,
QInfiniteScroll,
QSpinnerDots,
QPageScroller
},
props: {
data: Object,
columns: Array[Object],
loading: Boolean,
onLoadMore: Function,
endReached: Boolean,
onRowClick: Function
},
methods: {
getColumn (columnName) {
return this.columns.find(c => c.name === columnName) || {}
},
handleLoadMore (index, done) {
this.onLoadMore({ page: index })
.then(() => done())
.catch(() => done(true))
if (!this?.onLoadMore) {
done()
} else {
this.onLoadMore({ page: index })
.then(() => done())
.catch(() => done(true))
}
}
}
}
})
</script>
<style scoped lang="scss">

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
<template>
<q-page>
<slot/>
<slot />
</q-page>
</template>

View file

@ -1,21 +1,40 @@
<template>
<q-card flat bordered v-bind:class="['panel-health-check', {'panel-health-check-dense':isDense}]">
<q-scroll-area :thumb-style="appThumbStyle" style="height:100%;">
<q-card
flat
bordered
:class="['panel-health-check', {'panel-health-check-dense':isDense}]"
>
<q-scroll-area
:thumb-style="appThumbStyle"
style="height:100%;"
>
<q-card-section v-if="data.scheme || data.interval">
<div class="row items-start no-wrap">
<div class="col" v-if="data.scheme">
<div class="text-subtitle2">SCHEME</div>
<div
v-if="data.scheme"
class="col"
>
<div class="text-subtitle2">
SCHEME
</div>
<q-chip
dense
class="app-chip app-chip-options">
class="app-chip app-chip-options"
>
{{ data.scheme }}
</q-chip>
</div>
<div class="col" v-if="data.interval">
<div class="text-subtitle2">INTERVAL</div>
<div
v-if="data.interval"
class="col"
>
<div class="text-subtitle2">
INTERVAL
</div>
<q-chip
dense
class="app-chip app-chip-interval">
class="app-chip app-chip-interval"
>
{{ data.interval }}
</q-chip>
</div>
@ -23,19 +42,31 @@
</q-card-section>
<q-card-section v-if="data.path || data.timeout">
<div class="row items-start no-wrap">
<div class="col" v-if="data.path">
<div class="text-subtitle2">PATH</div>
<div
v-if="data.path"
class="col"
>
<div class="text-subtitle2">
PATH
</div>
<q-chip
dense
class="app-chip app-chip-entry-points">
class="app-chip app-chip-entry-points"
>
{{ data.path }}
</q-chip>
</div>
<div class="col" v-if="data.timeout">
<div class="text-subtitle2">TIMEOUT</div>
<div
v-if="data.timeout"
class="col"
>
<div class="text-subtitle2">
TIMEOUT
</div>
<q-chip
dense
class="app-chip app-chip-interval">
class="app-chip app-chip-interval"
>
{{ data.timeout }}
</q-chip>
</div>
@ -43,19 +74,31 @@
</q-card-section>
<q-card-section v-if="data.port || data.hostname">
<div class="row items-start no-wrap">
<div class="col" v-if="data.port">
<div class="text-subtitle2">PORT</div>
<div
v-if="data.port"
class="col"
>
<div class="text-subtitle2">
PORT
</div>
<q-chip
dense
class="app-chip app-chip-name">
class="app-chip app-chip-name"
>
{{ data.port }}
</q-chip>
</div>
<div class="col" v-if="data.hostname">
<div class="text-subtitle2">HOSTNAME</div>
<div
v-if="data.hostname"
class="col"
>
<div class="text-subtitle2">
HOSTNAME
</div>
<q-chip
dense
class="app-chip app-chip-rule">
class="app-chip app-chip-rule"
>
{{ data.hostname }}
</q-chip>
</div>
@ -64,12 +107,19 @@
<q-card-section v-if="data.headers">
<div class="row items-start">
<div class="col-12">
<div class="text-subtitle2">HEADERS</div>
<div class="text-subtitle2">
HEADERS
</div>
</div>
<div v-for="(header, index) in data.headers" :key="index" class="col-12">
<div
v-for="(header, index) in data.headers"
:key="index"
class="col-12"
>
<q-chip
dense
class="app-chip app-chip-wrap app-chip-service">
class="app-chip app-chip-wrap app-chip-service"
>
{{ index }}: {{ header }}
</q-chip>
</div>
@ -82,15 +132,18 @@
<script>
export default {
name: 'PanelHealthCheck',
props: ['data', 'dense'],
components: {
},
filters: {
},
props: {
data: Object,
dense: Boolean
},
computed: {
isDense () {
return this.dense !== undefined
}
},
filters: {
}
}
</script>

File diff suppressed because it is too large Load diff

View file

@ -1,29 +1,52 @@
<template>
<q-card flat bordered v-bind:class="['panel-services', {'panel-services-dense':isDense}]">
<q-scroll-area :thumb-style="appThumbStyle" style="height:100%;">
<q-card
flat
bordered
:class="['panel-services', {'panel-services-dense':isDense}]"
>
<q-scroll-area
:thumb-style="appThumbStyle"
style="height:100%;"
>
<q-card-section>
<div class="row items-start no-wrap">
<div class="col-6">
<div class="text-subtitle2 text-table">Name</div>
<div class="text-subtitle2 text-table">
Name
</div>
</div>
<div class="col-3">
<div class="text-subtitle2 text-table" style="text-align: right">Percent</div>
<div
class="text-subtitle2 text-table"
style="text-align: right"
>
Percent
</div>
</div>
<div class="col-3">
<div class="text-subtitle2 text-table" style="text-align: right">Provider</div>
<div
class="text-subtitle2 text-table"
style="text-align: right"
>
Provider
</div>
</div>
</div>
</q-card-section>
<q-separator />
<div v-for="(service, index) in data.mirroring.mirrors" :key="index">
<div
v-for="(service, index) in data.mirroring.mirrors"
:key="index"
>
<q-card-section>
<div class="row items-center no-wrap">
<div class="col-6">
<q-chip
dense
class="app-chip app-chip-rule app-chip-overflow">
class="app-chip app-chip-rule app-chip-overflow"
>
{{ service.name }}
<q-tooltip>{{service.name}}</q-tooltip>
<q-tooltip>{{ service.name }}</q-tooltip>
</q-chip>
</div>
<div class="col-3 text-right">
@ -46,8 +69,10 @@
export default {
name: 'PanelMirroringServices',
props: ['data', 'dense'],
components: {},
props: {
data: Object,
dense: Boolean
},
computed: {
isDense () {
return this.dense !== undefined
@ -70,13 +95,13 @@ export default {
return 'providers/plugin.svg'
}
if (name.startsWith('consul-')) {
return `providers/consul.svg`
return 'providers/consul.svg'
}
if (name.startsWith('consulcatalog-')) {
return `providers/consulcatalog.svg`
return 'providers/consulcatalog.svg'
}
if (name.startsWith('nomad-')) {
return `providers/nomad.svg`
return 'providers/nomad.svg'
}
return `providers/${name}.svg`

View file

@ -1,22 +1,37 @@
<template>
<q-card flat bordered v-bind:class="['panel-router-details']">
<q-scroll-area :thumb-style="appThumbStyle" style="height:100%;">
<q-card
flat
bordered
:class="['panel-router-details']"
>
<q-scroll-area
:thumb-style="appThumbStyle"
style="height:100%;"
>
<q-card-section>
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">STATUS</div>
<div class="text-subtitle2">
STATUS
</div>
<div class="block-right-text">
<avatar-state :state="data.status | status "/>
<div v-bind:class="['block-right-text-label', `block-right-text-label-${data.status}`]">{{data.status | statusLabel}}</div>
<avatar-state :state="status(data.status)" />
<div :class="['block-right-text-label', `block-right-text-label-${data.status}`]">
{{ statusLabel(data.status) }}
</div>
</div>
</div>
<div class="col">
<div class="text-subtitle2">PROVIDER</div>
<div class="text-subtitle2">
PROVIDER
</div>
<div class="block-right-text">
<q-avatar class="provider-logo">
<q-icon :name="`img:${getProviderLogoPath}`" />
</q-avatar>
<div class="block-right-text-label">{{data.provider}}</div>
<div class="block-right-text-label">
{{ data.provider }}
</div>
</div>
</div>
</div>
@ -24,10 +39,13 @@
<q-card-section v-if="data.rule">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">RULE</div>
<div class="text-subtitle2">
RULE
</div>
<q-chip
dense
class="app-chip app-chip-wrap app-chip-rule">
class="app-chip app-chip-wrap app-chip-rule"
>
{{ data.rule }}
</q-chip>
</div>
@ -36,10 +54,13 @@
<q-card-section v-if="data.name">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">NAME</div>
<div class="text-subtitle2">
NAME
</div>
<q-chip
dense
class="app-chip app-chip-wrap app-chip-name">
class="app-chip app-chip-wrap app-chip-name"
>
{{ data.name }}
</q-chip>
</div>
@ -48,11 +69,15 @@
<q-card-section v-if="data.using">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">ENTRYPOINTS</div>
<div class="text-subtitle2">
ENTRYPOINTS
</div>
<q-chip
v-for="(entryPoint, index) in data.using" :key="index"
v-for="(entryPoint, index) in data.using"
:key="index"
dense
class="app-chip app-chip-entry-points">
class="app-chip app-chip-entry-points"
>
{{ entryPoint }}
</q-chip>
</div>
@ -61,12 +86,15 @@
<q-card-section v-if="data.service">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">SERVICE</div>
<div class="text-subtitle2">
SERVICE
</div>
<q-chip
dense
clickable
@click.native="$router.push({ path: `/${protocol}/services/${getServiceId()}`})"
class="app-chip app-chip-wrap app-chip-service app-chip-overflow">
class="app-chip app-chip-wrap app-chip-service app-chip-overflow"
@click="$router.push({ path: `/${protocol}/services/${getServiceId()}`})"
>
{{ data.service }}
<q-tooltip>{{ data.service }}</q-tooltip>
</q-chip>
@ -76,10 +104,14 @@
<q-card-section v-if="data.error">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">ERRORS</div>
<div class="text-subtitle2">
ERRORS
</div>
<q-chip
v-for="(errorMsg, index) in data.error" :key="index"
class="app-chip app-chip-error">
v-for="(errorMsg, index) in data.error"
:key="index"
class="app-chip app-chip-error"
>
{{ errorMsg }}
</q-chip>
</div>
@ -90,14 +122,38 @@
</template>
<script>
import AvatarState from './AvatarState'
import { defineComponent } from 'vue'
import AvatarState from './AvatarState.vue'
export default {
export default defineComponent({
name: 'PanelRouterDetails',
props: ['data', 'protocol'],
components: {
AvatarState
},
props: {
data: Object,
protocol: String
},
computed: {
getProviderLogoPath () {
const name = this.data.provider.toLowerCase()
if (name.startsWith('plugin-')) {
return 'providers/plugin.svg'
}
if (name.startsWith('consul-')) {
return 'providers/consul.svg'
}
if (name.startsWith('consulcatalog-')) {
return 'providers/consulcatalog.svg'
}
if (name.startsWith('nomad-')) {
return 'providers/nomad.svg'
}
return `providers/${name}.svg`
}
},
methods: {
getServiceId () {
const words = this.data.service.split('@')
@ -106,9 +162,7 @@ export default {
}
return `${this.data.service}@${this.data.provider}`
}
},
filters: {
},
status (value) {
if (value === 'enabled') {
return 'positive'
@ -127,28 +181,8 @@ export default {
}
return value
}
},
computed: {
getProviderLogoPath () {
const name = this.data.provider.toLowerCase()
if (name.startsWith('plugin-')) {
return 'providers/plugin.svg'
}
if (name.startsWith('consul-')) {
return `providers/consul.svg`
}
if (name.startsWith('consulcatalog-')) {
return `providers/consulcatalog.svg`
}
if (name.startsWith('nomad-')) {
return `providers/nomad.svg`
}
return `providers/${name}.svg`
}
}
}
})
</script>
<style scoped lang="scss">

View file

@ -1,31 +1,59 @@
<template>
<q-card flat bordered v-bind:class="['panel-servers', {'panel-servers-dense':isDense}]">
<q-scroll-area v-if="data.loadBalancer.servers" :thumb-style="appThumbStyle" style="height:100%;">
<q-card
flat
bordered
:class="['panel-servers', {'panel-servers-dense':isDense}]"
>
<q-scroll-area
v-if="data.loadBalancer.servers"
:thumb-style="appThumbStyle"
style="height:100%;"
>
<q-card-section>
<div class="row items-start no-wrap">
<div class="col-3" v-if="showStatus">
<div class="text-subtitle2 text-table">Status</div>
<div
v-if="showStatus"
class="col-3"
>
<div class="text-subtitle2 text-table">
Status
</div>
</div>
<div class="col-9">
<div class="text-subtitle2 text-table">URL</div>
<div class="text-subtitle2 text-table">
URL
</div>
</div>
</div>
</q-card-section>
<q-separator />
<div v-for="(server, index) in data.loadBalancer.servers" :key="index">
<div
v-for="(server, index) in data.loadBalancer.servers"
:key="index"
>
<q-card-section>
<div class="row items-center no-wrap">
<div class="col-3" v-if="showStatus">
<div
v-if="showStatus"
class="col-3"
>
<div class="block-right-text">
<avatar-state v-if="data.serverStatus" :state="data.serverStatus[server.url || server.address] | status "/>
<avatar-state v-if="!data.serverStatus" :state="'DOWN' | status"/>
<avatar-state
v-if="data.serverStatus"
:state="status(data.serverStatus[server.url || server.address])"
/>
<avatar-state
v-if="!data.serverStatus"
:state="status('DOWN')"
/>
</div>
</div>
<div class="col-9">
<q-chip
dense
class="app-chip app-chip-rule">
{{ server.url || server.address}}
class="app-chip app-chip-rule"
>
{{ server.url || server.address }}
</q-chip>
</div>
</div>
@ -33,30 +61,51 @@
<q-separator />
</div>
</q-scroll-area>
<q-card-section v-else style="height: 100%">
<div class="row items-center" style="height: 100%">
<div class="col-12">
<div class="block-empty"></div>
<div class="q-pb-lg block-empty-logo">
<img v-if="$q.dark.isActive" alt="empty" src="~assets/middlewares-empty-dark.svg">
<img v-else alt="empty" src="~assets/middlewares-empty.svg">
</div>
<div class="block-empty-label">There is no<br>Server available</div>
<q-card-section
v-else
style="height: 100%"
>
<div
class="row items-center"
style="height: 100%"
>
<div class="col-12">
<div class="block-empty" />
<div class="q-pb-lg block-empty-logo">
<img
v-if="$q.dark.isActive"
alt="empty"
src="~assets/middlewares-empty-dark.svg"
>
<img
v-else
alt="empty"
src="~assets/middlewares-empty.svg"
>
</div>
<div class="block-empty-label">
There is no<br>Server available
</div>
</div>
</q-card-section>
</div>
</q-card-section>
</q-card>
</template>
<script>
import AvatarState from './AvatarState'
import { defineComponent } from 'vue'
import AvatarState from './AvatarState.vue'
export default {
export default defineComponent({
name: 'PanelServers',
props: ['data', 'dense', 'hasStatus'],
components: {
AvatarState
},
props: {
data: Object,
dense: Boolean,
hasStatus: Boolean
},
computed: {
isDense () {
return this.dense !== undefined
@ -65,7 +114,7 @@ export default {
return this.hasStatus !== undefined
}
},
filters: {
methods: {
status (value) {
if (value === 'UP') {
return 'positive'
@ -73,7 +122,7 @@ export default {
return 'negative'
}
}
}
})
</script>
<style scoped lang="scss">

View file

@ -1,23 +1,40 @@
<template>
<q-card flat bordered v-bind:class="['panel-service-details', {'panel-service-details-dense':isDense}]">
<q-scroll-area :thumb-style="appThumbStyle" style="height:100%;">
<q-card
flat
bordered
:class="['panel-service-details', {'panel-service-details-dense':isDense}]"
>
<q-scroll-area
:thumb-style="appThumbStyle"
style="height:100%;"
>
<q-card-section>
<div class="row items-start no-wrap">
<div class="col" v-if="data.type">
<div class="text-subtitle2">TYPE</div>
<div
v-if="data.type"
class="col"
>
<div class="text-subtitle2">
TYPE
</div>
<q-chip
dense
class="app-chip app-chip-entry-points">
class="app-chip app-chip-entry-points"
>
{{ data.type }}
</q-chip>
</div>
<div class="col">
<div class="text-subtitle2">PROVIDER</div>
<div class="text-subtitle2">
PROVIDER
</div>
<div class="block-right-text">
<q-avatar class="provider-logo">
<q-icon :name="`img:${getProviderLogoPath}`" />
</q-avatar>
<div class="block-right-text-label">{{data.provider}}</div>
<div class="block-right-text-label">
{{ data.provider }}
</div>
</div>
</div>
</div>
@ -25,10 +42,14 @@
<q-card-section>
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">STATUS</div>
<div class="text-subtitle2">
STATUS
</div>
<div class="block-right-text">
<avatar-state :state="data.status | status "/>
<div v-bind:class="['block-right-text-label', `block-right-text-label-${data.status}`]">{{data.status | statusLabel}}</div>
<avatar-state :state="status(data.status)" />
<div :class="['block-right-text-label', `block-right-text-label-${data.status}`]">
{{ statusLabel(data.status) }}
</div>
</div>
</div>
</div>
@ -36,10 +57,13 @@
<q-card-section v-if="data.mirroring">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">Main Service</div>
<div class="text-subtitle2">
Main Service
</div>
<q-chip
dense
class="app-chip app-chip-name app-chip-overflow">
class="app-chip app-chip-name app-chip-overflow"
>
{{ data.mirroring.service }}
<q-tooltip>{{ data.mirroring.service }}</q-tooltip>
</q-chip>
@ -49,8 +73,10 @@
<q-card-section v-if="data.loadBalancer && $route.meta.protocol !== 'tcp'">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">Pass Host Header</div>
<boolean-state :value="data.loadBalancer.passHostHeader"/>
<div class="text-subtitle2">
Pass Host Header
</div>
<boolean-state :value="data.loadBalancer.passHostHeader" />
</div>
</div>
</q-card-section>
@ -58,10 +84,13 @@
<q-card-section v-if="data.loadBalancer && data.loadBalancer.terminationDelay">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">Termination Delay</div>
<div class="text-subtitle2">
Termination Delay
</div>
<q-chip
dense
class="app-chip app-chip-name">
class="app-chip app-chip-name"
>
{{ data.loadBalancer.terminationDelay }} ms
</q-chip>
</div>
@ -71,10 +100,13 @@
<q-card-section v-if="data.loadBalancer && data.loadBalancer.proxyProtocol">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">Proxy Protocol</div>
<div class="text-subtitle2">
Proxy Protocol
</div>
<q-chip
dense
class="app-chip app-chip-name">
class="app-chip app-chip-name"
>
Version {{ data.loadBalancer.proxyProtocol.version }}
</q-chip>
</div>
@ -84,10 +116,13 @@
<q-card-section v-if="data.failover && data.failover.service">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">Main Service</div>
<div class="text-subtitle2">
Main Service
</div>
<q-chip
dense
class="app-chip app-chip-name app-chip-overflow">
class="app-chip app-chip-name app-chip-overflow"
>
{{ data.failover.service }}
<q-tooltip>{{ data.failover.service }}</q-tooltip>
</q-chip>
@ -98,10 +133,13 @@
<q-card-section v-if="data.failover && data.failover.fallback">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">Fallback Service</div>
<div class="text-subtitle2">
Fallback Service
</div>
<q-chip
dense
class="app-chip app-chip-name app-chip-overflow">
class="app-chip app-chip-name app-chip-overflow"
>
{{ data.failover.fallback }}
<q-tooltip>{{ data.failover.fallback }}</q-tooltip>
</q-chip>
@ -110,24 +148,32 @@
</q-card-section>
<q-separator v-if="sticky" />
<StickyServiceDetails v-if="sticky" :sticky="sticky" :dense="dense"/>
<StickyServiceDetails
v-if="sticky"
:sticky="sticky"
:dense="dense"
/>
</q-scroll-area>
</q-card>
</template>
<script>
import AvatarState from './AvatarState'
import BooleanState from './BooleanState'
import StickyServiceDetails from './StickyServiceDetails'
import { defineComponent } from 'vue'
import AvatarState from './AvatarState.vue'
import BooleanState from './BooleanState.vue'
import StickyServiceDetails from './StickyServiceDetails.vue'
export default {
export default defineComponent({
name: 'PanelServiceDetails',
props: ['data', 'dense'],
components: {
BooleanState,
AvatarState,
StickyServiceDetails
},
props: {
data: Object,
dense: Boolean
},
computed: {
isDense () {
return this.dense !== undefined
@ -150,19 +196,19 @@ export default {
return 'providers/plugin.svg'
}
if (name.startsWith('consul-')) {
return `providers/consul.svg`
return 'providers/consul.svg'
}
if (name.startsWith('consulcatalog-')) {
return `providers/consulcatalog.svg`
return 'providers/consulcatalog.svg'
}
if (name.startsWith('nomad-')) {
return `providers/nomad.svg`
return 'providers/nomad.svg'
}
return `providers/${name}.svg`
}
},
filters: {
methods: {
status (value) {
if (value === 'enabled') {
return 'positive'
@ -182,7 +228,7 @@ export default {
return value || 'error'
}
}
}
})
</script>
<style scoped lang="scss">

View file

@ -1,21 +1,34 @@
<template>
<q-card flat bordered v-bind:class="['panel-tls']">
<q-scroll-area v-if="data" :thumb-style="appThumbStyle" style="height:100%;">
<q-card
flat
bordered
:class="['panel-tls']"
>
<q-scroll-area
v-if="data"
:thumb-style="appThumbStyle"
style="height:100%;"
>
<q-card-section v-if="data">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">TLS</div>
<boolean-state :value="!!data"/>
<div class="text-subtitle2">
TLS
</div>
<boolean-state :value="!!data" />
</div>
</div>
</q-card-section>
<q-card-section v-if="data.options">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">OPTIONS</div>
<div class="text-subtitle2">
OPTIONS
</div>
<q-chip
dense
class="app-chip app-chip-options">
class="app-chip app-chip-options"
>
{{ data.options }}
</q-chip>
</div>
@ -24,18 +37,23 @@
<q-card-section v-if="protocol === 'tcp'">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">PASSTHROUGH</div>
<boolean-state :value="data.passthrough"></boolean-state>
<div class="text-subtitle2">
PASSTHROUGH
</div>
<boolean-state :value="data.passthrough" />
</div>
</div>
</q-card-section>
<q-card-section v-if="data.certResolver">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">CERTIFICATE RESOLVER</div>
<div class="text-subtitle2">
CERTIFICATE RESOLVER
</div>
<q-chip
dense
class="app-chip app-chip-service">
class="app-chip app-chip-service"
>
{{ data.certResolver }}
</q-chip>
</div>
@ -44,17 +62,26 @@
<q-card-section v-if="data.domains">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">DOMAINS</div>
<div v-for="(domain, key) in data.domains" :key="key" class="flex">
<div class="text-subtitle2">
DOMAINS
</div>
<div
v-for="(domain, key) in data.domains"
:key="key"
class="flex"
>
<q-chip
dense
class="app-chip app-chip-rule">
class="app-chip app-chip-rule"
>
{{ domain.main }}
</q-chip>
<q-chip
v-for="(domain, key) in domain.sans" :key="key"
v-for="(domain, key) in domain.sans"
:key="key"
dense
class="app-chip app-chip-entry-points">
class="app-chip app-chip-entry-points"
>
{{ domain }}
</q-chip>
</div>
@ -62,15 +89,31 @@
</div>
</q-card-section>
</q-scroll-area>
<q-card-section v-else style="height: 100%">
<div class="row items-center" style="height: 100%">
<q-card-section
v-else
style="height: 100%"
>
<div
class="row items-center"
style="height: 100%"
>
<div class="col-12">
<div class="block-empty"></div>
<div class="block-empty" />
<div class="q-pb-lg block-empty-logo">
<img v-if="$q.dark.isActive" alt="empty" src="~assets/middlewares-empty-dark.svg">
<img v-else alt="empty" src="~assets/middlewares-empty.svg">
<img
v-if="$q.dark.isActive"
alt="empty"
src="~assets/middlewares-empty-dark.svg"
>
<img
v-else
alt="empty"
src="~assets/middlewares-empty.svg"
>
</div>
<div class="block-empty-label">
There is no<br>TLS configured
</div>
<div class="block-empty-label">There is no<br>TLS configured</div>
</div>
</div>
</q-card-section>
@ -78,15 +121,19 @@
</template>
<script>
import BooleanState from './BooleanState'
import { defineComponent } from 'vue'
import BooleanState from './BooleanState.vue'
export default {
export default defineComponent({
name: 'PanelTLS',
components: {
BooleanState
},
props: ['data', 'protocol']
}
props: {
data: Object,
protocol: String
}
})
</script>
<style scoped lang="scss">

View file

@ -1,29 +1,46 @@
<template>
<q-card flat bordered v-bind:class="['panel-services', {'panel-services-dense':isDense}]">
<q-scroll-area :thumb-style="appThumbStyle" style="height:100%;">
<q-card
flat
bordered
:class="['panel-services', {'panel-services-dense':isDense}]"
>
<q-scroll-area
:thumb-style="appThumbStyle"
style="height:100%;"
>
<q-card-section>
<div class="row items-start no-wrap">
<div class="col-7">
<div class="text-subtitle2 text-table">Name</div>
<div class="text-subtitle2 text-table">
Name
</div>
</div>
<div class="col-3">
<div class="text-subtitle2 text-table">Weight</div>
<div class="text-subtitle2 text-table">
Weight
</div>
</div>
<div class="col-4">
<div class="text-subtitle2 text-table">Provider</div>
<div class="text-subtitle2 text-table">
Provider
</div>
</div>
</div>
</q-card-section>
<q-separator />
<div v-for="(service, index) in data.weighted.services" :key="index">
<div
v-for="(service, index) in data.weighted.services"
:key="index"
>
<q-card-section>
<div class="row items-center no-wrap">
<div class="col-7">
<q-chip
dense
class="app-chip app-chip-rule app-chip-overflow">
class="app-chip app-chip-rule app-chip-overflow"
>
{{ service.name }}
<q-tooltip>{{service.name}}</q-tooltip>
<q-tooltip>{{ service.name }}</q-tooltip>
</q-chip>
</div>
<div class="col-3">
@ -43,11 +60,15 @@
</template>
<script>
import { defineComponent } from 'vue'
export default {
export default defineComponent({
name: 'PanelWeightedServices',
props: ['data', 'dense'],
components: {},
props: {
data: Object,
dense: Boolean
},
computed: {
isDense () {
return this.dense !== undefined
@ -70,19 +91,19 @@ export default {
return 'providers/plugin.svg'
}
if (name.startsWith('consul-')) {
return `providers/consul.svg`
return 'providers/consul.svg'
}
if (name.startsWith('consulcatalog-')) {
return `providers/consulcatalog.svg`
return 'providers/consulcatalog.svg'
}
if (name.startsWith('nomad-')) {
return `providers/nomad.svg`
return 'providers/nomad.svg'
}
return `providers/${name}.svg`
}
}
}
})
</script>
<style scoped lang="scss">

View file

@ -5,8 +5,12 @@
</template>
<script>
export default {
props: ['name'],
import { defineComponent } from 'vue'
export default defineComponent({
props: {
name: String
},
computed: {
getLogoPath () {
const name = this.name.toLowerCase()
@ -15,19 +19,19 @@ export default {
return 'providers/plugin.svg'
}
if (name.startsWith('consul-')) {
return `providers/consul.svg`
return 'providers/consul.svg'
}
if (name.startsWith('consulcatalog-')) {
return `providers/consulcatalog.svg`
return 'providers/consulcatalog.svg'
}
if (name.startsWith('nomad-')) {
return `providers/nomad.svg`
return 'providers/nomad.svg'
}
return `providers/${name}.svg`
}
}
}
})
</script>
<style scoped lang="scss">

View file

@ -1,27 +1,34 @@
<template>
<div class="panel">
<div
v-if="isOpen"
class="panel-backdrop"
@click="close"
v-if="isOpen"
></div>
/>
<transition name="slide">
<div v-if="isOpen" class="panel-content">
<slot></slot>
<div
v-if="isOpen"
class="panel-content"
>
<slot />
</div>
</transition>
</div>
</template>
<script>
export default {
props: ['isOpen'],
<script>
import { defineComponent } from 'vue'
export default defineComponent({
props: {
isOpen: Boolean
},
methods: {
close () {
this.$emit('onClose')
}
}
}
})
</script>
<style scoped lang="scss">

View file

@ -1,13 +1,13 @@
<template>
<span
:style="{ height, width: computedWidth }"
v-bind:class="['SkeletonBox']"
:class="['SkeletonBox']"
/>
</template>
<script>
export default {
name: `SkeletonBox`,
name: 'SkeletonBox',
props: {
maxWidth: {
default: 100,
@ -18,7 +18,7 @@ export default {
type: Number
},
height: {
default: `2em`,
default: '2em',
type: String
},
width: {

View file

@ -1,49 +1,66 @@
<template>
<div>
<q-card-section>
<div class="row items-start no-wrap">
<div class="text-subtitle1">Sticky: Cookie </div>
<div>
<q-card-section>
<div class="row items-start no-wrap">
<div class="text-subtitle1">
Sticky: Cookie
</div>
</q-card-section>
<q-card-section>
<div class="row items-start no-wrap">
<div class="col" v-if="sticky.cookie && sticky.cookie.name">
<div class="text-subtitle2">NAME</div>
<q-chip
dense
class="app-chip app-chip-entry-points">
{{ sticky.cookie.name }}
</q-chip>
</div>
</q-card-section>
<q-card-section>
<div class="row items-start no-wrap">
<div
v-if="sticky.cookie && sticky.cookie.name"
class="col"
>
<div class="text-subtitle2">
NAME
</div>
<q-chip
dense
class="app-chip app-chip-entry-points"
>
{{ sticky.cookie.name }}
</q-chip>
</div>
</q-card-section>
<q-card-section>
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">SECURE</div>
<boolean-state :value="sticky.cookie.secure"/>
</div>
</q-card-section>
<q-card-section>
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">
SECURE
</div>
<boolean-state :value="sticky.cookie.secure" />
</div>
<div class="col">
<div class="text-subtitle2">HTTP Only</div>
<boolean-state :value="sticky.cookie.httpOnly"/>
<div class="col">
<div class="text-subtitle2">
HTTP Only
</div>
<boolean-state :value="sticky.cookie.httpOnly" />
</div>
</q-card-section>
</div>
</div>
</q-card-section>
</div>
</template>
<script>
import BooleanState from './BooleanState'
import { defineComponent } from 'vue'
import BooleanState from './BooleanState.vue'
export default {
export default defineComponent({
name: 'StickyServiceDetails',
components: {
BooleanState
},
props: ['sticky', 'dense']
}
props: {
sticky: Object,
dense: Boolean
}
})
</script>
<style scoped lang="scss">
@import "../../css/sass/variables";

View file

@ -1,14 +1,21 @@
<template>
<q-avatar text-color="dark">
<q-icon v-if="isTLS" name="eva-shield" />
<q-icon
v-if="isTLS"
name="eva-shield"
/>
</q-avatar>
</template>
<script>
export default {
import { defineComponent } from 'vue'
export default defineComponent({
name: 'TLSState',
props: ['isTLS']
}
props: {
isTLS: Boolean
}
})
</script>
<style scoped lang="scss">

View file

@ -1,23 +1,57 @@
<template>
<q-toolbar class="row no-wrap items-center">
<q-tabs align="left" inline-label indicator-color="transparent" stretch>
<q-route-tab :to="`/${protocol}/routers`" no-caps :label="`${protocolLabel} Routers`">
<q-badge v-if="routerTotal !== 0" align="middle" :label="routerTotal" class="q-ml-sm"/>
<q-tabs
align="left"
inline-label
indicator-color="transparent"
stretch
>
<q-route-tab
:to="`/${protocol}/routers`"
no-caps
:label="`${protocolLabel} Routers`"
>
<q-badge
v-if="routerTotal !== 0"
align="middle"
:label="routerTotal"
class="q-ml-sm"
/>
</q-route-tab>
<q-route-tab :to="`/${protocol}/services`" no-caps :label="`${protocolLabel} Services`">
<q-badge v-if="servicesTotal !== 0" align="middle" :label="servicesTotal" class="q-ml-sm"/>
<q-route-tab
:to="`/${protocol}/services`"
no-caps
:label="`${protocolLabel} Services`"
>
<q-badge
v-if="servicesTotal !== 0"
align="middle"
:label="servicesTotal"
class="q-ml-sm"
/>
</q-route-tab>
<q-route-tab v-if="protocol !== 'udp'" :to="`/${protocol}/middlewares`" no-caps :label="`${protocolLabel} Middlewares`">
<q-badge v-if="middlewaresTotal !== 0" align="middle" :label="middlewaresTotal" class="q-ml-sm"/>
<q-route-tab
v-if="protocol !== 'udp'"
:to="`/${protocol}/middlewares`"
no-caps
:label="`${protocolLabel} Middlewares`"
>
<q-badge
v-if="middlewaresTotal !== 0"
align="middle"
:label="middlewaresTotal"
class="q-ml-sm"
/>
</q-route-tab>
</q-tabs>
</q-toolbar>
</template>
<script>
import { mapActions, mapGetters } from 'vuex'
import { defineComponent } from 'vue'
import { useStore, mapActions, mapGetters } from 'vuex'
export default {
export default defineComponent({
name: 'ToolBar',
data () {
return {
@ -47,6 +81,16 @@ export default {
return (data && data.middlewares && data.middlewares.total) || 0
}
},
created () {
this.refreshAll()
this.intervalRefresh = setInterval(this.onGetAll, this.intervalRefreshTime)
},
beforeUnmount () {
const $store = useStore()
clearInterval(this.intervalRefresh)
$store.commit('core/getOverviewClear')
},
methods: {
...mapActions('core', { getOverview: 'getOverview' }),
refreshAll () {
@ -64,16 +108,8 @@ export default {
console.log('Error -> toolbar/overview', error)
})
}
},
created () {
this.refreshAll()
this.intervalRefresh = setInterval(this.onGetAll, this.intervalRefreshTime)
},
beforeDestroy () {
clearInterval(this.intervalRefresh)
this.$store.commit('core/getOverviewClear')
}
}
})
</script>
<style scoped lang="scss">

View file

@ -10,15 +10,25 @@
rounded
unelevated
:options="[
{label: 'All Status', value: ''},
{label: 'Success', value: 'enabled'},
{label: 'Warnings', value: 'warning'},
{label: 'Errors', value: 'disabled'}
]"
{label: 'All Status', value: ''},
{label: 'Success', value: 'enabled'},
{label: 'Warnings', value: 'warning'},
{label: 'Errors', value: 'disabled'}
]"
/>
<q-space />
<q-input v-model="getFilter" rounded dense outlined type="search" debounce="500" placeholder="Search" :bg-color="$q.dark.isActive ? undefined : 'white'" class="bar-search">
<template v-slot:append>
<q-input
v-model="getFilter"
rounded
dense
outlined
type="search"
debounce="500"
placeholder="Search"
:bg-color="$q.dark.isActive ? undefined : 'white'"
class="bar-search"
>
<template #append>
<q-icon name="eva-search-outline" />
</template>
</q-input>
@ -26,20 +36,14 @@
</template>
<script>
import { defineComponent } from 'vue'
import Helps from '../../_helpers/Helps'
export default {
export default defineComponent({
name: 'ToolBarTable',
props: ['status', 'filter'],
components: {
},
data () {
return {
}
},
mounted () {
this.routeToState(this.$route)
props: {
status: String,
filter: String
},
computed: {
getStatus: {
@ -66,6 +70,9 @@ export default {
this.routeToState(to)
}
},
mounted () {
this.routeToState(this.$route)
},
methods: {
routeToState (route) {
for (const query in route.query) {
@ -81,11 +88,8 @@ export default {
})
})
}
},
created () {
}
}
})
</script>
<style scoped lang="scss">

View file

@ -1,58 +1,98 @@
<template>
<q-card flat bordered>
<q-card
flat
bordered
>
<q-card-section>
<div class="row items-center no-wrap">
<div class="col">
<div class="text-h6 text-weight-bold">{{getName}}</div>
<div class="text-h6 text-weight-bold">
{{ getName }}
</div>
</div>
<div class="col-auto">
<q-btn :to="getUrl" color="accent" dense flat icon-right="eva-arrow-forward-outline" no-caps label="Explore" size="md" class="text-weight-bold"/>
<q-btn
:to="getUrl"
color="accent"
dense
flat
icon-right="eva-arrow-forward-outline"
no-caps
label="Explore"
size="md"
class="text-weight-bold"
/>
</div>
</div>
</q-card-section>
<q-card-section>
<div class="row items-center q-col-gutter-md">
<div class="col-12 col-sm-6">
<ChartDoughnut
:chartdata="getChartdata()"
:options="options"/>
<Doughnut
:data="getChartdata()"
:options="options"
/>
</div>
<div class="col-12 col-sm-6">
<q-list>
<q-item class="label-state">
<q-item-section avatar>
<avatar-state state="positive"/>
<avatar-state state="positive" />
</q-item-section>
<q-item-section class="label-state-text">
<q-item-label>Success</q-item-label>
<q-item-label caption lines="1">{{getSuccess(true)}}%</q-item-label>
<q-item-label
caption
lines="1"
>
{{ getSuccess(true) }}%
</q-item-label>
</q-item-section>
<q-item-section side class="label-state-side">
{{getSuccess()}}
<q-item-section
side
class="label-state-side"
>
{{ getSuccess() }}
</q-item-section>
</q-item>
<q-item class="label-state">
<q-item-section avatar>
<avatar-state state="warning"/>
<avatar-state state="warning" />
</q-item-section>
<q-item-section class="label-state-text">
<q-item-label>Warnings</q-item-label>
<q-item-label caption lines="1">{{getWarnings(true)}}%</q-item-label>
<q-item-label
caption
lines="1"
>
{{ getWarnings(true) }}%
</q-item-label>
</q-item-section>
<q-item-section side class="label-state-side">
{{getWarnings()}}
<q-item-section
side
class="label-state-side"
>
{{ getWarnings() }}
</q-item-section>
</q-item>
<q-item class="label-state">
<q-item-section avatar>
<avatar-state state="negative"/>
<avatar-state state="negative" />
</q-item-section>
<q-item-section class="label-state-text">
<q-item-label>Errors</q-item-label>
<q-item-label caption lines="1">{{getErrors(true)}}%</q-item-label>
<q-item-label
caption
lines="1"
>
{{ getErrors(true) }}%
</q-item-label>
</q-item-section>
<q-item-section side class="label-state-side">
{{getErrors()}}
<q-item-section
side
class="label-state-side"
>
{{ getErrors() }}
</q-item-section>
</q-item>
</q-list>
@ -63,23 +103,33 @@
</template>
<script>
import { defineComponent } from 'vue'
import Helps from '../../_helpers/Helps'
import ChartDoughnut from '../_commons/ChartDoughnut'
import AvatarState from '../_commons/AvatarState'
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'
import { Doughnut } from 'vue-chartjs'
import AvatarState from '../_commons/AvatarState.vue'
export default {
ChartJS.register(ArcElement, Tooltip, Legend)
export default defineComponent({
name: 'PanelChart',
props: ['name', 'data', 'type'],
components: {
ChartDoughnut,
Doughnut,
AvatarState
},
props: {
name: String,
data: Object,
type: String
},
data () {
return {
loading: true,
options: {
legend: {
display: false
plugins: {
legend: {
display: false
}
},
animation: {
duration: 1000
@ -171,7 +221,7 @@ export default {
}
}
}
}
})
</script>
<style scoped lang="scss">

View file

@ -1,26 +1,40 @@
<template>
<q-card flat bordered v-bind:class="['panel-entry', {'panel-entry-detail':type === 'detail'}, {'panel-entry-focus':focus}, {'panel-entry-ex-size':exSize}]">
<q-card
flat
bordered
:class="['panel-entry', {'panel-entry-detail':type === 'detail'}, {'panel-entry-focus':focus}, {'panel-entry-ex-size':exSize}]"
>
<q-card-section>
<div class="row items-center no-wrap">
<div class="col">
<div class="text-subtitle2">{{name}}</div>
<div class="text-subtitle2">
{{ name }}
</div>
</div>
</div>
</q-card-section>
<q-card-section>
<div class="text-h3 text-center text-weight-bold ellipsis">
<span>{{address}}</span>
<q-tooltip>{{address}}</q-tooltip>
<span>{{ address }}</span>
<q-tooltip>{{ address }}</q-tooltip>
</div>
</q-card-section>
</q-card>
</template>
<script>
export default {
import { defineComponent } from 'vue'
export default defineComponent({
name: 'PanelEntry',
props: ['address', 'name', 'type', 'focus', 'exSize']
}
props: {
address: String,
name: String,
type: String,
focus: Boolean,
exSize: Number
}
})
</script>
<style scoped lang="scss">

View file

@ -1,17 +1,24 @@
<template>
<q-card flat bordered v-bind:class="['panel-feature']">
<q-card
flat
bordered
:class="['panel-feature']"
>
<q-card-section>
<div class="row items-center no-wrap">
<div class="col">
<div class="text-subtitle2">{{featureKey}}</div>
<div class="text-subtitle2">
{{ featureKey }}
</div>
</div>
</div>
</q-card-section>
<q-card-section>
<div class="text-h3 text-center text-weight-bold">
<q-chip
v-bind:class="['feature-chip', {'feature-chip-string':isString}, {'feature-chip-boolean':isBoolean}, {'feature-chip-boolean-true':isTrue}]">
{{getVal}}
:class="['feature-chip', {'feature-chip-string':isString}, {'feature-chip-boolean':isBoolean}, {'feature-chip-boolean-true':isTrue}]"
>
{{ getVal }}
</q-chip>
</div>
</q-card-section>

View file

@ -1,5 +1,8 @@
<template>
<q-card flat bordered>
<q-card
flat
bordered
>
<q-card-section>
<div class="row items-center no-wrap">
<div class="col text-center">
@ -11,16 +14,20 @@
</q-card-section>
<q-card-section>
<div class="text-h6 text-center text-weight-bold">
{{getName}}
{{ getName }}
</div>
</q-card-section>
</q-card>
</template>
<script>
export default {
import { defineComponent } from 'vue'
export default defineComponent({
name: 'PanelProvider',
props: ['name'],
props: {
name: String
},
computed: {
getName () {
return this.name
@ -32,19 +39,19 @@ export default {
return 'providers/plugin.svg'
}
if (name.startsWith('consul-')) {
return `providers/consul.svg`
return 'providers/consul.svg'
}
if (name.startsWith('consulcatalog-')) {
return `providers/consulcatalog.svg`
return 'providers/consulcatalog.svg'
}
if (name.startsWith('nomad-')) {
return `providers/nomad.svg`
return 'providers/nomad.svg'
}
return `providers/${name}.svg`
}
}
}
})
</script>
<style scoped lang="scss">