Compare commits

...

42 Commits

Author SHA1 Message Date
Erki Aas eba065b5e0 Fix displaying images in ExamineCamModal
continuous-integration/drone Build is passing Details
2022-12-13 14:05:40 +02:00
Erki Aas fc571d5382 Fix dynamic importing in production build
continuous-integration/drone Build is passing Details
2022-12-13 12:28:18 +02:00
Erki Aas a918e50e2d Display reported backend as title
continuous-integration/drone Build is passing Details
2022-12-12 23:22:17 +02:00
Erki Aas 918401ab42 Dynamic column configuration depending on which reported by backend, added Camtiler column definition, Screenshot cell renderer and separate ExamineCamModal 2022-12-12 23:20:57 +02:00
Erki Aas a9d1211d83 Add VueViewer image viewer 2022-12-12 23:17:55 +02:00
Erki Aas a82373b856 Only close ExamineLogModal when clicked outside
continuous-integration/drone Build is passing Details
2022-11-17 14:46:21 +02:00
Erki Aas 9fb5d34118 Moved Header into separate component, added ConnectionMonitor
continuous-integration/drone Build is failing Details
2022-11-17 14:37:20 +02:00
Erki Aas 4452819aed Make Roboto Mono default font
continuous-integration/drone Build is passing Details
2022-11-16 15:03:00 +02:00
Erki Aas c57fbce360 Parse and display ANSI color codes found in some messages 2022-11-16 14:55:45 +02:00
Erki Aas fe21cd9e4a Force using base image from harbor.k-space.ee
continuous-integration/drone Build is passing Details
2022-11-16 13:57:38 +02:00
Erki Aas cab82049bf Remove level column, display level as colored icon before message
continuous-integration/drone Build is failing Details
2022-11-16 13:16:11 +02:00
Erki Aas ca467005a7 Add overlay which indicates that rows are being loaded
continuous-integration/drone Build is passing Details
2022-11-15 20:12:01 +02:00
Erki Aas 2573eb6675 Disable ExamineLogModal transitions for more fast experience 2022-11-15 19:21:17 +02:00
Erki Aas 58308d51ec Improve ExamineLogModal - click to copy value 2022-11-15 19:16:41 +02:00
Erki Aas aeef2f9852 Resize grid columns when window is resized 2022-11-15 19:06:33 +02:00
Erki Aas 95ec7e09e8 Improve ExamineLogModal 2022-11-15 18:32:19 +02:00
Erki Aas 79db9a63eb Inform the user that parent column combobox must be chosen first 2022-11-15 18:26:16 +02:00
Erki Aas 63d8c76c23 Fix glitch when opening column filter for the first time 2022-11-15 17:50:35 +02:00
Erki Aas 935a8ee923 Button to clear date filters 2022-11-15 17:34:12 +02:00
Erki Aas 87dee71a8c Date range picker for querying by timestamp 2022-11-11 15:35:42 +02:00
Erki Aas 6485e66931 Montserrat font for title 2022-11-11 15:34:48 +02:00
Erki Aas de259e7582 Restructuring 2022-11-11 15:33:33 +02:00
Erki Aas e6370642dd Rearrange some components 2022-11-09 20:08:35 +02:00
Erki Aas 11fbc22af5 Display level as colored chip 2022-11-09 12:04:04 +02:00
Erki Aas 09e732f78e Use log.level if exists, translate stream to level, display level as col 2022-11-09 12:02:39 +02:00
Erki Aas 14d35c7ec3 Prefer message from row's json object 2022-11-09 11:51:03 +02:00
Erki Aas ebcac70341 Main grid: fix row referencing, do not add duplicate rows 2022-11-09 11:28:08 +02:00
Erki Aas dc74e78d4b Keep query state in URL 2022-11-09 11:18:28 +02:00
Erki Aas 258571a064 Initial query mechanism 2022-11-08 20:17:45 +02:00
Erki Aas 63b2fb2c20 Performance improvements 2022-11-08 19:40:47 +02:00
Erki Aas d5c548193d Fix toggling streaming parameter in store 2022-11-08 19:40:33 +02:00
Erki Aas 38bbf5b8a7 Update package-lock.json 2022-11-08 19:39:52 +02:00
Erki Aas a87a1dbcfc Updated dev manifest, updated image name 2022-11-08 10:31:39 +02:00
Erki Aas 6df8a428e1 New streaming implementation
continuous-integration/drone Build is passing Details
2022-11-07 16:49:57 +02:00
Erki Aas fd372d09fc Started with new streaming implementation
continuous-integration/drone Build is failing Details
2022-11-07 16:43:22 +02:00
Erki Aas 30af9d31ec Store query as object not array 2022-11-07 16:41:23 +02:00
Erki Aas 1817adcc82 Notify user that the filtering/search query timed out 2022-11-07 15:21:13 +02:00
Erki Aas 5805b09725 Make timestamp column wider 2022-11-07 15:20:21 +02:00
Erki Aas 4fb4c9670d Dynamic filter options, Vuex store to pass them, improved code structure 2022-11-07 14:57:50 +02:00
Erki Aas ad2daab97d Formatting
continuous-integration/drone Build is passing Details
2022-11-05 22:42:28 +02:00
Erki Aas 4a4e4d0894 removed unused import
continuous-integration/drone Build is failing Details
2022-11-05 20:40:24 +02:00
Erki Aas c6fef60068 removed unused ScreenshotCell, introduced some directory structure, translation component and vue-select for column filtering
continuous-integration/drone Build is failing Details
2022-11-05 20:23:57 +02:00
28 changed files with 1701 additions and 255 deletions

View File

@ -1,4 +1,4 @@
FROM node AS dev
FROM harbor.k-space.ee/docker.io/library/node AS dev
WORKDIR /app
EXPOSE 8080

View File

@ -1,39 +1,37 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: playground
name: logmower-frontend
annotations:
kubernetes.io/ingress.class: traefik
cert-manager.io/cluster-issuer: default
kubernetes.io/ingress.class: erkiaas-yeaa8
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.middlewares: traefik-sso@kubernetescrd
traefik.ingress.kubernetes.io/router.tls: "true"
external-dns.alpha.kubernetes.io/target: traefik.k-space.ee
external-dns.alpha.kubernetes.io/target: traefik-yeaa8.codemowers.ee
spec:
rules:
- host: playground.k-space.ee
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: log-viewer-frontend
port:
number: 8080
- host: logs-yeaa8.codemowers.ee
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: logmower-frontend
port:
number: 8080
tls:
- hosts:
- playground.k-space.ee
secretName: playground-tls
- hosts:
- "*.codemowers.ee"
---
apiVersion: v1
kind: Service
metadata:
name: log-viewer-frontend
name: logmower-frontend
spec:
type: ClusterIP
selector:
app: log-viewer-frontend
app: logmower-frontend
ports:
- protocol: TCP
port: 8080
@ -41,18 +39,18 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
name: log-viewer-frontend
name: logmower-frontend
spec:
selector:
matchLabels:
app: log-viewer-frontend
app: logmower-frontend
template:
metadata:
labels:
app: log-viewer-frontend
app: logmower-frontend
spec:
containers:
- name: log-viewer-frontend
image: harbor.k-space.ee/playground/log-viewer-frontend
ports:
- containerPort: 8080
- name: logmower-frontend
image: harbor.codemowers.eu/eaas/logmower-frontend
ports:
- containerPort: 8080

570
package-lock.json generated
View File

