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 +1,7 @@
/dist /dist
/src-capacitor
/src-cordova
/.quasar
/node_modules
.eslintrc.cjs
/quasar.config.*.temporary.compiled*

View file

@ -2,21 +2,24 @@ module.exports = {
root: true, root: true,
parserOptions: { parserOptions: {
parser: 'babel-eslint', parser: '@babel/eslint-parser',
sourceType: 'module' ecmaVersion: 2021, // Allows for the parsing of modern ECMAScript features
}, },
env: { env: {
node: true,
browser: true, browser: true,
mocha: true mocha: true,
'vue/setup-compiler-macros': true
}, },
extends: [ extends: [
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
'plugin:vue/essential', 'plugin:vue/vue3-essential',
'@vue/standard', 'plugin:vue/vue3-recommended',
'plugin:mocha/recommended' 'plugin:mocha/recommended',
'standard'
], ],
// required to lint *.vue files // required to lint *.vue files
@ -26,10 +29,16 @@ module.exports = {
], ],
globals: { globals: {
'ga': true, // Google Analytics ga: 'readonly', // Google Analytics
'cordova': true, cordova: 'readonly',
'__statics': true, __statics: 'readonly',
'process': true __QUASAR_SSR__: 'readonly',
__QUASAR_SSR_SERVER__: 'readonly',
__QUASAR_SSR_CLIENT__: 'readonly',
__QUASAR_SSR_PWA__: 'readonly',
process: 'readonly',
Capacitor: 'readonly',
chrome: 'readonly'
}, },
// add your custom rules here // add your custom rules here
@ -39,6 +48,8 @@ module.exports = {
// allow paren-less arrow functions // allow paren-less arrow functions
'arrow-parens': 'off', 'arrow-parens': 'off',
'one-var': 'off', 'one-var': 'off',
'no-void': 'off',
'multiline-ternary': 'off',
'import/first': 'off', 'import/first': 'off',
'import/named': 'error', 'import/named': 'error',
@ -49,6 +60,7 @@ module.exports = {
'import/no-unresolved': 'off', 'import/no-unresolved': 'off',
'import/no-extraneous-dependencies': 'off', 'import/no-extraneous-dependencies': 'off',
'prefer-promise-reject-errors': 'off', 'prefer-promise-reject-errors': 'off',
'vue/multi-word-component-names': 'off',
// allow console.log during development only // allow console.log during development only
//'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', //'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
@ -56,3 +68,4 @@ module.exports = {
//'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' //'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
} }
} }

16
webui/babel.config.cjs Normal file
View file

@ -0,0 +1,16 @@
/* eslint-disable */
module.exports = api => {
return {
presets: [
[
'@quasar/babel-preset-app',
api.caller(caller => caller && caller.target === 'node')
? { targets: { node: 'current' } }
: {}
]
]
}
}

View file

@ -1,5 +0,0 @@
module.exports = {
presets: [
'@quasar/babel-preset-app'
]
}

View file

@ -20,7 +20,6 @@
<link rel="apple-touch-icon" sizes="180x180" href="icons/apple-icon-180x180.png"> <link rel="apple-touch-icon" sizes="180x180" href="icons/apple-icon-180x180.png">
</head> </head>
<body> <body>
<!-- DO NOT touch the following DIV --> <!-- quasar:entry-point -->
<div id="q-app"></div>
</body> </body>
</html> </html>

39
webui/jsconfig.json Normal file
View file

@ -0,0 +1,39 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"src/*": [
"src/*"
],
"app/*": [
"*"
],
"components/*": [
"src/components/*"
],
"layouts/*": [
"src/layouts/*"
],
"pages/*": [
"src/pages/*"
],
"assets/*": [
"src/assets/*"
],
"boot/*": [
"src/boot/*"
],
"stores/*": [
"src/stores/*"
],
"vue$": [
"node_modules/vue/dist/vue.runtime.esm-bundler.js"
]
}
},
"exclude": [
"dist",
".quasar",
"node_modules"
]
}

View file

@ -16,42 +16,45 @@
"build:nc": "yarn build" "build:nc": "yarn build"
}, },
"dependencies": { "dependencies": {
"@quasar/extras": "^1.11.2", "@quasar/extras": "^1.16.9",
"axios": "^0.21.1", "axios": "^1.6.7",
"bowser": "^2.5.2", "bowser": "^2.11.0",
"chart.js": "^2.8.0", "chart.js": "^4.4.1",
"dot-prop": "^5.2.0",
"core-js": "^3.35.1", "core-js": "^3.35.1",
"iframe-resizer": "^4.2.11", "dot-prop": "^8.0.2",
"iframe-resizer": "^4.3.9",
"lodash.isequal": "4.5.0", "lodash.isequal": "4.5.0",
"moment": "^2.24.0", "moment": "^2.30.1",
"quasar": "^1.22.10", "quasar": "^2.14.3",
"query-string": "^6.13.1", "query-string": "^8.1.0",
"vh-check": "^2.0.5", "vh-check": "^2.0.5",
"vue-chartjs": "^3.4.2", "vue": "^3.0.0",
"vuex-map-fields": "^1.3.4" "vue-chartjs": "^5.3.0",
"vue-router": "^4.0.12",
"vuex": "^4.1.0",
"vuex-map-fields": "^1.4.1"
}, },
"devDependencies": { "devDependencies": {
"@quasar/app": "^2.4.3", "@babel/core": "^7.23.9",
"@vue/eslint-config-standard": "^4.0.0", "@babel/eslint-parser": "^7.23.10",
"@vue/test-utils": "^1.0.0-beta.29", "@quasar/app-vite": "^1.4.3",
"babel-eslint": "^10.0.1", "@quasar/babel-preset-app": "^2.0.2",
"chai": "4.2.0", "@vue/test-utils": "^2.4.4",
"eslint": "^5.10.0", "autoprefixer": "^10.4.2",
"eslint-loader": "^2.1.1", "chai": "5.0.3",
"eslint-plugin-prettier": "3.1.1", "eslint": "^8.11.0",
"eslint-plugin-mocha": "6.2.1", "eslint-config-standard": "^17.0.0",
"eslint-plugin-vue": "^5.0.0", "eslint-plugin-import": "^2.19.1",
"mocha": "^6.2.2", "eslint-plugin-mocha": "^10.2.0",
"mocha-webpack": "^2.0.0-beta.0", "eslint-plugin-n": "^16.6.2",
"prettier": "1.19.1" "eslint-plugin-promise": "^6.0.0",
"eslint-plugin-vue": "^9.0.0",
"mocha": "^10.2.0",
"postcss": "^8.4.14"
}, },
"engines": { "engines": {
"node": ">= 8.9.0", "node": "^20 || ^18 || ^16",
"npm": ">= 5.6.0", "npm": ">= 6.13.4",
"yarn": ">= 1.6.0" "yarn": ">= 1.21.1"
}, }
"browserslist": [
"last 1 version, not dead, ie >= 11"
]
} }

27
webui/postcss.config.cjs Normal file
View file

@ -0,0 +1,27 @@
/* eslint-disable */
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
plugins: [
// https://github.com/postcss/autoprefixer
require('autoprefixer')({
overrideBrowserslist: [
'last 4 Chrome versions',
'last 4 Firefox versions',
'last 4 Edge versions',
'last 4 Safari versions',
'last 4 Android versions',
'last 4 ChromeAndroid versions',
'last 4 FirefoxAndroid versions',
'last 4 iOS versions'
]
})
// https://github.com/elchininet/postcss-rtlcss
// If you want to support RTL css, then
// 1. yarn/npm install postcss-rtlcss
// 2. optionally set quasar.config.js > framework > lang to an RTL language
// 3. uncomment the following line:
// require('postcss-rtlcss')
]
}

View file

