Refactored time filter.
This commit is contained in:
parent
4c908f4891
commit
9700bc9d3c
@ -63,7 +63,7 @@ func applyTimeOffset(t time.Time, duration time.Duration, amountStr string) time
|
|||||||
|
|
||||||
return t.Add(duration * time.Duration(amount))
|
return t.Add(duration * time.Duration(amount))
|
||||||
} // }}}
|
} // }}}
|
||||||
func presetTimeInterval(duration time.Duration, presetStr string, timeFrom, timeTo *time.Time) () {
|
func presetTimeInterval(duration time.Duration, presetStr string, timeFrom, timeTo *time.Time) () {// {{{
|
||||||
if presetStr == "" {
|
if presetStr == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -73,4 +73,4 @@ func presetTimeInterval(duration time.Duration, presetStr string, timeFrom, time
|
|||||||
(*timeFrom) = now.Add(duration * -1 * time.Duration(presetTime))
|
(*timeFrom) = now.Add(duration * -1 * time.Duration(presetTime))
|
||||||
(*timeTo) = now
|
(*timeTo) = now
|
||||||
return
|
return
|
||||||
}
|
}// }}}
|
||||||
|
73
main.go
73
main.go
@ -709,31 +709,14 @@ func pageDatapointValues(w http.ResponseWriter, r *http.Request, _ *session.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET parameters.
|
// Manage the values from the timefilter component
|
||||||
display := r.URL.Query().Get("display")
|
|
||||||
if display == "" && datapoint.Datatype == INT {
|
|
||||||
display = "graph"
|
|
||||||
}
|
|
||||||
|
|
||||||
var timeFrom, timeTo time.Time
|
var timeFrom, timeTo time.Time
|
||||||
yesterday := time.Now().Add(time.Duration(-24 * time.Hour))
|
timeFrom, timeTo, err = timefilterParse(
|
||||||
timeFrom, err = parseHTMLDateTime(r.URL.Query().Get("f"), yesterday)
|
r.URL.Query().Get("time-f"),
|
||||||
if err != nil {
|
r.URL.Query().Get("time-t"),
|
||||||
httpError(w, werr.Wrap(err).WithData(r.URL.Query().Get("f")).Log())
|
r.URL.Query().Get("time-offset"),
|
||||||
return
|
r.URL.Query().Get("time-preset"),
|
||||||
}
|
)
|
||||||
|
|
||||||
timeTo, err = parseHTMLDateTime(r.URL.Query().Get("t"), time.Now())
|
|
||||||
if err != nil {
|
|
||||||
httpError(w, werr.Wrap(err).WithData(r.URL.Query().Get("t")).Log())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
presetTimeInterval(time.Hour, r.URL.Query().Get("preset"), &timeFrom, &timeTo)
|
|
||||||
|
|
||||||
// Apply an optionally set offset (in seconds).
|
|
||||||
timeFrom = applyTimeOffset(timeFrom, time.Second, r.URL.Query().Get("offset-time"))
|
|
||||||
timeTo = applyTimeOffset(timeTo, time.Second, r.URL.Query().Get("offset-time"))
|
|
||||||
|
|
||||||
// Fetch data point values according to the times.
|
// Fetch data point values according to the times.
|
||||||
var values []DatapointValue
|
var values []DatapointValue
|
||||||
@ -743,6 +726,12 @@ func pageDatapointValues(w http.ResponseWriter, r *http.Request, _ *session.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET parameters.
|
||||||
|
display := r.URL.Query().Get("display")
|
||||||
|
if display == "" && datapoint.Datatype == INT {
|
||||||
|
display = "graph"
|
||||||
|
}
|
||||||
|
|
||||||
page := Page{
|
page := Page{
|
||||||
LAYOUT: "main",
|
LAYOUT: "main",
|
||||||
PAGE: "datapoint_values",
|
PAGE: "datapoint_values",
|
||||||
@ -755,9 +744,11 @@ func pageDatapointValues(w http.ResponseWriter, r *http.Request, _ *session.T) {
|
|||||||
page.Data = map[string]any{
|
page.Data = map[string]any{
|
||||||
"Datapoint": datapoint,
|
"Datapoint": datapoint,
|
||||||
"Values": values,
|
"Values": values,
|
||||||
|
"Display": display,
|
||||||
|
|
||||||
|
"TimeSubmit": "/datapoint/values/" + strconv.Itoa(datapoint.ID),
|
||||||
"TimeFrom": timeFrom.Format("2006-01-02T15:04:05"),
|
"TimeFrom": timeFrom.Format("2006-01-02T15:04:05"),
|
||||||
"TimeTo": timeTo.Format("2006-01-02T15:04:05"),
|
"TimeTo": timeTo.Format("2006-01-02T15:04:05"),
|
||||||
"Display": display,
|
|
||||||
}
|
}
|
||||||
page.Render(w, r)
|
page.Render(w, r)
|
||||||
return
|
return
|
||||||
@ -770,14 +761,14 @@ func actionDatapointJson(w http.ResponseWriter, r *http.Request, _ *session.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fromStr := r.URL.Query().Get("f")
|
fromStr := r.URL.Query().Get("time-f")
|
||||||
from, err := time.ParseInLocation("2006-01-02 15:04:05", fromStr[0:min(19, len(fromStr))], smonConfig.Timezone())
|
from, err := time.ParseInLocation("2006-01-02 15:04:05", fromStr[0:min(19, len(fromStr))], smonConfig.Timezone())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, werr.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
toStr := r.URL.Query().Get("t")
|
toStr := r.URL.Query().Get("time-t")
|
||||||
to, err := time.ParseInLocation("2006-01-02 15:04:05", toStr[0:min(19, len(toStr))], smonConfig.Timezone())
|
to, err := time.ParseInLocation("2006-01-02 15:04:05", toStr[0:min(19, len(toStr))], smonConfig.Timezone())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, werr.Wrap(err).Log())
|
httpError(w, werr.Wrap(err).Log())
|
||||||
@ -1198,27 +1189,14 @@ func actionConfigurationNotificationDelete(w http.ResponseWriter, r *http.Reques
|
|||||||
func pageNotifications(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
|
func pageNotifications(w http.ResponseWriter, r *http.Request, _ *session.T) { // {{{
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// GET parameters.
|
// Manage the values from the timefilter component
|
||||||
var timeFrom, timeTo time.Time
|
var timeFrom, timeTo time.Time
|
||||||
lastWeek := time.Now().Add(time.Duration(-7 * 24 * time.Hour))
|
timeFrom, timeTo, err = timefilterParse(
|
||||||
|
r.URL.Query().Get("time-f"),
|
||||||
timeFrom, err = parseHTMLDateTime(r.URL.Query().Get("f"), lastWeek)
|
r.URL.Query().Get("time-t"),
|
||||||
if err != nil {
|
r.URL.Query().Get("time-offset"),
|
||||||
httpError(w, werr.Wrap(err).Log())
|
r.URL.Query().Get("time-preset"),
|
||||||
return
|
)
|
||||||
}
|
|
||||||
|
|
||||||
timeTo, err = parseHTMLDateTime(r.URL.Query().Get("t"), time.Now())
|
|
||||||
if err != nil {
|
|
||||||
httpError(w, werr.Wrap(err).Log())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
presetTimeInterval(time.Hour, r.URL.Query().Get("preset"), &timeFrom, &timeTo)
|
|
||||||
|
|
||||||
// Apply an optionally set offset (in seconds).
|
|
||||||
timeFrom = applyTimeOffset(timeFrom, time.Second, r.URL.Query().Get("offset-time"))
|
|
||||||
timeTo = applyTimeOffset(timeTo, time.Second, r.URL.Query().Get("offset-time"))
|
|
||||||
|
|
||||||
nss, err := notificationsSent(timeFrom, timeTo)
|
nss, err := notificationsSent(timeFrom, timeTo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1231,6 +1209,7 @@ func pageNotifications(w http.ResponseWriter, r *http.Request, _ *session.T) { /
|
|||||||
CONFIG: smonConfig.Settings,
|
CONFIG: smonConfig.Settings,
|
||||||
Data: map[string]any{
|
Data: map[string]any{
|
||||||
"Notifications": nss,
|
"Notifications": nss,
|
||||||
|
"TimeSubmit": "/notifications",
|
||||||
"TimeFrom": timeFrom.Format("2006-01-02T15:04:05"),
|
"TimeFrom": timeFrom.Format("2006-01-02T15:04:05"),
|
||||||
"TimeTo": timeTo.Format("2006-01-02T15:04:05"),
|
"TimeTo": timeTo.Format("2006-01-02T15:04:05"),
|
||||||
},
|
},
|
||||||
|
1
sql/00023.sql
Normal file
1
sql/00023.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE public.problem ADD COLUMN trigger_expression VARCHAR NOT NULL DEFAULT '';
|
@ -99,25 +99,3 @@
|
|||||||
.graph #graph-values {
|
.graph #graph-values {
|
||||||
height: calc(100vh - 416px);
|
height: calc(100vh - 416px);
|
||||||
}
|
}
|
||||||
.time-offset {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: min-content repeat(3, min-content);
|
|
||||||
grid-gap: 16px;
|
|
||||||
margin-top: 16px;
|
|
||||||
align-items: center;
|
|
||||||
justify-items: center;
|
|
||||||
}
|
|
||||||
.time-offset .header-1 {
|
|
||||||
font-weight: bold;
|
|
||||||
justify-self: start;
|
|
||||||
}
|
|
||||||
.time-offset .header-2 {
|
|
||||||
font-weight: bold;
|
|
||||||
justify-self: start;
|
|
||||||
grid-column: 2 / -1;
|
|
||||||
}
|
|
||||||
.time-offset .preset {
|
|
||||||
white-space: nowrap;
|
|
||||||
justify-self: start;
|
|
||||||
padding-right: 32px;
|
|
||||||
}
|
|
||||||
|
@ -226,3 +226,37 @@ label {
|
|||||||
width: min-content;
|
width: min-content;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
#time-selector {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: min-content min-content;
|
||||||
|
grid-gap: 6px 16px;
|
||||||
|
width: min-content;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
#time-selector button {
|
||||||
|
width: 100px;
|
||||||
|
margin-top: 12px;
|
||||||
|
justify-self: end;
|
||||||
|
}
|
||||||
|
#time-selector #time-filter {
|
||||||
|
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;
|
||||||
|
justify-self: start;
|
||||||
|
padding-right: 32px;
|
||||||
|
}
|
||||||
|
@ -1,37 +1,3 @@
|
|||||||
#time-select {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: min-content min-content;
|
|
||||||
grid-gap: 6px 16px;
|
|
||||||
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"] {
|
input[type="datetime-local"] {
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
}
|
}
|
||||||
|
@ -99,25 +99,3 @@
|
|||||||
.graph #graph-values {
|
.graph #graph-values {
|
||||||
height: calc(100vh - 416px);
|
height: calc(100vh - 416px);
|
||||||
}
|
}
|
||||||
.time-offset {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: min-content repeat(3, min-content);
|
|
||||||
grid-gap: 16px;
|
|
||||||
margin-top: 16px;
|
|
||||||
align-items: center;
|
|
||||||
justify-items: center;
|
|
||||||
}
|
|
||||||
.time-offset .header-1 {
|
|
||||||
font-weight: bold;
|
|
||||||
justify-self: start;
|
|
||||||
}
|
|
||||||
.time-offset .header-2 {
|
|
||||||
font-weight: bold;
|
|
||||||
justify-self: start;
|
|
||||||
grid-column: 2 / -1;
|
|
||||||
}
|
|
||||||
.time-offset .preset {
|
|
||||||
white-space: nowrap;
|
|
||||||
justify-self: start;
|
|
||||||
padding-right: 32px;
|
|
||||||
}
|
|
||||||
|
@ -226,3 +226,37 @@ label {
|
|||||||
width: min-content;
|
width: min-content;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
#time-selector {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: min-content min-content;
|
||||||
|
grid-gap: 6px 16px;
|
||||||
|
width: min-content;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
#time-selector button {
|
||||||
|
width: 100px;
|
||||||
|
margin-top: 12px;
|
||||||
|
justify-self: end;
|
||||||
|
}
|
||||||
|
#time-selector #time-filter {
|
||||||
|
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;
|
||||||
|
justify-self: start;
|
||||||
|
padding-right: 32px;
|
||||||
|
}
|
||||||
|
@ -1,37 +1,3 @@
|
|||||||
#time-select {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: min-content min-content;
|
|
||||||
grid-gap: 6px 16px;
|
|
||||||
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"] {
|
input[type="datetime-local"] {
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,7 @@
|
|||||||
function preset(hours) {
|
function selectDisplay(display) {
|
||||||
const inputPreset = document.querySelector('input[name="preset"]')
|
const inputDisplay = document.getElementById('input-display')
|
||||||
inputPreset.value = hours
|
inputDisplay.value = display
|
||||||
inputPreset.form.submit()
|
inputDisplay.form.submit()
|
||||||
}
|
|
||||||
|
|
||||||
function offsetTime(seconds) {
|
|
||||||
const inputPreset = document.querySelector('input[name="offset-time"]')
|
|
||||||
inputPreset.value = seconds
|
|
||||||
inputPreset.form.submit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Graph {
|
class Graph {
|
||||||
@ -61,7 +55,9 @@ class Dataset {
|
|||||||
constructor(id, initialData) {
|
constructor(id, initialData) {
|
||||||
this.datapointID = id
|
this.datapointID = id
|
||||||
this.values = {}
|
this.values = {}
|
||||||
initialData.forEach(v=>this.values[v.ID] = v)
|
if (initialData === null)
|
||||||
|
return
|
||||||
|
initialData.forEach(v => this.values[v.ID] = v)
|
||||||
}
|
}
|
||||||
|
|
||||||
xValues() {
|
xValues() {
|
||||||
@ -76,7 +72,7 @@ class Dataset {
|
|||||||
return fetch(`/datapoint/json/${this.datapointID}?f=${from}&t=${to}`)
|
return fetch(`/datapoint/json/${this.datapointID}?f=${from}&t=${to}`)
|
||||||
.then(data => data.json())
|
.then(data => data.json())
|
||||||
.then(datapointValues => {
|
.then(datapointValues => {
|
||||||
datapointValues.forEach(dp=>{
|
datapointValues.forEach(dp => {
|
||||||
this.values[dp.ID] = dp
|
this.values[dp.ID] = dp
|
||||||
})
|
})
|
||||||
document.getElementById('num-values').innerText = Object.keys(this.values).length
|
document.getElementById('num-values').innerText = Object.keys(this.values).length
|
||||||
|
@ -120,30 +120,3 @@
|
|||||||
height: calc(100vh - 416px);
|
height: calc(100vh - 416px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.time-offset {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -280,3 +280,46 @@ label {
|
|||||||
width: min-content;
|
width: min-content;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#time-selector {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: min-content min-content;
|
||||||
|
grid-gap: 6px 16px;
|
||||||
|
|
||||||
|
width: min-content;
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100px;
|
||||||
|
margin-top: 12px;
|
||||||
|
justify-self: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
#time-filter {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,47 +1,5 @@
|
|||||||
@import "theme-@{THEME}.less";
|
@import "theme-@{THEME}.less";
|
||||||
|
|
||||||
#time-select {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: min-content min-content;
|
|
||||||
grid-gap: 6px 16px;
|
|
||||||
|
|
||||||
width: min-content;
|
|
||||||
border-radius: 6px;
|
|
||||||
|
|
||||||
button {
|
|
||||||
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"] {
|
input[type="datetime-local"] {
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
}
|
}
|
||||||
|
41
timefilter.go
Normal file
41
timefilter.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
// External
|
||||||
|
werr "git.gibonuddevalla.se/go/wrappederror"
|
||||||
|
|
||||||
|
// Standard
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func timefilterParse(timeFromStr, timeToStr, offset, preset string) (timeFrom, timeTo time.Time, err error) {
|
||||||
|
if preset != "" {
|
||||||
|
presetTimeInterval(time.Hour, preset, &timeFrom, &timeTo)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
yesterday := time.Now().Add(time.Duration(-24 * time.Hour))
|
||||||
|
timeFrom, err = parseHTMLDateTime(timeFromStr, yesterday)
|
||||||
|
if err != nil {
|
||||||
|
err = werr.Wrap(err).WithData(timeFromStr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
timeTo, err = parseHTMLDateTime(timeToStr, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
err = werr.Wrap(err).WithData(timeToStr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protect the user from switching from/to dates, leading to
|
||||||
|
// zero matching values from the database.
|
||||||
|
if timeFrom.After(timeTo) {
|
||||||
|
timeFrom, timeTo = timeTo, timeFrom
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply an optionally set offset (in seconds).
|
||||||
|
timeFrom = applyTimeOffset(timeFrom, time.Second, offset)
|
||||||
|
timeTo = applyTimeOffset(timeTo, time.Second, offset)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
61
views/components/timefilter.gotmpl
Normal file
61
views/components/timefilter.gotmpl
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
{{ define "timefilter" }}
|
||||||
|
<script type="text/javascript">
|
||||||
|
function preset(hours) {
|
||||||
|
const inputPreset = document.querySelector('input[name="time-preset"]')
|
||||||
|
inputPreset.value = hours
|
||||||
|
inputPreset.form.submit()
|
||||||
|
}
|
||||||
|
|
||||||
|
function offsetTime(seconds) {
|
||||||
|
const el = document.querySelector('input[name="time-offset"]')
|
||||||
|
el.value = seconds
|
||||||
|
el.form.submit()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<form action="{{ .Data.TimeSubmit }}" method="get" id="form-time-selector">
|
||||||
|
<input type="hidden" name="time-preset" value="">
|
||||||
|
<input type="hidden" name="time-offset" value=0>
|
||||||
|
|
||||||
|
<div id="time-selector">
|
||||||
|
<div>From</div>
|
||||||
|
<div>To</div>
|
||||||
|
|
||||||
|
<input name="time-f" value="{{ .Data.TimeFrom }}" type="datetime-local">
|
||||||
|
<input name="time-t" value="{{ .Data.TimeTo }}" type="datetime-local">
|
||||||
|
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<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 * 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>
|
||||||
|
{{ end }}
|
@ -10,61 +10,9 @@
|
|||||||
evt.target.close()
|
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>
|
</script>
|
||||||
|
|
||||||
<form action="/notifications" method="get">
|
{{ block "timefilter" . }}{{ end }}
|
||||||
<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 * 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 id="notifications">
|
||||||
<div class="header">Sent</div>
|
<div class="header">Sent</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user