@ -10,21 +10,28 @@
"dependencies": {
"@ag-grid-community/core": "^28.2.0",
"@mdi/font": "5.9.55",
"@meforma/vue-toaster": "^1.3.0",
"@vue/cli-service": "^5.0.8",
"ag-grid-vue3": "^28.2.0",
"ansi-to-html": "^0.7.2",
"core-js": "^3.25.1",
"event-hooks-webpack-plugin": "^2.2.0",
"pinia": "^2.0.21",
"roboto-fontface": "*",
"single-spa-vue": "^2.5.1",
"systemjs-webpack-interop": "^2.3.7",
"v-calendar": "next",
"v-viewer": "^3.0.11",
"vue": "^3.2.39",
"vue-select": "beta",
"vuetify": "^3.0.0-beta.0",
"vuex": "next",
"webfontloader": "^1.0.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^3.0.3",
"vite": "^3.0.9",
"vite-plugin-dynamic-import": "^1.2.4",
"vue-cli-plugin-vuetify": "~2.5.8",
"webpack-plugin-vuetify": "^2.0.0-alpha.0"
}
@ -580,6 +587,15 @@
"resolved": "https://registry.npmjs.org/@mdi/font/-/font-5.9.55.tgz",
"integrity": "sha512-jswRF6q3eq8NWpWiqct6q+6Fg/I7nUhrxYJfiEM8JJpap0wVJLQdbKtyS65GdlK7S7Ytnx3TTi/bmw+tBhkGmg=="
},
"node_modules/@meforma/vue-toaster": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@meforma/vue-toaster/-/vue-toaster-1.3.0.tgz",
"integrity": "sha512-jH0zOA/jTiT+UKHO9n5hjPTLkIfg7d66X4fnd7ssIbcXpZOoe+J8IY6Kf3nRW5iVD6/tkjeyp+tjVK8zk6zASg==",
"dependencies": {
"stylus": "~0.54.8",
"stylus-loader": "~3.0.2"
}
},
"node_modules/@node-ipc/js-queue": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@node-ipc/js-queue/-/js-queue-2.0.3.tgz",
@ -633,6 +649,15 @@
"integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==",
"license": "MIT"
},
"node_modules/@popperjs/core": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.4.0.tgz",
"integrity": "sha512-NMrDy6EWh9TPdSRiHmHH2ye1v5U0gBD7pRYwSwJvomx7Bm4GG04vu63dYiVzebLOx2obPpJugew06xVP0Nk7hA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@sideway/address": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz",
@ -808,6 +833,11 @@
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
"license": "MIT"
},
"node_modules/@types/lodash": {
"version": "4.14.189",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.189.tgz",
"integrity": "sha512-kb9/98N6X8gyME9Cf7YaqIMvYGnBSWqEci6tiettE6iJWH1XdJz/PO8LB0GtLCG7x8dU3KWhZT+lA1a35127tA=="
},
"node_modules/@types/mime": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
@ -1618,6 +1648,20 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/ansi-to-html": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.7.2.tgz",
"integrity": "sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g==",
"dependencies": {
"entities": "^2.2.0"
},
"bin": {
"ansi-to-html": "bin/ansi-to-html"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
@ -1690,6 +1734,17 @@
"node": ">= 4.0.0"
}
},
"node_modules/atob": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
"bin": {
"atob": "bin/atob.js"
},
"engines": {
"node": ">= 4.5.0"
}
},
"node_modules/autoprefixer": {
"version": "10.4.8",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.8.tgz",
@ -2415,6 +2470,17 @@
"semver": "bin/semver"
}
},
"node_modules/css": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz",
"integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==",
"dependencies": {
"inherits": "^2.0.3",
"source-map": "^0.6.1",
"source-map-resolve": "^0.5.2",
"urix": "^0.1.0"
}
},
"node_modules/css-declaration-sorter": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz",
@ -2510,6 +2576,14 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/css-parse": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz",
"integrity": "sha512-UNIFik2RgSbiTwIW1IsFwXWn6vs+bYdq83LKTSOsx7NJR7WII9dxewkHLltfTLVppoUApHV0118a4RZRI9FLwA==",
"dependencies": {
"css": "^2.0.0"
}
},
"node_modules/css-select": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
@ -2657,6 +2731,26 @@
"integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==",
"license": "MIT"
},
"node_modules/date-fns": {
"version": "2.29.3",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz",
"integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==",
"engines": {
"node": ">=0.11"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/date-fns"
}
},
"node_modules/date-fns-tz": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.3.7.tgz",
"integrity": "sha512-1t1b8zyJo+UI8aR+g3iqr5fkUHWpd58VBx8J/ZSQ+w7YrGlw80Ag4sA86qkfCXRBLmMc4I2US+aPMd4uKvwj5g==",
"peerDependencies": {
"date-fns": ">=2.0.0"
}
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@ -2681,6 +2775,14 @@
"callsite": "^1.0.0"
}
},
"node_modules/decode-uri-component": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
"engines": {
"node": ">=0.10"
}
},
"node_modules/deepmerge": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz",
@ -3411,9 +3513,9 @@
}
},
"node_modules/file-loader/node_modules/loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"devOptional": true,
"dependencies": {
"big.js": "^5.2.2",
@ -4426,10 +4528,9 @@
}
},
"node_modules/loader-utils": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
"license": "MIT",
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
"integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
"dependencies": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
@ -4457,6 +4558,11 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"node_modules/lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
},
"node_modules/lodash.defaultsdeep": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz",
@ -5059,9 +5165,9 @@
}
},
"node_modules/null-loader/node_modules/loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"devOptional": true,
"dependencies": {
"big.js": "^5.2.2",
@ -6386,6 +6492,12 @@
"node": ">=4"
}
},
"node_modules/resolve-url": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
"integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==",
"deprecated": "https://github.com/lydell/resolve-url#deprecated"
},
"node_modules/restore-cursor": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
@ -6524,6 +6636,11 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"license": "MIT"
},
"node_modules/sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"node_modules/schema-utils": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
@ -6857,6 +6974,19 @@
"node": ">=0.10.0"
}
},
"node_modules/source-map-resolve": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
"integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
"deprecated": "See https://github.com/lydell/source-map-resolve#deprecated",
"dependencies": {
"atob": "^2.1.2",
"decode-uri-component": "^0.2.0",
"resolve-url": "^0.2.1",
"source-map-url": "^0.4.0",
"urix": "^0.1.0"
}
},
"node_modules/source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
@ -6867,6 +6997,12 @@
"source-map": "^0.6.0"
}
},
"node_modules/source-map-url": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
"integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
"deprecated": "See https://github.com/lydell/source-map-url#deprecated"
},
"node_modules/sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
@ -7083,6 +7219,80 @@
"postcss": "^8.2.15"
}
},
"node_modules/stylus": {
"version": "0.54.8",
"resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.8.tgz",
"integrity": "sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg==",
"dependencies": {
"css-parse": "~2.0.0",
"debug": "~3.1.0",
"glob": "^7.1.6",
"mkdirp": "~1.0.4",
"safer-buffer": "^2.1.2",
"sax": "~1.2.4",
"semver": "^6.3.0",
"source-map": "^0.7.3"
},
"bin": {
"stylus": "bin/stylus"
},
"engines": {
"node": "*"
}
},
"node_modules/stylus-loader": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz",
"integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==",
"dependencies": {
"loader-utils": "^1.0.2",
"lodash.clonedeep": "^4.5.0",
"when": "~3.6.x"
},
"peerDependencies": {
"stylus": ">=0.52.4"
}
},
"node_modules/stylus/node_modules/debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/stylus/node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/stylus/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"node_modules/stylus/node_modules/semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/stylus/node_modules/source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
"engines": {
"node": ">= 8"
}
},
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@ -7261,10 +7471,9 @@
}
},
"node_modules/thread-loader/node_modules/loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"license": "MIT",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"dependencies": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
@ -7416,6 +7625,12 @@
"punycode": "^2.1.0"
}
},
"node_modules/urix": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
"integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==",
"deprecated": "Please see https://github.com/lydell/urix#deprecated"
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -7446,6 +7661,33 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/v-calendar": {
"version": "3.0.0-alpha.8",
"resolved": "https://registry.npmjs.org/v-calendar/-/v-calendar-3.0.0-alpha.8.tgz",
"integrity": "sha512-T23H5UbK0EomrwArlF/jrT2LFbV/lu+Bp9JroZ1paN6rPoaMyvE+HrLxvAmUgi+pODrdTURDMzM3+WPgeFKEBQ==",
"dependencies": {
"@popperjs/core": "2.4.0",
"@types/lodash": "^4.14.165",
"date-fns": "^2.16.1",
"date-fns-tz": "^1.0.12",
"lodash": "^4.17.20"
},
"peerDependencies": {
"vue": "^3.1.0"
}
},
"node_modules/v-viewer": {
"version": "3.0.11",
"resolved": "https://registry.npmjs.org/v-viewer/-/v-viewer-3.0.11.tgz",
"integrity": "sha512-E8LOdAxhzuktt4HB3PswVCccQ1Q1sYHYnLsS6zaJISpb5EvmAFs5sYNfXnDLFxVb5DQ82v4ZlGxkYlseXwWRJw==",
"dependencies": {
"lodash": "^4.17.21",
"viewerjs": "^1.9.0"
},
"peerDependencies": {
"vue": "^3.0.0"
}
},
"node_modules/validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
@ -7465,6 +7707,11 @@
"node": ">= 0.8"
}
},
"node_modules/viewerjs": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/viewerjs/-/viewerjs-1.11.1.tgz",
"integrity": "sha512-/VQ2zalHLZJOGIwlxOBtxagLZwNvU3Bf+nm692XlhNFxjBXRxpCVn+GeqmRFg9jK1Y2+Wf8PPGxZgTDN4pHXww=="
},
"node_modules/vite": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/vite/-/vite-3.1.0.tgz",
@ -7507,6 +7754,15 @@
}
}
},
"node_modules/vite-plugin-dynamic-import": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/vite-plugin-dynamic-import/-/vite-plugin-dynamic-import-1.2.4.tgz",
"integrity": "sha512-R6spqDhDm8VxaUNyEkybh9PM262ReO8WXJ6rjtyJ/H8Y4b+pq2uM9Xz5DMu7N7OxUGlqo9HSzT3VjhpbySXrng==",
"dev": true,
"dependencies": {
"fast-glob": "~3.2.12"
}
},
"node_modules/vue": {
"version": "3.2.39",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.39.tgz",
@ -7607,10 +7863,9 @@
}
},
"node_modules/vue-loader/node_modules/loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"license": "MIT",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"dependencies": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
@ -7620,6 +7875,14 @@
"node": ">=8.9.0"
}
},
"node_modules/vue-select": {
"version": "4.0.0-beta.5",
"resolved": "https://registry.npmjs.org/vue-select/-/vue-select-4.0.0-beta.5.tgz",
"integrity": "sha512-W9alTe9NwVn2GR9QFW5CbrX47yghEaJCpUVs9JTv9Q7CWmsNPp5kIlETdke4aFHphZvyjDUlyvH/7/8XzfVZkw==",
"peerDependencies": {
"vue": "3.x"
}
},
"node_modules/vue-style-loader": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
@ -7669,6 +7932,17 @@
}
}
},
"node_modules/vuex": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",
"integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
"dependencies": {
"@vue/devtools-api": "^6.0.0-beta.11"
},
"peerDependencies": {
"vue": "^3.0.2"
}
},
"node_modules/watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
@ -7981,9 +8255,9 @@
}
},
"node_modules/webpack-plugin-vuetify/node_modules/loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"devOptional": true,
"dependencies": {
"big.js": "^5.2.2",
@ -8066,6 +8340,11 @@
"webidl-conversions": "^3.0.0"
}
},
"node_modules/when": {
"version": "3.6.4",
"resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz",
"integrity": "sha512-d1VUP9F96w664lKINMGeElWdhhb5sC+thXM+ydZGU3ZnaE09Wv6FaS+mpM9570kcDs/xMfcXJBTLsMdHEFYY9Q=="
},
"node_modules/which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@ -8591,6 +8870,15 @@
"resolved": "https://registry.npmjs.org/@mdi/font/-/font-5.9.55.tgz",
"integrity": "sha512-jswRF6q3eq8NWpWiqct6q+6Fg/I7nUhrxYJfiEM8JJpap0wVJLQdbKtyS65GdlK7S7Ytnx3TTi/bmw+tBhkGmg=="
},
"@meforma/vue-toaster": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@meforma/vue-toaster/-/vue-toaster-1.3.0.tgz",
"integrity": "sha512-jH0zOA/jTiT+UKHO9n5hjPTLkIfg7d66X4fnd7ssIbcXpZOoe+J8IY6Kf3nRW5iVD6/tkjeyp+tjVK8zk6zASg==",
"requires": {
"stylus": "~0.54.8",
"stylus-loader": "~3.0.2"
}
},
"@node-ipc/js-queue": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@node-ipc/js-queue/-/js-queue-2.0.3.tgz",
@ -8627,6 +8915,11 @@
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz",
"integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g=="
},
"@popperjs/core": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.4.0.tgz",
"integrity": "sha512-NMrDy6EWh9TPdSRiHmHH2ye1v5U0gBD7pRYwSwJvomx7Bm4GG04vu63dYiVzebLOx2obPpJugew06xVP0Nk7hA=="
},
"@sideway/address": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz",
@ -8773,6 +9066,11 @@
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
"integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ=="
},
"@types/lodash": {
"version": "4.14.189",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.189.tgz",
"integrity": "sha512-kb9/98N6X8gyME9Cf7YaqIMvYGnBSWqEci6tiettE6iJWH1XdJz/PO8LB0GtLCG7x8dU3KWhZT+lA1a35127tA=="
},
"@types/mime": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
@ -9385,6 +9683,14 @@
"color-convert": "^2.0.1"
}
},
"ansi-to-html": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/ansi-to-html/-/ansi-to-html-0.7.2.tgz",
"integrity": "sha512-v6MqmEpNlxF+POuyhKkidusCHWWkaLcGRURzivcU3I9tv7k4JVhFcnukrM5Rlk2rUywdZuzYAZ+kbZqWCnfN3g==",
"requires": {
"entities": "^2.2.0"
}
},
"any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
@ -9427,6 +9733,11 @@
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg=="
},
"atob": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
},
"autoprefixer": {
"version": "10.4.8",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.8.tgz",
@ -9896,6 +10207,17 @@
}
}
},
"css": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz",
"integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==",
"requires": {
"inherits": "^2.0.3",
"source-map": "^0.6.1",
"source-map-resolve": "^0.5.2",
"urix": "^0.1.0"
}
},
"css-declaration-sorter": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz",
@ -9943,6 +10265,14 @@
}
}
},
"css-parse": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz",
"integrity": "sha512-UNIFik2RgSbiTwIW1IsFwXWn6vs+bYdq83LKTSOsx7NJR7WII9dxewkHLltfTLVppoUApHV0118a4RZRI9FLwA==",
"requires": {
"css": "^2.0.0"
}
},
"css-select": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
@ -10039,6 +10369,17 @@
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz",
"integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA=="
},
"date-fns": {
"version": "2.29.3",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz",
"integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA=="
},
"date-fns-tz": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.3.7.tgz",
"integrity": "sha512-1t1b8zyJo+UI8aR+g3iqr5fkUHWpd58VBx8J/ZSQ+w7YrGlw80Ag4sA86qkfCXRBLmMc4I2US+aPMd4uKvwj5g==",
"requires": {}
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@ -10063,6 +10404,11 @@
"callsite": "^1.0.0"
}
},
"decode-uri-component": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ=="
},
"deepmerge": {
"version": "1.5.2",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz",
@ -10561,9 +10907,9 @@
"devOptional": true
},
"loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"devOptional": true,
"requires": {
"big.js": "^5.2.2",
@ -11223,9 +11569,9 @@
"integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg=="
},
"loader-utils": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
"integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
@ -11245,6 +11591,11 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
},
"lodash.defaultsdeep": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz",
@ -11643,9 +11994,9 @@
"devOptional": true
},
"loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"devOptional": true,
"requires": {
"big.js": "^5.2.2",
@ -12464,6 +12815,11 @@
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
},
"resolve-url": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
"integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg=="
},
"restore-cursor": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
@ -12538,6 +12894,11 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"schema-utils": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
@ -12776,6 +13137,18 @@
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
},
"source-map-resolve": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
"integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
"requires": {
"atob": "^2.1.2",
"decode-uri-component": "^0.2.0",
"resolve-url": "^0.2.1",
"source-map-url": "^0.4.0",
"urix": "^0.1.0"
}
},
"source-map-support": {
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
@ -12785,6 +13158,11 @@
"source-map": "^0.6.0"
}
},
"source-map-url": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
"integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw=="
},
"sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
@ -12941,6 +13319,61 @@
"postcss-selector-parser": "^6.0.4"
}
},
"stylus": {
"version": "0.54.8",
"resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.8.tgz",
"integrity": "sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg==",
"requires": {
"css-parse": "~2.0.0",
"debug": "~3.1.0",
"glob": "^7.1.6",
"mkdirp": "~1.0.4",
"safer-buffer": "^2.1.2",
"sax": "~1.2.4",
"semver": "^6.3.0",
"source-map": "^0.7.3"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
},
"source-map": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="
}
}
},
"stylus-loader": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz",
"integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==",
"requires": {
"loader-utils": "^1.0.2",
"lodash.clonedeep": "^4.5.0",
"when": "~3.6.x"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@ -13043,9 +13476,9 @@
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA=="
},
"loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
@ -13140,6 +13573,11 @@
"punycode": "^2.1.0"
}
},
"urix": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
"integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg=="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -13160,6 +13598,27 @@
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
},
"v-calendar": {
"version": "3.0.0-alpha.8",
"resolved": "https://registry.npmjs.org/v-calendar/-/v-calendar-3.0.0-alpha.8.tgz",
"integrity": "sha512-T23H5UbK0EomrwArlF/jrT2LFbV/lu+Bp9JroZ1paN6rPoaMyvE+HrLxvAmUgi+pODrdTURDMzM3+WPgeFKEBQ==",
"requires": {
"@popperjs/core": "2.4.0",
"@types/lodash": "^4.14.165",
"date-fns": "^2.16.1",
"date-fns-tz": "^1.0.12",
"lodash": "^4.17.20"
}
},
"v-viewer": {
"version": "3.0.11",
"resolved": "https://registry.npmjs.org/v-viewer/-/v-viewer-3.0.11.tgz",
"integrity": "sha512-E8LOdAxhzuktt4HB3PswVCccQ1Q1sYHYnLsS6zaJISpb5EvmAFs5sYNfXnDLFxVb5DQ82v4ZlGxkYlseXwWRJw==",
"requires": {
"lodash": "^4.17.21",
"viewerjs": "^1.9.0"
}
},
"validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
@ -13174,6 +13633,11 @@
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
},
"viewerjs": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/viewerjs/-/viewerjs-1.11.1.tgz",
"integrity": "sha512-/VQ2zalHLZJOGIwlxOBtxagLZwNvU3Bf+nm692XlhNFxjBXRxpCVn+GeqmRFg9jK1Y2+Wf8PPGxZgTDN4pHXww=="
},
"vite": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/vite/-/vite-3.1.0.tgz",
@ -13187,6 +13651,15 @@
"rollup": "~2.78.0"
}
},
"vite-plugin-dynamic-import": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/vite-plugin-dynamic-import/-/vite-plugin-dynamic-import-1.2.4.tgz",
"integrity": "sha512-R6spqDhDm8VxaUNyEkybh9PM262ReO8WXJ6rjtyJ/H8Y4b+pq2uM9Xz5DMu7N7OxUGlqo9HSzT3VjhpbySXrng==",
"dev": true,
"requires": {
"fast-glob": "~3.2.12"
}
},
"vue": {
"version": "3.2.39",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.2.39.tgz",
@ -13242,9 +13715,9 @@
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA=="
},
"loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
@ -13253,6 +13726,12 @@
}
}
},
"vue-select": {
"version": "4.0.0-beta.5",
"resolved": "https://registry.npmjs.org/vue-select/-/vue-select-4.0.0-beta.5.tgz",
"integrity": "sha512-W9alTe9NwVn2GR9QFW5CbrX47yghEaJCpUVs9JTv9Q7CWmsNPp5kIlETdke4aFHphZvyjDUlyvH/7/8XzfVZkw==",
"requires": {}
},
"vue-style-loader": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
@ -13273,6 +13752,14 @@
"integrity": "sha512-CgDyKCIBZ0ogKUxRw3gNswx9GA6eSSp7otZcJapWJCVuon/RooQPQjf3bDuRb5ZpJ55NkX13ugjl2nq+b0U8SA==",
"requires": {}
},
"vuex": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",
"integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
"requires": {
"@vue/devtools-api": "^6.0.0-beta.11"
}
},
"watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
@ -13483,9 +13970,9 @@
"devOptional": true
},
"loader-utils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
"integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
"integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
"devOptional": true,
"requires": {
"big.js": "^5.2.2",
@ -13540,6 +14027,11 @@
"webidl-conversions": "^3.0.0"
}
},
"when": {
"version": "3.6.4",
"resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz",
"integrity": "sha512-d1VUP9F96w664lKINMGeElWdhhb5sC+thXM+ydZGU3ZnaE09Wv6FaS+mpM9570kcDs/xMfcXJBTLsMdHEFYY9Q=="
},
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",