@ -1,12 +1,18 @@
// Configuration for your app // Configuration for your app
// https://quasar.dev/quasar-cli/quasar-conf-js // https://quasar.dev/quasar-cli/quasar-conf-js
module.exports = function (ctx) { const { configure } = require('quasar/wrappers')
module.exports = configure(function (ctx) {
return { return {
eslint: {
warnings: true,
errors: true
},
// app boot file (/src/boot) // app boot file (/src/boot)
// --> boot files are part of "main.js" // --> boot files are part of "main.js"
boot: [ boot: [
'_globals',
'api', 'api',
'_hacks', '_hacks',
'_init' '_init'
@ -114,6 +120,17 @@ module.exports = function (ctx) {
supportIE: false, supportIE: false,
build: { build: {
viteVuePluginOptions: {
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('hub-')
}
}
},
target: {
browser: ['edge88', 'firefox78', 'chrome87', 'safari13.1'],
node: 'node20'
},
publicPath: process.env.APP_PUBLIC_PATH || '', publicPath: process.env.APP_PUBLIC_PATH || '',
env: process.env.APP_ENV === 'development' env: process.env.APP_ENV === 'development'
? { // staging: ? { // staging:
@ -131,22 +148,7 @@ module.exports = function (ctx) {
} }
}, },
scopeHoisting: true, scopeHoisting: true,
// vueRouterMode: 'history', vueRouterMode: 'hash' // available values: 'hash', 'history'
// vueCompiler: true,
// gzip: true,
// analyze: true,
// extractCSS: false,
extendWebpack (cfg) {
cfg.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /node_modules/,
options: {
formatter: require('eslint').CLIEngine.getFormatter('stylish')
}
})
}
}, },
devServer: { devServer: {
@ -166,16 +168,24 @@ module.exports = function (ctx) {
animations: [], animations: [],
ssr: { ssr: {
pwa: false pwa: false,
}, },
pwa: { pwa: {
workboxMode: 'injectManifest', // or 'generateSW'
// workboxPluginMode: 'InjectManifest', // workboxPluginMode: 'InjectManifest',
// workboxOptions: {}, // only for NON InjectManifest // workboxOptions: {}, // only for NON InjectManifest
workboxOptions: { workboxOptions: {
skipWaiting: true, skipWaiting: true,
clientsClaim: true clientsClaim: true
}, },
chainWebpackCustomSW (chain) {
chain.plugin('eslint-webpack-plugin')
.use(ESLintPlugin, [{ extensions: ['js'] }])
},
manifest: { manifest: {
// name: 'Traefik', // name: 'Traefik',
// short_name: 'Traefik', // short_name: 'Traefik',
@ -247,4 +257,4 @@ module.exports = function (ctx) {
} }
} }
} }
} })

View file

@ -13,6 +13,11 @@ export default {
computed: { computed: {
...mapGetters('core', { coreVersion: 'version' }) ...mapGetters('core', { coreVersion: 'version' })
}, },
watch: {
'$q.dark.isActive' (val) {
localStorage.setItem('traefik-dark', val)
}
},
beforeCreate () { beforeCreate () {
// Set vue instance // Set vue instance
APP.vue = () => this.$root APP.vue = () => this.$root
@ -21,11 +26,6 @@ export default {
console.log('Quasar -> ', this.$q.version) console.log('Quasar -> ', this.$q.version)
this.$q.dark.set(localStorage.getItem('traefik-dark') === 'true') this.$q.dark.set(localStorage.getItem('traefik-dark') === 'true')
},
watch: {
'$q.dark.isActive' (val) {
localStorage.setItem('traefik-dark', val)
}
} }
} }
</script> </script>

View file

@ -1,8 +1,16 @@
import Vue from 'vue' import iframeResize from 'iframe-resizer/js/iframeResizer'
import iFrameResize from 'iframe-resizer/js/iframeResizer'
Vue.directive('resize', { const resize = {
bind: function (el, { value = {} }) { mounted (el, binding) {
el.addEventListener('load', () => iFrameResize(value, el)) const options = binding.value || {}
el.addEventListener('load', () => iframeResize(options, el))
},
unmounted (el) {
const resizableEl = el
if (resizableEl.iFrameResizer) {
resizableEl.iFrameResizer.removeListeners()
}
} }
}) }
export default resize

View file

@ -22,7 +22,7 @@ class Errors {
static handleResponse (error) { static handleResponse (error) {
console.log('handleResponse', error, error.response) console.log('handleResponse', error, error.response)
let body = error.response.data const body = error.response.data
if (error.response.status === 401) { if (error.response.status === 401) {
// TODO - actions... // TODO - actions...
} }

View file

@ -1,4 +1,4 @@
import { get } from 'dot-prop' import { getProperty } from 'dot-prop'
class Helps { class Helps {
// Getters // Getters
@ -11,7 +11,7 @@ class Helps {
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
static get (obj, prop, def = undefined) { static get (obj, prop, def = undefined) {
return get(obj, prop, def) return getProperty(obj, prop, def)
} }
static hasIn (obj, prop) { static hasIn (obj, prop) {
@ -39,13 +39,12 @@ class Helps {
} }
static removeEmptyObjects (objects) { static removeEmptyObjects (objects) {
const obj = {} Object.entries(objects)
Object.entries(objects).map(item => { .filter(item => item[1] !== '')
if (item[1] !== '') { .reduce((acc, item) => {
obj[item[0]] = item[1] acc[item[0]] = item[1]
} return acc
}) }, {})
return obj
} }
// Helps -> Numbers // Helps -> Numbers

View file

@ -1,8 +1,8 @@
import { set, get } from 'dot-prop' import { setProperty, getProperty } from 'dot-prop'
export const withPagination = (type, opts = {}) => (state, data) => { export const withPagination = (type, opts = {}) => (state, data) => {
const { isSameContext, statePath } = opts const { isSameContext, statePath } = opts
const currentState = get(state, statePath) const currentState = getProperty(state, statePath)
let newState let newState
@ -13,7 +13,7 @@ export const withPagination = (type, opts = {}) => (state, data) => {
loading: true loading: true
} }
break break
case 'success': case 'success': {
const { body, page } = data const { body, page } = data
newState = { newState = {
...currentState, ...currentState,
@ -28,6 +28,7 @@ export const withPagination = (type, opts = {}) => (state, data) => {
loading: false loading: false
} }
break break
}
case 'failure': case 'failure':
newState = { newState = {
...currentState, ...currentState,
@ -39,6 +40,6 @@ export const withPagination = (type, opts = {}) => (state, data) => {
} }
if (newState) { if (newState) {
set(state, statePath, newState) setProperty(state, statePath, newState)
} }
} }

View file

@ -4,6 +4,11 @@ import Helps from '../_helpers/Helps'
const Boot = { const Boot = {
install (Vue, options) { install (Vue, options) {
Vue.mixin({ Vue.mixin({
filters: {
capFirstLetter (value) {
return Helps.capFirstLetter(value)
}
},
data () { data () {
return { return {
} }
@ -28,14 +33,9 @@ const Boot = {
} }
} }
}, },
methods: {
},
filters: {
capFirstLetter (value) {
return Helps.capFirstLetter(value)
}
},
created () { created () {
},
methods: {
} }
}) })
} }

View file

@ -1,9 +1,9 @@
import { get } from 'dot-prop' import { getProperty } from 'dot-prop'
import { QChip } from 'quasar' import { QChip } from 'quasar'
import Chips from '../components/_commons/Chips' import Chips from '../components/_commons/Chips.vue'
import ProviderIcon from '../components/_commons/ProviderIcon' import ProviderIcon from '../components/_commons/ProviderIcon.vue'
import AvatarState from '../components/_commons/AvatarState' import AvatarState from '../components/_commons/AvatarState.vue'
import TLSState from '../components/_commons/TLSState' import TLSState from '../components/_commons/TLSState.vue'
const allColumns = [ const allColumns = [
{ {
@ -141,7 +141,7 @@ const GetTablePropsMixin = {
path: `/${type.replace('-', '/', 'gi')}/${encodeURIComponent(row.name)}` path: `/${type.replace('-', '/', 'gi')}/${encodeURIComponent(row.name)}`
}), }),
columns: allColumns.filter(c => columns: allColumns.filter(c =>
get(propsByType, `${type}.columns`, []).includes(c.name) getProperty(propsByType, `${type}.columns`, []).includes(c.name)
) )
} }
} }

View file

@ -1,4 +1,4 @@
import { get } from 'dot-prop' import { getProperty } from 'dot-prop'
export default function PaginationMixin (opts = {}) { export default function PaginationMixin (opts = {}) {
const { pollingIntervalTime, rowsPerPage = 10 } = opts const { pollingIntervalTime, rowsPerPage = 10 } = opts
@ -28,7 +28,7 @@ export default function PaginationMixin (opts = {}) {
currentPage = page currentPage = page
currentLimit = limit || rowsPerPage currentLimit = limit || rowsPerPage
const fetchMethod = get(this, opts.fetchMethod) const fetchMethod = getProperty(this, opts.fetchMethod)
return fetchMethod({ return fetchMethod({
...params, ...params,
@ -41,7 +41,7 @@ export default function PaginationMixin (opts = {}) {
}) })
}, },
initFetch (params) { initFetch (params) {
const scrollerRef = get(this.$refs, opts.scrollerRef) const scrollerRef = getProperty(this.$refs, opts.scrollerRef)
if (scrollerRef) { if (scrollerRef) {
scrollerRef.stop() scrollerRef.stop()

View file

@ -1,8 +1,8 @@
import { APP } from '../_helpers/APP' import { APP } from '../_helpers/APP'
import Boot from '../_middleware/Boot' import Boot from '../_middleware/Boot'
export default async ({ app, router, store, Vue }) => { export default async ({ app, router, store }) => {
Vue.use(Boot) app.use(Boot)
APP.root = app APP.root = app
APP.router = router APP.router = router

View file

@ -1,10 +1,14 @@
import { APP } from '../_helpers/APP' import { APP } from '../_helpers/APP'
import errors from '../_helpers/Errors' import errors from '../_helpers/Errors'
import resize from '../_directives/resize'
export default async ({ app, router }) => {
// Directives
app.directive('resize', resize)
export default async ({ Vue }) => {
// Router // Router
// ---------------------------------------------- // ----------------------------------------------
APP.router.beforeEach(async (to, from, next) => { router.beforeEach(async (to, from, next) => {
// Set APP // Set APP
APP.routeTo = to APP.routeTo = to
APP.routeFrom = from APP.routeFrom = from

View file

@ -1,12 +1,16 @@
import { boot } from 'quasar/wrappers'
import axios from 'axios' import axios from 'axios'
import { APP } from '../_helpers/APP' import { APP } from '../_helpers/APP'
// Set config defaults when creating the instance // Set config defaults when creating the instance
const API = axios.create({ const api = axios.create({
baseURL: APP.config.apiUrl baseURL: APP.config.apiUrl
}) })
export default async ({ app, Vue }) => { export default boot(({ app }) => {
Vue.prototype.$api = app.api = APP.api = API app.config.globalProperties.$axios = axios
} app.config.globalProperties.$api = api
APP.api = api
})
export { api }

View file

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

View file

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

View file

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

View file

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

File diff suppressed because one or more lines are too long

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,15 +10,25 @@
rounded rounded
unelevated unelevated
:options="[ :options="[
{label: 'All Status', value: ''}, {label: 'All Status', value: ''},
{label: 'Success', value: 'enabled'}, {label: 'Success', value: 'enabled'},
{label: 'Warnings', value: 'warning'}, {label: 'Warnings', value: 'warning'},
{label: 'Errors', value: 'disabled'} {label: 'Errors', value: 'disabled'}
]" ]"
/> />
<q-space /> <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"> <q-input
<template v-slot:append> 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" /> <q-icon name="eva-search-outline" />
</template> </template>
</q-input> </q-input>
@ -26,20 +36,14 @@
</template> </template>
<script> <script>
import { defineComponent } from 'vue'
import Helps from '../../_helpers/Helps' import Helps from '../../_helpers/Helps'
export default { export default defineComponent({
name: 'ToolBarTable', name: 'ToolBarTable',
props: ['status', 'filter'], props: {
components: { status: String,
filter: String
},
data () {
return {
}
},
mounted () {
this.routeToState(this.$route)
}, },
computed: { computed: {
getStatus: { getStatus: {
@ -66,6 +70,9 @@ export default {
this.routeToState(to) this.routeToState(to)
} }
}, },
mounted () {
this.routeToState(this.$route)
},
methods: { methods: {
routeToState (route) { routeToState (route) {
for (const query in route.query) { for (const query in route.query) {
@ -81,11 +88,8 @@ export default {
}) })
}) })
} }
},
created () {
} }
} })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View file

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

View file

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

View file

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

View file

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

View file

@ -9,14 +9,14 @@
// to match your app's branding. // to match your app's branding.
// Tip: Use the "Theme Builder" on Quasar's documentation website. // Tip: Use the "Theme Builder" on Quasar's documentation website.
$primary = #06102a $primary : #06102a;
$secondary = #2A2A2B $secondary : #2A2A2B;
$accent = #1e54d5 $accent : #1e54d5;
$positive = #00a697 $positive : #00a697;
$negative = #ff0039 $negative : #ff0039;
$info = #31CCEC $info : #31CCEC;
$warning = #db7d11 $warning : #db7d11;
$separator-color = rgba(0, 0, 0, .12) $separator-color : rgba(0, 0, 0, .12);
$separator-dark-color = rgba(255, 255, 255, .28) $separator-dark-color : rgba(255, 255, 255, .28);

View file

@ -1,6 +1,5 @@
<template> <template>
<q-layout view="hHh lpR fFf"> <q-layout view="hHh lpR fFf">
<nav-bar> <nav-bar>
<router-view name="NavBar" /> <router-view name="NavBar" />
</nav-bar> </nav-bar>
@ -8,12 +7,11 @@
<q-page-container> <q-page-container>
<router-view /> <router-view />
</q-page-container> </q-page-container>
</q-layout> </q-layout>
</template> </template>
<script> <script>
import NavBar from '../components/_commons/NavBar' import NavBar from '../components/_commons/NavBar.vue'
export default { export default {
name: 'Default', name: 'Default',

View file

@ -1,12 +1,18 @@
<template> <template>
<div class="fixed-center text-center q-pa-md"> <div class="fixed-center text-center q-pa-md">
<h1 class="q-ma-md"><strong>404</strong></h1> <h1 class="q-ma-md">
<h5 class="q-ma-md">I'm sorry, nothing around here ...</h5> <strong>404</strong>
</h1>
<h5 class="q-ma-md">
I'm sorry, nothing around here ...
</h5>
<q-btn <q-btn
color="secondary" color="secondary"
style="width:200px;" style="width:200px;"
@click="$router.push('/')" @click="$router.push('/')"
>Go back</q-btn> >
Go back
</q-btn>
</div> </div>
</template> </template>

View file

@ -1,82 +1,121 @@
<template> <template>
<page-default> <page-default>
<section
<section v-if="!loading" class="app-section"> v-if="!loading"
class="app-section"
>
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-sm"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-sm">
<div v-if="middlewareByName.item" class="row no-wrap items-center app-title"> <div
<div class="app-title-label" style="font-size: 26px">{{ middlewareByName.item.name }}</div> v-if="middlewareByName.item"
class="row no-wrap items-center app-title"
>
<div
class="app-title-label"
style="font-size: 26px"
>
{{ middlewareByName.item.name }}
</div>
</div> </div>
</div> </div>
</section> </section>
<section class="app-section"> <section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-sm q-pb-lg"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-sm q-pb-lg">
<div v-if="!loading" class="row items-start q-col-gutter-md"> <div
v-if="!loading"
<div v-if="middlewareByName.item" class="col-12 col-md-4 q-mb-lg path-block"> class="row items-start q-col-gutter-md"
>
<div
v-if="middlewareByName.item"
class="col-12 col-md-4 q-mb-lg path-block"
>
<div class="row items-start q-col-gutter-lg"> <div class="row items-start q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
<div class="row items-start q-col-gutter-md"> <div class="row items-start q-col-gutter-md">
<div class="col-12"> <div class="col-12">
<panel-middlewares dense :data="[middlewareByName.item]" /> <panel-middlewares
dense
:data="[middlewareByName.item]"
/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div v-else class="row items-start q-mt-xl"> <div
v-else
class="row items-start q-mt-xl"
>
<div class="col-12"> <div class="col-12">
<p v-for="n in 4" :key="n" class="flex"> <p
<SkeletonBox :min-width="15" :max-width="15" style="margin-right: 2%"/> <SkeletonBox :min-width="50" :max-width="83"/> v-for="n in 4"
:key="n"
class="flex"
>
<SkeletonBox
:min-width="15"
:max-width="15"
style="margin-right: 2%"
/> <SkeletonBox
:min-width="50"
:max-width="83"
/>
</p> </p>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
<section v-if="!loading && allRouters.length" class="app-section"> <section
v-if="!loading && allRouters.length"
class="app-section"
>
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-xl"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-xl">
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<div class="app-title-label">Used by Routers</div> <div class="app-title-label">
Used by Routers
</div>
</div> </div>
<div class="row items-center q-col-gutter-lg"> <div class="row items-center q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
<main-table <main-table
:data="allRouters"
v-bind="getTableProps({ type: `${protocol}-routers` })" v-bind="getTableProps({ type: `${protocol}-routers` })"
v-model:pagination="routersPagination"
:data="allRouters"
:request="()=>{}" :request="()=>{}"
:loading="routersLoading" :loading="routersLoading"
:pagination.sync="routersPagination"
:filter="routersFilter" :filter="routersFilter"
/> />
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</page-default> </page-default>
</template> </template>
<script> <script>
import { defineComponent } from 'vue'
import { mapActions, mapGetters } from 'vuex' import { mapActions, mapGetters } from 'vuex'
import GetTablePropsMixin from '../../_mixins/GetTableProps' import GetTablePropsMixin from '../../_mixins/GetTableProps'
import PageDefault from '../../components/_commons/PageDefault' import PageDefault from '../../components/_commons/PageDefault.vue'
import SkeletonBox from '../../components/_commons/SkeletonBox' import SkeletonBox from '../../components/_commons/SkeletonBox.vue'
import PanelMiddlewares from '../../components/_commons/PanelMiddlewares' import PanelMiddlewares from '../../components/_commons/PanelMiddlewares.vue'
import MainTable from '../../components/_commons/MainTable' import MainTable from '../../components/_commons/MainTable.vue'
export default { export default defineComponent({
name: 'PageMiddlewareDetail', name: 'PageMiddlewareDetail',
props: ['name', 'type'],
mixins: [GetTablePropsMixin],
components: { components: {
PageDefault, PageDefault,
SkeletonBox, SkeletonBox,
PanelMiddlewares, PanelMiddlewares,
MainTable MainTable
}, },
mixins: [GetTablePropsMixin],
props: {
name: String,
type: String
},
data () { data () {
return { return {
loading: true, loading: true,
@ -110,6 +149,14 @@ export default {
return this[`${this.protocol}_getRouterByName`] return this[`${this.protocol}_getRouterByName`]
} }
}, },
created () {
this.refreshAll()
},
beforeUnmount () {
clearInterval(this.timeOutGetAll)
this.$store.commit('http/getMiddlewareByNameClear')
this.$store.commit('tcp/getMiddlewareByNameClear')
},
methods: { methods: {
...mapActions('http', { http_getMiddlewareByName: 'getMiddlewareByName', http_getRouterByName: 'getRouterByName' }), ...mapActions('http', { http_getMiddlewareByName: 'getMiddlewareByName', http_getRouterByName: 'getRouterByName' }),
...mapActions('tcp', { tcp_getMiddlewareByName: 'getMiddlewareByName', tcp_getRouterByName: 'getRouterByName' }), ...mapActions('tcp', { tcp_getMiddlewareByName: 'getMiddlewareByName', tcp_getRouterByName: 'getRouterByName' }),
@ -129,7 +176,7 @@ export default {
// Get routers // Get routers
if (body.usedBy) { if (body.usedBy) {
for (const router in body.usedBy) { for (const router in body.usedBy) {
if (body.usedBy.hasOwnProperty(router)) { if (Object.getOwnPropertyDescriptor(body.usedBy, router)) {
this.getRouterByName(body.usedBy[router]) this.getRouterByName(body.usedBy[router])
.then(body => { .then(body => {
if (body) { if (body) {
@ -152,19 +199,8 @@ export default {
console.log('Error -> middleware/byName', error) console.log('Error -> middleware/byName', error)
}) })
} }
},
created () {
this.refreshAll()
},
mounted () {
},
beforeDestroy () {
clearInterval(this.timeOutGetAll)
this.$store.commit('http/getMiddlewareByNameClear')
this.$store.commit('tcp/getMiddlewareByNameClear')
} }
} })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View file

@ -1,90 +1,158 @@
<template> <template>
<page-default> <page-default>
<section class="app-section"> <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-xl"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div v-if="!loading" class="row items-start"> <div
v-if="!loading"
<div v-if="entryPoints.length" class="col-12 col-md-3 q-mb-lg path-block"> class="row items-start"
>
<div
v-if="entryPoints.length"
class="col-12 col-md-3 q-mb-lg path-block"
>
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-log-in-outline"></q-icon> <q-icon name="eva-log-in-outline" />
<div class="app-title-label">Entrypoints</div> <div class="app-title-label">
Entrypoints
</div>
</div> </div>
<div class="row items-start q-col-gutter-lg"> <div class="row items-start q-col-gutter-lg">
<div class="col-12 col-md-8"> <div class="col-12 col-md-8">
<div class="row items-start q-col-gutter-md"> <div class="row items-start q-col-gutter-md">
<div v-for="(entryPoint, index) in entryPoints" :key="index" class="col-12"> <div
<panel-entry type="detail" exSize="true" :name="entryPoint.name" :address="entryPoint.address"/> v-for="(entryPoint, index) in entryPoints"
:key="index"
class="col-12"
>
<panel-entry
type="detail"
ex-size="true"
:name="entryPoint.name"
:address="entryPoint.address"
/>
</div> </div>
</div> </div>
</div> </div>
<div class="col-12 col-md-4 xs-hide sm-hide"> <div class="col-12 col-md-4 xs-hide sm-hide">
<q-icon name="eva-arrow-forward-outline" class="arrow"></q-icon> <q-icon
name="eva-arrow-forward-outline"
class="arrow"
/>
</div> </div>
</div> </div>
</div> </div>
<div v-if="routerByName.item.name" class="col-12 col-md-3 q-mb-lg path-block"> <div
v-if="routerByName.item.name"
class="col-12 col-md-3 q-mb-lg path-block"
>
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-globe-outline"></q-icon> <q-icon name="eva-globe-outline" />
<div class="app-title-label">{{ routerType }}</div> <div class="app-title-label">
{{ routerType }}
</div>
</div> </div>
<div class="row items-start q-col-gutter-lg"> <div class="row items-start q-col-gutter-lg">
<div class="col-12 col-md-8"> <div class="col-12 col-md-8">
<div class="row items-start q-col-gutter-md"> <div class="row items-start q-col-gutter-md">
<div class="col-12"> <div class="col-12">
<panel-entry focus="true" type="detail" name="router" :address="routerByName.item.name"/> <panel-entry
focus="true"
type="detail"
name="router"
:address="routerByName.item.name"
/>
</div> </div>
</div> </div>
</div> </div>
<div class="col-12 col-md-4 xs-hide sm-hide"> <div class="col-12 col-md-4 xs-hide sm-hide">
<q-icon name="eva-arrow-forward-outline" class="arrow"></q-icon> <q-icon
name="eva-arrow-forward-outline"
class="arrow"
/>
</div> </div>
</div> </div>
</div> </div>
<div v-if="hasMiddlewares" class="col-12 col-md-3 q-mb-lg path-block"> <div
v-if="hasMiddlewares"
class="col-12 col-md-3 q-mb-lg path-block"
>
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-layers"></q-icon> <q-icon name="eva-layers" />
<div class="app-title-label">{{ middlewareType }}</div> <div class="app-title-label">
{{ middlewareType }}
</div>
</div> </div>
<div class="row items-start q-col-gutter-lg"> <div class="row items-start q-col-gutter-lg">
<div class="col-12 col-md-8"> <div class="col-12 col-md-8">
<div class="row items-start q-col-gutter-md"> <div class="row items-start q-col-gutter-md">
<div v-for="(middleware, index) in middlewares" :key="index" class="col-12"> <div
<panel-entry type="detail" name="Middleware" :address="middleware.type"/> v-for="(middleware, index) in middlewares"
:key="index"
class="col-12"
>
<panel-entry
type="detail"
name="Middleware"
:address="middleware.type"
/>
</div> </div>
</div> </div>
</div> </div>
<div class="col-12 col-md-4 xs-hide sm-hide"> <div class="col-12 col-md-4 xs-hide sm-hide">
<q-icon name="eva-arrow-forward-outline" class="arrow"></q-icon> <q-icon
name="eva-arrow-forward-outline"
class="arrow"
/>
</div> </div>
</div> </div>
</div> </div>
<div v-if="routerByName.item.service" <div
class="service col-12 col-md-3 q-mb-lg path-block" v-if="routerByName.item.service"
@click="$router.push({ path: `/${protocol}/services/${getServiceId(routerByName.item)}`})"> class="service col-12 col-md-3 q-mb-lg path-block"
@click="$router.push({ path: `/${protocol}/services/${getServiceId(routerByName.item)}`})"
>
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-flash"></q-icon> <q-icon name="eva-flash" />
<div class="app-title-label">Service</div> <div class="app-title-label">
Service
</div>
</div> </div>
<div class="row items-start q-col-gutter-lg"> <div class="row items-start q-col-gutter-lg">
<div class="col-12 col-md-8"> <div class="col-12 col-md-8">
<div class="row items-start q-col-gutter-md"> <div class="row items-start q-col-gutter-md">
<div class="col-12"> <div class="col-12">
<panel-entry type="detail" name="Service" :address="routerByName.item.service"/> <panel-entry
type="detail"
name="Service"
:address="routerByName.item.service"
/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div v-else class="row items-start"> <div
v-else
class="row items-start"
>
<div class="col-12"> <div class="col-12">
<p v-for="n in 4" :key="n" class="flex"> <p
<SkeletonBox :min-width="15" :max-width="15" style="margin-right: 2%"/> <SkeletonBox :min-width="50" :max-width="83"/> v-for="n in 4"
:key="n"
class="flex"
>
<SkeletonBox
:min-width="15"
:max-width="15"
style="margin-right: 2%"
/> <SkeletonBox
:min-width="50"
:max-width="83"
/>
</p> </p>
</div> </div>
</div> </div>
@ -93,82 +161,117 @@
<section class="app-section"> <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-xl"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div v-if="!loading" class="row items-start q-col-gutter-md"> <div
v-if="!loading"
<div v-if="routerByName.item" class="col-12 col-md-4 q-mb-lg path-block"> class="row items-start q-col-gutter-md"
>
<div
v-if="routerByName.item"
class="col-12 col-md-4 q-mb-lg path-block"
>
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-info"></q-icon> <q-icon name="eva-info" />
<div class="app-title-label">Router Details</div> <div class="app-title-label">
Router Details
</div>
</div> </div>
<div class="row items-start q-col-gutter-lg"> <div class="row items-start q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
<div class="row items-start q-col-gutter-md"> <div class="row items-start q-col-gutter-md">
<div class="col-12"> <div class="col-12">
<panel-router-details :data="routerByName.item" :protocol="protocol"/> <panel-router-details
:data="routerByName.item"
:protocol="protocol"
/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-12 col-md-4 q-mb-lg path-block" v-if="protocol !== 'udp'"> <div
v-if="protocol !== 'udp'"
class="col-12 col-md-4 q-mb-lg path-block"
>
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-shield"></q-icon> <q-icon name="eva-shield" />
<div class="app-title-label">TLS</div> <div class="app-title-label">
TLS
</div>
</div> </div>
<div class="row items-start q-col-gutter-lg"> <div class="row items-start q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
<div class="row items-start q-col-gutter-md"> <div class="row items-start q-col-gutter-md">
<div class="col-12"> <div class="col-12">
<panel-t-l-s :data="routerByName.item.tls" :protocol="protocol"/> <panel-t-l-s
:data="routerByName.item.tls"
:protocol="protocol"
/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-12 col-md-4 q-mb-lg path-block" v-if="protocol !== 'udp'"> <div
v-if="protocol !== 'udp'"
class="col-12 col-md-4 q-mb-lg path-block"
>
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-layers"></q-icon> <q-icon name="eva-layers" />
<div class="app-title-label">Middlewares</div> <div class="app-title-label">
Middlewares
</div>
</div> </div>
<div class="row items-start q-col-gutter-lg"> <div class="row items-start q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
<div class="row items-start q-col-gutter-md"> <div class="row items-start q-col-gutter-md">
<div class="col-12"> <div class="col-12">
<panel-middlewares :data="middlewares"/> <panel-middlewares :data="middlewares" />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div v-else class="row items-start"> <div
v-else
class="row items-start"
>
<div class="col-12"> <div class="col-12">
<p v-for="n in 4" :key="n" class="flex"> <p
<SkeletonBox :min-width="15" :max-width="15" style="margin-right: 2%"/> <SkeletonBox :min-width="50" :max-width="83"/> v-for="n in 4"
:key="n"
class="flex"
>
<SkeletonBox
:min-width="15"
:max-width="15"
style="margin-right: 2%"
/> <SkeletonBox
:min-width="50"
:max-width="83"
/>
</p> </p>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</page-default> </page-default>
</template> </template>
<script> <script>
import { defineComponent } from 'vue'
import { mapActions, mapGetters } from 'vuex' import { mapActions, mapGetters } from 'vuex'
import PageDefault from '../../components/_commons/PageDefault' import PageDefault from '../../components/_commons/PageDefault.vue'
import SkeletonBox from '../../components/_commons/SkeletonBox' import SkeletonBox from '../../components/_commons/SkeletonBox.vue'
import PanelEntry from '../../components/dashboard/PanelEntry' import PanelEntry from '../../components/dashboard/PanelEntry.vue'
import PanelRouterDetails from '../../components/_commons/PanelRouterDetails' import PanelRouterDetails from '../../components/_commons/PanelRouterDetails.vue'
import PanelTLS from '../../components/_commons/PanelTLS' import PanelTLS from '../../components/_commons/PanelTLS.vue'
import PanelMiddlewares from '../../components/_commons/PanelMiddlewares' import PanelMiddlewares from '../../components/_commons/PanelMiddlewares.vue'
export default { export default defineComponent({
name: 'PageRouterDetail', name: 'PageRouterDetail',
props: ['name', 'type'],
components: { components: {
PageDefault, PageDefault,
SkeletonBox, SkeletonBox,
@ -177,6 +280,10 @@ export default {
PanelTLS, PanelTLS,
PanelMiddlewares PanelMiddlewares
}, },
props: {
name: String,
type: String
},
data () { data () {
return { return {
loading: true, loading: true,
@ -214,6 +321,15 @@ export default {
return this[`${this.protocol}_getMiddlewareByName`] return this[`${this.protocol}_getMiddlewareByName`]
} }
}, },
created () {
this.refreshAll()
},
beforeUnmount () {
clearInterval(this.timeOutGetAll)
this.$store.commit('http/getRouterByNameClear')
this.$store.commit('tcp/getRouterByNameClear')
this.$store.commit('udp/getRouterByNameClear')
},
methods: { methods: {
...mapActions('http', { http_getRouterByName: 'getRouterByName', http_getMiddlewareByName: 'getMiddlewareByName' }), ...mapActions('http', { http_getRouterByName: 'getRouterByName', http_getMiddlewareByName: 'getMiddlewareByName' }),
...mapActions('tcp', { tcp_getRouterByName: 'getRouterByName', tcp_getMiddlewareByName: 'getMiddlewareByName' }), ...mapActions('tcp', { tcp_getRouterByName: 'getRouterByName', tcp_getMiddlewareByName: 'getMiddlewareByName' }),
@ -235,7 +351,7 @@ export default {
// Get entryPoints // Get entryPoints
if (body.using) { if (body.using) {
for (const entryPoint in body.using) { for (const entryPoint in body.using) {
if (body.using.hasOwnProperty(entryPoint)) { if (Object.getOwnPropertyDescriptor(body.using, entryPoint)) {
this.getEntrypointsByName(body.using[entryPoint]) this.getEntrypointsByName(body.using[entryPoint])
.then(body => { .then(body => {
if (body) { if (body) {
@ -251,7 +367,7 @@ export default {
// Get middlewares // Get middlewares
if (body.middlewares) { if (body.middlewares) {
for (const middleware in body.middlewares) { for (const middleware in body.middlewares) {
if (body.middlewares.hasOwnProperty(middleware)) { if (Object.getOwnPropertyDescriptor(body.middlewares, middleware)) {
this.getMiddlewareByName(body.middlewares[middleware]) this.getMiddlewareByName(body.middlewares[middleware])
.then(body => { .then(body => {
if (body) { if (body) {
@ -281,20 +397,8 @@ export default {
return `${encodeURIComponent(data.service)}@${data.provider}` return `${encodeURIComponent(data.service)}@${data.provider}`
} }
},
created () {
this.refreshAll()
},
mounted () {
},
beforeDestroy () {
clearInterval(this.timeOutGetAll)
this.$store.commit('http/getRouterByNameClear')
this.$store.commit('tcp/getRouterByNameClear')
this.$store.commit('udp/getRouterByNameClear')
} }
} })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View file

@ -1,148 +1,217 @@
<template> <template>
<page-default> <page-default>
<section
<section v-if="!loading" class="app-section"> v-if="!loading"
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="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-lg">
<div v-if="serviceByName.item" class="row no-wrap items-center app-title"> <div
<div class="app-title-label" style="font-size: 26px">{{ serviceByName.item.name }}</div> v-if="serviceByName.item"
class="row no-wrap items-center app-title"
>
<div
class="app-title-label"
style="font-size: 26px"
>
{{ serviceByName.item.name }}
</div>
</div> </div>
</div> </div>
</section> </section>
<section class="app-section"> <section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-lg"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-lg">
<div v-if="!loading" class="row items-start q-col-gutter-md"> <div
v-if="!loading"
<div v-if="serviceByName.item" class="col-12 col-md-4 q-mb-lg path-block"> class="row items-start q-col-gutter-md"
>
<div
v-if="serviceByName.item"
class="col-12 col-md-4 q-mb-lg path-block"
>
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-info"></q-icon> <q-icon name="eva-info" />
<div class="app-title-label">Service Details</div> <div class="app-title-label">
Service Details
</div>
</div> </div>
<div class="row items-start q-col-gutter-lg"> <div class="row items-start q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
<div class="row items-start q-col-gutter-md"> <div class="row items-start q-col-gutter-md">
<div class="col-12"> <div class="col-12">
<panel-service-details dense :data="serviceByName.item" /> <panel-service-details
dense
:data="serviceByName.item"
/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div v-if="serviceByName.item.loadBalancer && serviceByName.item.loadBalancer.healthCheck" class="col-12 col-md-4 q-mb-lg path-block"> <div
v-if="serviceByName.item.loadBalancer && serviceByName.item.loadBalancer.healthCheck"
class="col-12 col-md-4 q-mb-lg path-block"
>
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-shield"></q-icon> <q-icon name="eva-shield" />
<div class="app-title-label">Health Check</div> <div class="app-title-label">
Health Check
</div>
</div> </div>
<div class="row items-start q-col-gutter-lg"> <div class="row items-start q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
<div class="row items-start q-col-gutter-md"> <div class="row items-start q-col-gutter-md">
<div class="col-12"> <div class="col-12">
<panel-health-check dense :data="serviceByName.item.loadBalancer.healthCheck"/> <panel-health-check
dense
:data="serviceByName.item.loadBalancer.healthCheck"
/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div v-if="serviceByName.item.loadBalancer" class="col-12 col-md-4 q-mb-lg path-block"> <div
v-if="serviceByName.item.loadBalancer"
class="col-12 col-md-4 q-mb-lg path-block"
>
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-globe-outline"></q-icon> <q-icon name="eva-globe-outline" />
<div class="app-title-label">Servers</div> <div class="app-title-label">
Servers
</div>
</div> </div>
<div class="row items-start q-col-gutter-lg"> <div class="row items-start q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
<div class="row items-start q-col-gutter-md"> <div class="row items-start q-col-gutter-md">
<div class="col-12"> <div class="col-12">
<panel-servers dense :data="serviceByName.item" :hasStatus="serviceByName.item.serverStatus"/> <panel-servers
dense
:data="serviceByName.item"
:has-status="serviceByName.item.serverStatus"
/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div v-if="serviceByName.item.weighted && serviceByName.item.weighted.services" class="col-12 col-md-4 q-mb-lg path-block"> <div
v-if="serviceByName.item.weighted && serviceByName.item.weighted.services"
class="col-12 col-md-4 q-mb-lg path-block"
>
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-globe-outline"></q-icon> <q-icon name="eva-globe-outline" />
<div class="app-title-label">Services</div> <div class="app-title-label">
Services
</div>
</div> </div>
<div class="row items-start q-col-gutter-lg"> <div class="row items-start q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
<div class="row items-start q-col-gutter-md"> <div class="row items-start q-col-gutter-md">
<div class="col-12"> <div class="col-12">
<panel-weighted-services dense :data="serviceByName.item"/> <panel-weighted-services
dense
:data="serviceByName.item"
/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div v-if="serviceByName.item.mirroring && serviceByName.item.mirroring.mirrors" class="col-12 col-md-4 q-mb-lg path-block"> <div
v-if="serviceByName.item.mirroring && serviceByName.item.mirroring.mirrors"
class="col-12 col-md-4 q-mb-lg path-block"
>
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-globe-outline"></q-icon> <q-icon name="eva-globe-outline" />
<div class="app-title-label">Mirror Services</div> <div class="app-title-label">
Mirror Services
</div>
</div> </div>
<div class="row items-start q-col-gutter-lg"> <div class="row items-start q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
<div class="row items-start q-col-gutter-md"> <div class="row items-start q-col-gutter-md">
<div class="col-12"> <div class="col-12">
<panel-mirroring-services dense :data="serviceByName.item"/> <panel-mirroring-services
dense
:data="serviceByName.item"
/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div v-else class="row items-start q-mt-xl"> <div
v-else
class="row items-start q-mt-xl"
>
<div class="col-12"> <div class="col-12">
<p v-for="n in 4" :key="n" class="flex"> <p
<SkeletonBox :min-width="15" :max-width="15" style="margin-right: 2%"/> <SkeletonBox :min-width="50" :max-width="83"/> v-for="n in 4"
:key="n"
class="flex"
>
<SkeletonBox
:min-width="15"
:max-width="15"
style="margin-right: 2%"
/> <SkeletonBox
:min-width="50"
:max-width="83"
/>
</p> </p>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
<section v-if="!loading && allRouters.length" class="app-section"> <section
v-if="!loading && allRouters.length"
class="app-section"
>
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-xl"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-xl">
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<div class="app-title-label">Used by Routers</div> <div class="app-title-label">
Used by Routers
</div>
</div> </div>
<div class="row items-center q-col-gutter-lg"> <div class="row items-center q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
<main-table <main-table
:data="allRouters"
v-bind="getTableProps({ type: `${protocol}-routers` })" v-bind="getTableProps({ type: `${protocol}-routers` })"
v-model:pagination="routersPagination"
:data="allRouters"
:request="()=>{}" :request="()=>{}"
:loading="routersLoading" :loading="routersLoading"
:pagination.sync="routersPagination"
:filter="routersFilter" :filter="routersFilter"
/> />
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</page-default> </page-default>
</template> </template>
<script> <script>
import { defineComponent } from 'vue'
import { mapActions, mapGetters } from 'vuex' import { mapActions, mapGetters } from 'vuex'
import GetTablePropsMixin from '../../_mixins/GetTableProps' import GetTablePropsMixin from '../../_mixins/GetTableProps'
import PageDefault from '../../components/_commons/PageDefault' import PageDefault from '../../components/_commons/PageDefault.vue'
import SkeletonBox from '../../components/_commons/SkeletonBox' import SkeletonBox from '../../components/_commons/SkeletonBox.vue'
import PanelServiceDetails from '../../components/_commons/PanelServiceDetails' import PanelServiceDetails from '../../components/_commons/PanelServiceDetails.vue'
import PanelHealthCheck from '../../components/_commons/PanelHealthCheck' import PanelHealthCheck from '../../components/_commons/PanelHealthCheck.vue'
import PanelServers from '../../components/_commons/PanelServers' import PanelServers from '../../components/_commons/PanelServers.vue'
import MainTable from '../../components/_commons/MainTable' import MainTable from '../../components/_commons/MainTable.vue'
import PanelWeightedServices from '../../components/_commons/PanelWeightedServices' import PanelWeightedServices from '../../components/_commons/PanelWeightedServices.vue'
import PanelMirroringServices from '../../components/_commons/PanelMirroringServices' import PanelMirroringServices from '../../components/_commons/PanelMirroringServices.vue'
export default { export default defineComponent({
name: 'PageServiceDetail', name: 'PageServiceDetail',
props: ['name', 'type'],
mixins: [GetTablePropsMixin],
components: { components: {
PanelMirroringServices, PanelMirroringServices,
PanelWeightedServices, PanelWeightedServices,
@ -153,6 +222,11 @@ export default {
PanelServers, PanelServers,
MainTable MainTable
}, },
mixins: [GetTablePropsMixin],
props: {
name: String,
type: String
},
data () { data () {
return { return {
loading: true, loading: true,
@ -187,6 +261,15 @@ export default {
return this[`${this.protocol}_getRouterByName`] return this[`${this.protocol}_getRouterByName`]
} }
}, },
created () {
this.refreshAll()
},
beforeUnmount () {
clearInterval(this.timeOutGetAll)
this.$store.commit('http/getServiceByNameClear')
this.$store.commit('tcp/getServiceByNameClear')
this.$store.commit('udp/getServiceByNameClear')
},
methods: { methods: {
...mapActions('http', { http_getServiceByName: 'getServiceByName', http_getRouterByName: 'getRouterByName' }), ...mapActions('http', { http_getServiceByName: 'getServiceByName', http_getRouterByName: 'getRouterByName' }),
...mapActions('tcp', { tcp_getServiceByName: 'getServiceByName', tcp_getRouterByName: 'getRouterByName' }), ...mapActions('tcp', { tcp_getServiceByName: 'getServiceByName', tcp_getRouterByName: 'getRouterByName' }),
@ -207,7 +290,7 @@ export default {
// Get routers // Get routers
if (body.usedBy) { if (body.usedBy) {
for (const router in body.usedBy) { for (const router in body.usedBy) {
if (body.usedBy.hasOwnProperty(router)) { if (Object.getOwnPropertyDescriptor(body.usedBy, router)) {
this.getRouterByName(body.usedBy[router]) this.getRouterByName(body.usedBy[router])
.then(body => { .then(body => {
if (body) { if (body) {
@ -230,20 +313,8 @@ export default {
console.log('Error -> service/byName', error) console.log('Error -> service/byName', error)
}) })
} }
},
created () {
this.refreshAll()
},
mounted () {
},
beforeDestroy () {
clearInterval(this.timeOutGetAll)
this.$store.commit('http/getServiceByNameClear')
this.$store.commit('tcp/getServiceByNameClear')
this.$store.commit('udp/getServiceByNameClear')
} }
} })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View file

@ -3,20 +3,44 @@
<section class="app-section"> <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="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-lg">
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-log-in-outline"></q-icon> <q-icon name="eva-log-in-outline" />
<div class="app-title-label">Entrypoints</div> <div class="app-title-label">
</div> Entrypoints
<div v-if="!loadingEntryGetAll" class="row items-center q-col-gutter-lg">
<div
v-for="(entryItems, index) in entryAll.items" :key="index"
class="col-12 col-sm-6 col-md-2">
<panel-entry :name="entryItems.name" :address="entryItems.address"/>
</div> </div>
</div> </div>
<div v-else class="row items-center q-col-gutter-lg"> <div
v-if="!loadingEntryGetAll"
class="row items-center q-col-gutter-lg"
>
<div
v-for="(entryItems, index) in entryAll.items"
:key="index"
class="col-12 col-sm-6 col-md-2"
>
<panel-entry
:name="entryItems.name"
:address="entryItems.address"
/>
</div>
</div>
<div
v-else
class="row items-center q-col-gutter-lg"
>
<div class="col-12 col-sm-6 col-md-2"> <div class="col-12 col-sm-6 col-md-2">
<p v-for="n in 3" :key="n" class="flex"> <p
<SkeletonBox :min-width="15" :max-width="15" style="margin-right: 2%"/> <SkeletonBox :min-width="50" :max-width="83"/> v-for="n in 3"
:key="n"
class="flex"
>
<SkeletonBox
:min-width="15"
:max-width="15"
style="margin-right: 2%"
/> <SkeletonBox
:min-width="50"
:max-width="83"
/>
</p> </p>
</div> </div>
</div> </div>
@ -26,20 +50,45 @@
<section class="app-section"> <section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-lg"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-lg">
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-globe-outline"></q-icon> <q-icon name="eva-globe-outline" />
<div class="app-title-label">HTTP</div> <div class="app-title-label">
</div> HTTP
<div v-if="!loadingOverview" class="row items-center q-col-gutter-lg">
<div
v-for="(overviewHTTP, index) in allHTTP" :key="index"
class="col-12 col-sm-6 col-md-4">
<panel-chart :name="index" :data="overviewHTTP" type="http"/>
</div> </div>
</div> </div>
<div v-else class="row items-center q-col-gutter-lg"> <div
v-if="!loadingOverview"
class="row items-center q-col-gutter-lg"
>
<div
v-for="(overviewHTTP, index) in allHTTP"
:key="index"
class="col-12 col-sm-6 col-md-4"
>
<panel-chart
:name="index"
:data="overviewHTTP"
type="http"
/>
</div>
</div>
<div
v-else
class="row items-center q-col-gutter-lg"
>
<div class="col-12 col-sm-6 col-md-4"> <div class="col-12 col-sm-6 col-md-4">
<p v-for="n in 6" :key="n" class="flex"> <p
<SkeletonBox :min-width="15" :max-width="15" style="margin-right: 2%"/> <SkeletonBox :min-width="50" :max-width="83"/> v-for="n in 6"
:key="n"
class="flex"
>
<SkeletonBox
:min-width="15"
:max-width="15"
style="margin-right: 2%"
/> <SkeletonBox
:min-width="50"
:max-width="83"
/>
</p> </p>
</div> </div>
</div> </div>
@ -49,20 +98,45 @@
<section class="app-section"> <section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-lg"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-lg">
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-globe-3"></q-icon> <q-icon name="eva-globe-3" />
<div class="app-title-label">TCP</div> <div class="app-title-label">
</div> TCP
<div v-if="!loadingOverview" class="row items-center q-col-gutter-lg">
<div
v-for="(overviewTCP, index) in allTCP" :key="index"
class="col-12 col-sm-6 col-md-4">
<panel-chart :name="index" :data="overviewTCP" type="tcp"/>
</div> </div>
</div> </div>
<div v-else class="row items-center q-col-gutter-lg"> <div
v-if="!loadingOverview"
class="row items-center q-col-gutter-lg"
>
<div
v-for="(overviewTCP, index) in allTCP"
:key="index"
class="col-12 col-sm-6 col-md-4"
>
<panel-chart
:name="index"
:data="overviewTCP"
type="tcp"
/>
</div>
</div>
<div
v-else
class="row items-center q-col-gutter-lg"
>
<div class="col-12 col-sm-6 col-md-4"> <div class="col-12 col-sm-6 col-md-4">
<p v-for="n in 6" :key="n" class="flex"> <p
<SkeletonBox :min-width="15" :max-width="15" style="margin-right: 2%"/> <SkeletonBox :min-width="50" :max-width="83"/> v-for="n in 6"
:key="n"
class="flex"
>
<SkeletonBox
:min-width="15"
:max-width="15"
style="margin-right: 2%"
/> <SkeletonBox
:min-width="50"
:max-width="83"
/>
</p> </p>
</div> </div>
</div> </div>
@ -72,20 +146,45 @@
<section class="app-section"> <section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-lg"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-lg">
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-globe-3"></q-icon> <q-icon name="eva-globe-3" />
<div class="app-title-label">UDP</div> <div class="app-title-label">
</div> UDP
<div v-if="!loadingOverview" class="row items-center q-col-gutter-lg">
<div
v-for="(overviewUDP, index) in allUDP" :key="index"
class="col-12 col-sm-6 col-md-4">
<panel-chart :name="index" :data="overviewUDP" type="udp"/>
</div> </div>
</div> </div>
<div v-else class="row items-center q-col-gutter-lg"> <div
v-if="!loadingOverview"
class="row items-center q-col-gutter-lg"
>
<div
v-for="(overviewUDP, index) in allUDP"
:key="index"
class="col-12 col-sm-6 col-md-4"
>
<panel-chart
:name="index"
:data="overviewUDP"
type="udp"
/>
</div>
</div>
<div
v-else
class="row items-center q-col-gutter-lg"
>
<div class="col-12 col-sm-6 col-md-4"> <div class="col-12 col-sm-6 col-md-4">
<p v-for="n in 6" :key="n" class="flex"> <p
<SkeletonBox :min-width="15" :max-width="15" style="margin-right: 2%"/> <SkeletonBox :min-width="50" :max-width="83"/> v-for="n in 6"
:key="n"
class="flex"
>
<SkeletonBox
:min-width="15"
:max-width="15"
style="margin-right: 2%"
/> <SkeletonBox
:min-width="50"
:max-width="83"
/>
</p> </p>
</div> </div>
</div> </div>
@ -95,20 +194,44 @@
<section class="app-section"> <section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-lg"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-lg">
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-toggle-right"></q-icon> <q-icon name="eva-toggle-right" />
<div class="app-title-label">Features</div> <div class="app-title-label">
</div> Features
<div v-if="!loadingOverview" class="row items-center q-col-gutter-lg">
<div
v-for="(overviewFeature, index) in allFeatures" :key="index"
class="col-12 col-sm-6 col-md-2">
<panel-feature :feature-key="index" :feature-val="overviewFeature"/>
</div> </div>
</div> </div>
<div v-else class="row items-center q-col-gutter-lg"> <div
v-if="!loadingOverview"
class="row items-center q-col-gutter-lg"
>
<div
v-for="(overviewFeature, index) in allFeatures"
:key="index"
class="col-12 col-sm-6 col-md-2"
>
<panel-feature
:feature-key="index"
:feature-val="overviewFeature"
/>
</div>
</div>
<div
v-else
class="row items-center q-col-gutter-lg"
>
<div class="col-12 col-sm-6 col-md-2"> <div class="col-12 col-sm-6 col-md-2">
<p v-for="n in 3" :key="n" class="flex"> <p
<SkeletonBox :min-width="15" :max-width="15" style="margin-right: 2%"/> <SkeletonBox :min-width="50" :max-width="83"/> v-for="n in 3"
:key="n"
class="flex"
>
<SkeletonBox
:min-width="15"
:max-width="15"
style="margin-right: 2%"
/> <SkeletonBox
:min-width="50"
:max-width="83"
/>
</p> </p>
</div> </div>
</div> </div>
@ -118,39 +241,60 @@
<section class="app-section"> <section class="app-section">
<div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-xl"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-lg q-pb-xl">
<div class="row no-wrap items-center q-mb-lg app-title"> <div class="row no-wrap items-center q-mb-lg app-title">
<q-icon name="eva-cube"></q-icon> <q-icon name="eva-cube" />
<div class="app-title-label">Providers</div> <div class="app-title-label">
</div> Providers
<div v-if="!loadingOverview" class="row items-center q-col-gutter-lg">
<div
v-for="(overviewProvider, index) in allProviders" :key="index"
class="col-12 col-sm-6 col-md-2">
<panel-provider :name="overviewProvider"/>
</div> </div>
</div> </div>
<div v-else class="row items-center q-col-gutter-lg"> <div
v-if="!loadingOverview"
class="row items-center q-col-gutter-lg"
>
<div
v-for="(overviewProvider, index) in allProviders"
:key="index"
class="col-12 col-sm-6 col-md-2"
>
<panel-provider :name="overviewProvider" />
</div>
</div>
<div
v-else
class="row items-center q-col-gutter-lg"
>
<div class="col-12 col-sm-6 col-md-2"> <div class="col-12 col-sm-6 col-md-2">
<p v-for="n in 3" :key="n" class="flex"> <p
<SkeletonBox :min-width="15" :max-width="15" style="margin-right: 2%"/> <SkeletonBox :min-width="50" :max-width="83"/> v-for="n in 3"
:key="n"
class="flex"
>
<SkeletonBox
:min-width="15"
:max-width="15"
style="margin-right: 2%"
/> <SkeletonBox
:min-width="50"
:max-width="83"
/>
</p> </p>
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</page-default> </page-default>
</template> </template>
<script> <script>
import { defineComponent } from 'vue'
import { mapActions, mapGetters } from 'vuex' import { mapActions, mapGetters } from 'vuex'
import PageDefault from '../../components/_commons/PageDefault' import PageDefault from '../../components/_commons/PageDefault.vue'
import SkeletonBox from '../../components/_commons/SkeletonBox' import SkeletonBox from '../../components/_commons/SkeletonBox.vue'
import PanelEntry from '../../components/dashboard/PanelEntry' import PanelEntry from '../../components/dashboard/PanelEntry.vue'
import PanelChart from '../../components/dashboard/PanelChart' import PanelChart from '../../components/dashboard/PanelChart.vue'
import PanelFeature from '../../components/dashboard/PanelFeature' import PanelFeature from '../../components/dashboard/PanelFeature.vue'
import PanelProvider from '../../components/dashboard/PanelProvider' import PanelProvider from '../../components/dashboard/PanelProvider.vue'
export default { export default defineComponent({
name: 'PageDashboardIndex', name: 'PageDashboardIndex',
components: { components: {
PageDefault, PageDefault,
@ -189,6 +333,17 @@ export default {
return this.overviewAll.items.providers return this.overviewAll.items.providers
} }
}, },
created () {
this.fetchAll()
this.intervalRefresh = setInterval(this.fetchOverview, this.intervalRefreshTime)
},
beforeUnmount () {
clearInterval(this.intervalRefresh)
clearTimeout(this.timeOutEntryGetAll)
clearTimeout(this.timeOutOverviewAll)
this.$store.commit('entrypoints/getAllClear')
this.$store.commit('core/getOverviewClear')
},
methods: { methods: {
...mapActions('entrypoints', { entryGetAll: 'getAll' }), ...mapActions('entrypoints', { entryGetAll: 'getAll' }),
...mapActions('core', { getOverview: 'getOverview' }), ...mapActions('core', { getOverview: 'getOverview' }),
@ -222,19 +377,8 @@ export default {
this.fetchEntries() this.fetchEntries()
this.fetchOverview() this.fetchOverview()
} }
},
created () {
this.fetchAll()
this.intervalRefresh = setInterval(this.fetchOverview, this.intervalRefreshTime)
},
beforeDestroy () {
clearInterval(this.intervalRefresh)
clearTimeout(this.timeOutEntryGetAll)
clearTimeout(this.timeOutOverviewAll)
this.$store.commit('entrypoints/getAllClear')
this.$store.commit('core/getOverviewClear')
} }
} })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View file

@ -1,10 +1,12 @@
<template> <template>
<page-default> <page-default>
<section class="app-section"> <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-xl"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div class="row no-wrap items-center q-mb-lg"> <div class="row no-wrap items-center q-mb-lg">
<tool-bar-table :status.sync="status" :filter.sync="filter"/> <tool-bar-table
v-model:status="status"
v-model:filter="filter"
/>
</div> </div>
<div class="row items-center q-col-gutter-lg"> <div class="row items-center q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
@ -12,28 +14,33 @@
ref="mainTable" ref="mainTable"
v-bind="getTableProps({ type: 'http-middlewares' })" v-bind="getTableProps({ type: 'http-middlewares' })"
:data="allMiddlewares.items" :data="allMiddlewares.items"
:onLoadMore="handleLoadMore" :on-load-more="handleLoadMore"
:endReached="allMiddlewares.endReached" :end-reached="allMiddlewares.endReached"
:loading="allMiddlewares.loading" :loading="allMiddlewares.loading"
/> />
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</page-default> </page-default>
</template> </template>
<script> <script>
import { defineComponent } from 'vue'
import { mapActions, mapGetters } from 'vuex' import { mapActions, mapGetters } from 'vuex'
import GetTablePropsMixin from '../../_mixins/GetTableProps' import GetTablePropsMixin from '../../_mixins/GetTableProps'
import PaginationMixin from '../../_mixins/Pagination' import PaginationMixin from '../../_mixins/Pagination'
import PageDefault from '../../components/_commons/PageDefault' import PageDefault from '../../components/_commons/PageDefault.vue'
import ToolBarTable from '../../components/_commons/ToolBarTable' import ToolBarTable from '../../components/_commons/ToolBarTable.vue'
import MainTable from '../../components/_commons/MainTable' import MainTable from '../../components/_commons/MainTable.vue'
export default { export default defineComponent({
name: 'PageHTTPMiddlewares', name: 'PageHTTPMiddlewares',
components: {
PageDefault,
ToolBarTable,
MainTable
},
mixins: [ mixins: [
GetTablePropsMixin, GetTablePropsMixin,
PaginationMixin({ PaginationMixin({
@ -42,11 +49,6 @@ export default {
pollingIntervalTime: 5000 pollingIntervalTime: 5000
}) })
], ],
components: {
PageDefault,
ToolBarTable,
MainTable
},
data () { data () {
return { return {
filter: '', filter: '',
@ -56,6 +58,17 @@ export default {
computed: { computed: {
...mapGetters('http', { allMiddlewares: 'allMiddlewares' }) ...mapGetters('http', { allMiddlewares: 'allMiddlewares' })
}, },
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
beforeUnmount () {
this.$store.commit('http/getAllMiddlewaresClear')
},
methods: { methods: {
...mapActions('http', { getAllMiddlewares: 'getAllMiddlewares' }), ...mapActions('http', { getAllMiddlewares: 'getAllMiddlewares' }),
getAllMiddlewaresWithParams (params) { getAllMiddlewaresWithParams (params) {
@ -75,19 +88,8 @@ export default {
handleLoadMore ({ page = 1 } = {}) { handleLoadMore ({ page = 1 } = {}) {
return this.fetchMore({ page }) return this.fetchMore({ page })
} }
},
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
beforeDestroy () {
this.$store.commit('http/getAllMiddlewaresClear')
} }
} })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View file

@ -1,10 +1,12 @@
<template> <template>
<page-default> <page-default>
<section class="app-section"> <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-xl"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div class="row no-wrap items-center q-mb-lg"> <div class="row no-wrap items-center q-mb-lg">
<tool-bar-table :status.sync="status" :filter.sync="filter"/> <tool-bar-table
v-model:status="status"
v-model:filter="filter"
/>
</div> </div>
<div class="row items-center q-col-gutter-lg"> <div class="row items-center q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
@ -12,28 +14,33 @@
ref="mainTable" ref="mainTable"
v-bind="getTableProps({ type: 'http-routers' })" v-bind="getTableProps({ type: 'http-routers' })"
:data="allRouters.items" :data="allRouters.items"
:onLoadMore="handleLoadMore" :on-load-more="handleLoadMore"
:endReached="allRouters.endReached" :end-reached="allRouters.endReached"
:loading="allRouters.loading" :loading="allRouters.loading"
/> />
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</page-default> </page-default>
</template> </template>
<script> <script>
import { defineComponent } from 'vue'
import { mapActions, mapGetters } from 'vuex' import { mapActions, mapGetters } from 'vuex'
import GetTablePropsMixin from '../../_mixins/GetTableProps' import GetTablePropsMixin from '../../_mixins/GetTableProps'
import PaginationMixin from '../../_mixins/Pagination' import PaginationMixin from '../../_mixins/Pagination'
import PageDefault from '../../components/_commons/PageDefault' import PageDefault from '../../components/_commons/PageDefault.vue'
import ToolBarTable from '../../components/_commons/ToolBarTable' import ToolBarTable from '../../components/_commons/ToolBarTable.vue'
import MainTable from '../../components/_commons/MainTable' import MainTable from '../../components/_commons/MainTable.vue'
export default { export default defineComponent({
name: 'PageHTTPRouters', name: 'PageHTTPRouters',
components: {
PageDefault,
ToolBarTable,
MainTable
},
mixins: [ mixins: [
GetTablePropsMixin, GetTablePropsMixin,
PaginationMixin({ PaginationMixin({
@ -42,11 +49,6 @@ export default {
pollingIntervalTime: 5000 pollingIntervalTime: 5000
}) })
], ],
components: {
PageDefault,
ToolBarTable,
MainTable
},
data () { data () {
return { return {
filter: '', filter: '',
@ -56,6 +58,17 @@ export default {
computed: { computed: {
...mapGetters('http', { allRouters: 'allRouters' }) ...mapGetters('http', { allRouters: 'allRouters' })
}, },
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
beforeUnmount () {
this.$store.commit('http/getAllRoutersClear')
},
methods: { methods: {
...mapActions('http', { getAllRouters: 'getAllRouters' }), ...mapActions('http', { getAllRouters: 'getAllRouters' }),
getAllRoutersWithParams (params) { getAllRoutersWithParams (params) {
@ -75,19 +88,8 @@ export default {
handleLoadMore ({ page = 1 } = {}) { handleLoadMore ({ page = 1 } = {}) {
return this.fetchMore({ page }) return this.fetchMore({ page })
} }
},
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
beforeDestroy () {
this.$store.commit('http/getAllRoutersClear')
} }
} })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View file

@ -1,10 +1,12 @@
<template> <template>
<page-default> <page-default>
<section class="app-section"> <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-xl"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div class="row no-wrap items-center q-mb-lg"> <div class="row no-wrap items-center q-mb-lg">
<tool-bar-table :status.sync="status" :filter.sync="filter"/> <tool-bar-table
v-model:status="status"
v-model:filter="filter"
/>
</div> </div>
<div class="row items-center q-col-gutter-lg"> <div class="row items-center q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
@ -12,28 +14,33 @@
ref="mainTable" ref="mainTable"
v-bind="getTableProps({ type: 'http-services' })" v-bind="getTableProps({ type: 'http-services' })"
:data="allServices.items" :data="allServices.items"
:onLoadMore="handleLoadMore" :on-load-more="handleLoadMore"
:endReached="allServices.endReached" :end-reached="allServices.endReached"
:loading="allServices.loading" :loading="allServices.loading"
/> />
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</page-default> </page-default>
</template> </template>
<script> <script>
import { defineComponent } from 'vue'
import { mapActions, mapGetters } from 'vuex' import { mapActions, mapGetters } from 'vuex'
import GetTablePropsMixin from '../../_mixins/GetTableProps' import GetTablePropsMixin from '../../_mixins/GetTableProps'
import PaginationMixin from '../../_mixins/Pagination' import PaginationMixin from '../../_mixins/Pagination'
import PageDefault from '../../components/_commons/PageDefault' import PageDefault from '../../components/_commons/PageDefault.vue'
import ToolBarTable from '../../components/_commons/ToolBarTable' import ToolBarTable from '../../components/_commons/ToolBarTable.vue'
import MainTable from '../../components/_commons/MainTable' import MainTable from '../../components/_commons/MainTable.vue'
export default { export default defineComponent({
name: 'PageHTTPServices', name: 'PageHTTPServices',
components: {
PageDefault,
ToolBarTable,
MainTable
},
mixins: [ mixins: [
GetTablePropsMixin, GetTablePropsMixin,
PaginationMixin({ PaginationMixin({
@ -42,11 +49,6 @@ export default {
pollingIntervalTime: 5000 pollingIntervalTime: 5000
}) })
], ],
components: {
PageDefault,
ToolBarTable,
MainTable
},
data () { data () {
return { return {
filter: '', filter: '',
@ -56,6 +58,17 @@ export default {
computed: { computed: {
...mapGetters('http', { allServices: 'allServices' }) ...mapGetters('http', { allServices: 'allServices' })
}, },
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
beforeUnmount () {
this.$store.commit('http/getAllServicesClear')
},
methods: { methods: {
...mapActions('http', { getAllServices: 'getAllServices' }), ...mapActions('http', { getAllServices: 'getAllServices' }),
getAllServicesWithParams (params) { getAllServicesWithParams (params) {
@ -75,19 +88,8 @@ export default {
handleLoadMore ({ page = 1 } = {}) { handleLoadMore ({ page = 1 } = {}) {
return this.fetchMore({ page }) return this.fetchMore({ page })
} }
},
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
beforeDestroy () {
this.$store.commit('http/getAllServicesClear')
} }
} })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View file

@ -1,10 +1,12 @@
<template> <template>
<page-default> <page-default>
<section class="app-section"> <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-xl"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div class="row no-wrap items-center q-mb-lg"> <div class="row no-wrap items-center q-mb-lg">
<tool-bar-table :status.sync="status" :filter.sync="filter"/> <tool-bar-table
v-model:status="status"
v-model:filter="filter"
/>
</div> </div>
<div class="row items-center q-col-gutter-lg"> <div class="row items-center q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
@ -12,28 +14,33 @@
ref="mainTable" ref="mainTable"
v-bind="getTableProps({ type: 'tcp-middlewares' })" v-bind="getTableProps({ type: 'tcp-middlewares' })"
:data="allMiddlewares.items" :data="allMiddlewares.items"
:onLoadMore="handleLoadMore" :on-load-more="handleLoadMore"
:endReached="allMiddlewares.endReached" :end-reached="allMiddlewares.endReached"
:loading="allMiddlewares.loading" :loading="allMiddlewares.loading"
/> />
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</page-default> </page-default>
</template> </template>
<script> <script>
import { defineComponent } from 'vue'
import { mapActions, mapGetters } from 'vuex' import { mapActions, mapGetters } from 'vuex'
import GetTablePropsMixin from '../../_mixins/GetTableProps' import GetTablePropsMixin from '../../_mixins/GetTableProps'
import PaginationMixin from '../../_mixins/Pagination' import PaginationMixin from '../../_mixins/Pagination'
import PageDefault from '../../components/_commons/PageDefault' import PageDefault from '../../components/_commons/PageDefault.vue'
import ToolBarTable from '../../components/_commons/ToolBarTable' import ToolBarTable from '../../components/_commons/ToolBarTable.vue'
import MainTable from '../../components/_commons/MainTable' import MainTable from '../../components/_commons/MainTable.vue'
export default { export default defineComponent({
name: 'PageTCPMiddlewares', name: 'PageTCPMiddlewares',
components: {
PageDefault,
ToolBarTable,
MainTable
},
mixins: [ mixins: [
GetTablePropsMixin, GetTablePropsMixin,
PaginationMixin({ PaginationMixin({
@ -42,11 +49,6 @@ export default {
pollingIntervalTime: 5000 pollingIntervalTime: 5000
}) })
], ],
components: {
PageDefault,
ToolBarTable,
MainTable
},
data () { data () {
return { return {
filter: '', filter: '',
@ -56,6 +58,17 @@ export default {
computed: { computed: {
...mapGetters('tcp', { allMiddlewares: 'allMiddlewares' }) ...mapGetters('tcp', { allMiddlewares: 'allMiddlewares' })
}, },
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
beforeUnmount () {
this.$store.commit('tcp/getAllMiddlewaresClear')
},
methods: { methods: {
...mapActions('tcp', { getAllMiddlewares: 'getAllMiddlewares' }), ...mapActions('tcp', { getAllMiddlewares: 'getAllMiddlewares' }),
getAllMiddlewaresWithParams (params) { getAllMiddlewaresWithParams (params) {
@ -75,19 +88,8 @@ export default {
handleLoadMore ({ page = 1 } = {}) { handleLoadMore ({ page = 1 } = {}) {
return this.fetchMore({ page }) return this.fetchMore({ page })
} }
},
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
beforeDestroy () {
this.$store.commit('tcp/getAllMiddlewaresClear')
} }
} })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View file

@ -1,10 +1,12 @@
<template> <template>
<page-default> <page-default>
<section class="app-section"> <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-xl"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div class="row no-wrap items-center q-mb-lg"> <div class="row no-wrap items-center q-mb-lg">
<tool-bar-table :status.sync="status" :filter.sync="filter"/> <tool-bar-table
v-model:status="status"
v-model:filter="filter"
/>
</div> </div>
<div class="row items-center q-col-gutter-lg"> <div class="row items-center q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
@ -12,28 +14,33 @@
ref="mainTable" ref="mainTable"
v-bind="getTableProps({ type: 'tcp-routers' })" v-bind="getTableProps({ type: 'tcp-routers' })"
:data="allRouters.items" :data="allRouters.items"
:onLoadMore="handleLoadMore" :on-load-more="handleLoadMore"
:endReached="allRouters.endReached" :end-reached="allRouters.endReached"
:loading="allRouters.loading" :loading="allRouters.loading"
/> />
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</page-default> </page-default>
</template> </template>
<script> <script>
import { defineComponent } from 'vue'
import { mapActions, mapGetters } from 'vuex' import { mapActions, mapGetters } from 'vuex'
import GetTablePropsMixin from '../../_mixins/GetTableProps' import GetTablePropsMixin from '../../_mixins/GetTableProps'
import PaginationMixin from '../../_mixins/Pagination' import PaginationMixin from '../../_mixins/Pagination'
import PageDefault from '../../components/_commons/PageDefault' import PageDefault from '../../components/_commons/PageDefault.vue'
import ToolBarTable from '../../components/_commons/ToolBarTable' import ToolBarTable from '../../components/_commons/ToolBarTable.vue'
import MainTable from '../../components/_commons/MainTable' import MainTable from '../../components/_commons/MainTable.vue'
export default { export default defineComponent({
name: 'PageTCPRouters', name: 'PageTCPRouters',
components: {
PageDefault,
ToolBarTable,
MainTable
},
mixins: [ mixins: [
GetTablePropsMixin, GetTablePropsMixin,
PaginationMixin({ PaginationMixin({
@ -42,11 +49,6 @@ export default {
pollingIntervalTime: 5000 pollingIntervalTime: 5000
}) })
], ],
components: {
PageDefault,
ToolBarTable,
MainTable
},
data () { data () {
return { return {
filter: '', filter: '',
@ -56,6 +58,17 @@ export default {
computed: { computed: {
...mapGetters('tcp', { allRouters: 'allRouters' }) ...mapGetters('tcp', { allRouters: 'allRouters' })
}, },
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
beforeUnmount () {
this.$store.commit('tcp/getAllRoutersClear')
},
methods: { methods: {
...mapActions('tcp', { getAllRouters: 'getAllRouters' }), ...mapActions('tcp', { getAllRouters: 'getAllRouters' }),
getAllRoutersWithParams (params) { getAllRoutersWithParams (params) {
@ -75,19 +88,8 @@ export default {
handleLoadMore ({ page = 1 } = {}) { handleLoadMore ({ page = 1 } = {}) {
return this.fetchMore({ page }) return this.fetchMore({ page })
} }
},
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
beforeDestroy () {
this.$store.commit('tcp/getAllRoutersClear')
} }
} })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View file

@ -1,10 +1,12 @@
<template> <template>
<page-default> <page-default>
<section class="app-section"> <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-xl"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div class="row no-wrap items-center q-mb-lg"> <div class="row no-wrap items-center q-mb-lg">
<tool-bar-table :status.sync="status" :filter.sync="filter"/> <tool-bar-table
v-model:status="status"
v-model:filter="filter"
/>
</div> </div>
<div class="row items-center q-col-gutter-lg"> <div class="row items-center q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
@ -12,15 +14,14 @@
ref="mainTable" ref="mainTable"
v-bind="getTableProps({ type: 'tcp-services' })" v-bind="getTableProps({ type: 'tcp-services' })"
:data="allServices.items" :data="allServices.items"
:onLoadMore="handleLoadMore" :on-load-more="handleLoadMore"
:endReached="allServices.endReached" :end-reached="allServices.endReached"
:loading="allServices.loading" :loading="allServices.loading"
/> />
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</page-default> </page-default>
</template> </template>
@ -28,12 +29,17 @@
import { mapActions, mapGetters } from 'vuex' import { mapActions, mapGetters } from 'vuex'
import GetTablePropsMixin from '../../_mixins/GetTableProps' import GetTablePropsMixin from '../../_mixins/GetTableProps'
import PaginationMixin from '../../_mixins/Pagination' import PaginationMixin from '../../_mixins/Pagination'
import PageDefault from '../../components/_commons/PageDefault' import PageDefault from '../../components/_commons/PageDefault.vue'
import ToolBarTable from '../../components/_commons/ToolBarTable' import ToolBarTable from '../../components/_commons/ToolBarTable.vue'
import MainTable from '../../components/_commons/MainTable' import MainTable from '../../components/_commons/MainTable.vue'
export default { export default {
name: 'PageTCPServices', name: 'PageTCPServices',
components: {
PageDefault,
ToolBarTable,
MainTable
},
mixins: [ mixins: [
GetTablePropsMixin, GetTablePropsMixin,
PaginationMixin({ PaginationMixin({
@ -42,11 +48,6 @@ export default {
pollingIntervalTime: 5000 pollingIntervalTime: 5000
}) })
], ],
components: {
PageDefault,
ToolBarTable,
MainTable
},
data () { data () {
return { return {
filter: '', filter: '',
@ -56,6 +57,17 @@ export default {
computed: { computed: {
...mapGetters('tcp', { allServices: 'allServices' }) ...mapGetters('tcp', { allServices: 'allServices' })
}, },
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
beforeUnmount () {
this.$store.commit('tcp/getAllServicesClear')
},
methods: { methods: {
...mapActions('tcp', { getAllServices: 'getAllServices' }), ...mapActions('tcp', { getAllServices: 'getAllServices' }),
getAllServicesWithParams (params) { getAllServicesWithParams (params) {
@ -75,17 +87,6 @@ export default {
handleLoadMore ({ page = 1 } = {}) { handleLoadMore ({ page = 1 } = {}) {
return this.fetchMore({ page }) return this.fetchMore({ page })
} }
},
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
beforeDestroy () {
this.$store.commit('tcp/getAllServicesClear')
} }
} }
</script> </script>

View file

@ -3,7 +3,10 @@
<section class="app-section"> <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-xl"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div class="row no-wrap items-center q-mb-lg"> <div class="row no-wrap items-center q-mb-lg">
<tool-bar-table :status.sync="status" :filter.sync="filter"/> <tool-bar-table
v-model:status="status"
v-model:filter="filter"
/>
</div> </div>
<div class="row items-center q-col-gutter-lg"> <div class="row items-center q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
@ -11,28 +14,33 @@
ref="mainTable" ref="mainTable"
v-bind="getTableProps({ type: 'udp-routers' })" v-bind="getTableProps({ type: 'udp-routers' })"
:data="allRouters.items" :data="allRouters.items"
:onLoadMore="handleLoadMore" :on-load-more="handleLoadMore"
:endReached="allRouters.endReached" :end-reached="allRouters.endReached"
:loading="allRouters.loading" :loading="allRouters.loading"
/> />
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</page-default> </page-default>
</template> </template>
<script> <script>
import { defineComponent } from 'vue'
import { mapActions, mapGetters } from 'vuex' import { mapActions, mapGetters } from 'vuex'
import GetTablePropsMixin from '../../_mixins/GetTableProps' import GetTablePropsMixin from '../../_mixins/GetTableProps'
import PaginationMixin from '../../_mixins/Pagination' import PaginationMixin from '../../_mixins/Pagination'
import PageDefault from '../../components/_commons/PageDefault' import PageDefault from '../../components/_commons/PageDefault.vue'
import ToolBarTable from '../../components/_commons/ToolBarTable' import ToolBarTable from '../../components/_commons/ToolBarTable.vue'
import MainTable from '../../components/_commons/MainTable' import MainTable from '../../components/_commons/MainTable.vue'
export default { export default defineComponent({
name: 'PageUDPRouters', name: 'PageUDPRouters',
components: {
PageDefault,
ToolBarTable,
MainTable
},
mixins: [ mixins: [
GetTablePropsMixin, GetTablePropsMixin,
PaginationMixin({ PaginationMixin({
@ -41,11 +49,6 @@ export default {
pollingIntervalTime: 5000 pollingIntervalTime: 5000
}) })
], ],
components: {
PageDefault,
ToolBarTable,
MainTable
},
data () { data () {
return { return {
filter: '', filter: '',
@ -55,6 +58,17 @@ export default {
computed: { computed: {
...mapGetters('udp', { allRouters: 'allRouters' }) ...mapGetters('udp', { allRouters: 'allRouters' })
}, },
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
beforeUnmount () {
this.$store.commit('udp/getAllRoutersClear')
},
methods: { methods: {
...mapActions('udp', { getAllRouters: 'getAllRouters' }), ...mapActions('udp', { getAllRouters: 'getAllRouters' }),
getAllRoutersWithParams (params) { getAllRoutersWithParams (params) {
@ -74,19 +88,8 @@ export default {
handleLoadMore ({ page = 1 } = {}) { handleLoadMore ({ page = 1 } = {}) {
return this.fetchMore({ page }) return this.fetchMore({ page })
} }
},
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
beforeDestroy () {
this.$store.commit('udp/getAllRoutersClear')
} }
} })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View file

@ -1,10 +1,12 @@
<template> <template>
<page-default> <page-default>
<section class="app-section"> <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-xl"> <div class="app-section-wrap app-boxed app-boxed-xl q-pl-md q-pr-md q-pt-xl q-pb-xl">
<div class="row no-wrap items-center q-mb-lg"> <div class="row no-wrap items-center q-mb-lg">
<tool-bar-table :status.sync="status" :filter.sync="filter"/> <tool-bar-table
v-model:status="status"
v-model:filter="filter"
/>
</div> </div>
<div class="row items-center q-col-gutter-lg"> <div class="row items-center q-col-gutter-lg">
<div class="col-12"> <div class="col-12">
@ -12,28 +14,33 @@
ref="mainTable" ref="mainTable"
v-bind="getTableProps({ type: 'udp-services' })" v-bind="getTableProps({ type: 'udp-services' })"
:data="allServices.items" :data="allServices.items"
:onLoadMore="handleLoadMore" :on-load-more="handleLoadMore"
:endReached="allServices.endReached" :end-reached="allServices.endReached"
:loading="allServices.loading" :loading="allServices.loading"
/> />
</div> </div>
</div> </div>
</div> </div>
</section> </section>
</page-default> </page-default>
</template> </template>
<script> <script>
import { defineComponent } from 'vue'
import { mapActions, mapGetters } from 'vuex' import { mapActions, mapGetters } from 'vuex'
import GetTablePropsMixin from '../../_mixins/GetTableProps' import GetTablePropsMixin from '../../_mixins/GetTableProps'
import PaginationMixin from '../../_mixins/Pagination' import PaginationMixin from '../../_mixins/Pagination'
import PageDefault from '../../components/_commons/PageDefault' import PageDefault from '../../components/_commons/PageDefault.vue'
import ToolBarTable from '../../components/_commons/ToolBarTable' import ToolBarTable from '../../components/_commons/ToolBarTable.vue'
import MainTable from '../../components/_commons/MainTable' import MainTable from '../../components/_commons/MainTable.vue'
export default { export default defineComponent({
name: 'PageUDPServices', name: 'PageUDPServices',
components: {
PageDefault,
ToolBarTable,
MainTable
},
mixins: [ mixins: [
GetTablePropsMixin, GetTablePropsMixin,
PaginationMixin({ PaginationMixin({
@ -42,11 +49,6 @@ export default {
pollingIntervalTime: 5000 pollingIntervalTime: 5000
}) })
], ],
components: {
PageDefault,
ToolBarTable,
MainTable
},
data () { data () {
return { return {
filter: '', filter: '',
@ -56,6 +58,17 @@ export default {
computed: { computed: {
...mapGetters('udp', { allServices: 'allServices' }) ...mapGetters('udp', { allServices: 'allServices' })
}, },
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
beforeUnmount () {
this.$store.commit('udp/getAllServicesClear')
},
methods: { methods: {
...mapActions('udp', { getAllServices: 'getAllServices' }), ...mapActions('udp', { getAllServices: 'getAllServices' }),
getAllServicesWithParams (params) { getAllServicesWithParams (params) {
@ -75,19 +88,8 @@ export default {
handleLoadMore ({ page = 1 } = {}) { handleLoadMore ({ page = 1 } = {}) {
return this.fetchMore({ page }) return this.fetchMore({ page })
} }
},
watch: {
'status' () {
this.refreshAll()
},
'filter' () {
this.refreshAll()
}
},
beforeDestroy () {
this.$store.commit('udp/getAllServicesClear')
} }
} })
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View file

@ -1,26 +1,18 @@
import Vue from 'vue' import { route } from 'quasar/wrappers'
import VueRouter from 'vue-router' import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } from 'vue-router'
import routes from './routes' import routes from './routes'
Vue.use(VueRouter) export default route(function (/* { store, ssrContext } */) {
const createHistory = process.env.SERVER
? createMemoryHistory
: (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory)
/* const Router = createRouter({
* If not building with SSR mode, you can scrollBehavior: () => ({ left: 0, top: 0 }),
* directly export the Router instantiation
*/
export default function (/* { store, ssrContext } */) {
const Router = new VueRouter({
scrollBehavior: () => ({ x: 0, y: 0 }),
routes, routes,
// Leave these as is and change from quasar.conf.js instead! history: createHistory(process.env.MODE === 'ssr' ? undefined : process.env.VUE_ROUTER_BASE)
// quasar.conf.js -> build -> vueRouterMode
// quasar.conf.js -> build -> publicPath
mode: process.env.VUE_ROUTER_MODE,
base: process.env.VUE_ROUTER_BASE
}) })
return Router return Router
} })

View file

@ -1,3 +1,5 @@
import Error404 from 'pages/_commons/Error404.vue'
const LayoutDefault = () => import('layouts/Default.vue') const LayoutDefault = () => import('layouts/Default.vue')
const routes = [ const routes = [
@ -249,8 +251,8 @@ const routes = [
// Always leave this as last one // Always leave this as last one
if (process.env.MODE !== 'ssr') { if (process.env.MODE !== 'ssr') {
routes.push({ routes.push({
path: '*', path: '/:catchAll(.*)*',
component: () => import('pages/_commons/Error404.vue'), component: Error404,
meta: { meta: {
title: '404' title: '404'
} }

View file

@ -1,5 +1,5 @@
import Vue from 'vue' import { createStore } from 'vuex'
import Vuex from 'vuex' import { store } from 'quasar/wrappers'
import core from './core' import core from './core'
import entrypoints from './entrypoints' import entrypoints from './entrypoints'
@ -8,15 +8,13 @@ import tcp from './tcp'
import udp from './udp' import udp from './udp'
import platform from './platform' import platform from './platform'
Vue.use(Vuex)
/* /*
* If not building with SSR mode, you can * If not building with SSR mode, you can
* directly export the Store instantiation * directly export the Store instantiation
*/ */
export default function (/* { ssrContext } */) { export default store((/* { ssrContext } */) => {
const Store = new Vuex.Store({ const Store = createStore({
modules: { modules: {
core, core,
entrypoints, entrypoints,
@ -32,4 +30,4 @@ export default function (/* { ssrContext } */) {
}) })
return Store return Store
} })

File diff suppressed because it is too large Load diff