Merge branch 'notifications'
This commit is contained in:
commit
32523270a8
60
main.go
60
main.go
@ -150,6 +150,8 @@ func main() { // {{{
|
||||
service.Register("/trigger/run/{id}", false, false, actionTriggerRun)
|
||||
service.Register("/trigger/delete/{id}", false, false, actionTriggerDelete)
|
||||
|
||||
service.Register("/notifications", false, false, pageNotifications)
|
||||
|
||||
service.Register("/configuration", false, false, pageConfiguration)
|
||||
service.Register("/configuration/theme", false, false, actionConfigurationTheme)
|
||||
service.Register("/configuration/timezone", false, false, actionConfigurationTimezone)
|
||||
@ -1194,3 +1196,61 @@ func actionConfigurationNotificationDelete(w http.ResponseWriter, r *http.Reques
|
||||
w.Header().Add("Location", "/configuration")
|
||||
w.WriteHeader(302)
|
||||
} // }}}
|
||||
|
||||
func pageNotifications(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
|
||||
var err error
|
||||
|
||||
// GET parameters.
|
||||
var timeFrom, timeTo time.Time
|
||||
lastWeek := time.Now().Add(time.Duration(-7 * 24 * time.Hour))
|
||||
|
||||
timeFrom, err = parseHTMLDateTime(r.URL.Query().Get("f"), lastWeek)
|
||||
if err != nil {
|
||||
httpError(w, werr.Wrap(err).Log())
|
||||
return
|
||||
}
|
||||
|
||||
timeTo, err = parseHTMLDateTime(r.URL.Query().Get("t"), time.Now())
|
||||
if err != nil {
|
||||
httpError(w, werr.Wrap(err).Log())
|
||||
return
|
||||
}
|
||||
|
||||
var presetTime int
|
||||
now := time.Now()
|
||||
presetStr := r.URL.Query().Get("preset")
|
||||
if presetStr != "" {
|
||||
presetTime, err = strconv.Atoi(presetStr)
|
||||
if err != nil {
|
||||
pageError(w, "/notifications", werr.Wrap(err).WithData(presetStr).Log())
|
||||
return
|
||||
}
|
||||
timeFrom = now.Add(time.Hour * -1 * time.Duration(presetTime))
|
||||
timeTo = now
|
||||
}
|
||||
|
||||
// Apply an optionally set offset (in seconds).
|
||||
var offsetTime int
|
||||
offsetTimeStr := r.URL.Query().Get("offset-time")
|
||||
offsetTime, err = strconv.Atoi(offsetTimeStr)
|
||||
timeFrom = timeFrom.Add(time.Second * time.Duration(offsetTime))
|
||||
timeTo = timeTo.Add(time.Second * time.Duration(offsetTime))
|
||||
|
||||
nss, err := notificationsSent(timeFrom, timeTo)
|
||||
if err != nil {
|
||||
pageError(w, "/", werr.Wrap(err).Log())
|
||||
}
|
||||
|
||||
page := Page{
|
||||
LAYOUT: "main",
|
||||
PAGE: "notifications",
|
||||
CONFIG: smonConfig.Settings,
|
||||
Data: map[string]any{
|
||||
"Notifications": nss,
|
||||
"TimeFrom": timeFrom.Format("2006-01-02T15:04:05"),
|
||||
"TimeTo": timeTo.Format("2006-01-02T15:04:05"),
|
||||
},
|
||||
}
|
||||
|
||||
page.Render(w, r)
|
||||
} // }}}
|
||||
|
@ -1,11 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
// External
|
||||
werr "git.gibonuddevalla.se/go/wrappederror"
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
// Internal
|
||||
"smon/notification"
|
||||
|
||||
// Standard
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
type NotificationSend struct {
|
||||
Prio int
|
||||
Service string
|
||||
ID int
|
||||
UUID string
|
||||
Sent time.Time `db:"send"`
|
||||
OK bool
|
||||
Error sql.NullString
|
||||
ErrorIndented string
|
||||
Acknowledged bool
|
||||
TriggerName string `db:"trigger_name"`
|
||||
}
|
||||
|
||||
func notificationLog(notificationService *notification.Service, problemID int, err error) {
|
||||
if err == nil {
|
||||
logger.Info("notification", "service", (*notificationService).GetType(), "problemID", problemID, "prio", (*notificationService).GetPrio(), "ok", true)
|
||||
@ -18,3 +39,61 @@ func notificationLog(notificationService *notification.Service, problemID int, e
|
||||
logger.Error("notification", "service", (*notificationService).GetType(), "problemID", problemID, "prio", (*notificationService).GetPrio(), "ok", false, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func notificationsSent(from, to time.Time) (nss []NotificationSend, err error) {
|
||||
var rows *sqlx.Rows
|
||||
rows, err = service.Db.Conn.Queryx(
|
||||
`
|
||||
SELECT
|
||||
n.prio,
|
||||
n.service,
|
||||
|
||||
t.name AS trigger_name,
|
||||
|
||||
ns.id,
|
||||
ns.uuid,
|
||||
ns.send,
|
||||
ns.ok,
|
||||
ns.error::varchar,
|
||||
ns.acknowledged
|
||||
|
||||
FROM public.notification_send ns
|
||||
INNER JOIN notification n ON ns.notification_id = n.id
|
||||
INNER JOIN problem p ON ns.problem_id = p.id
|
||||
INNER JOIN "trigger" t ON p.trigger_id = t.id
|
||||
WHERE
|
||||
ns.send >= $1 AND
|
||||
ns.send <= $2
|
||||
|
||||
ORDER BY
|
||||
send DESC
|
||||
`,
|
||||
from,
|
||||
to,
|
||||
)
|
||||
if err != nil {
|
||||
err = werr.Wrap(err)
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
ns := NotificationSend{}
|
||||
err = rows.StructScan(&ns)
|
||||
if err != nil {
|
||||
err = werr.Wrap(err)
|
||||
return
|
||||
}
|
||||
|
||||
// Error contains json (can be NULL),
|
||||
// and can at least be presented indented.
|
||||
foo := make(map[string]any)
|
||||
json.Unmarshal([]byte(ns.Error.String), &foo)
|
||||
var j []byte
|
||||
j, err = json.MarshalIndent(foo, "", " ")
|
||||
ns.ErrorIndented = string(j)
|
||||
|
||||
nss = append(nss, ns)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -40,11 +40,16 @@ button:focus {
|
||||
#areas .area .section .name {
|
||||
font-weight: normal;
|
||||
}
|
||||
dialog {
|
||||
border-radius: 8px;
|
||||
}
|
||||
dialog,
|
||||
#datapoints,
|
||||
#problems-list,
|
||||
#acknowledged-list,
|
||||
#values,
|
||||
#services {
|
||||
#services,
|
||||
#notifications {
|
||||
background-color: #fff !important;
|
||||
border: 1px solid #ddd;
|
||||
box-shadow: 5px 5px 8px 0px rgba(0, 0, 0, 0.25);
|
||||
|
@ -1,6 +1,9 @@
|
||||
body {
|
||||
background-image: url(/images/v0/gruvbox/background.svg);
|
||||
}
|
||||
#menu {
|
||||
box-shadow: 2px 0px 5px 3px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
#areas .area {
|
||||
box-shadow: 5px 5px 15px 0px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ html {
|
||||
#layout {
|
||||
display: grid;
|
||||
grid-template-areas: "menu content";
|
||||
grid-template-columns: 104px 1fr;
|
||||
grid-template-columns: 128px 1fr;
|
||||
height: 100vh;
|
||||
}
|
||||
#menu {
|
||||
|
65
static/css/default_light/notifications.css
Normal file
65
static/css/default_light/notifications.css
Normal file
@ -0,0 +1,65 @@
|
||||
#time-select {
|
||||
display: grid;
|
||||
grid-template-columns: min-content min-content;
|
||||
grid-gap: 6px 16px;
|
||||
padding: 16px;
|
||||
border: 1px solid #7bb8eb;
|
||||
width: min-content;
|
||||
border-radius: 6px;
|
||||
}
|
||||
#time-select button {
|
||||
width: 100px;
|
||||
margin-top: 12px;
|
||||
justify-self: end;
|
||||
}
|
||||
#time-select #time-offsets {
|
||||
display: grid;
|
||||
grid-template-columns: min-content repeat(3, min-content);
|
||||
grid-gap: 16px;
|
||||
margin-top: 16px;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
}
|
||||
#time-select #time-offsets .header-1 {
|
||||
font-weight: bold;
|
||||
justify-self: start;
|
||||
}
|
||||
#time-select #time-offsets .header-2 {
|
||||
font-weight: bold;
|
||||
justify-self: start;
|
||||
grid-column: 2 / -1;
|
||||
}
|
||||
#time-select #time-offsets .preset {
|
||||
white-space: nowrap;
|
||||
justify-self: start;
|
||||
padding-right: 32px;
|
||||
}
|
||||
input[type="datetime-local"] {
|
||||
padding: 6px;
|
||||
}
|
||||
#notifications {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 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;
|
||||
}
|
||||
#notifications div {
|
||||
white-space: nowrap;
|
||||
line-height: 24px;
|
||||
}
|
||||
#notifications .header {
|
||||
font-weight: 800;
|
||||
color: #7bb8eb;
|
||||
}
|
||||
#notifications .ok {
|
||||
color: #0a0;
|
||||
}
|
||||
#notifications .error {
|
||||
color: #a00;
|
||||
}
|
@ -40,11 +40,16 @@ button:focus {
|
||||
#areas .area .section .name {
|
||||
font-weight: normal;
|
||||
}
|
||||
dialog {
|
||||
border-radius: 8px;
|
||||
}
|
||||
dialog,
|
||||
#datapoints,
|
||||
#problems-list,
|
||||
#acknowledged-list,
|
||||
#values,
|
||||
#services {
|
||||
#services,
|
||||
#notifications {
|
||||
background-color: #fff !important;
|
||||
border: 1px solid #ddd;
|
||||
box-shadow: 5px 5px 8px 0px rgba(0, 0, 0, 0.25);
|
||||
|
@ -39,7 +39,7 @@ html {
|
||||
#layout {
|
||||
display: grid;
|
||||
grid-template-areas: "menu content";
|
||||
grid-template-columns: 104px 1fr;
|
||||
grid-template-columns: 128px 1fr;
|
||||
height: 100vh;
|
||||
}
|
||||
#menu {
|
||||
|
65
static/css/gruvbox/notifications.css
Normal file
65
static/css/gruvbox/notifications.css
Normal file
@ -0,0 +1,65 @@
|
||||
#time-select {
|
||||
display: grid;
|
||||
grid-template-columns: min-content min-content;
|
||||
grid-gap: 6px 16px;
|
||||
padding: 16px;
|
||||
border: 1px solid #777;
|
||||
width: min-content;
|
||||
border-radius: 6px;
|
||||
}
|
||||
#time-select button {
|
||||
width: 100px;
|
||||
margin-top: 12px;
|
||||
justify-self: end;
|
||||
}
|
||||
#time-select #time-offsets {
|
||||
display: grid;
|
||||
grid-template-columns: min-content repeat(3, min-content);
|
||||
grid-gap: 16px;
|
||||
margin-top: 16px;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
}
|
||||
#time-select #time-offsets .header-1 {
|
||||
font-weight: bold;
|
||||
justify-self: start;
|
||||
}
|
||||
#time-select #time-offsets .header-2 {
|
||||
font-weight: bold;
|
||||
justify-self: start;
|
||||
grid-column: 2 / -1;
|
||||
}
|
||||
#time-select #time-offsets .preset {
|
||||
white-space: nowrap;
|
||||
justify-self: start;
|
||||
padding-right: 32px;
|
||||
}
|
||||
input[type="datetime-local"] {
|
||||
padding: 6px;
|
||||
}
|
||||
#notifications {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 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;
|
||||
}
|
||||
#notifications div {
|
||||
white-space: nowrap;
|
||||
line-height: 24px;
|
||||
}
|
||||
#notifications .header {
|
||||
font-weight: 800;
|
||||
color: #777;
|
||||
}
|
||||
#notifications .ok {
|
||||
color: #0a0;
|
||||
}
|
||||
#notifications .error {
|
||||
color: #a00;
|
||||
}
|
51
static/images/default_light/notifications.svg
Normal file
51
static/images/default_light/notifications.svg
Normal file
@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="15.63086mm"
|
||||
height="8.46667mm"
|
||||
viewBox="0 0 15.63086 8.46667"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
|
||||
sodipodi:docname="notification_selected.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">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="11.313708"
|
||||
inkscape:cx="11.623068"
|
||||
inkscape:cy="6.3197669"
|
||||
inkscape:window-width="1916"
|
||||
inkscape:window-height="1161"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="18"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-101.86454,-146.84375)">
|
||||
<title
|
||||
id="title1">email-fast</title>
|
||||
<title
|
||||
id="title1-8">email-fast-outline</title>
|
||||
<path
|
||||
d="m 116.19281,146.84375 h -8.46666 c -0.7164,0 -1.30257,0.58614 -1.30257,1.30254 v 5.86156 c 0,0.72292 0.58617,1.30257 1.30257,1.30257 h 8.46666 c 0.72294,0 1.30259,-0.57965 1.30259,-1.30257 v -5.86156 c 0,-0.7164 -0.57965,-1.30254 -1.30259,-1.30254 m 0,7.1641 h -8.46666 v -4.77389 l 4.23333,2.16877 4.23333,-2.16877 v 4.77389 m -4.23333,-3.70579 -4.23333,-2.15577 h 8.46666 l -4.23333,2.15577 m -6.83846,3.70579 c 0,0.11076 0.0197,0.21487 0.0325,0.32565 h -2.63771 c -0.35951,0 -0.65127,-0.29308 -0.65127,-0.65129 0,-0.35821 0.29176,-0.65127 0.65127,-0.65127 h 2.60515 v 0.97691 m -1.30256,-6.18718 h 1.33512 c -0.0123,0.11075 -0.0325,0.21486 -0.0325,0.32562 v 0.97694 h -1.30256 c -0.35821,0 -0.65129,-0.29306 -0.65129,-0.65127 0,-0.35821 0.29308,-0.65129 0.65129,-0.65129 m -1.30259,3.25641 c 0,-0.35821 0.29311,-0.65127 0.6513,-0.65127 h 1.95385 v 1.30254 h -1.95385 c -0.35819,0 -0.6513,-0.29306 -0.6513,-0.65127 z"
|
||||
id="path1"
|
||||
style="stroke-width:0.431972;font-variation-settings:normal;opacity:1;vector-effect:none;fill:#7bb8eb;fill-opacity:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000;stop-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
49
static/images/default_light/notifications_selected.svg
Normal file
49
static/images/default_light/notifications_selected.svg
Normal file
@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="15.63086mm"
|
||||
height="8.46667mm"
|
||||
viewBox="0 0 15.63086 8.46667"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
|
||||
sodipodi:docname="notification_selected.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">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="11.313708"
|
||||
inkscape:cx="11.623068"
|
||||
inkscape:cy="6.850097"
|
||||
inkscape:window-width="1916"
|
||||
inkscape:window-height="1161"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="18"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-101.86456,-146.71146)">
|
||||
<title
|
||||
id="title1">email-fast</title>
|
||||
<path
|
||||
d="m 116.19286,146.71146 h -8.46669 c -0.7164,0 -1.30254,0.58614 -1.30254,1.30254 v 5.86156 c 0,0.72292 0.58614,1.30257 1.30254,1.30257 h 8.46669 c 0.72291,0 1.30256,-0.57965 1.30256,-1.30257 V 148.014 c 0,-0.7164 -0.57965,-1.30254 -1.30256,-1.30254 m 0,2.39021 -4.23334,2.16877 -4.23335,-2.16877 V 148.014 l 4.23335,2.15577 4.23334,-2.15577 v 1.08767 m -11.07182,4.77389 c 0,0.11076 0.0197,0.21487 0.0325,0.32565 h -2.63769 c -0.35951,0 -0.65129,-0.29308 -0.65129,-0.65129 0,-0.35821 0.29178,-0.65127 0.65129,-0.65127 h 2.60513 v 0.97691 m -1.30256,-6.18718 h 1.33512 c -0.0123,0.11075 -0.0325,0.21486 -0.0325,0.32562 v 0.97694 h -1.30256 c -0.35821,0 -0.65129,-0.29306 -0.65129,-0.65127 0,-0.35821 0.29308,-0.65129 0.65129,-0.65129 m -1.30256,3.25641 c 0,-0.35821 0.29308,-0.65127 0.65127,-0.65127 h 1.95385 v 1.30254 h -1.95385 c -0.35819,0 -0.65127,-0.29306 -0.65127,-0.65127 z"
|
||||
id="path1"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#9bff07;fill-opacity:1;stroke-width:0.32964801;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000;stop-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
51
static/images/gruvbox/notifications.svg
Normal file
51
static/images/gruvbox/notifications.svg
Normal file
@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="73.846642"
|
||||
height="39.999989"
|
||||
viewBox="0 0 19.53859 10.58333"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
|
||||
sodipodi:docname="notifications.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">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="11.313708"
|
||||
inkscape:cx="11.623068"
|
||||
inkscape:cy="6.3197671"
|
||||
inkscape:window-width="1916"
|
||||
inkscape:window-height="1161"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="18"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-101.86452,-146.84375)">
|
||||
<title
|
||||
id="title1">email-fast</title>
|
||||
<title
|
||||
id="title1-8">email-fast-outline</title>
|
||||
<path
|
||||
d="m 119.77487,146.84375 h -10.58332 c -0.8955,0 -1.62821,0.73267 -1.62821,1.62817 v 7.32695 c 0,0.90365 0.73271,1.62821 1.62821,1.62821 h 10.58332 c 0.90368,0 1.62824,-0.72456 1.62824,-1.62821 v -7.32695 c 0,-0.8955 -0.72456,-1.62817 -1.62824,-1.62817 m 0,8.95512 h -10.58332 v -5.96736 l 5.29166,2.71096 5.29166,-2.71096 v 5.96736 m -5.29166,-4.63223 -5.29166,-2.69472 h 10.58332 l -5.29166,2.69472 m -8.54807,4.63223 c 0,0.13845 0.0246,0.26859 0.0406,0.40706 h -3.29713 c -0.44939,0 -0.81409,-0.36635 -0.81409,-0.81411 0,-0.44776 0.3647,-0.81409 0.81409,-0.81409 h 3.25643 v 1.22114 m -1.6282,-7.73397 h 1.6689 c -0.0154,0.13844 -0.0406,0.26857 -0.0406,0.40702 v 1.22118 h -1.6282 c -0.44776,0 -0.81411,-0.36633 -0.81411,-0.81409 0,-0.44776 0.36635,-0.81411 0.81411,-0.81411 m -1.62824,4.07051 c 0,-0.44776 0.36639,-0.81409 0.81413,-0.81409 h 2.44231 v 1.62818 h -2.44231 c -0.44774,0 -0.81413,-0.36633 -0.81413,-0.81409 z"
|
||||
id="path1"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#777777;fill-opacity:1;stroke-width:0.41206;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000;stop-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
51
static/images/gruvbox/notifications_selected.svg
Normal file
51
static/images/gruvbox/notifications_selected.svg
Normal file
@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="73.846642"
|
||||
height="39.999989"
|
||||
viewBox="0 0 19.53859 10.58333"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
|
||||
sodipodi:docname="notification_selected.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">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="11.5"
|
||||
inkscape:cy="6.5"
|
||||
inkscape:window-width="1916"
|
||||
inkscape:window-height="1161"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="18"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-101.86452,-146.84375)">
|
||||
<title
|
||||
id="title1">email-fast</title>
|
||||
<title
|
||||
id="title1-8">email-fast-outline</title>
|
||||
<path
|
||||
d="m 119.77487,146.84375 h -10.58332 c -0.8955,0 -1.62821,0.73267 -1.62821,1.62817 v 7.32695 c 0,0.90365 0.73271,1.62821 1.62821,1.62821 h 10.58332 c 0.90368,0 1.62824,-0.72456 1.62824,-1.62821 v -7.32695 c 0,-0.8955 -0.72456,-1.62817 -1.62824,-1.62817 m 0,8.95512 h -10.58332 v -5.96736 l 5.29166,2.71096 5.29166,-2.71096 v 5.96736 m -5.29166,-4.63223 -5.29166,-2.69472 h 10.58332 l -5.29166,2.69472 m -8.54807,4.63223 c 0,0.13845 0.0246,0.26859 0.0406,0.40706 h -3.29713 c -0.44939,0 -0.81409,-0.36635 -0.81409,-0.81411 0,-0.44776 0.3647,-0.81409 0.81409,-0.81409 h 3.25643 v 1.22114 m -1.6282,-7.73397 h 1.6689 c -0.0154,0.13844 -0.0406,0.26857 -0.0406,0.40702 v 1.22118 h -1.6282 c -0.44776,0 -0.81411,-0.36633 -0.81411,-0.81409 0,-0.44776 0.36635,-0.81411 0.81411,-0.81411 m -1.62824,4.07051 c 0,-0.44776 0.36639,-0.81409 0.81413,-0.81409 h 2.44231 v 1.62818 h -2.44231 c -0.44774,0 -0.81413,-0.36633 -0.81413,-0.81409 z"
|
||||
id="path1"
|
||||
style="font-variation-settings:normal;opacity:1;vector-effect:none;fill:#fb4934;fill-opacity:1;stroke-width:0.41206;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000;stop-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.6 KiB |
@ -58,7 +58,11 @@ button {
|
||||
}
|
||||
}
|
||||
|
||||
#datapoints, #problems-list, #acknowledged-list, #values, #services {
|
||||
dialog {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
dialog, #datapoints, #problems-list, #acknowledged-list, #values, #services, #notifications {
|
||||
background-color: #fff !important;
|
||||
border: 1px solid #ddd;
|
||||
box-shadow: 5px 5px 8px 0px rgba(0,0,0,0.25);
|
||||
|
@ -52,7 +52,7 @@ html {
|
||||
#layout {
|
||||
display: grid;
|
||||
grid-template-areas: "menu content";
|
||||
grid-template-columns: 104px 1fr;
|
||||
grid-template-columns: 128px 1fr;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
@ -248,7 +248,7 @@ button {
|
||||
}
|
||||
|
||||
.line {
|
||||
grid-column: 1 / -1;
|
||||
grid-column: ~"1 / -1";
|
||||
border-bottom: 1px solid .lighterOrDarker(@bg1, 15%)[@result];
|
||||
}
|
||||
|
||||
|
81
static/less/notifications.less
Normal file
81
static/less/notifications.less
Normal file
@ -0,0 +1,81 @@
|
||||
@import "theme-@{THEME}.less";
|
||||
|
||||
#time-select {
|
||||
display: grid;
|
||||
grid-template-columns: min-content min-content;
|
||||
grid-gap: 6px 16px;
|
||||
|
||||
padding: 16px;
|
||||
border: 1px solid @text3;
|
||||
width: min-content;
|
||||
border-radius: 6px;
|
||||
|
||||
button {
|
||||
//grid-column: ~"1 / -1";
|
||||
width: 100px;
|
||||
margin-top: 12px;
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
#time-offsets {
|
||||
display: grid;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input[type="datetime-local"] {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
#notifications {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 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;
|
||||
}
|
||||
|
||||
.ok {
|
||||
color: #0a0;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #a00;
|
||||
}
|
||||
}
|
@ -52,7 +52,7 @@
|
||||
|
||||
.name {
|
||||
color: @text2;
|
||||
grid-column: 1 / -1;
|
||||
grid-column: ~"1 / -1";
|
||||
font-weight: bold !important;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
@ -28,6 +28,13 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="entry {{ if eq .MENU "notifications" }}selected{{ end }}">
|
||||
<a href="/notifications">
|
||||
<img src="/images/{{ .VERSION }}/{{ .CONFIG.THEME }}/notifications{{ if eq .MENU "notifications" }}_selected{{ end }}.svg" style="width: 36px">
|
||||
<div class="label">Notifications</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="entry {{ if eq .MENU "configuration" }}selected{{ end }}">
|
||||
<a href="/configuration">
|
||||
<img src="/images/{{ .VERSION }}/{{ .CONFIG.THEME }}/configuration{{ if eq .MENU "configuration" }}_selected{{ end }}.svg">
|
||||
|
98
views/pages/notifications.gotmpl
Normal file
98
views/pages/notifications.gotmpl
Normal file
@ -0,0 +1,98 @@
|
||||
{{ define "page" }}
|
||||
|
||||
{{ block "page_label" . }}{{end}}
|
||||
{{ $version := .VERSION }}
|
||||
{{ $theme := .CONFIG.THEME }}
|
||||
<link rel="stylesheet" type="text/css" href="/css/{{ .VERSION }}/{{ .CONFIG.THEME }}/notifications.css">
|
||||
<script type="text/javascript">
|
||||
function dialogClick(evt) {
|
||||
if (evt.target.tagName.toUpperCase() == 'DIALOG') {
|
||||
evt.target.close()
|
||||
}
|
||||
}
|
||||
|
||||
function preset(hours) {
|
||||
const inputPreset = document.querySelector('input[name="preset"]')
|
||||
inputPreset.value = hours
|
||||
inputPreset.form.submit()
|
||||
}
|
||||
|
||||
function offsetTime(seconds) {
|
||||
const el = document.querySelector('input[name="offset-time"]')
|
||||
el.value = seconds
|
||||
el.form.submit()
|
||||
}
|
||||
</script>
|
||||
|
||||
<form action="/notifications" method="get">
|
||||
<input type="hidden" name="preset" value="">
|
||||
<input type="hidden" name="offset-time" value="">
|
||||
|
||||
<div id="time-select">
|
||||
<div>From</div>
|
||||
<div>To</div>
|
||||
<input name="f" value="{{ .Data.TimeFrom }}" type="datetime-local">
|
||||
<input name="t" value="{{ .Data.TimeTo }}" type="datetime-local">
|
||||
|
||||
<div id="time-offsets">
|
||||
<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>
|
||||
<div>Hour</div>
|
||||
<div><a href="#" onclick="offsetTime(3600)">▶</a></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 * 7 * 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>
|
||||
</form>
|
||||
|
||||
<div id="notifications">
|
||||
<div class="header">Sent</div>
|
||||
<div class="header">OK</div>
|
||||
<div class="header">Trigger name</div>
|
||||
<div class="header">Service</div>
|
||||
<div class="header">Error</div>
|
||||
{{ range .Data.Notifications }}
|
||||
<div>{{ format_time .Sent }}</div>
|
||||
<div>{{ if .OK }}<span class="ok">✔</span>{{ else }}<span class="error">✗</span>{{ end }}</div>
|
||||
<div>{{ .TriggerName }}</div>
|
||||
<div>{{ .Prio }}:{{ .Service }}</div>
|
||||
<div>
|
||||
{{ if .Error.Valid }}
|
||||
<img src="/images/{{ $version }}/{{ $theme }}/info-filled.svg" onclick="document.getElementById('error-{{ .ID }}').showModal()">
|
||||
<dialog id="error-{{ .ID }}" onclick="dialogClick(event)">
|
||||
<div style="padding: 16px 32px">
|
||||
<pre>{{ .ErrorIndented }}</pre>
|
||||
<div style="text-align: center">
|
||||
<button onclick="document.getElementById('error-{{ .ID }}').close()">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
{{ else }}
|
||||
<img src="/images/{{ $version }}/{{ $theme }}/info-outline.svg">
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
{{ end }}
|
Loading…
Reference in New Issue
Block a user