View File

@ -10,21 +10,28 @@
"dependencies": {
"@ag-grid-community/core": "^28.2.0",
"@mdi/font": "5.9.55",
"@meforma/vue-toaster": "^1.3.0",
"@vue/cli-service": "^5.0.8",
"ag-grid-vue3": "^28.2.0",
"ansi-to-html": "^0.7.2",
"core-js": "^3.25.1",
"event-hooks-webpack-plugin": "^2.2.0",
"pinia": "^2.0.21",
"roboto-fontface": "*",
"single-spa-vue": "^2.5.1",
"systemjs-webpack-interop": "^2.3.7",
"v-calendar": "next",
"v-viewer": "^3.0.11",
"vue": "^3.2.39",
"vue-select": "beta",
"vuetify": "^3.0.0-beta.0",
"vuex": "next",
"webfontloader": "^1.0.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^3.0.3",
"vite": "^3.0.9",
"vite-plugin-dynamic-import": "^1.2.4",
"vue-cli-plugin-vuetify": "~2.5.8",
"webpack-plugin-vuetify": "^2.0.0-alpha.0"
}

View File

@ -1,11 +1,11 @@
apiVersion: skaffold/v3alpha1
kind: Config
metadata:
name: log-viewer-backend
name: logmower-frontend
build:
artifacts:
- image: harbor.k-space.ee/playground/log-viewer-frontend
- image: harbor.codemowers.eu/erkiaas/logmower-frontend
docker:
dockerfile: Dockerfile
@ -22,9 +22,9 @@ profiles:
- command: dev
build:
artifacts:
- image: harbor.k-space.ee/playground/log-viewer-frontend
- image: harbor.k-space.ee/playground/logmower-frontend
docker:
target: dev
target: prod
sync:
manual:
- src: 'src/**/*.vue'
@ -39,4 +39,4 @@ profiles:
dest: .
manifests:
rawYaml:
- k8s/dev/deployment.yaml
- k8s/dev/deployment-camtiler.yaml

