mirror of
https://github.com/TwiN/gatus.git
synced 2026-02-04 15:14:43 +00:00
fix(ui): Modernize response time chart (#1373)
This commit is contained in:
@@ -84,6 +84,7 @@ func (a *API) createRouter(cfg *config.Config) *fiber.App {
|
|||||||
unprotectedAPIRouter.Get("/v1/endpoints/:key/response-times/:duration", ResponseTimeRaw)
|
unprotectedAPIRouter.Get("/v1/endpoints/:key/response-times/:duration", ResponseTimeRaw)
|
||||||
unprotectedAPIRouter.Get("/v1/endpoints/:key/response-times/:duration/badge.svg", ResponseTimeBadge(cfg))
|
unprotectedAPIRouter.Get("/v1/endpoints/:key/response-times/:duration/badge.svg", ResponseTimeBadge(cfg))
|
||||||
unprotectedAPIRouter.Get("/v1/endpoints/:key/response-times/:duration/chart.svg", ResponseTimeChart)
|
unprotectedAPIRouter.Get("/v1/endpoints/:key/response-times/:duration/chart.svg", ResponseTimeChart)
|
||||||
|
unprotectedAPIRouter.Get("/v1/endpoints/:key/response-times/:duration/history", ResponseTimeHistory)
|
||||||
// This endpoint requires authz with bearer token, so technically it is protected
|
// This endpoint requires authz with bearer token, so technically it is protected
|
||||||
unprotectedAPIRouter.Post("/v1/endpoints/:key/external", CreateExternalEndpointResult(cfg))
|
unprotectedAPIRouter.Post("/v1/endpoints/:key/external", CreateExternalEndpointResult(cfg))
|
||||||
// SPA
|
// SPA
|
||||||
|
|||||||
60
api/chart.go
60
api/chart.go
@@ -126,3 +126,63 @@ func ResponseTimeChart(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ResponseTimeHistory(c *fiber.Ctx) error {
|
||||||
|
duration := c.Params("duration")
|
||||||
|
var from time.Time
|
||||||
|
switch duration {
|
||||||
|
case "30d":
|
||||||
|
from = time.Now().Truncate(time.Hour).Add(-30 * 24 * time.Hour)
|
||||||
|
case "7d":
|
||||||
|
from = time.Now().Truncate(time.Hour).Add(-7 * 24 * time.Hour)
|
||||||
|
case "24h":
|
||||||
|
from = time.Now().Truncate(time.Hour).Add(-24 * time.Hour)
|
||||||
|
default:
|
||||||
|
return c.Status(400).SendString("Durations supported: 30d, 7d, 24h")
|
||||||
|
}
|
||||||
|
endpointKey, err := url.QueryUnescape(c.Params("key"))
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(400).SendString("invalid key encoding")
|
||||||
|
}
|
||||||
|
hourlyAverageResponseTime, err := store.Get().GetHourlyAverageResponseTimeByKey(endpointKey, from, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, common.ErrEndpointNotFound) {
|
||||||
|
return c.Status(404).SendString(err.Error())
|
||||||
|
}
|
||||||
|
if errors.Is(err, common.ErrInvalidTimeRange) {
|
||||||
|
return c.Status(400).SendString(err.Error())
|
||||||
|
}
|
||||||
|
return c.Status(500).SendString(err.Error())
|
||||||
|
}
|
||||||
|
if len(hourlyAverageResponseTime) == 0 {
|
||||||
|
return c.Status(200).JSON(map[string]interface{}{
|
||||||
|
"timestamps": []int64{},
|
||||||
|
"values": []int{},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
hourlyTimestamps := make([]int, 0, len(hourlyAverageResponseTime))
|
||||||
|
earliestTimestamp := int64(0)
|
||||||
|
for hourlyTimestamp := range hourlyAverageResponseTime {
|
||||||
|
hourlyTimestamps = append(hourlyTimestamps, int(hourlyTimestamp))
|
||||||
|
if earliestTimestamp == 0 || hourlyTimestamp < earliestTimestamp {
|
||||||
|
earliestTimestamp = hourlyTimestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for earliestTimestamp > from.Unix() {
|
||||||
|
earliestTimestamp -= int64(time.Hour.Seconds())
|
||||||
|
hourlyTimestamps = append(hourlyTimestamps, int(earliestTimestamp))
|
||||||
|
}
|
||||||
|
sort.Ints(hourlyTimestamps)
|
||||||
|
timestamps := make([]int64, 0, len(hourlyTimestamps))
|
||||||
|
values := make([]int, 0, len(hourlyTimestamps))
|
||||||
|
for _, hourlyTimestamp := range hourlyTimestamps {
|
||||||
|
timestamp := int64(hourlyTimestamp)
|
||||||
|
averageResponseTime := hourlyAverageResponseTime[timestamp]
|
||||||
|
timestamps = append(timestamps, timestamp*1000)
|
||||||
|
values = append(values, averageResponseTime)
|
||||||
|
}
|
||||||
|
return c.Status(http.StatusOK).JSON(map[string]interface{}{
|
||||||
|
"timestamps": timestamps,
|
||||||
|
"values": values,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -81,3 +81,69 @@ func TestResponseTimeChart(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResponseTimeHistory(t *testing.T) {
|
||||||
|
defer store.Get().Clear()
|
||||||
|
defer cache.Clear()
|
||||||
|
cfg := &config.Config{
|
||||||
|
Metrics: true,
|
||||||
|
Endpoints: []*endpoint.Endpoint{
|
||||||
|
{
|
||||||
|
Name: "frontend",
|
||||||
|
Group: "core",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "backend",
|
||||||
|
Group: "core",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
watchdog.UpdateEndpointStatus(cfg.Endpoints[0], &endpoint.Result{Success: true, Duration: time.Millisecond, Timestamp: time.Now()})
|
||||||
|
watchdog.UpdateEndpointStatus(cfg.Endpoints[1], &endpoint.Result{Success: false, Duration: time.Second, Timestamp: time.Now()})
|
||||||
|
api := New(cfg)
|
||||||
|
router := api.Router()
|
||||||
|
type Scenario struct {
|
||||||
|
Name string
|
||||||
|
Path string
|
||||||
|
ExpectedCode int
|
||||||
|
}
|
||||||
|
scenarios := []Scenario{
|
||||||
|
{
|
||||||
|
Name: "history-response-time-24h",
|
||||||
|
Path: "/api/v1/endpoints/core_backend/response-times/24h/history",
|
||||||
|
ExpectedCode: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "history-response-time-7d",
|
||||||
|
Path: "/api/v1/endpoints/core_frontend/response-times/7d/history",
|
||||||
|
ExpectedCode: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "history-response-time-30d",
|
||||||
|
Path: "/api/v1/endpoints/core_frontend/response-times/30d/history",
|
||||||
|
ExpectedCode: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "history-response-time-with-invalid-duration",
|
||||||
|
Path: "/api/v1/endpoints/core_backend/response-times/3d/history",
|
||||||
|
ExpectedCode: http.StatusBadRequest,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "history-response-time-for-invalid-key",
|
||||||
|
Path: "/api/v1/endpoints/invalid_key/response-times/7d/history",
|
||||||
|
ExpectedCode: http.StatusNotFound,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, scenario := range scenarios {
|
||||||
|
t.Run(scenario.Name, func(t *testing.T) {
|
||||||
|
request := httptest.NewRequest("GET", scenario.Path, http.NoBody)
|
||||||
|
response, err := router.Test(request)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if response.StatusCode != scenario.ExpectedCode {
|
||||||
|
t.Errorf("%s %s should have returned %d, but returned %d instead", request.Method, request.URL, scenario.ExpectedCode, response.StatusCode)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
565
web/app/package-lock.json
generated
565
web/app/package-lock.json
generated
@@ -8,16 +8,17 @@
|
|||||||
"name": "gatus",
|
"name": "gatus",
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/vue": "^1.7.23",
|
"chart.js": "^4.5.1",
|
||||||
"@heroicons/vue": "^2.2.0",
|
"chartjs-adapter-date-fns": "^3.0.0",
|
||||||
"@vueuse/core": "^13.6.0",
|
"chartjs-plugin-annotation": "^3.1.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"core-js": "^3.45.0",
|
"core-js": "^3.45.0",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"lucide-vue-next": "^0.539.0",
|
"lucide-vue-next": "^0.539.0",
|
||||||
"radix-vue": "^1.9.17",
|
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"vue": "^3.5.18",
|
"vue": "^3.5.18",
|
||||||
|
"vue-chartjs": "^5.3.2",
|
||||||
"vue-router": "^4.5.1"
|
"vue-router": "^4.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -1861,42 +1862,6 @@
|
|||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@floating-ui/core": {
|
|
||||||
"version": "1.7.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
|
|
||||||
"integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@floating-ui/utils": "^0.2.10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@floating-ui/dom": {
|
|
||||||
"version": "1.7.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.3.tgz",
|
|
||||||
"integrity": "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@floating-ui/core": "^1.7.3",
|
|
||||||
"@floating-ui/utils": "^0.2.10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@floating-ui/utils": {
|
|
||||||
"version": "0.2.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
|
|
||||||
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@floating-ui/vue": {
|
|
||||||
"version": "1.1.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/@floating-ui/vue/-/vue-1.1.8.tgz",
|
|
||||||
"integrity": "sha512-SNJAa1jbT8Gh1LvWw2uIIViLL0saV2bCY59ISCvJzhbut5DSb2H3LKUK49Xkd7SixTNHKX4LFu59nbwIXt9jjQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@floating-ui/dom": "^1.7.3",
|
|
||||||
"@floating-ui/utils": "^0.2.10",
|
|
||||||
"vue-demi": ">=0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@hapi/hoek": {
|
"node_modules/@hapi/hoek": {
|
||||||
"version": "9.3.0",
|
"version": "9.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
||||||
@@ -1912,30 +1877,6 @@
|
|||||||
"@hapi/hoek": "^9.0.0"
|
"@hapi/hoek": "^9.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@headlessui/vue": {
|
|
||||||
"version": "1.7.23",
|
|
||||||
"resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.7.23.tgz",
|
|
||||||
"integrity": "sha512-JzdCNqurrtuu0YW6QaDtR2PIYCKPUWq28csDyMvN4zmGccmE7lz40Is6hc3LA4HFeCI7sekZ/PQMTNmn9I/4Wg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@tanstack/vue-virtual": "^3.0.0-beta.60"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"vue": "^3.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@heroicons/vue": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@heroicons/vue/-/vue-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-G3dbSxoeEKqbi/DFalhRxJU4mTXJn7GwZ7ae8NuEQzd1bqdd0jAbdaBZlHPcvPD2xI1iGzNVB4k20Un2AguYPw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"peerDependencies": {
|
|
||||||
"vue": ">= 3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@humanwhocodes/config-array": {
|
"node_modules/@humanwhocodes/config-array": {
|
||||||
"version": "0.13.0",
|
"version": "0.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
||||||
@@ -1974,24 +1915,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
"node_modules/@internationalized/date": {
|
|
||||||
"version": "3.8.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.2.tgz",
|
|
||||||
"integrity": "sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@swc/helpers": "^0.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@internationalized/number": {
|
|
||||||
"version": "3.6.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.4.tgz",
|
|
||||||
"integrity": "sha512-P+/h+RDaiX8EGt3shB9AYM1+QgkvHmJ5rKi4/59k4sg9g58k9rqsRW0WxRO7jCoHyvVbFRRFKmVTdFYdehrxHg==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@swc/helpers": "^0.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@jridgewell/gen-mapping": {
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
|
||||||
@@ -2063,6 +1986,12 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@kurkle/color": {
|
||||||
|
"version": "0.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
|
||||||
|
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@leichtgewicht/ip-codec": {
|
"node_modules/@leichtgewicht/ip-codec": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
|
||||||
@@ -2244,41 +2173,6 @@
|
|||||||
"integrity": "sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==",
|
"integrity": "sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@swc/helpers": {
|
|
||||||
"version": "0.5.17",
|
|
||||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
|
|
||||||
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@tanstack/virtual-core": {
|
|
||||||
"version": "3.13.12",
|
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz",
|
|
||||||
"integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/tannerlinsley"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@tanstack/vue-virtual": {
|
|
||||||
"version": "3.13.12",
|
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.12.tgz",
|
|
||||||
"integrity": "sha512-vhF7kEU9EXWXh+HdAwKJ2m3xaOnTTmgcdXcF2pim8g4GvI7eRrk2YRuV5nUlZnd/NbCIX4/Ja2OZu5EjJL06Ww==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@tanstack/virtual-core": "3.13.12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/tannerlinsley"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"vue": "^2.7.0 || ^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@trysound/sax": {
|
"node_modules/@trysound/sax": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
||||||
@@ -2472,12 +2366,6 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/web-bluetooth": {
|
|
||||||
"version": "0.0.21",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
|
|
||||||
"integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@types/ws": {
|
"node_modules/@types/ws": {
|
||||||
"version": "8.5.3",
|
"version": "8.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
||||||
@@ -3221,44 +3109,6 @@
|
|||||||
"integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==",
|
"integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@vueuse/core": {
|
|
||||||
"version": "13.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.6.0.tgz",
|
|
||||||
"integrity": "sha512-DJbD5fV86muVmBgS9QQPddVX7d9hWYswzlf4bIyUD2dj8GC46R1uNClZhVAmsdVts4xb2jwp1PbpuiA50Qee1A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/web-bluetooth": "^0.0.21",
|
|
||||||
"@vueuse/metadata": "13.6.0",
|
|
||||||
"@vueuse/shared": "13.6.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/antfu"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"vue": "^3.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@vueuse/metadata": {
|
|
||||||
"version": "13.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.6.0.tgz",
|
|
||||||
"integrity": "sha512-rnIH7JvU7NjrpexTsl2Iwv0V0yAx9cw7+clymjKuLSXG0QMcLD0LDgdNmXic+qL0SGvgSVPEpM9IDO/wqo1vkQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/antfu"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@vueuse/shared": {
|
|
||||||
"version": "13.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.6.0.tgz",
|
|
||||||
"integrity": "sha512-pDykCSoS2T3fsQrYqf9SyF0QXWHmcGPQ+qiOVjlYSzlWd9dgppB2bFSM1GgKKkt7uzn0BBMV3IbJsUfHG2+BCg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/antfu"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"vue": "^3.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@webassemblyjs/ast": {
|
"node_modules/@webassemblyjs/ast": {
|
||||||
"version": "1.11.1",
|
"version": "1.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
|
||||||
@@ -3670,18 +3520,6 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Python-2.0"
|
"license": "Python-2.0"
|
||||||
},
|
},
|
||||||
"node_modules/aria-hidden": {
|
|
||||||
"version": "1.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
|
|
||||||
"integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"tslib": "^2.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/array-flatten": {
|
"node_modules/array-flatten": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
|
||||||
@@ -4170,6 +4008,37 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chart.js": {
|
||||||
|
"version": "4.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
|
||||||
|
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@kurkle/color": "^0.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"pnpm": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chartjs-adapter-date-fns": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"chart.js": ">=2.8.0",
|
||||||
|
"date-fns": ">=2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chartjs-plugin-annotation": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chartjs-plugin-annotation/-/chartjs-plugin-annotation-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-EkAed6/ycXD/7n0ShrlT1T2Hm3acnbFhgkIEJLa0X+M6S16x0zwj1Fv4suv/2bwayCT3jGPdAtI9uLcAMToaQQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"chart.js": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "3.5.3",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
@@ -5002,6 +4871,16 @@
|
|||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/date-fns": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/kossnocorp"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
@@ -5146,12 +5025,6 @@
|
|||||||
"integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==",
|
"integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/defu": {
|
|
||||||
"version": "6.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
|
|
||||||
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/depd": {
|
"node_modules/depd": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
@@ -6237,7 +6110,8 @@
|
|||||||
"node_modules/fast-deep-equal": {
|
"node_modules/fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/fast-glob": {
|
"node_modules/fast-glob": {
|
||||||
"version": "3.2.11",
|
"version": "3.2.11",
|
||||||
@@ -9535,88 +9409,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/radix-vue": {
|
|
||||||
"version": "1.9.17",
|
|
||||||
"resolved": "https://registry.npmjs.org/radix-vue/-/radix-vue-1.9.17.tgz",
|
|
||||||
"integrity": "sha512-mVCu7I2vXt1L2IUYHTt0sZMz7s1K2ZtqKeTIxG3yC5mMFfLBG4FtE1FDeRMpDd+Hhg/ybi9+iXmAP1ISREndoQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@floating-ui/dom": "^1.6.7",
|
|
||||||
"@floating-ui/vue": "^1.1.0",
|
|
||||||
"@internationalized/date": "^3.5.4",
|
|
||||||
"@internationalized/number": "^3.5.3",
|
|
||||||
"@tanstack/vue-virtual": "^3.8.1",
|
|
||||||
"@vueuse/core": "^10.11.0",
|
|
||||||
"@vueuse/shared": "^10.11.0",
|
|
||||||
"aria-hidden": "^1.2.4",
|
|
||||||
"defu": "^6.1.4",
|
|
||||||
"fast-deep-equal": "^3.1.3",
|
|
||||||
"nanoid": "^5.0.7"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"vue": ">= 3.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/radix-vue/node_modules/@types/web-bluetooth": {
|
|
||||||
"version": "0.0.20",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
|
|
||||||
"integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/radix-vue/node_modules/@vueuse/core": {
|
|
||||||
"version": "10.11.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.1.tgz",
|
|
||||||
"integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/web-bluetooth": "^0.0.20",
|
|
||||||
"@vueuse/metadata": "10.11.1",
|
|
||||||
"@vueuse/shared": "10.11.1",
|
|
||||||
"vue-demi": ">=0.14.8"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/antfu"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/radix-vue/node_modules/@vueuse/metadata": {
|
|
||||||
"version": "10.11.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.1.tgz",
|
|
||||||
"integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/antfu"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/radix-vue/node_modules/@vueuse/shared": {
|
|
||||||
"version": "10.11.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.1.tgz",
|
|
||||||
"integrity": "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"vue-demi": ">=0.14.8"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/antfu"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/radix-vue/node_modules/nanoid": {
|
|
||||||
"version": "5.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz",
|
|
||||||
"integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ai"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"nanoid": "bin/nanoid.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^18 || >=20"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/randombytes": {
|
"node_modules/randombytes": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||||
@@ -10821,6 +10613,7 @@
|
|||||||
"version": "2.8.1",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
|
"dev": true,
|
||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
@@ -11026,30 +10819,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vue-demi": {
|
"node_modules/vue-chartjs": {
|
||||||
"version": "0.14.10",
|
"version": "5.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
|
"resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.2.tgz",
|
||||||
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
"integrity": "sha512-NrkbRRoYshbXbWqJkTN6InoDVwVb90C0R7eAVgMWcB9dPikbruaOoTFjFYHE/+tNPdIe6qdLCDjfjPHQ0fw4jw==",
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
|
||||||
"vue-demi-fix": "bin/vue-demi-fix.js",
|
|
||||||
"vue-demi-switch": "bin/vue-demi-switch.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/antfu"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@vue/composition-api": "^1.0.0-rc.1",
|
"chart.js": "^4.1.1",
|
||||||
"vue": "^3.0.0-0 || ^2.6.0"
|
"vue": "^3.0.0-0 || ^2.7.0"
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@vue/composition-api": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vue-eslint-parser": {
|
"node_modules/vue-eslint-parser": {
|
||||||
@@ -13323,38 +13100,6 @@
|
|||||||
"integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
|
"integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@floating-ui/core": {
|
|
||||||
"version": "1.7.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
|
|
||||||
"integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
|
|
||||||
"requires": {
|
|
||||||
"@floating-ui/utils": "^0.2.10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@floating-ui/dom": {
|
|
||||||
"version": "1.7.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.3.tgz",
|
|
||||||
"integrity": "sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==",
|
|
||||||
"requires": {
|
|
||||||
"@floating-ui/core": "^1.7.3",
|
|
||||||
"@floating-ui/utils": "^0.2.10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@floating-ui/utils": {
|
|
||||||
"version": "0.2.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
|
|
||||||
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="
|
|
||||||
},
|
|
||||||
"@floating-ui/vue": {
|
|
||||||
"version": "1.1.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/@floating-ui/vue/-/vue-1.1.8.tgz",
|
|
||||||
"integrity": "sha512-SNJAa1jbT8Gh1LvWw2uIIViLL0saV2bCY59ISCvJzhbut5DSb2H3LKUK49Xkd7SixTNHKX4LFu59nbwIXt9jjQ==",
|
|
||||||
"requires": {
|
|
||||||
"@floating-ui/dom": "^1.7.3",
|
|
||||||
"@floating-ui/utils": "^0.2.10",
|
|
||||||
"vue-demi": ">=0.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@hapi/hoek": {
|
"@hapi/hoek": {
|
||||||
"version": "9.3.0",
|
"version": "9.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
|
||||||
@@ -13370,20 +13115,6 @@
|
|||||||
"@hapi/hoek": "^9.0.0"
|
"@hapi/hoek": "^9.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@headlessui/vue": {
|
|
||||||
"version": "1.7.23",
|
|
||||||
"resolved": "https://registry.npmjs.org/@headlessui/vue/-/vue-1.7.23.tgz",
|
|
||||||
"integrity": "sha512-JzdCNqurrtuu0YW6QaDtR2PIYCKPUWq28csDyMvN4zmGccmE7lz40Is6hc3LA4HFeCI7sekZ/PQMTNmn9I/4Wg==",
|
|
||||||
"requires": {
|
|
||||||
"@tanstack/vue-virtual": "^3.0.0-beta.60"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@heroicons/vue": {
|
|
||||||
"version": "2.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@heroicons/vue/-/vue-2.2.0.tgz",
|
|
||||||
"integrity": "sha512-G3dbSxoeEKqbi/DFalhRxJU4mTXJn7GwZ7ae8NuEQzd1bqdd0jAbdaBZlHPcvPD2xI1iGzNVB4k20Un2AguYPw==",
|
|
||||||
"requires": {}
|
|
||||||
},
|
|
||||||
"@humanwhocodes/config-array": {
|
"@humanwhocodes/config-array": {
|
||||||
"version": "0.13.0",
|
"version": "0.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
||||||
@@ -13407,22 +13138,6 @@
|
|||||||
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
|
"integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@internationalized/date": {
|
|
||||||
"version": "3.8.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.2.tgz",
|
|
||||||
"integrity": "sha512-/wENk7CbvLbkUvX1tu0mwq49CVkkWpkXubGel6birjRPyo6uQ4nQpnq5xZu823zRCwwn82zgHrvgF1vZyvmVgA==",
|
|
||||||
"requires": {
|
|
||||||
"@swc/helpers": "^0.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@internationalized/number": {
|
|
||||||
"version": "3.6.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.4.tgz",
|
|
||||||
"integrity": "sha512-P+/h+RDaiX8EGt3shB9AYM1+QgkvHmJ5rKi4/59k4sg9g58k9rqsRW0WxRO7jCoHyvVbFRRFKmVTdFYdehrxHg==",
|
|
||||||
"requires": {
|
|
||||||
"@swc/helpers": "^0.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@jridgewell/gen-mapping": {
|
"@jridgewell/gen-mapping": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
|
||||||
@@ -13483,6 +13198,11 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@kurkle/color": {
|
||||||
|
"version": "0.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
|
||||||
|
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="
|
||||||
|
},
|
||||||
"@leichtgewicht/ip-codec": {
|
"@leichtgewicht/ip-codec": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz",
|
||||||
@@ -13629,27 +13349,6 @@
|
|||||||
"integrity": "sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==",
|
"integrity": "sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@swc/helpers": {
|
|
||||||
"version": "0.5.17",
|
|
||||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
|
|
||||||
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
|
||||||
"requires": {
|
|
||||||
"tslib": "^2.8.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@tanstack/virtual-core": {
|
|
||||||
"version": "3.13.12",
|
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz",
|
|
||||||
"integrity": "sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA=="
|
|
||||||
},
|
|
||||||
"@tanstack/vue-virtual": {
|
|
||||||
"version": "3.13.12",
|
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/vue-virtual/-/vue-virtual-3.13.12.tgz",
|
|
||||||
"integrity": "sha512-vhF7kEU9EXWXh+HdAwKJ2m3xaOnTTmgcdXcF2pim8g4GvI7eRrk2YRuV5nUlZnd/NbCIX4/Ja2OZu5EjJL06Ww==",
|
|
||||||
"requires": {
|
|
||||||
"@tanstack/virtual-core": "3.13.12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@trysound/sax": {
|
"@trysound/sax": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
|
||||||
@@ -13840,11 +13539,6 @@
|
|||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/web-bluetooth": {
|
|
||||||
"version": "0.0.21",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz",
|
|
||||||
"integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="
|
|
||||||
},
|
|
||||||
"@types/ws": {
|
"@types/ws": {
|
||||||
"version": "8.5.3",
|
"version": "8.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
||||||
@@ -14421,27 +14115,6 @@
|
|||||||
"integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==",
|
"integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@vueuse/core": {
|
|
||||||
"version": "13.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-13.6.0.tgz",
|
|
||||||
"integrity": "sha512-DJbD5fV86muVmBgS9QQPddVX7d9hWYswzlf4bIyUD2dj8GC46R1uNClZhVAmsdVts4xb2jwp1PbpuiA50Qee1A==",
|
|
||||||
"requires": {
|
|
||||||
"@types/web-bluetooth": "^0.0.21",
|
|
||||||
"@vueuse/metadata": "13.6.0",
|
|
||||||
"@vueuse/shared": "13.6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vueuse/metadata": {
|
|
||||||
"version": "13.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-13.6.0.tgz",
|
|
||||||
"integrity": "sha512-rnIH7JvU7NjrpexTsl2Iwv0V0yAx9cw7+clymjKuLSXG0QMcLD0LDgdNmXic+qL0SGvgSVPEpM9IDO/wqo1vkQ=="
|
|
||||||
},
|
|
||||||
"@vueuse/shared": {
|
|
||||||
"version": "13.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-13.6.0.tgz",
|
|
||||||
"integrity": "sha512-pDykCSoS2T3fsQrYqf9SyF0QXWHmcGPQ+qiOVjlYSzlWd9dgppB2bFSM1GgKKkt7uzn0BBMV3IbJsUfHG2+BCg==",
|
|
||||||
"requires": {}
|
|
||||||
},
|
|
||||||
"@webassemblyjs/ast": {
|
"@webassemblyjs/ast": {
|
||||||
"version": "1.11.1",
|
"version": "1.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
|
||||||
@@ -14776,14 +14449,6 @@
|
|||||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"aria-hidden": {
|
|
||||||
"version": "1.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
|
|
||||||
"integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
|
|
||||||
"requires": {
|
|
||||||
"tslib": "^2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"array-flatten": {
|
"array-flatten": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
|
||||||
@@ -15123,6 +14788,26 @@
|
|||||||
"supports-color": "^5.3.0"
|
"supports-color": "^5.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"chart.js": {
|
||||||
|
"version": "4.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
|
||||||
|
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
|
||||||
|
"requires": {
|
||||||
|
"@kurkle/color": "^0.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"chartjs-adapter-date-fns": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
|
"chartjs-plugin-annotation": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chartjs-plugin-annotation/-/chartjs-plugin-annotation-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-EkAed6/ycXD/7n0ShrlT1T2Hm3acnbFhgkIEJLa0X+M6S16x0zwj1Fv4suv/2bwayCT3jGPdAtI9uLcAMToaQQ==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"chokidar": {
|
"chokidar": {
|
||||||
"version": "3.5.3",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
@@ -15721,6 +15406,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
|
||||||
},
|
},
|
||||||
|
"date-fns": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="
|
||||||
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||||
@@ -15822,11 +15512,6 @@
|
|||||||
"integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==",
|
"integrity": "sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"defu": {
|
|
||||||
"version": "6.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
|
|
||||||
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="
|
|
||||||
},
|
|
||||||
"depd": {
|
"depd": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
@@ -16615,7 +16300,8 @@
|
|||||||
"fast-deep-equal": {
|
"fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"fast-glob": {
|
"fast-glob": {
|
||||||
"version": "3.2.11",
|
"version": "3.2.11",
|
||||||
@@ -18970,60 +18656,6 @@
|
|||||||
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
|
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"radix-vue": {
|
|
||||||
"version": "1.9.17",
|
|
||||||
"resolved": "https://registry.npmjs.org/radix-vue/-/radix-vue-1.9.17.tgz",
|
|
||||||
"integrity": "sha512-mVCu7I2vXt1L2IUYHTt0sZMz7s1K2ZtqKeTIxG3yC5mMFfLBG4FtE1FDeRMpDd+Hhg/ybi9+iXmAP1ISREndoQ==",
|
|
||||||
"requires": {
|
|
||||||
"@floating-ui/dom": "^1.6.7",
|
|
||||||
"@floating-ui/vue": "^1.1.0",
|
|
||||||
"@internationalized/date": "^3.5.4",
|
|
||||||
"@internationalized/number": "^3.5.3",
|
|
||||||
"@tanstack/vue-virtual": "^3.8.1",
|
|
||||||
"@vueuse/core": "^10.11.0",
|
|
||||||
"@vueuse/shared": "^10.11.0",
|
|
||||||
"aria-hidden": "^1.2.4",
|
|
||||||
"defu": "^6.1.4",
|
|
||||||
"fast-deep-equal": "^3.1.3",
|
|
||||||
"nanoid": "^5.0.7"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@types/web-bluetooth": {
|
|
||||||
"version": "0.0.20",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
|
|
||||||
"integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="
|
|
||||||
},
|
|
||||||
"@vueuse/core": {
|
|
||||||
"version": "10.11.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.1.tgz",
|
|
||||||
"integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==",
|
|
||||||
"requires": {
|
|
||||||
"@types/web-bluetooth": "^0.0.20",
|
|
||||||
"@vueuse/metadata": "10.11.1",
|
|
||||||
"@vueuse/shared": "10.11.1",
|
|
||||||
"vue-demi": ">=0.14.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@vueuse/metadata": {
|
|
||||||
"version": "10.11.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.1.tgz",
|
|
||||||
"integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw=="
|
|
||||||
},
|
|
||||||
"@vueuse/shared": {
|
|
||||||
"version": "10.11.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.1.tgz",
|
|
||||||
"integrity": "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==",
|
|
||||||
"requires": {
|
|
||||||
"vue-demi": ">=0.14.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nanoid": {
|
|
||||||
"version": "5.1.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz",
|
|
||||||
"integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"randombytes": {
|
"randombytes": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||||
@@ -19954,7 +19586,8 @@
|
|||||||
"tslib": {
|
"tslib": {
|
||||||
"version": "2.8.1",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"type-check": {
|
"type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
@@ -20092,10 +19725,10 @@
|
|||||||
"@vue/shared": "3.5.18"
|
"@vue/shared": "3.5.18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"vue-demi": {
|
"vue-chartjs": {
|
||||||
"version": "0.14.10",
|
"version": "5.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
|
"resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.2.tgz",
|
||||||
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
|
"integrity": "sha512-NrkbRRoYshbXbWqJkTN6InoDVwVb90C0R7eAVgMWcB9dPikbruaOoTFjFYHE/+tNPdIe6qdLCDjfjPHQ0fw4jw==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"vue-eslint-parser": {
|
"vue-eslint-parser": {
|
||||||
|
|||||||
@@ -8,22 +8,27 @@
|
|||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"chart.js": "^4.5.1",
|
||||||
|
"chartjs-adapter-date-fns": "^3.0.0",
|
||||||
|
"chartjs-plugin-annotation": "^3.1.0",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"core-js": "^3.45.0",
|
"core-js": "^3.45.0",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
"lucide-vue-next": "^0.539.0",
|
"lucide-vue-next": "^0.539.0",
|
||||||
"tailwind-merge": "^3.3.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"vue": "^3.5.18",
|
"vue": "^3.5.18",
|
||||||
|
"vue-chartjs": "^5.3.2",
|
||||||
"vue-router": "^4.5.1"
|
"vue-router": "^4.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/eslint-parser": "^7.25.1",
|
||||||
"@vue/cli-plugin-babel": "^5.0.8",
|
"@vue/cli-plugin-babel": "^5.0.8",
|
||||||
"@vue/cli-plugin-eslint": "^5.0.8",
|
"@vue/cli-plugin-eslint": "^5.0.8",
|
||||||
"@vue/cli-plugin-router": "^5.0.8",
|
"@vue/cli-plugin-router": "^5.0.8",
|
||||||
"@vue/cli-service": "^5.0.8",
|
"@vue/cli-service": "^5.0.8",
|
||||||
"@vue/compiler-sfc": "^3.5.18",
|
"@vue/compiler-sfc": "^3.5.18",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
"@babel/eslint-parser": "^7.25.1",
|
|
||||||
"eslint": "^8.57.1",
|
"eslint": "^8.57.1",
|
||||||
"eslint-plugin-vue": "^9.28.0",
|
"eslint-plugin-vue": "^9.28.0",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 51 KiB |
@@ -44,13 +44,6 @@
|
|||||||
:key="date"
|
:key="date"
|
||||||
class="relative"
|
class="relative"
|
||||||
>
|
>
|
||||||
<!-- Vertical line from date to last icon -->
|
|
||||||
<div
|
|
||||||
v-if="group.length > 0"
|
|
||||||
class="absolute left-3 w-0.5 bg-gray-300 dark:bg-gray-600 pointer-events-none"
|
|
||||||
:style="getTimelineHeight(group)"
|
|
||||||
></div>
|
|
||||||
|
|
||||||
<!-- Date Header -->
|
<!-- Date Header -->
|
||||||
<div class="flex items-center gap-3 mb-2 relative">
|
<div class="flex items-center gap-3 mb-2 relative">
|
||||||
<div class="relative z-10 bg-white dark:bg-gray-800 px-2 py-1 rounded-md border border-gray-200 dark:border-gray-600">
|
<div class="relative z-10 bg-white dark:bg-gray-800 px-2 py-1 rounded-md border border-gray-200 dark:border-gray-600">
|
||||||
@@ -69,18 +62,37 @@
|
|||||||
class="relative"
|
class="relative"
|
||||||
>
|
>
|
||||||
<!-- Timeline Icon -->
|
<!-- Timeline Icon -->
|
||||||
<div
|
<div
|
||||||
:class="[
|
:class="[
|
||||||
'absolute -left-[26px] top-1/2 -translate-y-1/2 w-5 h-5 rounded-full border bg-white dark:bg-gray-800 flex items-center justify-center z-10',
|
'absolute -left-[26px] w-5 h-5 rounded-full border bg-white dark:bg-gray-800 flex items-center justify-center z-10',
|
||||||
|
index === group.length - 1 ? 'top-3' : 'top-1/2 -translate-y-1/2',
|
||||||
getTypeClasses(announcement.type).border
|
getTypeClasses(announcement.type).border
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<component
|
<component
|
||||||
:is="getTypeIcon(announcement.type)"
|
:is="getTypeIcon(announcement.type)"
|
||||||
:class="['w-3 h-3', getTypeClasses(announcement.type).iconColor]"
|
:class="['w-3 h-3', getTypeClasses(announcement.type).iconColor]"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Vertical line segment connecting upward from first icon to date -->
|
||||||
|
<div
|
||||||
|
v-if="index === 0"
|
||||||
|
class="absolute w-0.5 bg-gray-300 dark:bg-gray-600 pointer-events-none"
|
||||||
|
style="left: -16px; top: -2.5rem; height: calc(50% + 2.5rem);"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<!-- Vertical line segment connecting downward to next icon -->
|
||||||
|
<div
|
||||||
|
v-if="index < group.length - 1"
|
||||||
|
class="absolute w-0.5 bg-gray-300 dark:bg-gray-600 pointer-events-none"
|
||||||
|
:style="{
|
||||||
|
left: '-16px',
|
||||||
|
top: '50%',
|
||||||
|
height: index === group.length - 2 ? 'calc(50% + 1.25rem)' : 'calc(50% + 2rem)'
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
|
||||||
<!-- Announcement Card -->
|
<!-- Announcement Card -->
|
||||||
<div
|
<div
|
||||||
:class="[
|
:class="[
|
||||||
@@ -220,14 +232,6 @@ const getTypeClasses = (type) => {
|
|||||||
return typeConfigs[type] || typeConfigs.none
|
return typeConfigs[type] || typeConfigs.none
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTimelineHeight = (group) => {
|
|
||||||
const height = group.length === 1 ? '2rem' : `${2 + (group.length - 1) * 3.5}rem`
|
|
||||||
return {
|
|
||||||
top: '1.5rem',
|
|
||||||
height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatDate = (dateString) => {
|
const formatDate = (dateString) => {
|
||||||
const date = new Date(dateString)
|
const date = new Date(dateString)
|
||||||
const today = new Date()
|
const today = new Date()
|
||||||
|
|||||||
294
web/app/src/components/ResponseTimeChart.vue
Normal file
294
web/app/src/components/ResponseTimeChart.vue
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
<template>
|
||||||
|
<div class="relative w-full" style="height: 300px;">
|
||||||
|
<div v-if="loading" class="absolute inset-0 flex items-center justify-center bg-background/50">
|
||||||
|
<Loading />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="error" class="absolute inset-0 flex items-center justify-center text-muted-foreground">
|
||||||
|
{{ error }}
|
||||||
|
</div>
|
||||||
|
<Line v-else :data="chartData" :options="chartOptions" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
|
||||||
|
import { Line } from 'vue-chartjs'
|
||||||
|
import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, Filler, TimeScale } from 'chart.js'
|
||||||
|
import annotationPlugin from 'chartjs-plugin-annotation'
|
||||||
|
import 'chartjs-adapter-date-fns'
|
||||||
|
import { generatePrettyTimeDifference } from '@/utils/time'
|
||||||
|
import Loading from './Loading.vue'
|
||||||
|
|
||||||
|
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, Filler, TimeScale, annotationPlugin)
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
endpointKey: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
duration: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
validator: (value) => ['24h', '7d', '30d'].includes(value)
|
||||||
|
},
|
||||||
|
serverUrl: {
|
||||||
|
type: String,
|
||||||
|
default: '..'
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(true)
|
||||||
|
const error = ref(null)
|
||||||
|
const timestamps = ref([])
|
||||||
|
const values = ref([])
|
||||||
|
const isDark = ref(document.documentElement.classList.contains('dark'))
|
||||||
|
const hoveredEventIndex = ref(null)
|
||||||
|
|
||||||
|
// Helper function to get color for unhealthy events
|
||||||
|
const getEventColor = () => {
|
||||||
|
// Only UNHEALTHY events are displayed on the chart
|
||||||
|
return 'rgba(239, 68, 68, 0.8)' // Red
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter events based on selected duration and calculate durations
|
||||||
|
const filteredEvents = computed(() => {
|
||||||
|
if (!props.events || props.events.length === 0) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const now = new Date()
|
||||||
|
let fromTime
|
||||||
|
switch (props.duration) {
|
||||||
|
case '24h':
|
||||||
|
fromTime = new Date(now.getTime() - 24 * 60 * 60 * 1000)
|
||||||
|
break
|
||||||
|
case '7d':
|
||||||
|
fromTime = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
|
||||||
|
break
|
||||||
|
case '30d':
|
||||||
|
fromTime = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only include UNHEALTHY events and calculate their duration
|
||||||
|
const unhealthyEvents = []
|
||||||
|
for (let i = 0; i < props.events.length; i++) {
|
||||||
|
const event = props.events[i]
|
||||||
|
if (event.type !== 'UNHEALTHY') continue
|
||||||
|
|
||||||
|
const eventTime = new Date(event.timestamp)
|
||||||
|
if (eventTime < fromTime || eventTime > now) continue
|
||||||
|
|
||||||
|
// Find the next event to calculate duration
|
||||||
|
let duration = null
|
||||||
|
let isOngoing = false
|
||||||
|
if (i + 1 < props.events.length) {
|
||||||
|
const nextEvent = props.events[i + 1]
|
||||||
|
duration = generatePrettyTimeDifference(nextEvent.timestamp, event.timestamp)
|
||||||
|
} else {
|
||||||
|
// Still ongoing - calculate duration from event time to now
|
||||||
|
duration = generatePrettyTimeDifference(now, event.timestamp)
|
||||||
|
isOngoing = true
|
||||||
|
}
|
||||||
|
|
||||||
|
unhealthyEvents.push({
|
||||||
|
...event,
|
||||||
|
duration,
|
||||||
|
isOngoing
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return unhealthyEvents
|
||||||
|
})
|
||||||
|
|
||||||
|
const chartData = computed(() => {
|
||||||
|
if (timestamps.value.length === 0) {
|
||||||
|
return {
|
||||||
|
labels: [],
|
||||||
|
datasets: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const labels = timestamps.value.map(ts => new Date(ts))
|
||||||
|
return {
|
||||||
|
labels,
|
||||||
|
datasets: [{
|
||||||
|
label: 'Response Time (ms)',
|
||||||
|
data: values.value,
|
||||||
|
borderColor: isDark.value ? 'rgb(96, 165, 250)' : 'rgb(59, 130, 246)',
|
||||||
|
backgroundColor: isDark.value ? 'rgba(96, 165, 250, 0.1)' : 'rgba(59, 130, 246, 0.1)',
|
||||||
|
borderWidth: 2,
|
||||||
|
pointRadius: 2,
|
||||||
|
pointHoverRadius: 4,
|
||||||
|
tension: 0.1,
|
||||||
|
fill: true
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const chartOptions = computed(() => {
|
||||||
|
// Include hoveredEventIndex in dependency tracking
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const _ = hoveredEventIndex.value
|
||||||
|
|
||||||
|
// Calculate max Y value for positioning annotations
|
||||||
|
const maxY = values.value.length > 0 ? Math.max(...values.value) : 0
|
||||||
|
const midY = maxY / 2
|
||||||
|
|
||||||
|
return {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
interaction: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
backgroundColor: isDark.value ? 'rgba(31, 41, 55, 0.95)' : 'rgba(255, 255, 255, 0.95)',
|
||||||
|
titleColor: isDark.value ? '#f9fafb' : '#111827',
|
||||||
|
bodyColor: isDark.value ? '#d1d5db' : '#374151',
|
||||||
|
borderColor: isDark.value ? '#4b5563' : '#e5e7eb',
|
||||||
|
borderWidth: 1,
|
||||||
|
padding: 12,
|
||||||
|
displayColors: false,
|
||||||
|
callbacks: {
|
||||||
|
title: (tooltipItems) => {
|
||||||
|
if (tooltipItems.length > 0) {
|
||||||
|
const date = new Date(tooltipItems[0].parsed.x)
|
||||||
|
return date.toLocaleString()
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
},
|
||||||
|
label: (context) => {
|
||||||
|
const value = context.parsed.y
|
||||||
|
return `${value}ms`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
annotation: {
|
||||||
|
annotations: filteredEvents.value.reduce((acc, event, index) => {
|
||||||
|
// Find closest data point to determine annotation position
|
||||||
|
const eventTimestamp = new Date(event.timestamp).getTime()
|
||||||
|
let closestValue = 0
|
||||||
|
|
||||||
|
if (timestamps.value.length > 0 && values.value.length > 0) {
|
||||||
|
const closestIndex = timestamps.value.reduce((closest, ts, idx) => {
|
||||||
|
const tsTime = new Date(ts).getTime()
|
||||||
|
const currentDistance = Math.abs(tsTime - eventTimestamp)
|
||||||
|
const closestDistance = Math.abs(new Date(timestamps.value[closest]).getTime() - eventTimestamp)
|
||||||
|
return currentDistance < closestDistance ? idx : closest
|
||||||
|
}, 0)
|
||||||
|
closestValue = values.value[closestIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position annotation at bottom if data point is in lower half, at top if in upper half
|
||||||
|
const position = closestValue <= midY ? 'end' : 'start'
|
||||||
|
|
||||||
|
acc[`event-${index}`] = {
|
||||||
|
type: 'line',
|
||||||
|
xMin: new Date(event.timestamp),
|
||||||
|
xMax: new Date(event.timestamp),
|
||||||
|
borderColor: getEventColor(),
|
||||||
|
borderWidth: 1,
|
||||||
|
borderDash: [5, 5],
|
||||||
|
enter() {
|
||||||
|
hoveredEventIndex.value = index
|
||||||
|
},
|
||||||
|
leave() {
|
||||||
|
hoveredEventIndex.value = null
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
display: () => hoveredEventIndex.value === index,
|
||||||
|
content: [event.isOngoing ? `Status: ONGOING` : `Status: RESOLVED`, `Unhealthy for ${event.duration}`, `Started at ${new Date(event.timestamp).toLocaleString()}`],
|
||||||
|
backgroundColor: getEventColor(),
|
||||||
|
color: '#ffffff',
|
||||||
|
font: {
|
||||||
|
size: 11
|
||||||
|
},
|
||||||
|
padding: 6,
|
||||||
|
position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
type: 'time',
|
||||||
|
time: {
|
||||||
|
unit: props.duration === '24h' ? 'hour' : props.duration === '7d' ? 'day' : 'day',
|
||||||
|
displayFormats: {
|
||||||
|
hour: 'MMM d, ha',
|
||||||
|
day: 'MMM d'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
color: isDark.value ? 'rgba(75, 85, 99, 0.3)' : 'rgba(229, 231, 235, 0.8)',
|
||||||
|
drawBorder: false
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: isDark.value ? '#9ca3af' : '#6b7280',
|
||||||
|
maxRotation: 0,
|
||||||
|
autoSkipPadding: 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
beginAtZero: true,
|
||||||
|
grid: {
|
||||||
|
color: isDark.value ? 'rgba(75, 85, 99, 0.3)' : 'rgba(229, 231, 235, 0.8)',
|
||||||
|
drawBorder: false
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: isDark.value ? '#9ca3af' : '#6b7280',
|
||||||
|
callback: (value) => `${value}ms`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
error.value = null
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${props.serverUrl}/api/v1/endpoints/${props.endpointKey}/response-times/${props.duration}/history`, {
|
||||||
|
credentials: 'include'
|
||||||
|
})
|
||||||
|
if (response.status === 200) {
|
||||||
|
const data = await response.json()
|
||||||
|
timestamps.value = data.timestamps || []
|
||||||
|
values.value = data.values || []
|
||||||
|
} else {
|
||||||
|
error.value = 'Failed to load chart data'
|
||||||
|
console.error('[ResponseTimeChart] Error:', await response.text())
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
error.value = 'Failed to load chart data'
|
||||||
|
console.error('[ResponseTimeChart] Error:', err)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => props.duration, () => {
|
||||||
|
fetchData()
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
isDark.value = document.documentElement.classList.contains('dark')
|
||||||
|
})
|
||||||
|
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] })
|
||||||
|
onUnmounted(() => observer.disconnect())
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -31,8 +31,28 @@ export const generatePrettyTimeAgo = (timestamp) => {
|
|||||||
* @returns {string} Time difference string
|
* @returns {string} Time difference string
|
||||||
*/
|
*/
|
||||||
export const generatePrettyTimeDifference = (start, end) => {
|
export const generatePrettyTimeDifference = (start, end) => {
|
||||||
let minutes = Math.ceil((new Date(start) - new Date(end)) / 1000 / 60);
|
const ms = new Date(start) - new Date(end)
|
||||||
return minutes + (minutes === 1 ? ' minute' : ' minutes');
|
const seconds = Math.floor(ms / 1000)
|
||||||
|
const minutes = Math.floor(seconds / 60)
|
||||||
|
const hours = Math.floor(minutes / 60)
|
||||||
|
|
||||||
|
if (hours > 0) {
|
||||||
|
const remainingMinutes = minutes % 60
|
||||||
|
const hoursText = hours + (hours === 1 ? ' hour' : ' hours')
|
||||||
|
if (remainingMinutes > 0) {
|
||||||
|
return hoursText + ' ' + remainingMinutes + (remainingMinutes === 1 ? ' minute' : ' minutes')
|
||||||
|
}
|
||||||
|
return hoursText
|
||||||
|
} else if (minutes > 0) {
|
||||||
|
const remainingSeconds = seconds % 60
|
||||||
|
const minutesText = minutes + (minutes === 1 ? ' minute' : ' minutes')
|
||||||
|
if (remainingSeconds > 0) {
|
||||||
|
return minutesText + ' ' + remainingSeconds + (remainingSeconds === 1 ? ' second' : ' seconds')
|
||||||
|
}
|
||||||
|
return minutesText
|
||||||
|
} else {
|
||||||
|
return seconds + (seconds === 1 ? ' second' : ' seconds')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -117,7 +117,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<img :src="generateResponseTimeChartImageURL(selectedChartDuration)" alt="Response time chart" class="w-full" />
|
<ResponseTimeChart
|
||||||
|
v-if="endpointStatus && endpointStatus.key"
|
||||||
|
:endpointKey="endpointStatus.key"
|
||||||
|
:duration="selectedChartDuration"
|
||||||
|
:serverUrl="serverUrl"
|
||||||
|
:events="endpointStatus.events || []"
|
||||||
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
@@ -206,6 +212,7 @@ import EndpointCard from '@/components/EndpointCard.vue'
|
|||||||
import Settings from '@/components/Settings.vue'
|
import Settings from '@/components/Settings.vue'
|
||||||
import Pagination from '@/components/Pagination.vue'
|
import Pagination from '@/components/Pagination.vue'
|
||||||
import Loading from '@/components/Loading.vue'
|
import Loading from '@/components/Loading.vue'
|
||||||
|
import ResponseTimeChart from '@/components/ResponseTimeChart.vue'
|
||||||
import { SERVER_URL } from '@/main.js'
|
import { SERVER_URL } from '@/main.js'
|
||||||
import { generatePrettyTimeAgo, generatePrettyTimeDifference } from '@/utils/time'
|
import { generatePrettyTimeAgo, generatePrettyTimeDifference } from '@/utils/time'
|
||||||
|
|
||||||
@@ -389,10 +396,6 @@ const generateResponseTimeBadgeImageURL = (duration) => {
|
|||||||
return `${serverUrl}/api/v1/endpoints/${endpointStatus.value.key}/response-times/${duration}/badge.svg`
|
return `${serverUrl}/api/v1/endpoints/${endpointStatus.value.key}/response-times/${duration}/badge.svg`
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateResponseTimeChartImageURL = (duration) => {
|
|
||||||
return `${serverUrl}/api/v1/endpoints/${endpointStatus.value.key}/response-times/${duration}/chart.svg`
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchData()
|
fetchData()
|
||||||
})
|
})
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user