Compare commits
23 commits
Author | SHA1 | Date | |
---|---|---|---|
|
8fa8bb4c3a | ||
|
7cf2b60803 | ||
|
db41d360dc | ||
|
6685f8bc46 | ||
|
96f7b50e4e | ||
|
8ef6a2bbfa | ||
|
d1599fe2b9 | ||
|
6909b223a7 | ||
|
570ea064aa | ||
|
f8a64e4dfd | ||
|
a8bdeae3a9 | ||
|
1deb80c776 | ||
|
85a6da0b0a | ||
|
17e555e7fc | ||
|
29e1001665 | ||
|
fe48cf780e | ||
|
169c881134 | ||
|
e55e4261dd | ||
|
c3fbfa307f | ||
|
cd123ae1c1 | ||
|
9689283c0e | ||
|
3adf85a0f6 | ||
|
2c5b434fd2 |
41
datapoint.go
|
@ -162,8 +162,6 @@ func DatapointAdd[T any](name string, value T) (err error) { // {{{
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
service.Db.Conn.Exec(`UPDATE datapoint SET last_value = NOW(), nodata_is_problem = false WHERE id=$1`, dpID)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
} // }}}
|
} // }}}
|
||||||
|
|
||||||
|
@ -172,30 +170,19 @@ func DatapointsRetrieve() (dps []Datapoint, err error) { // {{{
|
||||||
var rows *sqlx.Rows
|
var rows *sqlx.Rows
|
||||||
rows, err = service.Db.Conn.Queryx(`
|
rows, err = service.Db.Conn.Queryx(`
|
||||||
SELECT
|
SELECT
|
||||||
dp.id,
|
id, name, datatype, last_value, "group", comment, nodata_problem_seconds,
|
||||||
dp.name,
|
last_value_id AS v_id,
|
||||||
dp.datatype,
|
CASE
|
||||||
dp.last_value,
|
WHEN last_value_id IS NULL THEN null
|
||||||
dp.group,
|
ELSE last_value
|
||||||
dp.comment,
|
END AS ts,
|
||||||
dp.nodata_problem_seconds,
|
last_value_int AS value_int,
|
||||||
|
last_value_string AS value_string,
|
||||||
dpv.id AS v_id,
|
last_value_datetime AS value_datetime
|
||||||
dpv.ts,
|
FROM datapoint
|
||||||
dpv.value_int,
|
|
||||||
dpv.value_string,
|
|
||||||
dpv.value_datetime
|
|
||||||
|
|
||||||
FROM public.datapoint dp
|
|
||||||
LEFT JOIN (
|
|
||||||
SELECT
|
|
||||||
*,
|
|
||||||
row_number() OVER (PARTITION BY "datapoint_id" ORDER BY ts DESC) AS rn
|
|
||||||
FROM datapoint_value
|
|
||||||
) dpv ON dpv.datapoint_id = dp.id AND rn = 1
|
|
||||||
ORDER BY
|
ORDER BY
|
||||||
dp.group ASC,
|
"group" ASC,
|
||||||
dp.name ASC
|
name ASC
|
||||||
`)
|
`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = werr.Wrap(err)
|
err = werr.Wrap(err)
|
||||||
|
@ -255,11 +242,11 @@ func DatapointRetrieve(id int, name string) (dp Datapoint, err error) { // {{{
|
||||||
var query string
|
var query string
|
||||||
var param any
|
var param any
|
||||||
if id > 0 {
|
if id > 0 {
|
||||||
query = `SELECT *, true AS found FROM datapoint WHERE id = $1`
|
query = `SELECT id, "group", name, "datatype", comment, last_value, nodata_problem_seconds, nodata_is_problem, true AS found FROM public.datapoint WHERE id = $1`
|
||||||
param = id
|
param = id
|
||||||
dp.ID = id
|
dp.ID = id
|
||||||
} else {
|
} else {
|
||||||
query = `SELECT *, true AS found FROM datapoint WHERE name = $1`
|
query = `SELECT id, "group", name, "datatype", comment, last_value, nodata_problem_seconds, nodata_is_problem, true AS found FROM public.datapoint WHERE name = $1`
|
||||||
param = name
|
param = name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
56
main.go
|
@ -29,7 +29,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const VERSION = "v32"
|
const VERSION = "v41"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
|
@ -663,6 +663,27 @@ func pageDatapointEdit(w http.ResponseWriter, r *http.Request, _ *session.T) { /
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Triggers using this datapoint is provided as a list to update
|
||||||
|
* if changing the datapoint name. Parsing expr and automatically
|
||||||
|
* changing it to renamed datapoints would be nice in the future. */
|
||||||
|
var triggers []Trigger
|
||||||
|
triggers, err = TriggersRetrieveByDatapoint(datapoint.Name)
|
||||||
|
if err != nil {
|
||||||
|
httpError(w, werr.Wrap(err).Log())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
slices.SortFunc(triggers, func(a, b Trigger) int {
|
||||||
|
an := strings.ToUpper(a.Name)
|
||||||
|
bn := strings.ToUpper(b.Name)
|
||||||
|
if an < bn {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if an > bn {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
page := Page{
|
page := Page{
|
||||||
LAYOUT: "main",
|
LAYOUT: "main",
|
||||||
PAGE: "datapoint_edit",
|
PAGE: "datapoint_edit",
|
||||||
|
@ -674,6 +695,7 @@ func pageDatapointEdit(w http.ResponseWriter, r *http.Request, _ *session.T) { /
|
||||||
|
|
||||||
page.Data = map[string]any{
|
page.Data = map[string]any{
|
||||||
"Datapoint": datapoint,
|
"Datapoint": datapoint,
|
||||||
|
"Triggers": triggers,
|
||||||
}
|
}
|
||||||
page.Render(w, r)
|
page.Render(w, r)
|
||||||
return
|
return
|
||||||
|
@ -689,8 +711,15 @@ func actionDatapointUpdate(w http.ResponseWriter, r *http.Request, _ *session.T)
|
||||||
var nodataSeconds int
|
var nodataSeconds int
|
||||||
nodataSeconds, _ = strconv.Atoi(r.FormValue("nodata_seconds"))
|
nodataSeconds, _ = strconv.Atoi(r.FormValue("nodata_seconds"))
|
||||||
|
|
||||||
|
// Datapoint needs to be retrieved from database for the name.
|
||||||
|
// If name has changed, trigger expressions needs to be updated.
|
||||||
var dp Datapoint
|
var dp Datapoint
|
||||||
dp.ID = id
|
dp, err = DatapointRetrieve(id, "")
|
||||||
|
if err != nil {
|
||||||
|
httpError(w, werr.Wrap(err).WithData(id).Log())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
prevDatapointName := dp.Name
|
||||||
dp.Group = r.FormValue("group")
|
dp.Group = r.FormValue("group")
|
||||||
dp.Name = r.FormValue("name")
|
dp.Name = r.FormValue("name")
|
||||||
dp.Datatype = DatapointType(r.FormValue("datatype"))
|
dp.Datatype = DatapointType(r.FormValue("datatype"))
|
||||||
|
@ -702,6 +731,29 @@ func actionDatapointUpdate(w http.ResponseWriter, r *http.Request, _ *session.T)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the trigger expressions using this
|
||||||
|
// datapoint name if changed.
|
||||||
|
if prevDatapointName != dp.Name {
|
||||||
|
var triggers []Trigger
|
||||||
|
triggers, err = TriggersRetrieveByDatapoint(dp.Name)
|
||||||
|
if err != nil {
|
||||||
|
httpError(w, werr.Wrap(err).WithData(dp.Name))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, trigger := range triggers {
|
||||||
|
err = trigger.RenameDatapoint(prevDatapointName, dp.Name)
|
||||||
|
if err != nil {
|
||||||
|
httpError(w, werr.Wrap(err).WithData([]string{prevDatapointName, dp.Name}))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = trigger.Update()
|
||||||
|
if err != nil {
|
||||||
|
httpError(w, werr.Wrap(err).WithData([]string{prevDatapointName, dp.Name, trigger.Name}))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
w.Header().Add("Location", "/datapoints")
|
w.Header().Add("Location", "/datapoints")
|
||||||
w.WriteHeader(302)
|
w.WriteHeader(302)
|
||||||
} // }}}
|
} // }}}
|
||||||
|
|
|
@ -81,7 +81,7 @@ func (ntfy NTFY) Send(problemID int, msg []byte) (err error) {
|
||||||
|
|
||||||
ackURL := fmt.Sprintf("http, OK, %s/notification/ack?problemID=%d", ntfy.AcknowledgeURL, problemID)
|
ackURL := fmt.Sprintf("http, OK, %s/notification/ack?problemID=%d", ntfy.AcknowledgeURL, problemID)
|
||||||
req.Header.Add("X-Actions", ackURL)
|
req.Header.Add("X-Actions", ackURL)
|
||||||
req.Header.Add("X-Priority", "4") // XXX: should be 5
|
req.Header.Add("X-Priority", "5")
|
||||||
req.Header.Add("X-Tags", "calendar")
|
req.Header.Add("X-Tags", "calendar")
|
||||||
|
|
||||||
res, err = http.DefaultClient.Do(req)
|
res, err = http.DefaultClient.Do(req)
|
||||||
|
|
6
page.go
|
@ -2,7 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// External
|
// External
|
||||||
we "git.gibonuddevalla.se/go/wrappederror"
|
werr "git.gibonuddevalla.se/go/wrappederror"
|
||||||
|
|
||||||
// Standard
|
// Standard
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -25,7 +25,7 @@ type Page struct {
|
||||||
func (p *Page) Render(w http.ResponseWriter, r *http.Request) {
|
func (p *Page) Render(w http.ResponseWriter, r *http.Request) {
|
||||||
tmpl, err := getPage(p.LAYOUT, p.PAGE)
|
tmpl, err := getPage(p.LAYOUT, p.PAGE)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +58,6 @@ func (p *Page) Render(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
err = tmpl.Execute(w, data)
|
err = tmpl.Execute(w, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, we.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Before Width: | Height: | Size: 128 KiB After Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 132 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 8.9 KiB |
62
sql/00026.sql
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
/* Adding last values to the datapoint table since they are a regularly used value. */
|
||||||
|
ALTER TABLE public.datapoint ADD COLUMN last_value_id int4 NULL;
|
||||||
|
ALTER TABLE public.datapoint ADD COLUMN last_value_int int8 NULL;
|
||||||
|
ALTER TABLE public.datapoint ADD COLUMN last_value_string varchar NULL;
|
||||||
|
ALTER TABLE public.datapoint ADD COLUMN last_value_datetime timestamptz NULL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Once-run query to update it to the latest, to avoid user having to wait for the next entry. */
|
||||||
|
UPDATE public.datapoint AS dp
|
||||||
|
SET
|
||||||
|
last_value_id = dpv.id,
|
||||||
|
last_value_int = dpv.value_int,
|
||||||
|
last_value_string = dpv.value_string,
|
||||||
|
last_value_datetime = dpv.value_datetime
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
dp.id AS datapoint_id,
|
||||||
|
dpv.id,
|
||||||
|
dpv.value_int,
|
||||||
|
dpv.value_string,
|
||||||
|
dpv.value_datetime
|
||||||
|
FROM public.datapoint dp
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
*,
|
||||||
|
row_number() OVER (PARTITION BY "datapoint_id" ORDER BY ts DESC) AS rn
|
||||||
|
FROM datapoint_value
|
||||||
|
) dpv ON dpv.datapoint_id = dp.id AND rn = 1
|
||||||
|
) AS dpv
|
||||||
|
WHERE
|
||||||
|
dpv.datapoint_id = dp.id;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* A trigger keeps the value current without bugs introduced in software missing the entry. */
|
||||||
|
CREATE OR REPLACE FUNCTION datapoint_entry()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
|
AS
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
UPDATE public.datapoint
|
||||||
|
SET
|
||||||
|
nodata_is_problem = false,
|
||||||
|
last_value = NEW.ts,
|
||||||
|
last_value_id = NEW.id,
|
||||||
|
last_value_int = NEW.value_int,
|
||||||
|
last_value_string = NEW.value_string,
|
||||||
|
last_value_datetime = NEW.value_datetime
|
||||||
|
WHERE
|
||||||
|
id = NEW.datapoint_id;
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE TRIGGER datapoint_entry
|
||||||
|
AFTER INSERT
|
||||||
|
ON public.datapoint_value
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE datapoint_entry();
|
22
sql/00027.sql
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/* Updating a datapoint name also updates the jsonb array entry */
|
||||||
|
CREATE OR REPLACE FUNCTION update_triggers_datapoint_name()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
|
AS
|
||||||
|
$$
|
||||||
|
BEGIN
|
||||||
|
UPDATE "trigger"
|
||||||
|
SET
|
||||||
|
datapoints = (datapoints - OLD.name) || jsonb_build_array(NEW.name)
|
||||||
|
WHERE
|
||||||
|
datapoints ? OLD.name;
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE TRIGGER datapoint_renamed
|
||||||
|
AFTER UPDATE
|
||||||
|
ON public.datapoint
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE update_triggers_datapoint_name();
|
|
@ -1,3 +1,6 @@
|
||||||
|
#datapoints-filter.invalid-regex {
|
||||||
|
background-color: #ffd5d5;
|
||||||
|
}
|
||||||
#datapoints {
|
#datapoints {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(6, min-content);
|
grid-template-columns: repeat(6, min-content);
|
||||||
|
@ -97,5 +100,5 @@
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
.graph #graph-values {
|
.graph #graph-values {
|
||||||
height: calc(100vh - 416px);
|
height: calc(100vh - 200px);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,9 @@ dialog,
|
||||||
#acknowledged-list,
|
#acknowledged-list,
|
||||||
#values,
|
#values,
|
||||||
#services,
|
#services,
|
||||||
#notifications {
|
#notifications,
|
||||||
|
#group,
|
||||||
|
.table {
|
||||||
background-color: #fff !important;
|
background-color: #fff !important;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
box-shadow: 5px 5px 8px 0px rgba(0, 0, 0, 0.25);
|
box-shadow: 5px 5px 8px 0px rgba(0, 0, 0, 0.25);
|
||||||
|
|
|
@ -20,5 +20,4 @@ input[type="datetime-local"] {
|
||||||
background-color: #1b4e78;
|
background-color: #1b4e78;
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
border: 1px solid #535353;
|
border: 1px solid #535353;
|
||||||
padding: 6px;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,28 @@
|
||||||
|
.table {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 6px 16px;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 32px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
background-color: #2979b8;
|
||||||
|
padding: 16px 24px;
|
||||||
|
width: min-content;
|
||||||
|
border-top-left-radius: 8px;
|
||||||
|
border-top-right-radius: 8px;
|
||||||
|
}
|
||||||
|
.table .row {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
.table > div {
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
.table .header {
|
||||||
|
font-size: 0.85em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #7bb8eb;
|
||||||
|
line-height: unset !important;
|
||||||
|
}
|
||||||
html {
|
html {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
@ -233,42 +258,34 @@ label {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
#time-selector {
|
#time-selector {
|
||||||
|
position: absolute;
|
||||||
|
top: 16px;
|
||||||
|
right: 16px;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: min-content min-content;
|
grid-template-columns: 8px repeat(2, min-content) 8px min-content 8px 1px 8px repeat(3, min-content) 8px repeat(3, min-content) 8px 1px 8px repeat(2, min-content) 8px;
|
||||||
grid-gap: 6px 16px;
|
grid-gap: 6px 8px;
|
||||||
|
align-items: center;
|
||||||
width: min-content;
|
width: min-content;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border: 1px solid #2979b8;
|
border: 1px solid #2979b8;
|
||||||
padding: 16px;
|
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
#time-selector.hidden {
|
#time-selector.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
#time-selector .vertical-line {
|
||||||
|
background-color: #2979b8;
|
||||||
|
}
|
||||||
|
#time-selector .header {
|
||||||
|
padding-top: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
#time-selector button {
|
#time-selector button {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
justify-self: end;
|
justify-self: end;
|
||||||
}
|
}
|
||||||
#time-selector #time-filter {
|
#time-selector div {
|
||||||
display: grid;
|
|
||||||
grid-template-columns: min-content repeat(3, min-content);
|
|
||||||
grid-gap: 16px;
|
|
||||||
margin-top: 16px;
|
|
||||||
align-items: center;
|
|
||||||
justify-items: center;
|
|
||||||
}
|
|
||||||
#time-selector #time-filter .header-1 {
|
|
||||||
font-weight: bold;
|
|
||||||
justify-self: start;
|
|
||||||
}
|
|
||||||
#time-selector #time-filter .header-2 {
|
|
||||||
font-weight: bold;
|
|
||||||
justify-self: start;
|
|
||||||
grid-column: 2 / -1;
|
|
||||||
}
|
|
||||||
#time-selector #time-filter .preset {
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
justify-self: start;
|
|
||||||
padding-right: 32px;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
input[type="datetime-local"] {
|
|
||||||
padding: 6px;
|
|
||||||
}
|
|
||||||
#notifications {
|
#notifications {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(5, min-content);
|
grid-template-columns: repeat(5, min-content);
|
||||||
grid-gap: 4px 16px;
|
grid-gap: 4px 16px;
|
||||||
margin-top: 32px;
|
margin-top: 96px;
|
||||||
margin-bottom: 32px;
|
margin-bottom: 32px;
|
||||||
background-color: #2979b8;
|
background-color: #2979b8;
|
||||||
padding: 16px 24px;
|
padding: 16px 24px;
|
||||||
|
|
|
@ -1,61 +1,45 @@
|
||||||
#problems-list,
|
#problems-list,
|
||||||
#acknowledged-list {
|
#acknowledged-list {
|
||||||
display: grid;
|
grid-template-columns: repeat(7, min-content);
|
||||||
grid-template-columns: repeat(6, min-content);
|
|
||||||
grid-gap: 4px 16px;
|
|
||||||
margin-top: 32px;
|
|
||||||
margin-bottom: 32px;
|
|
||||||
background-color: #2979b8;
|
|
||||||
padding: 16px 24px;
|
|
||||||
width: min-content;
|
|
||||||
border-top-left-radius: 8px;
|
|
||||||
border-top-right-radius: 8px;
|
|
||||||
}
|
|
||||||
#problems-list div,
|
|
||||||
#acknowledged-list div {
|
|
||||||
white-space: nowrap;
|
|
||||||
line-height: 24px;
|
|
||||||
}
|
|
||||||
#problems-list .header,
|
|
||||||
#acknowledged-list .header {
|
|
||||||
font-weight: 800;
|
|
||||||
color: #7bb8eb;
|
|
||||||
}
|
}
|
||||||
#problems-list .trigger,
|
#problems-list .trigger,
|
||||||
#acknowledged-list .trigger {
|
#acknowledged-list .trigger {
|
||||||
color: #1b4e78;
|
color: #1b4e78;
|
||||||
font-weight: 800;
|
|
||||||
}
|
}
|
||||||
#problems-list .acknowledge img,
|
#problems-list img.acknowledge,
|
||||||
#acknowledged-list .acknowledge img {
|
#acknowledged-list img.acknowledge {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
#problems-list .info,
|
#problems-list .info,
|
||||||
#acknowledged-list .info {
|
#acknowledged-list .info {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
#problems-list .icons,
|
||||||
|
#acknowledged-list .icons {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: min-content min-content;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
#acknowledged-list.hidden {
|
#acknowledged-list.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#areas {
|
#area-grouped {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
#areas .area .section {
|
#area-grouped .area {
|
||||||
display: grid;
|
grid-template-columns: repeat(5, min-content);
|
||||||
grid-template-columns: repeat(4, min-content);
|
|
||||||
grid-gap: 8px 12px;
|
|
||||||
}
|
}
|
||||||
#areas .area .section .name {
|
#area-grouped .area .section {
|
||||||
color: #000;
|
padding: 4px 10px;
|
||||||
grid-column: 1 / -1;
|
border-radius: 5px;
|
||||||
font-weight: bold !important;
|
background: #2979b8;
|
||||||
line-height: 24px;
|
color: #fff;
|
||||||
}
|
width: min-content;
|
||||||
#areas .area .section div {
|
margin-bottom: 8px;
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
25
static/css/default_light/table.css
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
.table {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 6px 16px;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 32px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
background-color: #2979b8;
|
||||||
|
padding: 16px 24px;
|
||||||
|
width: min-content;
|
||||||
|
border-top-left-radius: 8px;
|
||||||
|
border-top-right-radius: 8px;
|
||||||
|
}
|
||||||
|
.table .row {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
.table > div {
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
.table .header {
|
||||||
|
font-size: 0.85em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #7bb8eb;
|
||||||
|
line-height: unset !important;
|
||||||
|
}
|
|
@ -13,17 +13,24 @@
|
||||||
.widgets .datapoints {
|
.widgets .datapoints {
|
||||||
font: "Roboto Mono", monospace;
|
font: "Roboto Mono", monospace;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: min-content min-content 1fr;
|
grid-template-columns: repeat(4, min-content);
|
||||||
gap: 6px 8px;
|
gap: 6px 8px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
.widgets .datapoints div {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
.widgets .datapoints .invalid {
|
.widgets .datapoints .invalid {
|
||||||
color: #c83737;
|
color: #c83737;
|
||||||
}
|
}
|
||||||
.widgets .datapoints .delete img {
|
.widgets .datapoints .delete img {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
.widgets .datapoints .values img {
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
.widgets .action {
|
.widgets .action {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: min-content min-content 1fr;
|
grid-template-columns: min-content min-content 1fr;
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
#datapoints-filter.invalid-regex {
|
||||||
|
background-color: #ffd5d5;
|
||||||
|
}
|
||||||
#datapoints {
|
#datapoints {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(6, min-content);
|
grid-template-columns: repeat(6, min-content);
|
||||||
|
@ -97,5 +100,5 @@
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
.graph #graph-values {
|
.graph #graph-values {
|
||||||
height: calc(100vh - 416px);
|
height: calc(100vh - 200px);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,9 @@ dialog,
|
||||||
#acknowledged-list,
|
#acknowledged-list,
|
||||||
#values,
|
#values,
|
||||||
#services,
|
#services,
|
||||||
#notifications {
|
#notifications,
|
||||||
|
#group,
|
||||||
|
.table {
|
||||||
background-color: #fff !important;
|
background-color: #fff !important;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
box-shadow: 5px 5px 8px 0px rgba(0, 0, 0, 0.25);
|
box-shadow: 5px 5px 8px 0px rgba(0, 0, 0, 0.25);
|
||||||
|
|
|
@ -20,5 +20,4 @@ input[type="datetime-local"] {
|
||||||
background-color: #202020;
|
background-color: #202020;
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
border: 1px solid #535353;
|
border: 1px solid #535353;
|
||||||
padding: 6px;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,28 @@
|
||||||
|
.table {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 6px 16px;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 32px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
background-color: #333;
|
||||||
|
padding: 16px 24px;
|
||||||
|
width: min-content;
|
||||||
|
border-top-left-radius: 8px;
|
||||||
|
border-top-right-radius: 8px;
|
||||||
|
}
|
||||||
|
.table .row {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
.table > div {
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
.table .header {
|
||||||
|
font-size: 0.85em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #777;
|
||||||
|
line-height: unset !important;
|
||||||
|
}
|
||||||
html {
|
html {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
@ -233,42 +258,34 @@ label {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
#time-selector {
|
#time-selector {
|
||||||
|
position: absolute;
|
||||||
|
top: 16px;
|
||||||
|
right: 16px;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: min-content min-content;
|
grid-template-columns: 8px repeat(2, min-content) 8px min-content 8px 1px 8px repeat(3, min-content) 8px repeat(3, min-content) 8px 1px 8px repeat(2, min-content) 8px;
|
||||||
grid-gap: 6px 16px;
|
grid-gap: 6px 8px;
|
||||||
|
align-items: center;
|
||||||
width: min-content;
|
width: min-content;
|
||||||
background-color: #282828;
|
background-color: #282828;
|
||||||
border: 1px solid #333;
|
border: 1px solid #333;
|
||||||
padding: 16px;
|
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
#time-selector.hidden {
|
#time-selector.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
#time-selector .vertical-line {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
#time-selector .header {
|
||||||
|
padding-top: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
#time-selector button {
|
#time-selector button {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
justify-self: end;
|
justify-self: end;
|
||||||
}
|
}
|
||||||
#time-selector #time-filter {
|
#time-selector div {
|
||||||
display: grid;
|
|
||||||
grid-template-columns: min-content repeat(3, min-content);
|
|
||||||
grid-gap: 16px;
|
|
||||||
margin-top: 16px;
|
|
||||||
align-items: center;
|
|
||||||
justify-items: center;
|
|
||||||
}
|
|
||||||
#time-selector #time-filter .header-1 {
|
|
||||||
font-weight: bold;
|
|
||||||
justify-self: start;
|
|
||||||
}
|
|
||||||
#time-selector #time-filter .header-2 {
|
|
||||||
font-weight: bold;
|
|
||||||
justify-self: start;
|
|
||||||
grid-column: 2 / -1;
|
|
||||||
}
|
|
||||||
#time-selector #time-filter .preset {
|
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
justify-self: start;
|
|
||||||
padding-right: 32px;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
input[type="datetime-local"] {
|
|
||||||
padding: 6px;
|
|
||||||
}
|
|
||||||
#notifications {
|
#notifications {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(5, min-content);
|
grid-template-columns: repeat(5, min-content);
|
||||||
grid-gap: 4px 16px;
|
grid-gap: 4px 16px;
|
||||||
margin-top: 32px;
|
margin-top: 96px;
|
||||||
margin-bottom: 32px;
|
margin-bottom: 32px;
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
padding: 16px 24px;
|
padding: 16px 24px;
|
||||||
|
|
|
@ -1,61 +1,45 @@
|
||||||
#problems-list,
|
#problems-list,
|
||||||
#acknowledged-list {
|
#acknowledged-list {
|
||||||
display: grid;
|
grid-template-columns: repeat(7, min-content);
|
||||||
grid-template-columns: repeat(6, min-content);
|
|
||||||
grid-gap: 4px 16px;
|
|
||||||
margin-top: 32px;
|
|
||||||
margin-bottom: 32px;
|
|
||||||
background-color: #333;
|
|
||||||
padding: 16px 24px;
|
|
||||||
width: min-content;
|
|
||||||
border-top-left-radius: 8px;
|
|
||||||
border-top-right-radius: 8px;
|
|
||||||
}
|
|
||||||
#problems-list div,
|
|
||||||
#acknowledged-list div {
|
|
||||||
white-space: nowrap;
|
|
||||||
line-height: 24px;
|
|
||||||
}
|
|
||||||
#problems-list .header,
|
|
||||||
#acknowledged-list .header {
|
|
||||||
font-weight: 800;
|
|
||||||
color: #777;
|
|
||||||
}
|
}
|
||||||
#problems-list .trigger,
|
#problems-list .trigger,
|
||||||
#acknowledged-list .trigger {
|
#acknowledged-list .trigger {
|
||||||
color: #fb4934;
|
color: #fb4934;
|
||||||
font-weight: 800;
|
|
||||||
}
|
}
|
||||||
#problems-list .acknowledge img,
|
#problems-list img.acknowledge,
|
||||||
#acknowledged-list .acknowledge img {
|
#acknowledged-list img.acknowledge {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
#problems-list .info,
|
#problems-list .info,
|
||||||
#acknowledged-list .info {
|
#acknowledged-list .info {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
#problems-list .icons,
|
||||||
|
#acknowledged-list .icons {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: min-content min-content;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
#acknowledged-list.hidden {
|
#acknowledged-list.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
#areas {
|
#area-grouped {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
#areas .area .section {
|
#area-grouped .area {
|
||||||
display: grid;
|
grid-template-columns: repeat(5, min-content);
|
||||||
grid-template-columns: repeat(4, min-content);
|
|
||||||
grid-gap: 8px 12px;
|
|
||||||
}
|
}
|
||||||
#areas .area .section .name {
|
#area-grouped .area .section {
|
||||||
color: #f7edd7;
|
padding: 4px 10px;
|
||||||
grid-column: 1 / -1;
|
border-radius: 5px;
|
||||||
font-weight: bold !important;
|
background: #333;
|
||||||
line-height: 24px;
|
color: #fff;
|
||||||
}
|
width: min-content;
|
||||||
#areas .area .section div {
|
margin-bottom: 8px;
|
||||||
white-space: nowrap;
|
|
||||||
}
|
}
|
||||||
.hidden {
|
.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
25
static/css/gruvbox/table.css
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
.table {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 6px 16px;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 32px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
background-color: #333;
|
||||||
|
padding: 16px 24px;
|
||||||
|
width: min-content;
|
||||||
|
border-top-left-radius: 8px;
|
||||||
|
border-top-right-radius: 8px;
|
||||||
|
}
|
||||||
|
.table .row {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
.table > div {
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
.table .header {
|
||||||
|
font-size: 0.85em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #777;
|
||||||
|
line-height: unset !important;
|
||||||
|
}
|
|
@ -13,17 +13,24 @@
|
||||||
.widgets .datapoints {
|
.widgets .datapoints {
|
||||||
font: "Roboto Mono", monospace;
|
font: "Roboto Mono", monospace;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: min-content min-content 1fr;
|
grid-template-columns: repeat(4, min-content);
|
||||||
gap: 6px 8px;
|
gap: 6px 8px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
.widgets .datapoints div {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
.widgets .datapoints .invalid {
|
.widgets .datapoints .invalid {
|
||||||
color: #c83737;
|
color: #c83737;
|
||||||
}
|
}
|
||||||
.widgets .datapoints .delete img {
|
.widgets .datapoints .delete img {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
.widgets .datapoints .values img {
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
.widgets .action {
|
.widgets .action {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: min-content min-content 1fr;
|
grid-template-columns: min-content min-content 1fr;
|
||||||
|
|
69
static/images/default_light/forward.svg
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="19.999975"
|
||||||
|
height="19.999975"
|
||||||
|
viewBox="0 0 5.29166 5.2916603"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)"
|
||||||
|
sodipodi:docname="forward.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="22.627417"
|
||||||
|
inkscape:cx="9.3028736"
|
||||||
|
inkscape:cy="12.772116"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="2190"
|
||||||
|
inkscape:window-height="1404"
|
||||||
|
inkscape:window-x="1463"
|
||||||
|
inkscape:window-y="16"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:showpageshadow="true"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d6d6d6"
|
||||||
|
showborder="true" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-123.825,-155.04583)">
|
||||||
|
<title
|
||||||
|
id="title1">alert</title>
|
||||||
|
<title
|
||||||
|
id="title1-1">arrow-right-bold-circle</title>
|
||||||
|
<path
|
||||||
|
d="m 123.825,157.69166 a 2.6458334,2.6458334 0 0 1 2.64583,-2.64583 2.6458334,2.6458334 0 0 1 2.64583,2.64583 2.6458334,2.6458334 0 0 1 -2.64583,2.64583 2.6458334,2.6458334 0 0 1 -2.64583,-2.64583 m 3.96875,0 -1.32292,-1.32292 v 0.79375 h -1.05833 v 1.05834 h 1.05833 v 0.79375 z"
|
||||||
|
id="path1"
|
||||||
|
style="stroke-width:0.264583;fill:#abc837" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
67
static/images/default_light/ok.svg
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="20.000002"
|
||||||
|
height="20.000013"
|
||||||
|
viewBox="0 0 5.291667 5.2916703"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)"
|
||||||
|
sodipodi:docname="ok.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="2.9208594"
|
||||||
|
inkscape:cx="9.7574023"
|
||||||
|
inkscape:cy="10.27095"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="2190"
|
||||||
|
inkscape:window-height="1404"
|
||||||
|
inkscape:window-x="1463"
|
||||||
|
inkscape:window-y="16"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:showpageshadow="true"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d6d6d6"
|
||||||
|
showborder="true" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-90.222917,-148.43125)">
|
||||||
|
<title
|
||||||
|
id="title1">check-circle</title>
|
||||||
|
<path
|
||||||
|
d="m 92.86875,148.43125 c -1.455208,0 -2.645833,1.19063 -2.645833,2.64583 0,1.45521 1.190625,2.64584 2.645833,2.64584 1.455209,0 2.645834,-1.19063 2.645834,-2.64584 0,-1.4552 -1.190625,-2.64583 -2.645834,-2.64583 m -0.529166,3.96875 -1.322917,-1.32292 0.373062,-0.37306 0.949855,0.94721 2.008187,-2.00819 0.373063,0.37571 z"
|
||||||
|
id="path1"
|
||||||
|
style="fill:#aad400;stroke-width:0.264583" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
68
static/images/default_light/warning.svg
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="23.157846"
|
||||||
|
height="20.000013"
|
||||||
|
viewBox="0 0 6.1271801 5.2916704"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)"
|
||||||
|
sodipodi:docname="warning.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="22.627417"
|
||||||
|
inkscape:cx="9.3028736"
|
||||||
|
inkscape:cy="12.772116"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="2190"
|
||||||
|
inkscape:window-height="1404"
|
||||||
|
inkscape:window-x="1463"
|
||||||
|
inkscape:window-y="16"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:showpageshadow="true"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d6d6d6"
|
||||||
|
showborder="true" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-123.825,-155.04583)">
|
||||||
|
<title
|
||||||
|
id="title1">alert</title>
|
||||||
|
<path
|
||||||
|
d="m 127.21387,158.38793 h -0.65056 v -1.39253 h 0.65056 m 0,2.50657 h -0.65056 v -0.55701 h 0.65056 m -3.38887,1.39254 h 6.12718 l -3.06358,-5.29167 z"
|
||||||
|
id="path1"
|
||||||
|
style="fill:#ff9800;fill-opacity:1;stroke-width:0.278508"
|
||||||
|
sodipodi:nodetypes="cccccccccccc" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
69
static/images/gruvbox/forward.svg
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="19.999975"
|
||||||
|
height="19.999975"
|
||||||
|
viewBox="0 0 5.29166 5.2916603"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)"
|
||||||
|
sodipodi:docname="forward.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="22.627417"
|
||||||
|
inkscape:cx="9.3028736"
|
||||||
|
inkscape:cy="12.772116"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="2190"
|
||||||
|
inkscape:window-height="1404"
|
||||||
|
inkscape:window-x="1463"
|
||||||
|
inkscape:window-y="16"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:showpageshadow="true"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d6d6d6"
|
||||||
|
showborder="true" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-123.825,-155.04583)">
|
||||||
|
<title
|
||||||
|
id="title1">alert</title>
|
||||||
|
<title
|
||||||
|
id="title1-1">arrow-right-bold-circle</title>
|
||||||
|
<path
|
||||||
|
d="m 123.825,157.69166 a 2.6458334,2.6458334 0 0 1 2.64583,-2.64583 2.6458334,2.6458334 0 0 1 2.64583,2.64583 2.6458334,2.6458334 0 0 1 -2.64583,2.64583 2.6458334,2.6458334 0 0 1 -2.64583,-2.64583 m 3.96875,0 -1.32292,-1.32292 v 0.79375 h -1.05833 v 1.05834 h 1.05833 v 0.79375 z"
|
||||||
|
id="path1"
|
||||||
|
style="stroke-width:0.264583;fill:#abc837" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
67
static/images/gruvbox/ok.svg
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="20.000002"
|
||||||
|
height="20.000013"
|
||||||
|
viewBox="0 0 5.291667 5.2916703"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)"
|
||||||
|
sodipodi:docname="ok.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="2.9208594"
|
||||||
|
inkscape:cx="9.7574023"
|
||||||
|
inkscape:cy="10.27095"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="2190"
|
||||||
|
inkscape:window-height="1404"
|
||||||
|
inkscape:window-x="1463"
|
||||||
|
inkscape:window-y="16"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:showpageshadow="true"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d6d6d6"
|
||||||
|
showborder="true" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-90.222917,-148.43125)">
|
||||||
|
<title
|
||||||
|
id="title1">check-circle</title>
|
||||||
|
<path
|
||||||
|
d="m 92.86875,148.43125 c -1.455208,0 -2.645833,1.19063 -2.645833,2.64583 0,1.45521 1.190625,2.64584 2.645833,2.64584 1.455209,0 2.645834,-1.19063 2.645834,-2.64584 0,-1.4552 -1.190625,-2.64583 -2.645834,-2.64583 m -0.529166,3.96875 -1.322917,-1.32292 0.373062,-0.37306 0.949855,0.94721 2.008187,-2.00819 0.373063,0.37571 z"
|
||||||
|
id="path1"
|
||||||
|
style="fill:#aad400;stroke-width:0.264583" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
68
static/images/gruvbox/warning.svg
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="23.157846"
|
||||||
|
height="20.000013"
|
||||||
|
viewBox="0 0 6.1271801 5.2916704"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="1.3.2 (1:1.3.2+202311252150+091e20ef0f)"
|
||||||
|
sodipodi:docname="warning.svg"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="22.627417"
|
||||||
|
inkscape:cx="9.3028736"
|
||||||
|
inkscape:cy="12.772116"
|
||||||
|
inkscape:document-units="px"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="2190"
|
||||||
|
inkscape:window-height="1404"
|
||||||
|
inkscape:window-x="1463"
|
||||||
|
inkscape:window-y="16"
|
||||||
|
inkscape:window-maximized="0"
|
||||||
|
inkscape:showpageshadow="true"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d6d6d6"
|
||||||
|
showborder="true" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Layer 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(-123.825,-155.04583)">
|
||||||
|
<title
|
||||||
|
id="title1">alert</title>
|
||||||
|
<path
|
||||||
|
d="m 127.21387,158.38793 h -0.65056 v -1.39253 h 0.65056 m 0,2.50657 h -0.65056 v -0.55701 h 0.65056 m -3.38887,1.39254 h 6.12718 l -3.06358,-5.29167 z"
|
||||||
|
id="path1"
|
||||||
|
style="fill:#ff9800;fill-opacity:1;stroke-width:0.278508"
|
||||||
|
sodipodi:nodetypes="cccccccccccc" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -1,5 +1,6 @@
|
||||||
export class UI {
|
export class UI {
|
||||||
constructor() {
|
constructor(datapointData) {
|
||||||
|
this.datapoint = datapointData
|
||||||
document.addEventListener('keydown', evt=>this.keyHandler(evt))
|
document.addEventListener('keydown', evt=>this.keyHandler(evt))
|
||||||
document.querySelector('input[name="group"]').focus()
|
document.querySelector('input[name="group"]').focus()
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,10 @@ export class UI {
|
||||||
let html = Object.keys(this.trigger.datapoints).sort().map(dpName => {
|
let html = Object.keys(this.trigger.datapoints).sort().map(dpName => {
|
||||||
const dp = this.trigger.datapoints[dpName]
|
const dp = this.trigger.datapoints[dpName]
|
||||||
return `
|
return `
|
||||||
<div class="datapoint delete"><a href="#" onclick="_ui.deleteDatapoint('${dp.Name}')"><img src="/images/${this.version}/${this.theme}/delete.svg"></a></div>
|
|
||||||
<div class="datapoint name ${dp.Found ? 'valid' : 'invalid'}"><b>${dp.Name}</b></div>
|
<div class="datapoint name ${dp.Found ? 'valid' : 'invalid'}"><b>${dp.Name}</b></div>
|
||||||
<div class="datapoint value">${dp.Found ? dp.LastDatapointValue.TemplateValue : ''}</div>
|
<div class="datapoint value">${dp.Found ? dp.LastDatapointValue.TemplateValue : ''}</div>
|
||||||
|
<div class="daatpoint values"><a href="/datapoint/values/${dp.ID}"><img src="/images/${this.version}/${this.theme}/values.svg"></a></div>
|
||||||
|
<div class="datapoint delete"><a href="#" onclick="_ui.deleteDatapoint('${dp.Name}')"><img src="/images/${this.version}/${this.theme}/delete.svg"></a></div>
|
||||||
`
|
`
|
||||||
}).join('')
|
}).join('')
|
||||||
datapoints.innerHTML += html
|
datapoints.innerHTML += html
|
||||||
|
@ -84,7 +85,7 @@ export class UI {
|
||||||
})
|
})
|
||||||
}//}}}
|
}//}}}
|
||||||
deleteDatapoint(name) {//{{{
|
deleteDatapoint(name) {//{{{
|
||||||
if (!confirm(`Delete ${name}?`)) {
|
if (!confirm(`Remove datapoint ${name} from this trigger?`)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
@import "theme-@{THEME}.less";
|
@import "theme-@{THEME}.less";
|
||||||
|
|
||||||
|
#datapoints-filter.invalid-regex {
|
||||||
|
background-color: #ffd5d5;
|
||||||
|
}
|
||||||
|
|
||||||
#datapoints {
|
#datapoints {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(6, min-content);
|
grid-template-columns: repeat(6, min-content);
|
||||||
|
@ -117,6 +121,6 @@
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
|
|
||||||
#graph-values {
|
#graph-values {
|
||||||
height: calc(100vh - 416px);
|
height: calc(100vh - 200px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,7 +62,7 @@ dialog {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog, #datapoints, #problems-list, #acknowledged-list, #values, #services, #notifications {
|
dialog, #datapoints, #problems-list, #acknowledged-list, #values, #services, #notifications, #group, .table {
|
||||||
background-color: #fff !important;
|
background-color: #fff !important;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #ddd;
|
||||||
box-shadow: 5px 5px 8px 0px rgba(0,0,0,0.25);
|
box-shadow: 5px 5px 8px 0px rgba(0,0,0,0.25);
|
||||||
|
|
|
@ -29,5 +29,4 @@ input[type="datetime-local"] {
|
||||||
background-color: @bg2;
|
background-color: @bg2;
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
border: 1px solid #535353;
|
border: 1px solid #535353;
|
||||||
padding: 6px;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
@import "theme-@{THEME}.less";
|
@import "theme-@{THEME}.less";
|
||||||
|
@import "table.less";
|
||||||
|
|
||||||
html {
|
html {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@ -290,51 +291,42 @@ label {
|
||||||
}
|
}
|
||||||
|
|
||||||
#time-selector {
|
#time-selector {
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
top: 16px;
|
||||||
|
right: 16px;
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: min-content min-content;
|
grid-template-columns: 8px repeat(2,min-content) 8px min-content 8px 1px 8px repeat(3,min-content) 8px repeat(3,min-content) 8px 1px 8px repeat(2,min-content) 8px;
|
||||||
grid-gap: 6px 16px;
|
grid-gap: 6px 8px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
width: min-content;
|
width: min-content;
|
||||||
background-color: @bg1;
|
background-color: @bg1;
|
||||||
border: 1px solid @bg3;
|
border: 1px solid @bg3;
|
||||||
padding: 16px;
|
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
|
||||||
&.hidden {
|
&.hidden {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vertical-line {
|
||||||
|
background-color: @bg3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
padding-top: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.85em;
|
||||||
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
justify-self: end;
|
justify-self: end;
|
||||||
}
|
}
|
||||||
|
|
||||||
#time-filter {
|
div {
|
||||||
display: grid;
|
white-space: nowrap;
|
||||||
grid-template-columns: min-content repeat(3, min-content);
|
|
||||||
grid-gap: 16px;
|
|
||||||
margin-top: 16px;
|
|
||||||
|
|
||||||
align-items: center;
|
|
||||||
justify-items: center;
|
|
||||||
|
|
||||||
.header-1 {
|
|
||||||
font-weight: bold;
|
|
||||||
justify-self: start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-2 {
|
|
||||||
font-weight: bold;
|
|
||||||
justify-self: start;
|
|
||||||
grid-column: ~"2 / -1";
|
|
||||||
}
|
|
||||||
|
|
||||||
.preset {
|
|
||||||
white-space: nowrap;
|
|
||||||
justify-self: start;
|
|
||||||
padding-right: 32px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
@import "theme-@{THEME}.less";
|
@import "theme-@{THEME}.less";
|
||||||
|
|
||||||
input[type="datetime-local"] {
|
|
||||||
padding: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#notifications {
|
#notifications {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(5, min-content);
|
grid-template-columns: repeat(5, min-content);
|
||||||
grid-gap: 4px 16px;
|
grid-gap: 4px 16px;
|
||||||
margin-top: 32px;
|
margin-top: 96px;
|
||||||
margin-bottom: 32px;
|
margin-bottom: 32px;
|
||||||
background-color: @bg3;
|
background-color: @bg3;
|
||||||
padding: 16px 24px;
|
padding: 16px 24px;
|
||||||
|
|
|
@ -1,69 +1,48 @@
|
||||||
@import "theme-@{THEME}.less";
|
@import "theme-@{THEME}.less";
|
||||||
|
|
||||||
#problems-list, #acknowledged-list {
|
#problems-list, #acknowledged-list {
|
||||||
display: grid;
|
grid-template-columns: repeat(7, min-content);
|
||||||
grid-template-columns: repeat(6, min-content);
|
|
||||||
grid-gap: 4px 16px;
|
|
||||||
margin-top: 32px;
|
|
||||||
margin-bottom: 32px;
|
|
||||||
background-color: @bg3;
|
|
||||||
padding: 16px 24px;
|
|
||||||
width: min-content;
|
|
||||||
border-top-left-radius: 8px;
|
|
||||||
border-top-right-radius: 8px;
|
|
||||||
|
|
||||||
div {
|
|
||||||
white-space: nowrap;
|
|
||||||
line-height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
font-weight: @bold;
|
|
||||||
color: @text3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.trigger {
|
.trigger {
|
||||||
color: @color1;
|
color: @color1;
|
||||||
font-weight: @bold;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.acknowledge {
|
img.acknowledge {
|
||||||
img {
|
height: 16px;
|
||||||
height: 16px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icons {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: min-content min-content;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#acknowledged-list.hidden{
|
#acknowledged-list.hidden{
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#areas {
|
#area-grouped {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
.area {
|
.area {
|
||||||
|
grid-template-columns: repeat(5, min-content);
|
||||||
|
|
||||||
.section {
|
.section {
|
||||||
display: grid;
|
padding: 4px 10px;
|
||||||
grid-template-columns: repeat(4, min-content);
|
border-radius: 5px;
|
||||||
grid-gap: 8px 12px;
|
background: @bg3;
|
||||||
|
color: #fff;
|
||||||
.name {
|
width: min-content;
|
||||||
color: @text2;
|
margin-bottom: 8px;
|
||||||
grid-column: ~"1 / -1";
|
|
||||||
font-weight: bold !important;
|
|
||||||
line-height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
31
static/less/table.less
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
@import "theme-@{THEME}.less";
|
||||||
|
|
||||||
|
.table {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: 6px 16px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.row {
|
||||||
|
grid-column: ~"1 / -1";
|
||||||
|
}
|
||||||
|
|
||||||
|
margin-top: 32px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
background-color: @bg3;
|
||||||
|
padding: 16px 24px;
|
||||||
|
width: min-content;
|
||||||
|
border-top-left-radius: 8px;
|
||||||
|
border-top-right-radius: 8px;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
white-space: nowrap;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
font-size: 0.85em;
|
||||||
|
font-weight: bold;
|
||||||
|
color: @text3;
|
||||||
|
line-height: unset !important;
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,11 +19,15 @@
|
||||||
.datapoints {
|
.datapoints {
|
||||||
font: "Roboto Mono", monospace;
|
font: "Roboto Mono", monospace;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: min-content min-content 1fr;
|
grid-template-columns: repeat(4, min-content);
|
||||||
gap: 6px 8px;
|
gap: 6px 8px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
|
div {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.invalid {
|
.invalid {
|
||||||
color: #c83737;
|
color: #c83737;
|
||||||
}
|
}
|
||||||
|
@ -31,6 +35,11 @@
|
||||||
.delete img {
|
.delete img {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.values img {
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.action {
|
.action {
|
||||||
|
|
40
trigger.go
|
@ -4,6 +4,8 @@ import (
|
||||||
// External
|
// External
|
||||||
werr "git.gibonuddevalla.se/go/wrappederror"
|
werr "git.gibonuddevalla.se/go/wrappederror"
|
||||||
"github.com/expr-lang/expr"
|
"github.com/expr-lang/expr"
|
||||||
|
"github.com/expr-lang/expr/ast"
|
||||||
|
"github.com/expr-lang/expr/parser"
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
|
|
||||||
// Standard
|
// Standard
|
||||||
|
@ -22,6 +24,17 @@ type Trigger struct {
|
||||||
DatapointValues map[string]any
|
DatapointValues map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ExprRenamePatcher struct {
|
||||||
|
OldName string
|
||||||
|
NewName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ExprRenamePatcher) Visit(node *ast.Node) {
|
||||||
|
if n, ok := (*node).(*ast.IdentifierNode); ok && n.Value == p.OldName {
|
||||||
|
ast.Patch(node, &ast.IdentifierNode{Value: p.NewName})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TriggerCreate(sectionID int, name string) (t Trigger, err error) { // {{{
|
func TriggerCreate(sectionID int, name string) (t Trigger, err error) { // {{{
|
||||||
t.SectionID = sectionID
|
t.SectionID = sectionID
|
||||||
t.Name = name
|
t.Name = name
|
||||||
|
@ -127,6 +140,14 @@ func TriggerRetrieve(id int) (trigger Trigger, err error) { // {{{
|
||||||
err = json.Unmarshal(jsonData, &trigger)
|
err = json.Unmarshal(jsonData, &trigger)
|
||||||
return
|
return
|
||||||
} // }}}
|
} // }}}
|
||||||
|
func TriggerDelete(id int) (err error) { // {{{
|
||||||
|
_, err = service.Db.Conn.Exec(`DELETE FROM public.trigger WHERE id=$1`, id)
|
||||||
|
if err != nil {
|
||||||
|
return werr.Wrap(err).WithData(id)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} // }}}
|
||||||
|
|
||||||
func (t *Trigger) Validate() (ok bool, err error) { // {{{
|
func (t *Trigger) Validate() (ok bool, err error) { // {{{
|
||||||
if strings.TrimSpace(t.Name) == "" {
|
if strings.TrimSpace(t.Name) == "" {
|
||||||
err = fmt.Errorf("Name can't be empty")
|
err = fmt.Errorf("Name can't be empty")
|
||||||
|
@ -212,14 +233,6 @@ func (t *Trigger) Update() (err error) { // {{{
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
} // }}}
|
} // }}}
|
||||||
func TriggerDelete(id int) (err error) { // {{{
|
|
||||||
_, err = service.Db.Conn.Exec(`DELETE FROM public.trigger WHERE id=$1`, id)
|
|
||||||
if err != nil {
|
|
||||||
return werr.Wrap(err).WithData(id)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
} // }}}
|
|
||||||
|
|
||||||
func (t *Trigger) Run() (output any, err error) { // {{{
|
func (t *Trigger) Run() (output any, err error) { // {{{
|
||||||
datapoints := make(map[string]Datapoint)
|
datapoints := make(map[string]Datapoint)
|
||||||
for _, dpname := range t.Datapoints {
|
for _, dpname := range t.Datapoints {
|
||||||
|
@ -248,3 +261,14 @@ func (t *Trigger) Run() (output any, err error) { // {{{
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
} // }}}
|
} // }}}
|
||||||
|
func (t *Trigger) RenameDatapoint(from, to string) error { // {{{
|
||||||
|
tree, err := parser.Parse(t.Expression)
|
||||||
|
if err != nil {
|
||||||
|
return werr.Wrap(err).WithData(t.Expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
ast.Walk(&tree.Node, ExprRenamePatcher{from, to})
|
||||||
|
t.Expression = tree.Node.String()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
} // }}}
|
||||||
|
|
|
@ -11,6 +11,11 @@
|
||||||
el.value = seconds
|
el.value = seconds
|
||||||
el.form.submit()
|
el.form.submit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function enterHandler(evt) {
|
||||||
|
if (evt.key == 'Enter')
|
||||||
|
document.getElementById('form-time-selector').submit()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,43 +24,95 @@
|
||||||
<input type="hidden" name="time-offset" value=0>
|
<input type="hidden" name="time-offset" value=0>
|
||||||
|
|
||||||
<div id="time-selector">
|
<div id="time-selector">
|
||||||
|
{{/* ====== Row 1 ====== */}}
|
||||||
|
<div></div>
|
||||||
|
<div class="header" style="grid-column: 2 / 6">Date and time</div>
|
||||||
|
|
||||||
|
<div></div>
|
||||||
|
<div class="vertical-line" style="grid-column: 7; grid-row: 1 / 5; height: 100%"> </div>
|
||||||
|
<div></div>
|
||||||
|
|
||||||
|
<div class="header" style="grid-column: 9 / 16">Offsets</div>
|
||||||
|
|
||||||
|
<div></div>
|
||||||
|
<div class="vertical-line" style="grid-column: 17; grid-row: 1 / 5; height: 100%"></div>
|
||||||
|
<div></div>
|
||||||
|
|
||||||
|
<div class="header" style="grid-column: 19 / 21">Presets</div>
|
||||||
|
<div></div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{{/* ====== Row 2 ====== */}}
|
||||||
|
<div></div>
|
||||||
<div>From</div>
|
<div>From</div>
|
||||||
|
<input name="time-f" value="{{ .Data.TimeFrom }}" type="datetime-local" onkeydown="enterHandler(event)">
|
||||||
|
<div></div>
|
||||||
|
<div></div>
|
||||||
|
|
||||||
|
<div></div>
|
||||||
|
{{/* Vertical line */}}
|
||||||
|
<div></div>
|
||||||
|
|
||||||
|
<div><a href="#" onclick="offsetTime(-3600)">◀</a></div>
|
||||||
|
<div>Hour</div>
|
||||||
|
<div><a href="#" onclick="offsetTime(3600)">▶</a></div>
|
||||||
|
|
||||||
|
<div></div>
|
||||||
|
|
||||||
|
<div><a href="#" onclick="offsetTime(-7 * 86400)">◀</a></div>
|
||||||
|
<div>Week</div>
|
||||||
|
<div><a href="#" onclick="offsetTime(7 * 86400)">▶</a></div>
|
||||||
|
|
||||||
|
<div></div>
|
||||||
|
{{/* Vertical line */}}
|
||||||
|
<div></div>
|
||||||
|
|
||||||
|
<div class="preset">⚫︎ <a href="#" onclick="preset(1)">Last hour</a></div>
|
||||||
|
<div class="preset">⚫︎ <a href="#" onclick="preset(24 * 7)">Last 7 days</a></div>
|
||||||
|
<div></div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{{/* ====== Row 3 ====== */}}
|
||||||
|
<div></div>
|
||||||
<div>To</div>
|
<div>To</div>
|
||||||
|
<input name="time-t" value="{{ .Data.TimeTo }}" type="datetime-local" onkeydown="enterHandler(event)">
|
||||||
|
<div><img src="/images/{{ .VERSION }}/{{ .CONFIG.THEME }}/forward.svg" onclick="document.getElementById('form-time-selector').submit()"></div>
|
||||||
|
<div></div>
|
||||||
|
|
||||||
<input name="time-f" value="{{ .Data.TimeFrom }}" type="datetime-local">
|
<div></div>
|
||||||
<input name="time-t" value="{{ .Data.TimeTo }}" type="datetime-local">
|
{{/* Vertical line */}}
|
||||||
|
<div></div>
|
||||||
|
|
||||||
|
<div><a href="#" onclick="offsetTime(-86400)">◀</a></div>
|
||||||
|
<div>Day</div>
|
||||||
|
<div><a href="#" onclick="offsetTime(86400)">▶</a></div>
|
||||||
|
|
||||||
|
<div></div>
|
||||||
|
|
||||||
|
<div><a href="#" onclick="offsetTime(-31 * 86400)">◀</a></div>
|
||||||
|
<div>Month</div>
|
||||||
|
<div><a href="#" onclick="offsetTime(31 * 86400)">▶</a></div>
|
||||||
|
|
||||||
|
<div></div>
|
||||||
|
{{/* Vertical line */}}
|
||||||
|
<div></div>
|
||||||
|
|
||||||
|
<div class="preset">⚫︎ <a href="#" onclick="preset(24)">Last 24 hours</a></div>
|
||||||
|
<div class="preset">⚫︎ <a href="#" onclick="preset(24 * 31)">Last 31 days</a></div>
|
||||||
|
|
||||||
|
<div></div>
|
||||||
|
|
||||||
|
|
||||||
<div id="time-filter">
|
|
||||||
<div class="header-1">Presets</div>
|
|
||||||
<div class="header-2">Offsets</div>
|
|
||||||
|
|
||||||
<div class="preset"><a href="#" onclick="preset(1)">Last hour</a></div>
|
|
||||||
|
|
||||||
<div><a href="#" onclick="offsetTime(-3600)">◀</a></div>
|
{{/* ====== Row 4 ====== */}}
|
||||||
<div>Hour</div>
|
<div style="grid-column: 1 / 5; height: 8px"></div>
|
||||||
<div><a href="#" onclick="offsetTime(3600)">▶</a></div>
|
<div style="grid-column: 8 / 17; height: 8px"></div>
|
||||||
|
<div style="grid-column: 18 / 22; height: 8px"></div>
|
||||||
<div class="preset"><a href="#" onclick="preset(24)">Last 24 hours</a></div>
|
|
||||||
|
|
||||||
<div><a href="#" onclick="offsetTime(-86400)">◀</a></div>
|
|
||||||
<div>Day</div>
|
|
||||||
<div><a href="#" onclick="offsetTime(86400)">▶</a></div>
|
|
||||||
|
|
||||||
<div class="preset"><a href="#" onclick="preset(24 * 7)">Last 7 days</a></div>
|
|
||||||
|
|
||||||
<div><a href="#" onclick="offsetTime(-7 * 86400)">◀</a></div>
|
|
||||||
<div>Week</div>
|
|
||||||
<div><a href="#" onclick="offsetTime(7 * 86400)">▶</a></div>
|
|
||||||
|
|
||||||
<div class="preset"><a href="#" onclick="preset(24 * 31)">Last 31 days</a></div>
|
|
||||||
|
|
||||||
<div><a href="#" onclick="offsetTime(-31 * 86400)">◀</a></div>
|
|
||||||
<div>Month</div>
|
|
||||||
<div><a href="#" onclick="offsetTime(31 * 86400)">▶</a></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button>OK</button>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -3,48 +3,59 @@
|
||||||
|
|
||||||
<script type="module" defer>
|
<script type="module" defer>
|
||||||
import {UI} from "/js/{{ .VERSION }}/datapoint_edit.mjs"
|
import {UI} from "/js/{{ .VERSION }}/datapoint_edit.mjs"
|
||||||
window._ui = new UI()
|
window._ui = new UI({{ .Data.Datapoint }})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{{ block "page_label" . }}{{end}}
|
{{ block "page_label" . }}{{end}}
|
||||||
|
|
||||||
<form id="form-trigger" action="/datapoint/update/{{ .Data.Datapoint.ID }}" method="post">
|
<form id="form-trigger" action="/datapoint/update/{{ .Data.Datapoint.ID }}" method="post">
|
||||||
<div id="widgets" class="widgets">
|
<div id="widgets" class="widgets">
|
||||||
<div class="label">Group</div>
|
<div class="label">Group</div>
|
||||||
<div><input type="text" name="group" value="{{ .Data.Datapoint.Group }}"></div>
|
<div><input type="text" name="group" value="{{ .Data.Datapoint.Group }}"></div>
|
||||||
|
|
||||||
<div class="label">Name</div>
|
<div class="label">Name</div>
|
||||||
<div><input type="text" name="name" value="{{ .Data.Datapoint.Name }}"></div>
|
<div><input type="text" name="name" value="{{ .Data.Datapoint.Name }}"></div>
|
||||||
|
|
||||||
<div class="label">Datatype</div>
|
<div class="label">Datatype</div>
|
||||||
<div>
|
<div>
|
||||||
<select name="datatype">
|
<select name="datatype">
|
||||||
<option {{ if eq .Data.Datapoint.Datatype "INT" }}selected{{end}}>INT</option>
|
<option {{ if eq .Data.Datapoint.Datatype "INT" }}selected{{end}}>INT</option>
|
||||||
<option {{ if eq .Data.Datapoint.Datatype "STRING" }}selected{{end}}>STRING</option>
|
<option {{ if eq .Data.Datapoint.Datatype "STRING" }}selected{{end}}>STRING</option>
|
||||||
<option {{ if eq .Data.Datapoint.Datatype "DATETIME" }}selected{{end}}>DATETIME</option>
|
<option {{ if eq .Data.Datapoint.Datatype "DATETIME" }}selected{{end}}>DATETIME</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="label">No data<br>problem time<br>(seconds)</div>
|
<div class="label">No data<br>problem time<br>(seconds)</div>
|
||||||
<div>
|
<div>
|
||||||
<input type="text" name="nodata_seconds" value="{{ .Data.Datapoint.NodataProblemSeconds }}">
|
<input type="text" name="nodata_seconds" value="{{ .Data.Datapoint.NodataProblemSeconds }}">
|
||||||
<div class="description">A problem is raised and notified if an entry isn't made within this time.</div>
|
<div class="description">A problem is raised and notified if an entry isn't made within this time.</div>
|
||||||
<div class="description">Set to 0 to disable.</div>
|
<div class="description">Set to 0 to disable.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="label">Comment</div>
|
<div class="label">Comment</div>
|
||||||
<div>
|
<div>
|
||||||
<textarea name="comment" rows=4>{{ .Data.Datapoint.Comment }}</textarea>
|
<textarea name="comment" rows=4>{{ .Data.Datapoint.Comment }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div></div>
|
<div></div>
|
||||||
<div class="action">
|
<div class="action">
|
||||||
{{ if eq .Data.Datapoint.ID 0 }}
|
{{ if eq .Data.Datapoint.ID 0 }}
|
||||||
<button id="button-update">Create</button>
|
<button id="button-update">Create</button>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<button id="button-update">Update</button>
|
<button id="button-update">Update</button>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div></div>
|
||||||
|
<div style="margin-top: 32px">
|
||||||
|
<b>Used in the following triggers:</b>
|
||||||
|
<ul>
|
||||||
|
{{ range .Data.Triggers }}
|
||||||
|
<li><a href="/trigger/edit/{{ .ID }}">{{ .Name }}</a></li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
{{ if eq .Data.Datapoint.Datatype "INT" }}
|
{{ if eq .Data.Datapoint.Datatype "INT" }}
|
||||||
<div style="margin-top: 16px">
|
<div style="margin-top: 16px">
|
||||||
<input onchange="selectDisplay('graph')" name="display" type="radio" id="display-graph" {{ if $graph }} checked {{ end}}> <label for="display-graph">Graph</label>
|
<input onchange="selectDisplay('graph')" name="display" type="radio" id="display-graph" {{ if $graph }} checked {{ end}}> <label for="display-graph">Graph</label>
|
||||||
|
<br>
|
||||||
<input onchange="selectDisplay('list')" name="display" type="radio" id="display-list" {{ if not $graph }} checked {{ end }}> <label for="display-list">List</label>
|
<input onchange="selectDisplay('list')" name="display" type="radio" id="display-list" {{ if not $graph }} checked {{ end }}> <label for="display-list">List</label>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -3,6 +3,46 @@
|
||||||
{{ $theme := .CONFIG.THEME }}
|
{{ $theme := .CONFIG.THEME }}
|
||||||
<link rel="stylesheet" type="text/css" href="/css/{{ .VERSION }}/{{ .CONFIG.THEME }}/datapoints.css">
|
<link rel="stylesheet" type="text/css" href="/css/{{ .VERSION }}/{{ .CONFIG.THEME }}/datapoints.css">
|
||||||
<script type="text/javascript" defer>
|
<script type="text/javascript" defer>
|
||||||
|
|
||||||
|
function validateRegex(rxp) {
|
||||||
|
try {
|
||||||
|
''.match(rxp)
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showDatapoints(id) {
|
||||||
|
if (!id)
|
||||||
|
document
|
||||||
|
.querySelectorAll(`[x-datapoint-id]`)
|
||||||
|
.forEach(matchedDP=>matchedDP.classList.remove('hidden'))
|
||||||
|
else
|
||||||
|
document
|
||||||
|
.querySelectorAll(`[x-datapoint-id="${id}"]`)
|
||||||
|
.forEach(matchedDP=>matchedDP.classList.remove('hidden'))
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideDatapoints(id) {
|
||||||
|
if (!id)
|
||||||
|
document.querySelectorAll(`[x-datapoint-id]`).forEach(matchedDP=>
|
||||||
|
matchedDP.classList.add('hidden')
|
||||||
|
)
|
||||||
|
else
|
||||||
|
document.querySelectorAll(`[x-datapoint-id="${id}"]`).forEach(matchedDP=>
|
||||||
|
matchedDP.classList.add('hidden')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateRegexValidatedStatus(validated) {
|
||||||
|
const inputFilter = document.getElementById('datapoints-filter')
|
||||||
|
if (validated)
|
||||||
|
inputFilter.classList.remove('invalid-regex')
|
||||||
|
else
|
||||||
|
inputFilter.classList.add('invalid-regex')
|
||||||
|
}
|
||||||
|
|
||||||
function filterDatapoints(inputFilter) {
|
function filterDatapoints(inputFilter) {
|
||||||
const filter = inputFilter.value.toLowerCase()
|
const filter = inputFilter.value.toLowerCase()
|
||||||
const datapoints = document.querySelectorAll('#datapoints .name')
|
const datapoints = document.querySelectorAll('#datapoints .name')
|
||||||
|
@ -10,25 +50,25 @@
|
||||||
|
|
||||||
// Shortcut to show everything if a filter is not given.
|
// Shortcut to show everything if a filter is not given.
|
||||||
if (filter == '') {
|
if (filter == '') {
|
||||||
document.querySelectorAll(`[x-datapoint-id]`).forEach(matchedDP=>
|
showDatapoints()
|
||||||
matchedDP.classList.remove('hidden')
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show nothing if the regex is invalid and can't matching anything.
|
||||||
|
if (!validateRegex(filter)) {
|
||||||
|
hideDatapoints()
|
||||||
|
updateRegexValidatedStatus(false)
|
||||||
|
return
|
||||||
|
} else
|
||||||
|
updateRegexValidatedStatus(true)
|
||||||
|
|
||||||
datapoints.forEach(dp=>{
|
datapoints.forEach(dp=>{
|
||||||
const dpName = dp.getAttribute('x-datapoint-name')
|
const dpName = dp.getAttribute('x-datapoint-name')
|
||||||
if (dpName.toLowerCase().includes(filter)) {
|
datapointID = dp.getAttribute('x-datapoint-id')
|
||||||
datapointID = dp.getAttribute('x-datapoint-id')
|
if (dpName.toLowerCase().match(filter))
|
||||||
document.querySelectorAll(`[x-datapoint-id="${datapointID}"]`).forEach(matchedDP=>
|
showDatapoints(datapointID)
|
||||||
matchedDP.classList.remove('hidden')
|
else
|
||||||
)
|
hideDatapoints(datapointID)
|
||||||
} else {
|
|
||||||
datapointID = dp.getAttribute('x-datapoint-id')
|
|
||||||
document.querySelectorAll(`[x-datapoint-id="${datapointID}"]`).forEach(matchedDP=>
|
|
||||||
matchedDP.classList.add('hidden')
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +89,7 @@
|
||||||
<a href="/datapoint/edit/0">Create</a>
|
<a href="/datapoint/edit/0">Create</a>
|
||||||
|
|
||||||
<div style="margin-top: 16px">
|
<div style="margin-top: 16px">
|
||||||
<input id="datapoints-filter" type="text" placeholder="Filter" style="width: 320px;" oninput="filterDatapoints(this)">
|
<input id="datapoints-filter" type="text" placeholder="Filter (regexp)" style="width: 320px;" oninput="filterDatapoints(this)">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="datapoints">
|
<div id="datapoints">
|
||||||
|
|
|
@ -10,123 +10,141 @@
|
||||||
|
|
||||||
{{ block "page_label" . }}{{ end }}
|
{{ block "page_label" . }}{{ end }}
|
||||||
|
|
||||||
<div style="margin-bottom: 16px">
|
<div style="margin-bottom: 16px; display: grid; grid-template-columns: min-content min-content; grid-gap: 32px;">
|
||||||
<input {{ if eq .Data.Selection "CURRENT" }}checked{{ end }} type="radio" name="selection" id="selection-current" onclick="_ui.selectCurrent()"> <label for="selection-current">Current problems</label>
|
<div style="white-space: nowrap">
|
||||||
<input {{ if eq .Data.Selection "ALL" }}checked{{ end }} type="radio" name="selection" id="selection-all" onclick="_ui.selectAll()"> <label for="selection-all">Time-filtered problems</label>
|
<b>Problem selection</b><br>
|
||||||
</div>
|
<input {{ if eq .Data.Selection "CURRENT" }}checked{{ end }} type="radio" name="selection" id="selection-current" onclick="_ui.selectCurrent()"> <label for="selection-current">Current</label>
|
||||||
|
<br>
|
||||||
<hr style="margin-bottom: 16px">
|
<input {{ if eq .Data.Selection "ALL" }}checked{{ end }} type="radio" name="selection" id="selection-all" onclick="_ui.selectAll()"> <label for="selection-all">All</label>
|
||||||
|
</div>
|
||||||
{{ block "timefilter" . }}{{ end }}
|
<div style="white-space: nowrap">
|
||||||
|
<b>Show</b><br>
|
||||||
<div style="margin-top: 16px">
|
<input type="radio" name="display" id="display-table" onclick="_ui.displayAreas()"> <label for="display-table">Areas</label>
|
||||||
<input type="radio" name="display" id="display-table" onclick="_ui.displayAreas()"> <label for="display-table">Areas</label>
|
<br>
|
||||||
<input type="radio" name="display" id="display-list" onclick="_ui.displayList()"> <label for="display-list">List</label>
|
<input type="radio" name="display" id="display-list" onclick="_ui.displayList()"> <label for="display-list">List</label>
|
||||||
<div style="margin-top: 8px">
|
<div style="margin-top: 8px">
|
||||||
<input type="checkbox" id="show-acked" onclick="_ui.toggleAcknowledged(event)"> <label for="show-acked">Show acknowledged</label>
|
<input type="checkbox" id="show-acked" onclick="_ui.toggleAcknowledged(event)"> <label for="show-acked">Show acknowledged</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="display-list hidden">
|
{{ block "timefilter" . }}{{ end }}
|
||||||
<div id="problems-list">
|
|
||||||
<div style="grid-column: 1/-1;"><h2>Unacknowledged</h2></div>
|
|
||||||
|
|
||||||
|
<div class="display-list hidden">
|
||||||
|
<div id="problems-list" class="table">
|
||||||
|
<div class="row"><h2>Unacknowledged</h2></div>
|
||||||
|
|
||||||
|
<div class="header">OK</div>
|
||||||
<div class="header">Trigger</div>
|
<div class="header">Trigger</div>
|
||||||
<div class="header">Area</div>
|
<div class="header">Area</div>
|
||||||
<div class="header">Section</div>
|
<div class="header">Section</div>
|
||||||
<div class="header">Since</div>
|
<div class="header">Since</div>
|
||||||
|
<div class="header">Until</div>
|
||||||
|
<div class="header"></div>
|
||||||
|
|
||||||
{{ range .Data.Problems }}
|
{{ range .Data.Problems }}
|
||||||
{{ if .Acknowledged }}
|
{{ if .Acknowledged }}
|
||||||
{{ continue }}
|
{{ continue }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
|
|
||||||
|
{{/* NODATA datapoints */}}
|
||||||
{{ if eq .TriggerID -1 }}
|
{{ if eq .TriggerID -1 }}
|
||||||
|
<div class="{{ if .Acknowledged }}acked hidden{{ end }}">{{ if .IsArchived }}<img src="/images/{{ $version }}/{{ $theme }}/ok.svg">{{ else }}<img src="/images/{{ $version }}/{{ $theme }}/warning.svg">{{ end }}</div>
|
||||||
<div class="trigger">{{ .TriggerName }}</div>
|
<div class="trigger">{{ .TriggerName }}</div>
|
||||||
<div class="area">{{ .AreaName }}</div>
|
<div class="area">{{ .AreaName }}</div>
|
||||||
<div class="section">{{ .SectionName }}</div>
|
<div class="section">{{ .SectionName }}</div>
|
||||||
<div class="start"></div>
|
<div class="start">{{ format_time .Start }}</div>
|
||||||
<div class="acknowledge">
|
<div class="end"></div>
|
||||||
|
<div class="icons">
|
||||||
<img class="info" src="/images/{{ $version }}/{{ $theme }}/info-outline.svg">
|
<img class="info" src="/images/{{ $version }}/{{ $theme }}/info-outline.svg">
|
||||||
<img src="/images/{{ $version }}/{{ $theme }}/acknowledge.svg">
|
<img class="acknowledge" src="/images/{{ $version }}/{{ $theme }}/acknowledge.svg">
|
||||||
</div>
|
</div>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
|
<div class="{{ if .Acknowledged }}acked hidden{{ end }}">{{ if .IsArchived }}<img src="/images/{{ $version }}/{{ $theme }}/ok.svg">{{ else }}<img src="/images/{{ $version }}/{{ $theme }}/warning.svg">{{ end }}</div>
|
||||||
<div class="trigger"><a href="/trigger/edit/{{ .TriggerID }}">{{ .TriggerName }}</a></div>
|
<div class="trigger"><a href="/trigger/edit/{{ .TriggerID }}">{{ .TriggerName }}</a></div>
|
||||||
<div class="area">{{ .AreaName }}</div>
|
<div class="area">{{ .AreaName }}</div>
|
||||||
<div class="section">{{ .SectionName }}</div>
|
<div class="section">{{ .SectionName }}</div>
|
||||||
<div class="start">{{ format_time .Start }}</div>
|
<div class="start">{{ format_time .Start }}</div>
|
||||||
<div class="acknowledge">
|
<div class="end">{{ if not .End.IsZero }}{{ format_time .End }}{{ else }}-{{ end }}</div>
|
||||||
|
<div class="icons">
|
||||||
{{ if .FormattedValues }}
|
{{ if .FormattedValues }}
|
||||||
<img class="info" src="/images/{{ $version }}/{{ $theme }}/info-filled.svg" title="{{ .FormattedValues }}">
|
<img class="info" src="/images/{{ $version }}/{{ $theme }}/info-filled.svg" title="{{ .FormattedValues }}">
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<img class="info" src="/images/{{ $version }}/{{ $theme }}/info-outline.svg">
|
<img class="info" src="/images/{{ $version }}/{{ $theme }}/info-outline.svg">
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
<a href="/problem/acknowledge/{{ .ID }}">
|
<img class="acknowledge" onclick="location.href = '/problem/acknowledge/{{ .ID }}'" src="/images/{{ $version }}/{{ $theme }}/acknowledge-filled.svg">
|
||||||
<img src="/images/{{ $version }}/{{ $theme }}/acknowledge-filled.svg">
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="acknowledged-list" class="hidden">
|
<div id="acknowledged-list" class="table hidden">
|
||||||
<div style="grid-column: 1/-1;"><h2>Acknowledged</h2></div>
|
<div class="row"><h2>Acknowledged</h2></div>
|
||||||
|
|
||||||
|
<div class="header">OK</div>
|
||||||
<div class="header">Trigger</div>
|
<div class="header">Trigger</div>
|
||||||
<div class="header">Area</div>
|
<div class="header">Area</div>
|
||||||
<div class="header">Section</div>
|
<div class="header">Section</div>
|
||||||
<div class="header">Since</div>
|
<div class="header">Since</div>
|
||||||
|
<div class="header">Until</div>
|
||||||
|
<div class="header"></div>
|
||||||
|
|
||||||
{{ range .Data.Problems }}
|
{{ range .Data.Problems }}
|
||||||
{{ if not .Acknowledged }}
|
{{ if not .Acknowledged }}
|
||||||
{{ continue }}
|
{{ continue }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<div class="line"></div>
|
<div class="line"></div>
|
||||||
|
<div class="{{ if .Acknowledged }}acked hidden{{ end }}">{{ if .IsArchived }}<img src="/images/{{ $version }}/{{ $theme }}/ok.svg">{{ else }}<img src="/images/{{ $version }}/{{ $theme }}/warning.svg">{{ end }}</div>
|
||||||
<div class="trigger"><a href="/trigger/edit/{{ .TriggerID }}">{{ .TriggerName }}</a></div>
|
<div class="trigger"><a href="/trigger/edit/{{ .TriggerID }}">{{ .TriggerName }}</a></div>
|
||||||
<div class="area">{{ .AreaName }}</div>
|
<div class="area">{{ .AreaName }}</div>
|
||||||
<div class="section">{{ .SectionName }}</div>
|
<div class="section">{{ .SectionName }}</div>
|
||||||
<div class="start">{{ format_time .Start }}</div>
|
<div class="start">{{ format_time .Start }}</div>
|
||||||
<div class="acknowledge">
|
<div class="end">{{ if not .End.IsZero }}{{ format_time .End }}{{ else }}-{{ end }}</div>
|
||||||
|
<div class="icons">
|
||||||
{{ if .FormattedValues }}
|
{{ if .FormattedValues }}
|
||||||
<img class="info" src="/images/{{ $version }}/{{ $theme }}/info-filled.svg" title="{{ .FormattedValues }}">
|
<img class="info" src="/images/{{ $version }}/{{ $theme }}/info-filled.svg" title="{{ .FormattedValues }}">
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<img class="info" src="/images/{{ $version }}/{{ $theme }}/info-outline.svg">
|
<img class="info" src="/images/{{ $version }}/{{ $theme }}/info-outline.svg">
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<a href="/problem/unacknowledge/{{ .ID }}"><img src="/images/{{ $version }}/{{ $theme }}/acknowledge-outline.svg"></a>
|
<img class="acknowledge" onclick="location.href = '/problem/unacknowledge/{{ .ID }}'" src="/images/{{ $version }}/{{ $theme }}/acknowledge-outline.svg">
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="display-areas hidden">
|
<div class="display-areas hidden">
|
||||||
<div id="areas">
|
<div id="area-grouped">
|
||||||
{{ range $areaName, $sections := .Data.ProblemsGrouped }}
|
{{ range $areaName, $sections := .Data.ProblemsGrouped }}
|
||||||
<div class="area">
|
<div class="area table">
|
||||||
<div class="name">{{ $areaName }}</div>
|
<div class="row"><h2>{{ $areaName }}</h2></div>
|
||||||
|
|
||||||
{{ range $sectionName, $problems := $sections }}
|
{{ range $sectionName, $problems := $sections }}
|
||||||
<div class="section problems">
|
<div class="section row" style="margin-top: 16px; font-weight: bold;">{{ $sectionName }}</div>
|
||||||
<div class="name">{{ $sectionName }}</div>
|
|
||||||
|
<div class="header">OK</div>
|
||||||
|
<div class="header">Trigger</div>
|
||||||
|
<div class="header">Since</div>
|
||||||
|
<div class="header">Until</div>
|
||||||
|
<div class="header"></div>
|
||||||
|
<div class="line"></div>
|
||||||
|
|
||||||
{{ range $problems }}
|
{{ range $problems }}
|
||||||
<div class="{{ if .Acknowledged }}acked hidden{{ end }}">{{ if .IsArchived }}<span class="ok">Archived</span>{{ else }}<span class="error">Current</span>{{ end }}</div>
|
<div class="{{ if .Acknowledged }}acked hidden{{ end }}">{{ if .IsArchived }}<img src="/images/{{ $version }}/{{ $theme }}/ok.svg">{{ else }}<img src="/images/{{ $version }}/{{ $theme }}/warning.svg">{{ end }}</div>
|
||||||
|
|
||||||
<div class="{{ if .Acknowledged }}acked hidden{{ end }} trigger">{{ .TriggerName }}</div>
|
<div class="{{ if .Acknowledged }}acked hidden{{ end }} trigger">{{ .TriggerName }}</div>
|
||||||
|
|
||||||
{{ if eq (.Start | html) "0001-01-01 00:00:00 +0000 UTC" }}
|
<div class="{{ if .Acknowledged }}acked hidden{{ end }} since">{{ if not .Start.IsZero }}{{ format_time .Start }}{{ else }}-{{ end }}</div>
|
||||||
<div class="{{ if .Acknowledged }}acked hidden{{ end }} since"></div>
|
<div class="{{ if .Acknowledged }}acked hidden{{ end }} until">{{ if not .End.IsZero }}{{ format_time .End }}{{ else }}-{{ end }}</div>
|
||||||
{{ else }}
|
|
||||||
<div class="{{ if .Acknowledged }}acked hidden{{ end }} since">{{ format_time .Start }}</div>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ if .FormattedValues }}
|
{{ if .FormattedValues }}
|
||||||
<div class="{{ if .Acknowledged }}acked hidden{{ end }}"><img src="/images/{{ $version }}/{{ $theme }}/info-filled.svg" title="{{ .FormattedValues }}"></div>
|
<div class="{{ if .Acknowledged }}acked hidden{{ end }}"><img src="/images/{{ $version }}/{{ $theme }}/info-filled.svg" title="{{ .FormattedValues }}"></div>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<div class="{{ if .Acknowledged }}acked hidden{{ end }}"><img src="/images/{{ $version }}/{{ $theme }}/info-outline.svg"></div>
|
<div class="{{ if .Acknowledged }}acked hidden{{ end }}"><img src="/images/{{ $version }}/{{ $theme }}/info-outline.svg"></div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|