View File

@ -1,15 +1,51 @@
div#app {
width: 100%;
height: 100vh;
font-family: 'Roboto Mono';
}
.screenshots-drawer {
position: fixed;
display: flex;
flex-direction: column;
flex-direction: row;
}
.screenshots-drawer img {
margin-right: 0;
}
.ag-theme-material {
--ag-value-change-value-highlight-background-color: #f9ff99;
}
}
.ag-popup-child .ag-filter {
width: 300px;
height: 390px;
}
.app-title {
font-family: 'Montserrat';
text-transform: capitalize;
}
.vc-container {
font-family: 'Roboto Mono' !important;
}
:root {
--vs-font-size: 1em!important;
}
.ag-overlay-panel {
display: flex;
width: 100%;
height: calc(100% - 56px);
margin-top: 56px;
}
.ag-theme-material {
--ag-font-family: 'Roboto Mono'!important;
}
.v--default-css .c-toast {
font-family: 'Roboto Mono'!important;
}

View File

@ -1,29 +0,0 @@
export default {
template: `
<select v-model="filter" class="v-select">
<option value=""> </option>
<option v-for="option in params.options" :value="option">
{{ option }}
</option>
</select>
`,
data: function () {
return {
filter: '',
};
},
methods: {
updateFilter() {
this.params.filterChangedCallback();
},
doesFilterPass(params) {
const value = this.params.field.split('.').reduce((a, b) => a[b], params.data);
return value === this.filter;
},
isFilterActive() {
return this.filter !== ''
},
},
};

View File

@ -0,0 +1,53 @@
<template>
<v-select
v-model="filterValue"
:options="options"
:placeholder="placeholder"
@open="updateOptions"
></v-select>
</template>
<script>
import vSelect from "vue-select"
export default {
name: "Combobox",
components: {
vSelect
},
props: {
field: {
},
changeValue: {
},
filter: {
},
placeholder: {
},
},
data() {
return {
options: []
}
},
computed: {
filterValue: {
get() {
return this.filter
},
set(newValue) {
this.changeValue(newValue)
}
}
},
methods: {
updateOptions() {
this.options = this.$store.state.filterOptions[this.field] ?? []
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,51 @@
import Combobox from "./Combobox.vue";
export default {
// This is a helper component to translate between ag-grid's custom filter logic and a regular Vue component
// https://www.ag-grid.com/vue-data-grid/component-filter/#custom-filter-interface-3
components: {
Combobox
},
template: `<Combobox
:field="params.field"
:filter="filter"
:change-value="updateFilter"
:placeholder="placeholder"
/>`,
mounted() {
this.params.api.sizeColumnsToFit()
},
data: function () {
return {
filter: '',
};
},
computed: {
placeholder() {
let parentColumnName = this.params.column.userProvidedColDef.filterParams.parentColumn
if (parentColumnName) {
let filterInstance = this.params.api.getFilterInstance(parentColumnName)
if (!filterInstance.filter || filterInstance.filter === '') {
let displayName = filterInstance.params.column.userProvidedColDef.headerName
return `Please select ${displayName} first`
}
}
return ''
}
},
methods: {
updateFilter(value) {
this.filter = value
this.params.filterChangedCallback();
},
doesFilterPass(params) {
const value = this.params.field.split('.').reduce((a, b) => a[b], params.data);
return value === this.filter;
},
isFilterActive() {
return this.filter !== '' && this.filter
},
},
};

View File

@ -0,0 +1,96 @@
<template>
<v-date-picker mode="dateTime" v-model="dateRange" locale="et" :is24hr="true" is-range timezone="UTC">
<template v-slot="{ inputValue, inputEvents }">
<div class="flex justify-center items-center align-center">
<input
:value="inputValue.start"
v-on="inputEvents.start"
class="border px-2 py-1 w-32 rounded focus:outline-none focus:border-indigo-300"
/>
<svg
class="mx-2"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
width="20"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M14 5l7 7m0 0l-7 7m7-7H3"
/>
</svg>
<input
:value="inputValue.end"
v-on="inputEvents.end"
class="border px-2 py-1 w-32 rounded focus:outline-none focus:border-indigo-300"
/>
<v-icon class="ml-2" @click="clearDates">mdi-close</v-icon>
</div>
</template>
</v-date-picker>
</template>
<script>
import {mapActions, mapGetters} from "vuex";
import { VIcon } from 'vuetify/components/VIcon'
import { VBtn } from 'vuetify/components/VBtn'
export default {
name: "Datepicker",
components: {
VIcon,
VBtn
},
props: {
refresh: {}
},
data() {
return {
}
},
computed: {
...mapGetters([
'filterQuery',
]),
dateRange: {
get() {
return {
start: this.filterQuery.from ? new Date(this.filterQuery.from) : null,
end: this.filterQuery.to ? new Date(this.filterQuery.to) : null,
}
},
set(value) {
if (value) {
let toDate = new Date(value.end);
toDate.setSeconds(59, 999);
this.setFilterQueryTimeRange({
from: (new Date(value.start).getTime()),
to: (toDate.getTime()),
})
this.refresh()
}
}
}
},
methods: {
...mapActions({
setFilterQueryTimeRange: 'setFilterQueryTimeRange',
}),
clearDates () {
this.setFilterQueryTimeRange({
from: null,
to: null,
})
this.refresh()
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,52 @@
import { VRow, VCol } from 'vuetify/components/VGrid'
import { VIcon } from 'vuetify/components/VIcon'
import 'ansi-to-html'
import Convert from "ansi-to-html";
export default {
template: `<v-row>
<v-col>
<v-icon v-if="icon" :color="color" class="mr-2">{{ icon }}</v-icon>
<span v-html="message"></span>
</v-col>
</v-row>`,
components: {
VRow,
VCol,
VIcon
},
setup(props) {
let message = props.params.value
let converter = new Convert()
message = converter.toHtml(message)
let level = props.params.data.level
let icons = {
'emergency': 'mdi-alert-circle',
'alert': 'mdi-alert-circle',
'critical': 'mdi-alert-circle',
'error': 'mdi-alert-circle',
'warning': 'mdi-alert-circle',
'notice': 'mdi-alert-circle',
'info': 'mdi-information',
'debug': 'mdi-information',
}
let colors = {
'emergency': 'red',
'alert': 'red',
'critical': 'red',
'error': 'red',
'warning': 'orange',
'notice': 'orange',
'info': 'green',
'debug': 'green',
}
let color = colors[level] ?? null
let icon = icons[level] ?? null
return {
message,
color,
icon
};
},
};

View File

@ -1,30 +1,20 @@
export default {
template: `<div>
<a @click="openDrawer">View screenshots</a>
<div v-if="drawerOpen" class="screenshots-drawer">
<img v-for="screenshot in screenshots" :src="screenshot.orig"/>
<div class="screenshots-drawer">
<img v-for="screenshot in screenshots" :src="screenshot.thumb"/>
</div>
</div>`,
data: function () {
return {
screenshots: [],
drawerOpen: false,
};
},
beforeMount() {
this.updateImage(this.params);
this.updateImage(this.params);
},
methods: {
updateImage(params) {
this.screenshots = params.value
this.value = params.value;
},
refresh(params) {
this.updateImage(params);
},
openDrawer () {
this.drawerOpen = true
}
},
};
};

View File

@ -0,0 +1,46 @@
const config = {
defaultColDef: {
width: 120,
initialPinned: true,
resizable: true,
enableCellChangeFlash: true
},
columnDefs: [
{
field: '@timestamp',
width: 140,
sortable: true
},
{
field: 'source',
tooltipValueGetter: (params) => params.value,
filter: 'ComboboxFilter',
filterParams: {
options: [],
field: 'source',
parentColumn: null,
}
},
{
field: 'event',
tooltipValueGetter: (params) => params.value,
},
{
field: 'started',
width: 140,
tooltipValueGetter: (params) => params.value,
},
{
field: 'finished',
width: 140,
tooltipValueGetter: (params) => params.value,
},
{
field: 'screenshots',
cellRenderer: 'ScreenshotRenderer',
width: 450,
},
],
}
export default config

View File

@ -0,0 +1,58 @@
import ComboboxFilter from "../Filter/ComboboxFilter";
const config = {
defaultColDef: {
width: 120,
initialPinned: true,
resizable: true,
enableCellChangeFlash: true
},
columnDefs: [
{
field: '@timestamp',
width: 120,
sortable: true
},
{
field: 'kubernetes.namespace',
headerName: 'namespace',
tooltipValueGetter: (params) => params.value,
filter: ComboboxFilter,
filterParams: {
options: [],
field: 'kubernetes.namespace',
parentColumn: null,
}
},
{
field: 'kubernetes.pod.name',
headerName: 'pod',
tooltipValueGetter: (params) => params.value,
filter: ComboboxFilter,
filterParams: {
options: [],
field: 'kubernetes.pod.name',
parentColumn: 'kubernetes.namespace',
}
},
{
field: 'kubernetes.container.name',
headerName: 'container',
tooltipValueGetter: (params) => params.value,
filter: ComboboxFilter,
filterParams: {
options: [],
field: 'kubernetes.container.name',
parentColumn: 'kubernetes.pod.name',
}
},
{
field: 'message',
tooltipValueGetter: (params) => params.value,
cellRenderer: 'MessageWithLevelRenderer',
width: 500,
},
],
}
export default config

View File

@ -0,0 +1,7 @@
export default {
template: `
<div class="ag-overlay-loading-center">
Loading
</div>
`,
};

View File

@ -0,0 +1,85 @@
<template>
<v-tooltip :text="`Last ping from server received at ${secondsFromLastPing} seconds ago`" location="bottom">
<template v-slot:activator="{ props }">
<v-icon :color="(secondsFromLastPing > pingThreshold || secondsFromLastPing === null) ? 'red' : 'green'" v-bind="props" class="mr-6">mdi-server</v-icon>
</template>
</v-tooltip>
</template>
<script>
import { VTooltip } from 'vuetify/components/VTooltip'
import { VIcon } from 'vuetify/components/VIcon'
import {mapGetters} from "vuex";
export default {
name: "ConnectionMonitor",
components: {
VTooltip,
VIcon
},
data() {
return {
pingThreshold: 10,
pingError: false,
secondsFromLastPing: null,
setupStreamInterval: null,
}
},
computed: {
...mapGetters([
'lastPingReceived'
])
},
mounted() {
// window.setInterval(() => {
// this.monitorServerConnection()
// }, 10000)
window.setInterval(() => {
this.secondsFromLastPing = Math.floor(((new Date()).getTime() - this.lastPingReceived) / 1000)
this.pingError = this.secondsFromLastPing > this.pingThreshold
}, 1000)
},
watch: {
pingError(error) {
if (error) {
this.$toast.error(`Connection with server lost, attempting to reconnect...`, {
position: "top-right",
duration: false,
});
this.setupStreamInterval = window.setInterval(() => {
this.$emit('setupStream')
}, 3000)
} else {
window.clearTimeout(this.setupStreamInterval);
this.$toast.clear();
this.$toast.success(`Connection with server reestablished`, {
position: "top-right",
});
}
},
},
methods: {
monitorServerConnection () {
if (this.secondsFromLastPing > this.pingThreshold) {
this.pingError = true;
this.$toast.error(`Connection with server lost, attempting to reconnect...`, {
position: "top-right",
});
this.setupStream();
} else {
if (this.pingError) {
this.$toast.success(`Connection with server reestablished`, {
position: "top-right",
});
setTimeout(this.$toast.clear, 3000);
}
this.pingError = false;
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,59 @@
<template>
<v-row no-gutters>
<v-col cols="12" sm="5" class="d-flex justify-start flex-wrap" >
<Datepicker class="ma-2" :refresh="refreshFilterState" />
</v-col>
<v-col cols="12" sm="2" class="d-flex justify-center flex-wrap">
<h1 class="app-title"> {{ title }} </h1>
</v-col>
<v-col cols="12" sm="5" class="d-flex justify-end flex-wrap align-center">
<ConnectionMonitor @setup-stream="setupStream" />
<v-btn
color="blue-grey"
class="ma-2"
:prepend-icon="streaming ? 'mdi-pause' :'mdi-play'"
@click="toggleFilterQueryStreaming"
>
Stream new lines
</v-btn>
</v-col>
</v-row>
</template>
<script>
import { VRow, VCol } from 'vuetify/components/VGrid'
import { VBtn } from 'vuetify/components/VBtn'
import Datepicker from "../Grid/Main/Filter/Datepicker.vue";
import {mapActions, mapGetters} from "vuex";
import ConnectionMonitor from "./ConnectionMonitor.vue";
export default {
components: {
ConnectionMonitor,
Datepicker,
VRow,
VCol,
VBtn
},
props: {
refreshFilterState: Function,
title: String
},
computed: {
...mapGetters([
'streaming'
]),
},
methods: {
...mapActions({
toggleFilterQueryStreaming: 'toggleFilterQueryStreaming',
}),
setupStream () {
this.$emit('setupStream')
}
}
}
</script>
<style scoped>
</style>

View File

@ -1,11 +1,14 @@
<template>
<div style="height: 100%; width: 100%">
<ag-grid-vue
style="width: 100%; height: 100%;"
<div style="height: 100%; width: 100%;" v-resize="onResize">
<Header :refresh-filter-state="refreshFilterState" :title="backend" @setup-stream="setupStream" />
<ag-grid-vue
v-if="columnDefs"
style="width: 100%; height: calc(100% - 52px);"
class="ag-theme-material"
@grid-ready="onGridReady"
:defaultColDef="defaultColDef"
:columnDefs="columnDefs"
:getRowId="params => params.data._id"
:pagination="true"
:paginationAutoPageSize=true
:row-data="null"
@ -13,8 +16,12 @@
:onRowSelected="openExamineLog"
:supress-horisontal-scroll="true"
:enable-scrolling="true"
:isExternalFilterPresent="() => {return true}"
:doesExternalFilterPass="doesExternalFilterPass"
:loadingOverlayComponent="'loadingOverlay'"
></ag-grid-vue>
<ExamineLogModal :examine-log-content="examineLogContent" :close-modal="closeExamineLog" />
<ExamineLogModal v-if="backend === 'logmower'" :examine-log-content="examineLogContent" :close-modal="closeExamineLog" />
<ExamineCamModal v-if="backend === 'camtiler'" :examine-log-content="examineLogContent" :close-modal="closeExamineLog" />
</div>
</template>
@ -22,123 +29,243 @@
import { AgGridVue } from "ag-grid-vue3";
import "ag-grid-community/styles//ag-grid.css";
import "ag-grid-community/styles//ag-theme-material.css";
import ScreenshotCell from "./ScreenshotCell.js";
import ExamineLogModal from "./ExamineLogModal.vue";
import ComboboxFilter from "./ComboboxFilter.js";
import { Resize } from 'vuetify/directives';
import ExamineLogModal from "./Modal/ExamineLogModal.vue";
import ExamineCamModal from "./Modal/ExamineCamModal.vue";
import ComboboxFilter from "./Grid/Main/Filter/ComboboxFilter.js";
import MessageWithLevelRenderer from "./Grid/Main/MessageWithLevelRenderer";
import ScreenshotRenderer from "./Grid/Main/ScreenshotRenderer";
import flattenObj from "../helpers/flattenObj";
import parseEventData from "../helpers/parseEventData";
import {mapActions, mapGetters} from 'vuex';
import loadingOverlay from "./Grid/Main/loadingOverlay";
import Header from "./Header/Header.vue";
export default {
components: {
ExamineLogModal,
Header,
ExamineLogModal, // TODO: dynamic loading
ExamineCamModal,
AgGridVue,
ComboboxFilter,
ScreenshotCell: ScreenshotCell
MessageWithLevelRenderer,
ScreenshotRenderer,
loadingOverlay
},
directives: {
Resize
},
data() {
return {
examineLogContent: null,
gridApi: null,
gridColumnApi: null,
defaultColDef: {
width: 50,
initialPinned: true,
resizable: true,
enableCellChangeFlash: true
},
currentRowCount: 0,
comboBoxOptions: {},
viewRowCount: 20,
es: null,
initialFilter: null,
defaultColDef: null,
columnDefs: null,
backend: 'logmower'
}
},
computed: {
columnDefs() {
return [
{
field: '@timestamp',
width: 70,
sortable: true
},
{
field: 'kubernetes.namespace',
headerName: 'namespace',
tooltipValueGetter: (params) => params.value,
filter: ComboboxFilter,
filterParams: {
options: this.comboBoxOptions['kubernetes.namespace'],
field: 'kubernetes.namespace',
}
},
{
field: 'kubernetes.pod.name',
headerName: 'pod',
tooltipValueGetter: (params) => params.value,
filter: ComboboxFilter,
filterParams: {
options: this.comboBoxOptions['kubernetes.pod.name'],
field: 'kubernetes.pod.name',
}
},
{
field: 'kubernetes.container.name',
headerName: 'container',
tooltipValueGetter: (params) => params.value,
filter: ComboboxFilter,
filterParams: {
options: this.comboBoxOptions['kubernetes.container.name'],
field: 'kubernetes.container.name',
}
},
{
field: 'message',
tooltipValueGetter: (params) => params.value,
width: 500,
},
{
field: 'stream',
},
];
}
...mapGetters([
'filterQuery',
'streaming'
]),
},
watch: {
filterQuery() {
if (this.backend) {
this.setupStream()
}
},
streaming() {
if (this.backend) {
this.setupStream()
}
},
},
created() {
this.setupStream()
fetch('/events/backend')
.then((response) => response.text())
.then((response) => {
this.backend = response // TODO handle bad gateway etc
import(`./Grid/Main/configs/${response}.js`)
.then(module => {
this.defaultColDef = module.default.defaultColDef
this.columnDefs = module.default.columnDefs
}).catch(err => {
console.error(err)
this.$toast.error(`Backend '${response}' not supported`, {
position: "top-right",
});
}).then(() => {
let queryParams = new URLSearchParams(window.location.search);
queryParams = Object.fromEntries(queryParams);
this.initialFilter = queryParams
queryParams['initial'] = true
queryParams['from'] && (queryParams['from'] = Number(queryParams['from']))
queryParams['to'] && (queryParams['to'] = Number(queryParams['to']))
this.setFilterQuery(queryParams)
});
})
},
methods: {
...mapActions({
setFilterOptions: 'setFilterOptions',
setFilterQuery: 'setFilterQuery',
setLastPingReceived: 'setLastPingReceived',
}),
onResize () {
if (this.gridApi) {
this.gridApi.sizeColumnsToFit()
}
},
refreshFilterState() {
this.gridApi.onFilterChanged();
},
setupStream() {
let es = new EventSource('/events');
es.onmessage = (e) => this.handleReceiveMessage(e)
es.addEventListener("filters", (e) => this.handleReceiveFilters(e))
this.es && this.es.close();
let url = new URL('/events', window.location.href);
for (const key in this.filterQuery) {
url.searchParams.append(key, this.filterQuery[key]);
}
if (url.searchParams.keys().next()) {
let es = new EventSource(url.toString());
es.onmessage = (e) => this.handleReceiveMessage(e)
es.addEventListener("ping", (e) => this.handleReceivePing())
es.addEventListener("filters", (e) => this.handleReceiveFilters(e))
es.addEventListener("timeout", (e) => this.handleReceiveTimeout(e))
es.addEventListener("completed", (e) => this.handleAllReceived(e))
this.es = es
if (this.gridApi) {
this.gridApi.showLoadingOverlay();
}
}
url.searchParams.delete('initial')
if (url.searchParams.get('streaming') === 'false') {
url.searchParams.delete('streaming')
}
window.history.replaceState({}, '', `${location.pathname}?${url.searchParams.toString()}`);
},
onGridReady(params) {
this.gridApi = params.api;
this.gridColumnApi = params.columnApi;
this.gridColumnApi.applyColumnState({
state: [{
colId: '@timestamp',
sort: 'desc'
}]
});
for (let k in this.initialFilter) {
let filterInstance = params.api.getFilterInstance(k);
if (filterInstance) {
filterInstance.updateFilter(this.initialFilter[k]);
}
}
params.api.onFilterChanged();
this.initialFilter = null
this.gridApi.addGlobalListener((type, event) => {
if (type === 'filterChanged') {
let changedColumn = event.columns[0] ? (event.columns[0].colId) : null
let query = {}
let gridColumns = event.columnApi.columnModel.gridColumns
gridColumns.map((column) => {
// Reset child column filter if parent changed
let parentColumn = column?.colDef?.filterParams?.parentColumn
if (parentColumn && changedColumn === parentColumn) {
let filterInstance = this.gridApi.getFilterInstance(column.colId);
column.filterActive = null
filterInstance.updateFilter(null)
this.gridApi.onFilterChanged();
}
if (column.filterActive) {
query[column.colId] = column.filterActive
}
})
query['streaming'] = this.streaming
this.filterQuery.from && (query['from'] = this.filterQuery.from)
this.filterQuery.to && (query['to'] = this.filterQuery.to)
this.setFilterQuery(query)
}
});
},
handleReceiveMessage (event) {
const eventData = this.parseEventData(event.data);
const res = this.gridApi.applyTransaction({
add: [eventData]
});
const rowNode = res.add[0]
this.gridApi.flashCells({ rowNodes: [rowNode]});
this.gridApi.sizeColumnsToFit()
const eventData = parseEventData(event.data);
if (!this.gridApi.getRowNode(eventData._id)) {
this.gridApi.applyTransactionAsync({
add: [eventData]
}, (res) => {
const rowNode = res.add[0]
this.gridApi.flashCells({ rowNodes: [rowNode]});
})
}
},
handleReceiveFilters (event) {
this.comboBoxOptions = this.parseEventData(event.data);
},
parseEventData (eventData) {
try {
let json = JSON.parse(eventData)
if (!json.message) {
json.message = JSON.stringify(json.json)
let data = parseEventData(event.data);
let opts = this.comboBoxOptions
for (let k in data) {
if (!(k in opts)) {
opts[k] = []
}
// TODO: proper merging
if ((data[k].parentKey) || (opts[k].length === 0)) {
opts[k].push(data[k])
}
return json
} catch (e) {
console.error(e, eventData)
}
this.comboBoxOptions = opts
let correctOptions = {};
for (let column in opts) {
correctOptions[column] = []
let columnDef = this.columnDefs.find((columnDef) => {
return columnDef.field === column
});
let parentColumnName = columnDef?.filterParams?.parentColumn;
let possibleColumnOptions = opts[column].filter((k) => {
return k.parentKey === parentColumnName
})
if (possibleColumnOptions.length === 1) {
correctOptions[column] = possibleColumnOptions[0].options
} else if (possibleColumnOptions.length > 1) {
let filterInstance = this.gridApi.getFilterInstance(parentColumnName)
possibleColumnOptions.forEach((opt) => {
if (filterInstance && (opt.parentValue === filterInstance.filter)) {
correctOptions[column] = opt.options
}
})
}
}
this.gridApi.sizeColumnsToFit()
this.setFilterOptions(correctOptions)
},
handleReceiveTimeout () {
this.$toast.warning(`Not all rows were loaded. Please use more precise filtering`, {
position: "top-right",
});
setTimeout(this.$toast.clear, 3000);
this.gridApi.hideOverlay();
},
handleAllReceived () {
this.gridApi.hideOverlay();
},
handleReceivePing () {
this.setLastPingReceived()
},
doesExternalFilterPass(node) {
if (node.data && this.filterQuery.from && this.filterQuery.to) {
let ts = new Date(node.data['@timestamp']).getTime()
return (ts >= this.filterQuery.from && ts <= this.filterQuery.to)
}
return true;
},
openExamineLog (row) {
const selectedRow = row.data
row.node.setSelected(false)
this.examineLog = true
const flattened = flattenObj(selectedRow)
const pairs = [];
Object.keys(flattened).map((key) => {
@ -155,19 +282,5 @@ export default {
},
}
const flattenObj = (ob) => {
let result = {};
for (const i in ob) {
if ((typeof ob[i]) === 'object' && !Array.isArray(ob[i])) {
const temp = flattenObj(ob[i]);
for (const j in temp) {
result[i + '.' + j] = temp[j];
}
}
else {
result[i] = ob[i];
}
}
return result;
};
</script>

View File

@ -0,0 +1,155 @@
<template>
<v-dialog
v-model="examineLog"
width="50wv"
@click.outside="close"
transition="false"
>
<v-card>
<v-card-text :style="{ height: gridHeight + 'px' }">
<ag-grid-vue
v-if="examineLogContent"
style="width: 100%; height: 100%;"
class="ag-theme-material"
@grid-ready="onGridReady"
:columnDefs="columnDefs"
:row-data="examineLogContent.filter((val) => {return val.key !== 'screenshots'})"
:supress-horisontal-scroll="true"
:enable-scrolling="true"
:enableCellTextSelection="true"
:ensureDomOrder="true"
@cell-clicked="copyText"
></ag-grid-vue>
</v-card-text>
<v-card-text v-if="screenshots.length">
<h5>Screenshots</h5>
<br>
<viewer :images="screenshots" class="screenshots" :style="{ height: screenshotsHeight + 'px' }">
<img v-for="screenshot in screenshots" :src="screenshot.orig"/>
</viewer>
</v-card-text>
<v-card-actions>
<v-btn color="primary" block @click="closeModal" transition="false">Close</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
import { AgGridVue } from "ag-grid-vue3";
import "ag-grid-community/styles//ag-grid.css";
import "ag-grid-community/styles//ag-theme-material.css";
import { VCard, VCardText, VCardActions } from 'vuetify/components/VCard'
import { VDialog } from 'vuetify/components/VDialog'
import { VBtn } from 'vuetify/components/VBtn'
import { VTable } from 'vuetify/components/VTable'
import ValueRenderer from "./ValueRenderer";
export default {
name: "ExamineCamModal",
components: {
AgGridVue,
VCard,
VCardText,
VCardActions,
VBtn,
VDialog,
VTable,
ValueRenderer
},
data() {
return {
columnDefs: [
{
field: 'key',
sortable: true,
filter: 'agTextColumnFilter',
resizable: true,
width: 10,
},
{
field: 'value',
sortable: true,
filter: 'agTextColumnFilter',
resizable: true,
cellRenderer: 'ValueRenderer'
},
],
screenshots: []
}
},
props: {
examineLogContent: Array,
closeModal: Function
},
computed: {
examineLog() {
return !!this.examineLogContent
},
gridHeight() {
const computed = ((this.examineLogContent ? this.examineLogContent.length : 0) + 1) * 50
const max = window.innerHeight * 0.5
const min = window.innerHeight * 0.2
return (computed < max ? (computed > min ? computed : min) : max);
},
screenshotsHeight() {
return (window.innerHeight * 0.8) - this.gridHeight
}
},
watch: {
examineLogContent(content, oldContent) {
if (content && oldContent) {
return
}
let screenshots = this.examineLogContent && this.examineLogContent.find((elem) => {
return elem.key === 'screenshots'
}) || null
if (screenshots) {
let id = this.examineLogContent.find((elem) => {
return elem.key === '_id'
}).value
fetch('/events/details/' + id)
.then((response) => response.json())
.then((response) => {
this.screenshots = response.screenshots
});
}
}
},
methods: {
onGridReady(params) {
params.api.sizeColumnsToFit()
},
close (e) {
if (e.target.className === "v-overlay__scrim") {
this.closeModal()
}
},
copyText(e) {
navigator.clipboard.writeText(e.value);
this.$toast.success(`Value copied to clipboard`, {
position: "top-right",
});
setTimeout(this.$toast.clear, 3000);
}
}
}
</script>
<style>
.screenshots {
display: flex;
flex-direction: row;
flex-wrap: wrap;
overflow-y: auto;
height: 500px;
}
.screenshots img {
width: auto;
height: 150px;
cursor: pointer;
margin-right: 20px;
margin-bottom: 20px;
}
</style>

View File

@ -2,6 +2,8 @@
<v-dialog
v-model="examineLog"
width="50wv"
@click.outside="close"
transition="false"
>
<v-card>
<v-card-text style="height: 70vh">
@ -15,10 +17,11 @@
:enable-scrolling="true"
:enableCellTextSelection="true"
:ensureDomOrder="true"
></ag-grid-vue>
@cell-clicked="copyText"
></ag-grid-vue>
</v-card-text>
<v-card-actions>
<v-btn color="primary" block @click="closeModal">Close</v-btn>
<v-btn color="primary" block @click="closeModal" transition="false">Close</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
@ -28,14 +31,14 @@
import { AgGridVue } from "ag-grid-vue3";
import "ag-grid-community/styles//ag-grid.css";
import "ag-grid-community/styles//ag-theme-material.css";
import ScreenshotCell from "./ScreenshotCell.js";
import { VCard, VCardText, VCardActions } from 'vuetify/components/VCard'
import { VDialog } from 'vuetify/components/VDialog'
import { VBtn } from 'vuetify/components/VBtn'
import { VTable } from 'vuetify/components/VTable'
import ValueRenderer from "./ValueRenderer";
export default {
name: "ExamineLogModal",
components: {
AgGridVue,
VCard,
@ -44,7 +47,7 @@ export default {
VBtn,
VDialog,
VTable,
ScreenshotCell: ScreenshotCell
ValueRenderer
},
data() {
return {
@ -53,13 +56,15 @@ export default {
field: 'key',
sortable: true,
filter: 'agTextColumnFilter',
resizable: true
resizable: true,
width: 10,
},
{
field: 'value',
sortable: true,
filter: 'agTextColumnFilter',
resizable: true
resizable: true,
cellRenderer: 'ValueRenderer'
},
]
}
@ -77,6 +82,18 @@ export default {
onGridReady(params) {
params.api.sizeColumnsToFit()
},
close (e) {
if (e.target.className === "v-overlay__scrim") {
this.closeModal()
}
},
copyText(e) {
navigator.clipboard.writeText(e.value);
this.$toast.success(`Value copied to clipboard`, {
position: "top-right",
});
setTimeout(this.$toast.clear, 3000);
}
}
}

View File

@ -0,0 +1,27 @@
import { VRow, VCol } from 'vuetify/components/VGrid'
import 'ansi-to-html'
import Convert from "ansi-to-html";
export default {
template: `<v-row>
<v-col>
<span v-html="message"></span>
</v-col>
</v-row>`,
components: {
VRow,
VCol,
},
setup(props) {
let message;
if (props.params.data.key === 'message') {
let parser = new Convert()
message = parser.toHtml(props.params.value)
} else {
message = props.params.value
}
return {
message
}
},
};

17
src/helpers/flattenObj.js Normal file
View File

@ -0,0 +1,17 @@
const flattenObj = (ob) => {
let result = {};
for (const i in ob) {
if ((typeof ob[i]) === 'object' && !Array.isArray(ob[i])) {
const temp = flattenObj(ob[i]);
for (const j in temp) {
result[i + '.' + j] = temp[j];
}
}
else {
result[i] = ob[i];
}
}
return result;
};
export default flattenObj;

View File

@ -0,0 +1,30 @@
const parseEventData = (eventData) => {
try {
let json = JSON.parse(eventData)
let message;
if (json.json && json.json.message) {
message = json.json.message
} else if (json.message) {
message = json.message
} else if (json.json) {
message = JSON.stringify(json.json)
}
if (message) {
json.message = message
}
let level;
if (json.log && json.log.level) {
level = json.log.level
} else if (json.stream) {
level = json.stream === 'stderr' ? 'error' : 'info'
}
if (level) {
json.level = level
}
return json
} catch (e) {
console.error(e, eventData)
}
};
export default parseEventData;

View File

@ -1,10 +1,26 @@
import { createApp } from 'vue'
import store from "./stores";
import App from './App.vue'
import vuetify from './plugins/vuetify'
import Toaster from "@meforma/vue-toaster";
import VCalendar from 'v-calendar';
import VueViewer from 'v-viewer'
import { loadFonts } from './plugins/webfontloader'
import './assets/main.css'
import 'vue-select/dist/vue-select.css';
import 'v-calendar/dist/style.css';
import 'viewerjs/dist/viewer.css'
loadFonts()
createApp(App)
.use(vuetify)
.mount('#app')
const app = createApp(App);
app.use(store);
app.use(vuetify);
app.use(Toaster);
app.use(VCalendar, {});
app.use(VueViewer, {
defaultOptions: {
zIndex: 9999
}
})
app.mount("#app");

View File

@ -5,11 +5,13 @@
*/
export async function loadFonts () {
const webFontLoader = await import(/* webpackChunkName: "webfontloader" */'webfontloader')
const webFontLoader = await import('webfontloader')
webFontLoader.load({
google: {
families: ['Roboto:100,300,400,500,700,900&display=swap'],
families: [
'Montserrat', 'Roboto Mono',
],
},
})
}

View File

@ -1,14 +0,0 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})

71
src/stores/index.js Normal file
View File

@ -0,0 +1,71 @@
import { createStore } from "vuex"
const store = createStore({
state: {
filterOptions: {},
filterQuery: {},
lastPingReceived: null,
},
getters: {
streaming (state) {
return state.filterQuery['streaming'] ?? false
},
filterQuery (state) {
return state.filterQuery
},
lastPingReceived (state) {
return state.lastPingReceived
},
},
actions: {
setFilterOptions(context, payload) {
context.commit("SET_FILTER_OPTIONS", payload);
},
setFilterQuery(context, payload) {
context.commit("SET_FILTER_QUERY", payload);
},
toggleFilterQueryStreaming(context) {
context.commit("TOGGLE_FILTER_QUERY_STREAMING");
},
setFilterQueryTimeRange({commit, state}, {from, to}) {
let query = state.filterQuery
query.from = from
query.to = to
query.streaming = false
commit("SET_FILTER_QUERY", query);
},
setLastPingReceived(context) {
context.commit("SET_LAST_PING_RECEIVED");
},
},
mutations: {
SET_FILTER_OPTIONS(state, payload) {
state.filterOptions = payload
},
SET_FILTER_QUERY(state, payload) {
let query = payload
if (Object.keys(state.filterOptions).length) {
query['initial'] = false
}
state.filterQuery = query
},
TOGGLE_FILTER_QUERY_STREAMING(state) {
let query = state.filterQuery
let streaming = (query['streaming'] === undefined) ? false : query['streaming']
query['streaming'] = !(streaming)
query['initial'] = false
if (!streaming) {
delete query['from']
delete query['to']
}
state.filterQuery = query
},
SET_LAST_PING_RECEIVED(state) {
state.lastPingReceived = (new Date()).getTime()
}
},
});
export default store

View File

@ -1,9 +1,12 @@
import vue from '@vitejs/plugin-vue'
import dynamicImport from 'vite-plugin-dynamic-import'
import * as path from "path";
export default {
resolve: {
alias: {
vue: 'vue/dist/vue.esm-bundler.js'
vue: 'vue/dist/vue.esm-bundler.js',
'@': path.join(__dirname, 'src/components/Grid/Main/configs'),
}
},
rollupOptions: {
@ -11,16 +14,24 @@ export default {
format: 'system',
preserveEntrySignatures: true
},
plugins: [vue({
template: {
transformAssetUrls: {
base: '/src'
}
}
})],
plugins: [
vue({
template: {
transformAssetUrls: {
base: '/src'
}
},
}),
dynamicImport(/* options */)
],
pluginOptions: {
vuetify: {
// https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vuetify-loader
}
}
},
optimizeDeps: {
exclude: [
"@meforma/vue-toaster"
]
},
}