Stn/add receiver support (#872)
Add ability to filter alerts by receiver in UI. This adds changes both in the Elm UI, as well as the Go backend.
This commit is contained in:
parent
6ef5ca6225
commit
a7009a9db7
71
api/api.go
71
api/api.go
|
@ -17,6 +17,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"regexp"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -75,6 +76,7 @@ type API struct {
|
||||||
alerts provider.Alerts
|
alerts provider.Alerts
|
||||||
silences *silence.Silences
|
silences *silence.Silences
|
||||||
config *config.Config
|
config *config.Config
|
||||||
|
route *dispatch.Route
|
||||||
resolveTimeout time.Duration
|
resolveTimeout time.Duration
|
||||||
uptime time.Time
|
uptime time.Time
|
||||||
mrouter *mesh.Router
|
mrouter *mesh.Router
|
||||||
|
@ -119,6 +121,7 @@ func (api *API) Register(r *route.Router) {
|
||||||
r = r.WithPrefix("/v1")
|
r = r.WithPrefix("/v1")
|
||||||
|
|
||||||
r.Get("/status", ihf("status", api.status))
|
r.Get("/status", ihf("status", api.status))
|
||||||
|
r.Get("/receivers", ihf("receivers", api.receivers))
|
||||||
r.Get("/alerts/groups", ihf("alert_groups", api.alertGroups))
|
r.Get("/alerts/groups", ihf("alert_groups", api.alertGroups))
|
||||||
|
|
||||||
r.Get("/alerts", ihf("list_alerts", api.listAlerts))
|
r.Get("/alerts", ihf("list_alerts", api.listAlerts))
|
||||||
|
@ -137,6 +140,7 @@ func (api *API) Update(cfg *config.Config, resolveTimeout time.Duration) error {
|
||||||
|
|
||||||
api.resolveTimeout = resolveTimeout
|
api.resolveTimeout = resolveTimeout
|
||||||
api.config = cfg
|
api.config = cfg
|
||||||
|
api.route = dispatch.NewRoute(cfg.Route, nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,6 +161,18 @@ func (e *apiError) Error() string {
|
||||||
return fmt.Sprintf("%s: %s", e.typ, e.err)
|
return fmt.Sprintf("%s: %s", e.typ, e.err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *API) receivers(w http.ResponseWriter, req *http.Request) {
|
||||||
|
api.mtx.RLock()
|
||||||
|
defer api.mtx.RUnlock()
|
||||||
|
|
||||||
|
receivers := make([]string, 0, len(api.config.Receivers))
|
||||||
|
for _, r := range api.config.Receivers {
|
||||||
|
receivers = append(receivers, r.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
respond(w, receivers)
|
||||||
|
}
|
||||||
|
|
||||||
func (api *API) status(w http.ResponseWriter, req *http.Request) {
|
func (api *API) status(w http.ResponseWriter, req *http.Request) {
|
||||||
api.mtx.RLock()
|
api.mtx.RLock()
|
||||||
|
|
||||||
|
@ -217,10 +233,11 @@ func getMeshStatus(api *API) meshStatus {
|
||||||
return strippedStatus
|
return strippedStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *API) alertGroups(w http.ResponseWriter, req *http.Request) {
|
func (api *API) alertGroups(w http.ResponseWriter, r *http.Request) {
|
||||||
var err error
|
var err error
|
||||||
matchers := []*labels.Matcher{}
|
matchers := []*labels.Matcher{}
|
||||||
if filter := req.FormValue("filter"); filter != "" {
|
|
||||||
|
if filter := r.FormValue("filter"); filter != "" {
|
||||||
matchers, err = parse.Matchers(filter)
|
matchers, err = parse.Matchers(filter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
respondError(w, apiError{
|
respondError(w, apiError{
|
||||||
|
@ -236,18 +253,13 @@ func (api *API) alertGroups(w http.ResponseWriter, req *http.Request) {
|
||||||
respond(w, groups)
|
respond(w, groups)
|
||||||
}
|
}
|
||||||
|
|
||||||
type APIAlert struct {
|
|
||||||
*model.Alert
|
|
||||||
|
|
||||||
Status types.AlertStatus `json:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (api *API) listAlerts(w http.ResponseWriter, r *http.Request) {
|
func (api *API) listAlerts(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
|
re *regexp.Regexp
|
||||||
// Initialize result slice to prevent api returning `null` when there
|
// Initialize result slice to prevent api returning `null` when there
|
||||||
// are no alerts present
|
// are no alerts present
|
||||||
res = []*APIAlert{}
|
res = []*dispatch.APIAlert{}
|
||||||
matchers = []*labels.Matcher{}
|
matchers = []*labels.Matcher{}
|
||||||
showSilenced = true
|
showSilenced = true
|
||||||
)
|
)
|
||||||
|
@ -278,6 +290,20 @@ func (api *API) listAlerts(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if receiverParam := r.FormValue("receiver"); receiverParam != "" {
|
||||||
|
re, err = regexp.Compile("^(?:" + receiverParam + ")$")
|
||||||
|
if err != nil {
|
||||||
|
respondError(w, apiError{
|
||||||
|
typ: errorBadData,
|
||||||
|
err: fmt.Errorf(
|
||||||
|
"failed to parse receiver param: %s",
|
||||||
|
receiverParam,
|
||||||
|
),
|
||||||
|
}, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
alerts := api.alerts.GetPending()
|
alerts := api.alerts.GetPending()
|
||||||
defer alerts.Close()
|
defer alerts.Close()
|
||||||
|
|
||||||
|
@ -287,6 +313,16 @@ func (api *API) listAlerts(w http.ResponseWriter, r *http.Request) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
routes := api.route.Match(a.Labels)
|
||||||
|
receivers := make([]string, 0, len(routes))
|
||||||
|
for _, r := range routes {
|
||||||
|
receivers = append(receivers, r.RouteOpts.Receiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
if re != nil && !regexpAny(re, receivers) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if !alertMatchesFilterLabels(&a.Alert, matchers) {
|
if !alertMatchesFilterLabels(&a.Alert, matchers) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -302,9 +338,10 @@ func (api *API) listAlerts(w http.ResponseWriter, r *http.Request) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
apiAlert := &APIAlert{
|
apiAlert := &dispatch.APIAlert{
|
||||||
Alert: &a.Alert,
|
Alert: &a.Alert,
|
||||||
Status: status,
|
Status: status,
|
||||||
|
Receivers: receivers,
|
||||||
}
|
}
|
||||||
|
|
||||||
res = append(res, apiAlert)
|
res = append(res, apiAlert)
|
||||||
|
@ -320,6 +357,16 @@ func (api *API) listAlerts(w http.ResponseWriter, r *http.Request) {
|
||||||
respond(w, res)
|
respond(w, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func regexpAny(re *regexp.Regexp, ss []string) bool {
|
||||||
|
for _, s := range ss {
|
||||||
|
if re.MatchString(s) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func alertMatchesFilterLabels(a *model.Alert, matchers []*labels.Matcher) bool {
|
func alertMatchesFilterLabels(a *model.Alert, matchers []*labels.Matcher) bool {
|
||||||
for _, m := range matchers {
|
for _, m := range matchers {
|
||||||
if v, prs := a.Labels[model.LabelName(m.Name)]; !prs || !m.Matches(string(v)) {
|
if v, prs := a.Labels[model.LabelName(m.Name)]; !prs || !m.Matches(string(v)) {
|
||||||
|
|
|
@ -80,7 +80,8 @@ type AlertBlock struct {
|
||||||
// annotated with silencing and inhibition info.
|
// annotated with silencing and inhibition info.
|
||||||
type APIAlert struct {
|
type APIAlert struct {
|
||||||
*model.Alert
|
*model.Alert
|
||||||
Status types.AlertStatus `json:"status"`
|
Status types.AlertStatus `json:"status"`
|
||||||
|
Receivers []string `json:"receivers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AlertGroup is a list of alert blocks grouped by the same label set.
|
// AlertGroup is a list of alert blocks grouped by the same label set.
|
||||||
|
|
|
@ -6,9 +6,9 @@ route:
|
||||||
group_wait: 10s
|
group_wait: 10s
|
||||||
group_interval: 10s
|
group_interval: 10s
|
||||||
repeat_interval: 1h
|
repeat_interval: 1h
|
||||||
receiver: 'webhook'
|
receiver: 'web.hook'
|
||||||
receivers:
|
receivers:
|
||||||
- name: 'webhook'
|
- name: 'web.hook'
|
||||||
webhook_configs:
|
webhook_configs:
|
||||||
- url: 'http://127.0.0.1:5001/'
|
- url: 'http://127.0.0.1:5001/'
|
||||||
inhibit_rules:
|
inhibit_rules:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{{ define "__alertmanager" }}AlertManager{{ end }}
|
{{ define "__alertmanager" }}AlertManager{{ end }}
|
||||||
{{ define "__alertmanagerURL" }}{{ .ExternalURL }}/#/alerts{{ end }}
|
{{ define "__alertmanagerURL" }}{{ .ExternalURL }}/#/alerts?receiver={{ .Receiver }}{{ end }}
|
||||||
|
|
||||||
{{ define "__subject" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }}{{ end }}
|
{{ define "__subject" }}[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}] {{ .GroupLabels.SortedPairs.Values | join " " }} {{ if gt (len .CommonLabels) (len .GroupLabels) }}({{ with .CommonLabels.Remove .GroupLabels.Names }}{{ .Values | join " " }}{{ end }}){{ end }}{{ end }}
|
||||||
{{ define "__description" }}{{ end }}
|
{{ define "__description" }}{{ end }}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -269,7 +269,7 @@ func (as Alerts) Resolved() []Alert {
|
||||||
// Data assembles data for template expansion.
|
// Data assembles data for template expansion.
|
||||||
func (t *Template) Data(recv string, groupLabels model.LabelSet, alerts ...*types.Alert) *Data {
|
func (t *Template) Data(recv string, groupLabels model.LabelSet, alerts ...*types.Alert) *Data {
|
||||||
data := &Data{
|
data := &Data{
|
||||||
Receiver: strings.SplitN(recv, "/", 2)[0],
|
Receiver: regexp.QuoteMeta(strings.SplitN(recv, "/", 2)[0]),
|
||||||
Status: string(types.Alerts(alerts...).Status()),
|
Status: string(types.Alerts(alerts...).Status()),
|
||||||
Alerts: make(Alerts, 0, len(alerts)),
|
Alerts: make(Alerts, 0, len(alerts)),
|
||||||
GroupLabels: KV{},
|
GroupLabels: KV{},
|
||||||
|
|
|
@ -1,17 +1,26 @@
|
||||||
module Alerts.Api exposing (..)
|
module Alerts.Api exposing (..)
|
||||||
|
|
||||||
import Alerts.Types exposing (Alert, RouteOpts, Block, AlertGroup)
|
import Alerts.Types exposing (Alert, AlertGroup, Block, RouteOpts)
|
||||||
import Json.Decode as Json exposing (..)
|
import Json.Decode as Json exposing (..)
|
||||||
import Utils.Api exposing (iso8601Time)
|
import Utils.Api exposing (iso8601Time)
|
||||||
import Utils.Types exposing (ApiData)
|
|
||||||
import Utils.Filter exposing (Filter, generateQueryString)
|
import Utils.Filter exposing (Filter, generateQueryString)
|
||||||
|
import Utils.Types exposing (ApiData)
|
||||||
|
|
||||||
|
|
||||||
|
fetchReceivers : String -> Cmd (ApiData (List String))
|
||||||
|
fetchReceivers apiUrl =
|
||||||
|
Utils.Api.send
|
||||||
|
(Utils.Api.get
|
||||||
|
(apiUrl ++ "/receivers")
|
||||||
|
(field "data" (list string))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
fetchAlerts : String -> Filter -> Cmd (ApiData (List Alert))
|
fetchAlerts : String -> Filter -> Cmd (ApiData (List Alert))
|
||||||
fetchAlerts apiUrl filter =
|
fetchAlerts apiUrl filter =
|
||||||
let
|
let
|
||||||
url =
|
url =
|
||||||
String.join "/" [ apiUrl, "alerts" ++ (generateQueryString filter) ]
|
String.join "/" [ apiUrl, "alerts" ++ generateQueryString filter ]
|
||||||
in
|
in
|
||||||
Utils.Api.send (Utils.Api.get url alertsDecoder)
|
Utils.Api.send (Utils.Api.get url alertsDecoder)
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ import Set
|
||||||
type alias Filter =
|
type alias Filter =
|
||||||
{ text : Maybe String
|
{ text : Maybe String
|
||||||
, group : Maybe String
|
, group : Maybe String
|
||||||
, receiver : Maybe Matcher
|
, receiver : Maybe String
|
||||||
, showSilenced : Maybe Bool
|
, showSilenced : Maybe Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,10 +46,10 @@ generateQueryParam name =
|
||||||
generateQueryString : Filter -> String
|
generateQueryString : Filter -> String
|
||||||
generateQueryString { receiver, showSilenced, text, group } =
|
generateQueryString { receiver, showSilenced, text, group } =
|
||||||
let
|
let
|
||||||
-- TODO: Re-add receiver once it is parsed on the server side.
|
|
||||||
parts =
|
parts =
|
||||||
[ ( "silenced", Maybe.withDefault False showSilenced |> toString |> String.toLower |> Just )
|
[ ( "silenced", Maybe.withDefault False showSilenced |> toString |> String.toLower |> Just )
|
||||||
, ( "filter", emptyToNothing text )
|
, ( "filter", emptyToNothing text )
|
||||||
|
, ( "receiver", emptyToNothing receiver )
|
||||||
, ( "group", group )
|
, ( "group", group )
|
||||||
]
|
]
|
||||||
|> List.filterMap (uncurry generateQueryParam)
|
|> List.filterMap (uncurry generateQueryParam)
|
||||||
|
|
|
@ -7,31 +7,14 @@ import Utils.Filter exposing (Filter, parseMatcher, MatchOperator(RegexMatch))
|
||||||
boolParam : String -> UrlParser.QueryParser (Maybe Bool -> a) a
|
boolParam : String -> UrlParser.QueryParser (Maybe Bool -> a) a
|
||||||
boolParam name =
|
boolParam name =
|
||||||
UrlParser.customParam name
|
UrlParser.customParam name
|
||||||
(\x ->
|
(Maybe.map (String.toLower >> (/=) "false"))
|
||||||
case x of
|
|
||||||
Nothing ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
Just value ->
|
|
||||||
if (String.toLower value) == "false" then
|
|
||||||
Just False
|
|
||||||
else
|
|
||||||
Just True
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
alertsParser : Parser (Filter -> a) a
|
alertsParser : Parser (Filter -> a) a
|
||||||
alertsParser =
|
alertsParser =
|
||||||
map
|
s "alerts"
|
||||||
(\filter group receiver silenced ->
|
<?> stringParam "filter"
|
||||||
let
|
<?> stringParam "group"
|
||||||
parsed =
|
<?> stringParam "receiver"
|
||||||
Maybe.map
|
<?> boolParam "silenced"
|
||||||
(\r ->
|
|> map Filter
|
||||||
{ key = "receiver", op = RegexMatch, value = "^(?:" ++ r ++ ")$" }
|
|
||||||
)
|
|
||||||
receiver
|
|
||||||
in
|
|
||||||
Filter filter group parsed silenced
|
|
||||||
)
|
|
||||||
(s "alerts" <?> stringParam "filter" <?> stringParam "group" <?> stringParam "receiver" <?> boolParam "silenced")
|
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
module Views.AlertList.Types exposing (AlertListMsg(..), Model, Tab(..), initAlertList)
|
module Views.AlertList.Types exposing (AlertListMsg(..), Model, Tab(..), initAlertList)
|
||||||
|
|
||||||
import Utils.Types exposing (ApiData(Initial))
|
|
||||||
import Alerts.Types exposing (Alert)
|
import Alerts.Types exposing (Alert)
|
||||||
|
import Utils.Types exposing (ApiData(Initial))
|
||||||
import Views.FilterBar.Types as FilterBar
|
import Views.FilterBar.Types as FilterBar
|
||||||
import Views.GroupBar.Types as GroupBar
|
import Views.GroupBar.Types as GroupBar
|
||||||
|
|
||||||
|
|
||||||
type AlertListMsg
|
type AlertListMsg
|
||||||
= AlertsFetched (ApiData (List Alert))
|
= AlertsFetched (ApiData (List Alert))
|
||||||
|
| ReceiversFetched (ApiData (List String))
|
||||||
|
| ToggleReceivers Bool
|
||||||
|
| SelectReceiver (Maybe String)
|
||||||
| FetchAlerts
|
| FetchAlerts
|
||||||
| MsgForFilterBar FilterBar.Msg
|
| MsgForFilterBar FilterBar.Msg
|
||||||
| MsgForGroupBar GroupBar.Msg
|
| MsgForGroupBar GroupBar.Msg
|
||||||
|
@ -23,6 +26,8 @@ type Tab
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
{ alerts : ApiData (List Alert)
|
{ alerts : ApiData (List Alert)
|
||||||
|
, receivers : List String
|
||||||
|
, showRecievers : Bool
|
||||||
, groupBar : GroupBar.Model
|
, groupBar : GroupBar.Model
|
||||||
, filterBar : FilterBar.Model
|
, filterBar : FilterBar.Model
|
||||||
, tab : Tab
|
, tab : Tab
|
||||||
|
@ -33,6 +38,8 @@ type alias Model =
|
||||||
initAlertList : Model
|
initAlertList : Model
|
||||||
initAlertList =
|
initAlertList =
|
||||||
{ alerts = Initial
|
{ alerts = Initial
|
||||||
|
, receivers = []
|
||||||
|
, showRecievers = False
|
||||||
, groupBar = GroupBar.initGroupBar
|
, groupBar = GroupBar.initGroupBar
|
||||||
, filterBar = FilterBar.initFilterBar
|
, filterBar = FilterBar.initFilterBar
|
||||||
, tab = FilterTab
|
, tab = FilterTab
|
||||||
|
|
|
@ -7,6 +7,7 @@ import Utils.Filter exposing (Filter, parseFilter)
|
||||||
import Utils.Types exposing (ApiData(Initial, Loading, Success, Failure))
|
import Utils.Types exposing (ApiData(Initial, Loading, Success, Failure))
|
||||||
import Types exposing (Msg(MsgForAlertList, Noop))
|
import Types exposing (Msg(MsgForAlertList, Noop))
|
||||||
import Set
|
import Set
|
||||||
|
import Regex
|
||||||
import Navigation
|
import Navigation
|
||||||
import Utils.Filter exposing (generateQueryString)
|
import Utils.Filter exposing (generateQueryString)
|
||||||
import Views.GroupBar.Updates as GroupBar
|
import Views.GroupBar.Updates as GroupBar
|
||||||
|
@ -47,9 +48,26 @@ update msg ({ groupBar, filterBar } as model) filter apiUrl basePath =
|
||||||
FilterBar.setMatchers filter filterBar
|
FilterBar.setMatchers filter filterBar
|
||||||
in
|
in
|
||||||
( { model | alerts = Loading, filterBar = newFilterBar, groupBar = newGroupBar, activeId = Nothing }
|
( { model | alerts = Loading, filterBar = newFilterBar, groupBar = newGroupBar, activeId = Nothing }
|
||||||
, Api.fetchAlerts apiUrl filter |> Cmd.map (AlertsFetched >> MsgForAlertList)
|
, Cmd.batch
|
||||||
|
[ Api.fetchAlerts apiUrl filter |> Cmd.map (AlertsFetched >> MsgForAlertList)
|
||||||
|
, Api.fetchReceivers apiUrl |> Cmd.map (ReceiversFetched >> MsgForAlertList)
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ReceiversFetched (Success receivers) ->
|
||||||
|
( { model | receivers = receivers }, Cmd.none )
|
||||||
|
|
||||||
|
ReceiversFetched _ ->
|
||||||
|
( model, Cmd.none )
|
||||||
|
|
||||||
|
ToggleReceivers show ->
|
||||||
|
( { model | showRecievers = show }, Cmd.none )
|
||||||
|
|
||||||
|
SelectReceiver receiver ->
|
||||||
|
( { model | showRecievers = False }
|
||||||
|
, Navigation.newUrl (alertsUrl ++ generateQueryString { filter | receiver = Maybe.map Regex.escape receiver })
|
||||||
|
)
|
||||||
|
|
||||||
ToggleSilenced showSilenced ->
|
ToggleSilenced showSilenced ->
|
||||||
( model
|
( model
|
||||||
, Navigation.newUrl (alertsUrl ++ generateQueryString { filter | showSilenced = Just showSilenced })
|
, Navigation.newUrl (alertsUrl ++ generateQueryString { filter | showSilenced = Just showSilenced })
|
||||||
|
|
|
@ -12,15 +12,16 @@ import Utils.Views
|
||||||
import Utils.List
|
import Utils.List
|
||||||
import Views.AlertList.AlertView as AlertView
|
import Views.AlertList.AlertView as AlertView
|
||||||
import Views.GroupBar.Types as GroupBar
|
import Views.GroupBar.Types as GroupBar
|
||||||
import Views.AlertList.Types exposing (AlertListMsg(MsgForFilterBar, MsgForGroupBar, SetTab, ToggleSilenced), Model, Tab(..))
|
import Views.AlertList.Types exposing (AlertListMsg(..), Model, Tab(..))
|
||||||
import Types exposing (Msg(Noop, CreateSilenceFromAlert, MsgForAlertList))
|
import Types exposing (Msg(Noop, CreateSilenceFromAlert, MsgForAlertList))
|
||||||
import Views.GroupBar.Views as GroupBar
|
import Views.GroupBar.Views as GroupBar
|
||||||
import Dict exposing (Dict)
|
import Dict exposing (Dict)
|
||||||
|
import Regex
|
||||||
|
|
||||||
|
|
||||||
renderSilenced : Maybe Bool -> Html Msg
|
renderSilenced : Maybe Bool -> Html Msg
|
||||||
renderSilenced maybeShowSilenced =
|
renderSilenced maybeShowSilenced =
|
||||||
li [ class "nav-item ml-auto " ]
|
li [ class "nav-item" ]
|
||||||
[ label [ class "mt-1 custom-control custom-checkbox" ]
|
[ label [ class "mt-1 custom-control custom-checkbox" ]
|
||||||
[ input
|
[ input
|
||||||
[ type_ "checkbox"
|
[ type_ "checkbox"
|
||||||
|
@ -36,7 +37,7 @@ renderSilenced maybeShowSilenced =
|
||||||
|
|
||||||
|
|
||||||
view : Model -> Filter -> Html Msg
|
view : Model -> Filter -> Html Msg
|
||||||
view { alerts, groupBar, filterBar, tab, activeId } filter =
|
view { alerts, groupBar, filterBar, receivers, showRecievers, tab, activeId } filter =
|
||||||
div []
|
div []
|
||||||
[ div
|
[ div
|
||||||
[ class "card mb-5" ]
|
[ class "card mb-5" ]
|
||||||
|
@ -44,6 +45,7 @@ view { alerts, groupBar, filterBar, tab, activeId } filter =
|
||||||
[ ul [ class "nav nav-tabs card-header-tabs" ]
|
[ ul [ class "nav nav-tabs card-header-tabs" ]
|
||||||
[ Utils.Views.tab FilterTab tab (SetTab >> MsgForAlertList) [ text "Filter" ]
|
[ Utils.Views.tab FilterTab tab (SetTab >> MsgForAlertList) [ text "Filter" ]
|
||||||
, Utils.Views.tab GroupTab tab (SetTab >> MsgForAlertList) [ text "Group" ]
|
, Utils.Views.tab GroupTab tab (SetTab >> MsgForAlertList) [ text "Group" ]
|
||||||
|
, renderReceivers filter.receiver receivers showRecievers
|
||||||
, renderSilenced filter.showSilenced
|
, renderSilenced filter.showSilenced
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -113,3 +115,66 @@ alertList activeId labels filter alerts =
|
||||||
else
|
else
|
||||||
ul [ class "list-group mb-4" ] (List.map (AlertView.view labels activeId) alerts)
|
ul [ class "list-group mb-4" ] (List.map (AlertView.view labels activeId) alerts)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
renderReceivers : Maybe String -> List String -> Bool -> Html Msg
|
||||||
|
renderReceivers receiver receivers opened =
|
||||||
|
let
|
||||||
|
autoCompleteClass =
|
||||||
|
if opened then
|
||||||
|
"show"
|
||||||
|
else
|
||||||
|
""
|
||||||
|
|
||||||
|
navLinkClass =
|
||||||
|
if opened then
|
||||||
|
"active"
|
||||||
|
else
|
||||||
|
""
|
||||||
|
|
||||||
|
-- Try to find the regex-escaped receiver in the list of unescaped receivers:
|
||||||
|
unescapedReceiver =
|
||||||
|
receivers
|
||||||
|
|> List.filter (Regex.escape >> Just >> (==) receiver)
|
||||||
|
|> List.map Just
|
||||||
|
|> List.head
|
||||||
|
|> Maybe.withDefault receiver
|
||||||
|
in
|
||||||
|
li
|
||||||
|
[ class ("nav-item ml-auto autocomplete-menu " ++ autoCompleteClass)
|
||||||
|
, onBlur (ToggleReceivers False |> MsgForAlertList)
|
||||||
|
, tabindex 1
|
||||||
|
, style
|
||||||
|
[ ( "position", "relative" )
|
||||||
|
, ( "outline", "none" )
|
||||||
|
]
|
||||||
|
]
|
||||||
|
[ div
|
||||||
|
[ onClick (ToggleReceivers (not opened) |> MsgForAlertList)
|
||||||
|
, class "mt-1 mr-4"
|
||||||
|
, style [ ( "cursor", "pointer" ) ]
|
||||||
|
]
|
||||||
|
[ text ("Receiver: " ++ Maybe.withDefault "All" unescapedReceiver) ]
|
||||||
|
, receivers
|
||||||
|
|> List.map Just
|
||||||
|
|> (::) Nothing
|
||||||
|
|> List.map (receiverField unescapedReceiver)
|
||||||
|
|> div [ class "dropdown-menu dropdown-menu-right" ]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
receiverField : Maybe String -> Maybe String -> Html Msg
|
||||||
|
receiverField selected maybeReceiver =
|
||||||
|
let
|
||||||
|
attrs =
|
||||||
|
if selected == maybeReceiver then
|
||||||
|
[ class "dropdown-item active" ]
|
||||||
|
else
|
||||||
|
[ class "dropdown-item"
|
||||||
|
, style [ ( "cursor", "pointer" ) ]
|
||||||
|
, onClick (SelectReceiver maybeReceiver |> MsgForAlertList)
|
||||||
|
]
|
||||||
|
in
|
||||||
|
div
|
||||||
|
attrs
|
||||||
|
[ text (Maybe.withDefault "All" maybeReceiver) ]
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue