mirror of
https://github.com/prometheus/alertmanager
synced 2025-01-01 02:52:06 +00:00
Merge pull request #785 from mxinden/silence-state
Silence State from API and UI
This commit is contained in:
commit
e5042e4d3b
@ -574,6 +574,9 @@ func silenceFromProto(s *silencepb.Silence) (*types.Silence, error) {
|
||||
StartsAt: s.StartsAt,
|
||||
EndsAt: s.EndsAt,
|
||||
UpdatedAt: s.UpdatedAt,
|
||||
Status: types.SilenceStatus{
|
||||
State: types.CalcSilenceState(s.StartsAt, s.EndsAt),
|
||||
},
|
||||
}
|
||||
for _, m := range s.Matchers {
|
||||
matcher := &types.Matcher{
|
||||
|
@ -333,6 +333,31 @@ type Silence struct {
|
||||
// timeFunc provides the time against which to evaluate
|
||||
// the silence. Used for test injection.
|
||||
now func() time.Time
|
||||
|
||||
Status SilenceStatus `json:"status"`
|
||||
}
|
||||
|
||||
type SilenceStatus struct {
|
||||
State SilenceState `json:"state"`
|
||||
}
|
||||
|
||||
type SilenceState string
|
||||
|
||||
const (
|
||||
SilenceStateExpired SilenceState = "expired"
|
||||
SilenceStateActive SilenceState = "active"
|
||||
SilenceStatePending SilenceState = "pending"
|
||||
)
|
||||
|
||||
func CalcSilenceState(start, end time.Time) SilenceState {
|
||||
current := time.Now()
|
||||
if current.Before(start) {
|
||||
return SilenceStatePending
|
||||
}
|
||||
if current.Before(end) {
|
||||
return SilenceStateActive
|
||||
}
|
||||
return SilenceStateExpired
|
||||
}
|
||||
|
||||
// Validate returns true iff all fields of the silence have valid values.
|
||||
|
@ -9,18 +9,18 @@
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
|
||||
<script src="https://use.fontawesome.com/b7508bb100.js"></script>
|
||||
<style>
|
||||
.alert-list-item:nth-child(odd) {
|
||||
.list-item:nth-child(odd) {
|
||||
background: #f7f7f9;
|
||||
}
|
||||
.alert-list-item:not(:hover) .btn {
|
||||
.list-item:not(:hover) .btn {
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
color: gray;
|
||||
}
|
||||
.alert-list-item:not(:hover) .badge {
|
||||
.list-item:not(:hover) .badge {
|
||||
background-color: gray;
|
||||
}
|
||||
.alert-list-item:hover {
|
||||
.list-item:hover {
|
||||
background: #f7f7f9;
|
||||
}
|
||||
</style>
|
||||
|
@ -4,7 +4,7 @@ import Http
|
||||
import Silences.Types exposing (Silence)
|
||||
import Utils.Types exposing (ApiResponse(..), ApiData)
|
||||
import Utils.Filter exposing (Filter)
|
||||
import Silences.Decoders exposing (..)
|
||||
import Silences.Decoders exposing (show, list, create, destroy)
|
||||
import Silences.Encoders
|
||||
import Utils.Api exposing (baseUrl)
|
||||
import Utils.Filter exposing (generateQueryString)
|
||||
|
@ -1,19 +1,19 @@
|
||||
module Silences.Decoders exposing (..)
|
||||
module Silences.Decoders exposing (show, list, create, destroy)
|
||||
|
||||
import Json.Decode as Json exposing (field)
|
||||
import Json.Decode as Json exposing (field, succeed, fail)
|
||||
import Utils.Api exposing (iso8601Time, (|:))
|
||||
import Silences.Types exposing (Silence)
|
||||
import Silences.Types exposing (Silence, Status, State(Active, Pending, Expired))
|
||||
import Utils.Types exposing (Matcher, Time, ApiResponse(Success))
|
||||
|
||||
|
||||
show : Json.Decoder Silence
|
||||
show =
|
||||
Json.at [ "data" ] silence
|
||||
Json.at [ "data" ] silenceDecoder
|
||||
|
||||
|
||||
list : Json.Decoder (List Silence)
|
||||
list =
|
||||
Json.at [ "data" ] (Json.list silence)
|
||||
Json.at [ "data" ] (Json.list silenceDecoder)
|
||||
|
||||
|
||||
create : Json.Decoder String
|
||||
@ -21,17 +21,13 @@ create =
|
||||
Json.at [ "data", "silenceId" ] Json.string
|
||||
|
||||
|
||||
|
||||
-- This should just be the ID
|
||||
|
||||
|
||||
destroy : Json.Decoder String
|
||||
destroy =
|
||||
Json.at [ "status" ] Json.string
|
||||
|
||||
|
||||
silence : Json.Decoder Silence
|
||||
silence =
|
||||
silenceDecoder : Json.Decoder Silence
|
||||
silenceDecoder =
|
||||
Json.succeed Silence
|
||||
|: (field "id" Json.string)
|
||||
|: (field "createdBy" Json.string)
|
||||
@ -43,12 +39,38 @@ silence =
|
||||
|: (field "startsAt" iso8601Time)
|
||||
|: (field "endsAt" iso8601Time)
|
||||
|: (field "updatedAt" iso8601Time)
|
||||
|: (field "matchers" (Json.list matcher))
|
||||
|: (field "matchers" (Json.list matcherDecoder))
|
||||
|: (Json.succeed <| Success [])
|
||||
|: (field "status" statusDecoder)
|
||||
|
||||
|
||||
matcher : Json.Decoder Matcher
|
||||
matcher =
|
||||
statusDecoder : Json.Decoder Status
|
||||
statusDecoder =
|
||||
Json.succeed Status
|
||||
|: (field "state" Json.string |> Json.andThen stateDecoder)
|
||||
|
||||
|
||||
stateDecoder : String -> Json.Decoder State
|
||||
stateDecoder state =
|
||||
case state of
|
||||
"active" ->
|
||||
succeed Active
|
||||
|
||||
"pending" ->
|
||||
succeed Pending
|
||||
|
||||
"expired" ->
|
||||
succeed Expired
|
||||
|
||||
_ ->
|
||||
fail <|
|
||||
"Silence.status.state must be one of 'active', 'pending' or 'expired' but was'"
|
||||
++ state
|
||||
++ "'."
|
||||
|
||||
|
||||
matcherDecoder : Json.Decoder Matcher
|
||||
matcherDecoder =
|
||||
Json.map3 Matcher
|
||||
(field "name" Json.string)
|
||||
(field "value" Json.string)
|
||||
|
@ -1,4 +1,15 @@
|
||||
module Silences.Types exposing (Silence, nullSilence, nullMatcher, nullTime, SilenceId)
|
||||
module Silences.Types
|
||||
exposing
|
||||
( Silence
|
||||
, SilenceId
|
||||
, Status
|
||||
, State(Active, Pending, Expired)
|
||||
, nullSilence
|
||||
, nullSilenceStatus
|
||||
, nullMatcher
|
||||
, nullTime
|
||||
, stateToString
|
||||
)
|
||||
|
||||
import Alerts.Types exposing (Alert)
|
||||
import Utils.Types exposing (Matcher, ApiData, ApiResponse(Success))
|
||||
@ -15,6 +26,13 @@ nullSilence =
|
||||
, updatedAt = 0
|
||||
, matchers = [ nullMatcher ]
|
||||
, silencedAlerts = Success []
|
||||
, status = nullSilenceStatus
|
||||
}
|
||||
|
||||
|
||||
nullSilenceStatus : Status
|
||||
nullSilenceStatus =
|
||||
{ state = Expired
|
||||
}
|
||||
|
||||
|
||||
@ -37,8 +55,33 @@ type alias Silence =
|
||||
, updatedAt : Time
|
||||
, matchers : List Matcher
|
||||
, silencedAlerts : ApiData (List Alert)
|
||||
, status : Status
|
||||
}
|
||||
|
||||
|
||||
type alias Status =
|
||||
{ state : State
|
||||
}
|
||||
|
||||
|
||||
type State
|
||||
= Active
|
||||
| Pending
|
||||
| Expired
|
||||
|
||||
|
||||
stateToString : State -> String
|
||||
stateToString state =
|
||||
case state of
|
||||
Active ->
|
||||
"active"
|
||||
|
||||
Pending ->
|
||||
"pending"
|
||||
|
||||
Expired ->
|
||||
"expired"
|
||||
|
||||
|
||||
type alias SilenceId =
|
||||
String
|
||||
|
@ -67,12 +67,12 @@ durationFormat time =
|
||||
|
||||
dateFormat : Time.Time -> String
|
||||
dateFormat =
|
||||
Date.fromTime >> (Date.Extra.Format.format config Date.Extra.Format.isoDateFormat)
|
||||
Date.fromTime >> (Date.Extra.Format.formatUtc config Date.Extra.Format.isoDateFormat)
|
||||
|
||||
|
||||
timeFormat : Time.Time -> String
|
||||
timeFormat =
|
||||
Date.fromTime >> (Date.Extra.Format.format config Date.Extra.Format.isoTimeFormat)
|
||||
Date.fromTime >> (Date.Extra.Format.formatUtc config Date.Extra.Format.isoTimeFormat)
|
||||
|
||||
|
||||
dateTimeFormat : Time.Time -> String
|
||||
|
14
ui/app/src/Utils/String.elm
Normal file
14
ui/app/src/Utils/String.elm
Normal file
@ -0,0 +1,14 @@
|
||||
module Utils.String exposing (capitalizeFirst)
|
||||
|
||||
import String
|
||||
import Char
|
||||
|
||||
|
||||
capitalizeFirst : String -> String
|
||||
capitalizeFirst string =
|
||||
case String.uncons string of
|
||||
Nothing ->
|
||||
string
|
||||
|
||||
Just ( char, rest ) ->
|
||||
String.cons (Char.toUpper char) rest
|
@ -16,7 +16,7 @@ import Time exposing (Time)
|
||||
view : Alert -> Html Msg
|
||||
view alert =
|
||||
li
|
||||
[ class "align-items-center list-group-item alert-list-item p-0 d-inline-flex justify-content-start"
|
||||
[ class "align-items-center list-group-item list-item p-0 d-inline-flex justify-content-start"
|
||||
]
|
||||
[ dateView alert.startsAt
|
||||
, labelButtons alert.labels
|
||||
|
@ -9,4 +9,4 @@ import Views.Shared.AlertCompact
|
||||
view : List Alert -> Html msg
|
||||
view alerts =
|
||||
List.map Views.Shared.AlertCompact.view alerts
|
||||
|> ol [ class "list pa0" ]
|
||||
|> ol [ class "list pa0 w-100" ]
|
||||
|
@ -1,8 +1,9 @@
|
||||
module Views.Shared.SilenceBase exposing (view)
|
||||
|
||||
import Html exposing (Html, div, a, p, text, b)
|
||||
import Html.Attributes exposing (class, href)
|
||||
import Silences.Types exposing (Silence)
|
||||
import Html exposing (Html, div, a, p, text, b, i, span, small, button)
|
||||
import Html.Attributes exposing (class, href, style)
|
||||
import Html.Events exposing (onClick)
|
||||
import Silences.Types exposing (Silence, State(Expired))
|
||||
import Types exposing (Msg(Noop, MsgForSilenceList))
|
||||
import Views.SilenceList.Types exposing (SilenceListMsg(DestroySilence, MsgForFilterBar))
|
||||
import Utils.Date
|
||||
@ -11,36 +12,40 @@ import Utils.Types exposing (Matcher)
|
||||
import Utils.Filter
|
||||
import Utils.List
|
||||
import Views.FilterBar.Types as FilterBarTypes
|
||||
import Time exposing (Time)
|
||||
|
||||
|
||||
view : Silence -> Html Msg
|
||||
view silence =
|
||||
let
|
||||
alertName =
|
||||
silence.matchers
|
||||
|> List.filter (\m -> m.name == "alertname")
|
||||
|> List.head
|
||||
|> Maybe.map .value
|
||||
|> Maybe.withDefault ""
|
||||
|
||||
editUrl =
|
||||
String.join "/" [ "#/silences", silence.id, "edit" ]
|
||||
in
|
||||
div [ class "f6 mb3" ]
|
||||
[ a
|
||||
[ class "db link blue mb3"
|
||||
, href ("#/silences/" ++ silence.id)
|
||||
]
|
||||
[ b [ class "db f4 mb1" ]
|
||||
[ text alertName ]
|
||||
]
|
||||
, div [ class "mb1" ]
|
||||
[ buttonLink "fa fa-pencil" editUrl "blue" Noop
|
||||
, buttonLink "fa fa-trash-o" "#/silences" "dark-red" (MsgForSilenceList (DestroySilence silence))
|
||||
, p [ class "dib mr2" ] [ text <| "Until " ++ Utils.Date.dateTimeFormat silence.endsAt ]
|
||||
]
|
||||
, div [ class "mb2 w-80-l w-100-m" ] (List.map matcherButton silence.matchers)
|
||||
div [ class "d-inline-flex align-items-center justify-content-start w-100" ]
|
||||
[ datesView silence.startsAt silence.endsAt
|
||||
, div [ class "" ] (List.map matcherButton silence.matchers)
|
||||
, div [ class "ml-auto d-inline-flex align-self-stretch p-2", style [ ( "border-left", "1px solid #ccc" ) ] ]
|
||||
[ editButton silence.id
|
||||
, deleteButton silence
|
||||
, detailsButton silence.id
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
datesView : Time -> Time -> Html Msg
|
||||
datesView start end =
|
||||
i [ class "d-inline-flex align-items-center", style [ ( "border-right", "1px solid #ccc" ) ] ]
|
||||
[ dateView start
|
||||
, i [ class "text-muted" ] [ text "-" ]
|
||||
, dateView end
|
||||
]
|
||||
|
||||
|
||||
dateView : Time -> Html Msg
|
||||
dateView time =
|
||||
i
|
||||
[ class "h-100 p-2 d-flex flex-column justify-content-center, text-muted"
|
||||
, style [ ( "font-family", "monospace" ) ]
|
||||
]
|
||||
[ span [] [ text <| Utils.Date.timeFormat time ]
|
||||
, small [] [ text <| Utils.Date.dateFormat time ]
|
||||
]
|
||||
|
||||
|
||||
matcherButton : Matcher -> Html Msg
|
||||
@ -63,3 +68,34 @@ matcherButton matcher =
|
||||
)
|
||||
in
|
||||
Utils.Views.labelButton (Just msg) (Utils.List.mstring matcher)
|
||||
|
||||
|
||||
editButton : String -> Html Msg
|
||||
editButton silenceId =
|
||||
let
|
||||
editUrl =
|
||||
String.join "/" [ "#/silences", silenceId, "edit" ]
|
||||
in
|
||||
a [ class "h-100 btn btn-success rounded-0", href editUrl ]
|
||||
[ span [ class "fa fa-pencil" ] [] ]
|
||||
|
||||
|
||||
deleteButton : Silence -> Html Msg
|
||||
deleteButton silence =
|
||||
if silence.status.state == Expired then
|
||||
text ""
|
||||
else
|
||||
a
|
||||
[ class "h-100 btn btn-danger rounded-0"
|
||||
, onClick (MsgForSilenceList (DestroySilence silence))
|
||||
, href "#/silences"
|
||||
]
|
||||
[ span [ class "fa fa-trash" ] []
|
||||
]
|
||||
|
||||
|
||||
detailsButton : String -> Html Msg
|
||||
detailsButton silenceId =
|
||||
a [ class "h-100 btn btn-primary rounded-0", href ("#/silences/" ++ silenceId) ]
|
||||
[ span [ class "fa fa-info" ] []
|
||||
]
|
||||
|
@ -2,6 +2,7 @@ module Views.Shared.SilencePreview exposing (view)
|
||||
|
||||
import Silences.Types exposing (Silence)
|
||||
import Html exposing (Html, div, text)
|
||||
import Html.Attributes exposing (class)
|
||||
import Utils.Types exposing (ApiResponse(Success, Loading, Failure))
|
||||
import Views.Shared.AlertListCompact
|
||||
import Utils.Views exposing (error, loading)
|
||||
@ -14,7 +15,7 @@ view s =
|
||||
if List.isEmpty alerts then
|
||||
div [] [ text "No matches" ]
|
||||
else
|
||||
div [] [ Views.Shared.AlertListCompact.view alerts ]
|
||||
div [ class "w-100" ] [ Views.Shared.AlertListCompact.view alerts ]
|
||||
|
||||
Loading ->
|
||||
loading
|
||||
|
@ -1,21 +1,21 @@
|
||||
module Views.Silence.Views exposing (view)
|
||||
|
||||
import Silences.Types exposing (Silence)
|
||||
import Html exposing (Html, div, h2, p, text, label)
|
||||
import Silences.Types exposing (Silence, stateToString)
|
||||
import Html exposing (Html, div, h2, p, text, label, b, h1)
|
||||
import Html.Attributes exposing (class)
|
||||
import Time exposing (Time)
|
||||
import Types exposing (Model, Msg)
|
||||
import Utils.Types exposing (ApiResponse(Success, Loading, Failure))
|
||||
import Utils.Views exposing (loading, error)
|
||||
import Views.Shared.SilencePreview
|
||||
import Views.Shared.SilenceBase
|
||||
import Utils.Date exposing (dateTimeFormat)
|
||||
import Utils.List
|
||||
|
||||
|
||||
view : Model -> Html Msg
|
||||
view model =
|
||||
case model.silence of
|
||||
Success sil ->
|
||||
silence sil model.currentTime
|
||||
silence2 sil
|
||||
|
||||
Loading ->
|
||||
loading
|
||||
@ -24,41 +24,29 @@ view model =
|
||||
error msg
|
||||
|
||||
|
||||
silence : Silence -> Time -> Html Msg
|
||||
silence silence currentTime =
|
||||
silence2 : Silence -> Html Msg
|
||||
silence2 silence =
|
||||
div []
|
||||
[ Views.Shared.SilenceBase.view silence
|
||||
, silenceExtra silence currentTime
|
||||
, h2 [ class "h6 dark-red" ] [ text "Affected alerts" ]
|
||||
, Views.Shared.SilencePreview.view silence
|
||||
[ h1 [] [ text "Silence" ]
|
||||
, formGroup "ID" <| text silence.id
|
||||
, formGroup "Starts at" <| text <| dateTimeFormat silence.startsAt
|
||||
, formGroup "Ends at" <| text <| dateTimeFormat silence.endsAt
|
||||
, formGroup "Updated at" <| text <| dateTimeFormat silence.updatedAt
|
||||
, formGroup "Created by" <| text silence.createdBy
|
||||
, formGroup "Comment" <| text silence.comment
|
||||
, formGroup "State" <| text <| stateToString silence.status.state
|
||||
, formGroup "Matchers" <|
|
||||
div [] <|
|
||||
List.map (Utils.List.mstring >> Utils.Views.labelButton Nothing) silence.matchers
|
||||
, formGroup "Affected alerts" <| Views.Shared.SilencePreview.view silence
|
||||
]
|
||||
|
||||
|
||||
silenceExtra : Silence -> Time -> Html msg
|
||||
silenceExtra silence currentTime =
|
||||
div [ class "f6" ]
|
||||
[ div [ class "mb1" ]
|
||||
[ p []
|
||||
[ text "Status: "
|
||||
, Utils.Views.button "ph3 pv2" (status silence currentTime)
|
||||
]
|
||||
, div []
|
||||
[ label [ class "f6 dib mb2 mr2 w-40" ] [ text "Created by" ]
|
||||
, p [] [ text silence.createdBy ]
|
||||
]
|
||||
, div []
|
||||
[ label [ class "f6 dib mb2 mr2 w-40" ] [ text "Comment" ]
|
||||
, p [] [ text silence.comment ]
|
||||
]
|
||||
formGroup : String -> Html Msg -> Html Msg
|
||||
formGroup key content =
|
||||
div [ class "form-group row" ]
|
||||
[ label [ class "col-2 col-form-label" ] [ b [] [ text key ] ]
|
||||
, div [ class "col-10 d-flex align-items-center" ]
|
||||
[ content
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
status : Silence -> Time -> String
|
||||
status { endsAt, startsAt } currentTime =
|
||||
if endsAt <= currentTime then
|
||||
"expired"
|
||||
else if startsAt > currentTime then
|
||||
"pending"
|
||||
else
|
||||
"active"
|
||||
|
@ -10,7 +10,7 @@ module Views.SilenceForm.Types
|
||||
, initSilenceForm
|
||||
)
|
||||
|
||||
import Silences.Types exposing (Silence, SilenceId)
|
||||
import Silences.Types exposing (Silence, SilenceId, nullSilenceStatus)
|
||||
import Alerts.Types exposing (Alert)
|
||||
import Utils.Types exposing (Matcher, ApiData, Duration, ApiResponse(..))
|
||||
import Time exposing (Time)
|
||||
@ -42,6 +42,9 @@ toSilence { createdBy, comment, startsAt, endsAt, matchers } =
|
||||
|
||||
{- ignored -}
|
||||
, id = ""
|
||||
|
||||
{- ignored -}
|
||||
, status = nullSilenceStatus
|
||||
}
|
||||
)
|
||||
(timeFromString startsAt)
|
||||
|
@ -7,7 +7,7 @@ import Views.FilterBar.Types as FilterBar
|
||||
|
||||
|
||||
type SilenceListMsg
|
||||
= SilenceDestroy (ApiData String)
|
||||
= SilenceDestroyed (ApiData String)
|
||||
| DestroySilence Silence
|
||||
| SilencesFetch (ApiData (List Silence))
|
||||
| FetchSilences
|
||||
|
@ -28,11 +28,10 @@ update msg model silence filter =
|
||||
)
|
||||
|
||||
DestroySilence silence ->
|
||||
( model, Loading, Api.destroy silence (SilenceDestroy >> MsgForSilenceList) )
|
||||
( model, Loading, Api.destroy silence (SilenceDestroyed >> MsgForSilenceList) )
|
||||
|
||||
SilenceDestroy silence ->
|
||||
SilenceDestroyed statusCode ->
|
||||
-- TODO: "Deleted id: ID" growl
|
||||
-- TODO: Add DELETE to accepted CORS methods in alertmanager
|
||||
-- TODO: Check why POST isn't there but is accepted
|
||||
( model, Loading, Navigation.newUrl "/#/silences" )
|
||||
|
||||
|
@ -1,62 +1,80 @@
|
||||
module Views.SilenceList.Views exposing (..)
|
||||
|
||||
-- External Imports
|
||||
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick, onInput)
|
||||
import Views.SilenceList.Types exposing (SilenceListMsg(..), Model)
|
||||
import Views.Shared.SilenceBase
|
||||
import Silences.Types exposing (Silence)
|
||||
import Silences.Types exposing (Silence, State(..), stateToString)
|
||||
import Utils.Types exposing (Matcher, ApiResponse(..), ApiData)
|
||||
import Utils.Filter exposing (Filter)
|
||||
import Utils.Views exposing (iconButtonMsg, checkbox, textField, formInput, formField, buttonLink, error, loading)
|
||||
import Time
|
||||
import Types exposing (Msg(UpdateFilter, MsgForSilenceList, Noop))
|
||||
import Views.FilterBar.Views as FilterBar
|
||||
import Views.FilterBar.Types as FilterBarTypes
|
||||
import Utils.String as StringUtils
|
||||
|
||||
|
||||
view : Model -> Time.Time -> Html Msg
|
||||
view model currentTime =
|
||||
case model.silences of
|
||||
Success sils ->
|
||||
-- Add buttons at the top to filter Active/Pending/Expired
|
||||
silences sils model.filterBar (text "")
|
||||
div []
|
||||
[ Html.map (MsgForFilterBar >> MsgForSilenceList) (FilterBar.view model.filterBar)
|
||||
, a [ class "mb-4 btn btn-primary", href "#/silences/new" ] [ text "New Silence" ]
|
||||
, silenceListView sils
|
||||
]
|
||||
|
||||
Loading ->
|
||||
loading
|
||||
|
||||
Failure msg ->
|
||||
silences [] model.filterBar (error msg)
|
||||
error msg
|
||||
|
||||
|
||||
silences : List Silence -> FilterBarTypes.Model -> Html Msg -> Html Msg
|
||||
silences silences filterBar errorHtml =
|
||||
silenceListView : List Silence -> Html Msg
|
||||
silenceListView silences =
|
||||
div [] <|
|
||||
List.map silenceGroupView <|
|
||||
groupSilencesByState silences
|
||||
|
||||
|
||||
silenceGroupView : ( State, List Silence ) -> Html Msg
|
||||
silenceGroupView ( state, silences ) =
|
||||
let
|
||||
html =
|
||||
silencesView =
|
||||
if List.isEmpty silences then
|
||||
div [ class "mt2" ] [ text "no silences found" ]
|
||||
div [] [ text "No silences found" ]
|
||||
else
|
||||
ul
|
||||
[ classList
|
||||
[ ( "list", True )
|
||||
, ( "pa0", True )
|
||||
]
|
||||
]
|
||||
(List.map silenceList silences)
|
||||
ul [ class "list-group" ]
|
||||
(List.map silenceView silences)
|
||||
in
|
||||
div []
|
||||
[ Html.map (MsgForFilterBar >> MsgForSilenceList) (FilterBar.view filterBar)
|
||||
, a [ class "f6 link br2 ba ph3 pv2 mr2 dib blue", href "#/silences/new" ] [ text "New Silence" ]
|
||||
, errorHtml
|
||||
, html
|
||||
div [ class "mb-4" ]
|
||||
[ h3 [] [ text <| StringUtils.capitalizeFirst <| stateToString state ]
|
||||
, silencesView
|
||||
]
|
||||
|
||||
|
||||
silenceList : Silence -> Html Msg
|
||||
silenceList silence =
|
||||
silenceView : Silence -> Html Msg
|
||||
silenceView silence =
|
||||
li
|
||||
[ class "pa3 pa4-ns bb b--black-10" ]
|
||||
[ class "list-group-item p-0 list-item" ]
|
||||
[ Views.Shared.SilenceBase.view silence
|
||||
]
|
||||
|
||||
|
||||
groupSilencesByState : List Silence -> List ( State, List Silence )
|
||||
groupSilencesByState silences =
|
||||
List.map (\state -> ( state, filterSilencesByState state silences )) states
|
||||
|
||||
|
||||
states : List State
|
||||
states =
|
||||
[ Active, Pending, Expired ]
|
||||
|
||||
|
||||
|
||||
-- TODO: Replace this with Utils.List.groupBy
|
||||
|
||||
|
||||
filterSilencesByState : State -> List Silence -> List Silence
|
||||
filterSilencesByState state =
|
||||
List.filter (\{ status } -> status.state == state)
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user