diff --git a/webui/package-lock.json b/webui/package-lock.json
index 6f75224c1..909e47be8 100644
--- a/webui/package-lock.json
+++ b/webui/package-lock.json
@@ -2176,6 +2176,15 @@
"which": "^2.0.1"
}
},
+ "dot-prop": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.1.1.tgz",
+ "integrity": "sha512-QCHI6Lkf+9fJMpwfAFsTvbiSh6ujoPmhCLiDvD/n4dGtLvHfhuBwPdN6z2x4YSOwwtTcLoO/LP70xELWGF/JVA==",
+ "dev": true,
+ "requires": {
+ "is-obj": "^2.0.0"
+ }
+ },
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -2218,6 +2227,12 @@
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true
},
+ "is-obj": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+ "dev": true
+ },
"mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
@@ -5101,10 +5116,9 @@
}
},
"dot-prop": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.1.1.tgz",
- "integrity": "sha512-QCHI6Lkf+9fJMpwfAFsTvbiSh6ujoPmhCLiDvD/n4dGtLvHfhuBwPdN6z2x4YSOwwtTcLoO/LP70xELWGF/JVA==",
- "dev": true,
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
+ "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
"requires": {
"is-obj": "^2.0.0"
},
@@ -5112,8 +5126,7 @@
"is-obj": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
- "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
- "dev": true
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="
}
}
},
@@ -5600,6 +5613,15 @@
}
}
},
+ "eslint-plugin-prettier": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.1.tgz",
+ "integrity": "sha512-A+TZuHZ0KU0cnn56/9mfR7/KjUJ9QNVXUhwvRFSR7PGPe0zQR6PTkmyqg1AtUUEOzTqeRsUwyKFh0oVZKVCrtA==",
+ "dev": true,
+ "requires": {
+ "prettier-linter-helpers": "^1.0.0"
+ }
+ },
"eslint-plugin-promise": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz",
@@ -5965,6 +5987,12 @@
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
"dev": true
},
+ "fast-diff": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+ "dev": true
+ },
"fast-glob": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.0.tgz",
@@ -10181,6 +10209,15 @@
"integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
"dev": true
},
+ "prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "requires": {
+ "fast-diff": "^1.1.2"
+ }
+ },
"pretty-bytes": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz",
diff --git a/webui/package.json b/webui/package.json
index a959db1c9..9280222b6 100644
--- a/webui/package.json
+++ b/webui/package.json
@@ -20,6 +20,7 @@
"axios": "^0.19.0",
"bowser": "^2.5.2",
"chart.js": "^2.8.0",
+ "dot-prop": "5.2.0",
"lodash": "^4.17.15",
"moment": "^2.24.0",
"quasar": "^1.4.4",
@@ -33,8 +34,10 @@
"babel-eslint": "^10.0.1",
"eslint": "^5.10.0",
"eslint-loader": "^2.1.1",
+ "eslint-plugin-prettier": "3.1.1",
"eslint-plugin-vue": "^5.0.0",
"node-sass": "^4.12.0",
+ "prettier": "1.19.1",
"sass-loader": "^7.1.0"
},
"engines": {
diff --git a/webui/src/_mixins/GetTableProps.js b/webui/src/_mixins/GetTableProps.js
new file mode 100644
index 000000000..a15ddcb28
--- /dev/null
+++ b/webui/src/_mixins/GetTableProps.js
@@ -0,0 +1,141 @@
+import { get } from 'dot-prop'
+import { QChip } from 'quasar'
+import Chips from '../components/_commons/Chips'
+import ProviderIcon from '../components/_commons/ProviderIcon'
+import AvatarState from '../components/_commons/AvatarState'
+import TLSState from '../components/_commons/TLSState'
+
+const allColumns = [
+ {
+ name: 'status',
+ required: true,
+ label: 'Status',
+ align: 'left',
+ fieldToProps: row => ({
+ state: row.status === 'enabled' ? 'positive' : 'negative'
+ }),
+ component: AvatarState
+ },
+ {
+ name: 'tls',
+ align: 'left',
+ label: 'TLS',
+ fieldToProps: row => ({ isTLS: row.tls }),
+ component: TLSState
+ },
+ {
+ name: 'rule',
+ align: 'left',
+ label: 'Rule',
+ component: QChip,
+ fieldToProps: () => ({ class: 'app-chip app-chip-rule', dense: true }),
+ content: row => row.rule
+ },
+ {
+ name: 'entryPoints',
+ align: 'left',
+ label: 'Entrypoints',
+ component: Chips,
+ fieldToProps: row => ({
+ classNames: 'app-chip app-chip-entry-points',
+ dense: true,
+ list: row.entryPoints
+ })
+ },
+ {
+ name: 'name',
+ align: 'left',
+ label: 'Name',
+ component: QChip,
+ fieldToProps: () => ({ class: 'app-chip app-chip-name', dense: true }),
+ content: row => row.name
+ },
+ {
+ name: 'type',
+ align: 'left',
+ label: 'Type',
+ component: QChip,
+ fieldToProps: () => ({
+ class: 'app-chip app-chip-entry-points',
+ dense: true
+ }),
+ content: row => row.type
+ },
+ {
+ name: 'servers',
+ align: 'right',
+ label: 'Servers',
+ fieldToProps: () => ({ class: 'servers-label' }),
+ content: function (value) {
+ if (value.loadBalancer && value.loadBalancer.servers) {
+ return value.loadBalancer.servers.length
+ }
+ return 0
+ }
+ },
+ {
+ name: 'service',
+ align: 'left',
+ label: 'Service',
+ component: QChip,
+ fieldToProps: () => ({ class: 'app-chip app-chip-service', dense: true }),
+ content: row => row.service
+ },
+ {
+ name: 'provider',
+ align: 'center',
+ label: 'Provider',
+ fieldToProps: row => ({ name: row.provider }),
+ component: ProviderIcon
+ }
+]
+
+const columnsByResource = {
+ routers: [
+ 'status',
+ 'rule',
+ 'entryPoints',
+ 'name',
+ 'service',
+ 'tls',
+ 'provider'
+ ],
+ services: ['status', 'name', 'type', 'servers', 'provider'],
+ middlewares: ['status', 'name', 'type', 'provider']
+}
+
+const propsByType = {
+ 'http-routers': {
+ columns: columnsByResource.routers
+ },
+ 'tcp-routers': {
+ columns: columnsByResource.routers
+ },
+ 'http-services': {
+ columns: columnsByResource.services
+ },
+ 'tcp-services': {
+ columns: columnsByResource.services
+ },
+ 'http-middlewares': {
+ columns: columnsByResource.middlewares
+ }
+}
+
+const GetTablePropsMixin = {
+ methods: {
+ getTableProps ({ type }) {
+ return {
+ onRowClick: row =>
+ this.$router.push({
+ path: `/${type.replace('-', '/', 'gi')}/${row.name}`
+ }),
+ columns: allColumns.filter(c =>
+ get(propsByType, `${type}.columns`, []).includes(c.name)
+ )
+ }
+ }
+ }
+}
+
+export default GetTablePropsMixin
diff --git a/webui/src/components/_commons/Chips.vue b/webui/src/components/_commons/Chips.vue
new file mode 100644
index 000000000..2d77d8c25
--- /dev/null
+++ b/webui/src/components/_commons/Chips.vue
@@ -0,0 +1,17 @@
+
+
{{ column.label }}
@@ -20,63 +20,29 @@