Allow first day of week to be Sunday or Monday (#3093)

* Allow week to start at Monday or Sunday

Signed-off-by: Martin Schimandl <martin.schimandl@gmail.com>

* refactor from Int to DataType

Signed-off-by: Martin Schimandl <martin.schimandl@gmail.com>

* refactor to use name 'firstDayOfWeek' everywhere

Signed-off-by: Martin Schimandl <martin.schimandl@gmail.com>

* CSS fine tuning

Signed-off-by: Martin Schimandl <martin.schimandl@gmail.com>

* Check firstDayOfWeek with case statements

Signed-off-by: Martin Schimandl <martin.schimandl@gmail.com>

* Change default to Sunday. Use radio buttons

Signed-off-by: Martin Schimandl <martin.schimandl@gmail.com>

* Update ui/app/src/Views/Settings/Views.elm

Co-authored-by: Andrey Kuzmin <unsoundscapes@gmail.com>
Signed-off-by: Martin Schimandl <martin.schimandl@gmail.com>

* Regenerate assets_vfsdata.go

Signed-off-by: Martin Schimandl <martin.schimandl@gmail.com>

Signed-off-by: Martin Schimandl <martin.schimandl@gmail.com>
Co-authored-by: Andrey Kuzmin <unsoundscapes@gmail.com>
This commit is contained in:
Martin Schimandl 2022-10-19 17:01:24 +01:00 committed by GitHub
parent d4d36e400a
commit 893ad67978
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 258 additions and 45 deletions

File diff suppressed because one or more lines are too long

View File

@ -20,6 +20,7 @@
var app = Elm.Main.init({
flags: {
production: true,
firstDayOfWeek: JSON.parse(localStorage.getItem('firstDayOfWeek')),
defaultCreator: localStorage.getItem('defaultCreator'),
groupExpandAll: JSON.parse(localStorage.getItem('groupExpandAll'))
}
@ -30,6 +31,9 @@
app.ports.persistGroupExpandAll.subscribe(function(expanded) {
localStorage.setItem('groupExpandAll', JSON.stringify(expanded));
});
app.ports.persistFirstDayOfWeek.subscribe(function(firstDayOfWeek) {
localStorage.setItem('firstDayOfWeek', JSON.stringify(firstDayOfWeek));
});
</script>
</body>
</html>

View File

@ -8,6 +8,7 @@ import Types exposing (Model, Msg(..), Route(..))
import Updates exposing (update)
import Url exposing (Url)
import Utils.Api as Api
import Utils.DateTimePicker.Utils exposing (FirstDayOfWeek(..))
import Utils.Filter exposing (nullFilter)
import Utils.Types exposing (ApiData(..))
import Views
@ -86,12 +87,25 @@ init flags url key =
else
"/"
firstDayOfWeek =
flags
|> Json.decodeValue (Json.field "firstDayOfWeek" Json.string)
|> Result.withDefault "Sunday"
|> (\d ->
case d of
"Sunday" ->
Sunday
_ ->
Monday
)
in
update (urlUpdate url)
(Model
(initSilenceList key)
(initSilenceView key)
(initSilenceForm key)
(initSilenceForm key firstDayOfWeek)
(initAlertList key groupExpandAll)
route
filter
@ -105,6 +119,8 @@ init flags url key =
defaultCreator
groupExpandAll
key
{ firstDayOfWeek = firstDayOfWeek
}
)
@ -133,6 +149,9 @@ urlUpdate url =
StatusRoute ->
NavigateToStatus
SettingsRoute ->
NavigateToSettings
TopLevelRoute ->
RedirectAlerts

View File

@ -5,6 +5,7 @@ import Types exposing (Route(..))
import Url exposing (Url)
import Url.Parser exposing (Parser, map, oneOf, parse, top)
import Views.AlertList.Parsing exposing (alertsParser)
import Views.Settings.Parsing exposing (settingsViewParser)
import Views.SilenceForm.Parsing exposing (silenceFormEditParser, silenceFormNewParser)
import Views.SilenceList.Parsing exposing (silenceListParser)
import Views.SilenceView.Parsing exposing (silenceViewParser)
@ -46,6 +47,7 @@ routeParser =
oneOf
[ map SilenceListRoute silenceListParser
, map StatusRoute statusParser
, map SettingsRoute settingsViewParser
, map SilenceFormNewRoute silenceFormNewParser
, map SilenceViewRoute silenceViewParser
, map SilenceFormEditRoute silenceFormEditParser

View File

@ -4,6 +4,7 @@ import Browser.Navigation exposing (Key)
import Utils.Filter exposing (Filter, SilenceFormGetParams)
import Utils.Types exposing (ApiData)
import Views.AlertList.Types as AlertList exposing (AlertListMsg)
import Views.Settings.Types as SettingsView exposing (SettingsMsg)
import Views.SilenceForm.Types as SilenceForm exposing (SilenceFormMsg)
import Views.SilenceList.Types as SilenceList exposing (SilenceListMsg)
import Views.SilenceView.Types as SilenceView exposing (SilenceViewMsg)
@ -27,6 +28,7 @@ type alias Model =
, defaultCreator : String
, expandAll : Bool
, key : Key
, settings : SettingsView.Model
}
@ -36,6 +38,7 @@ type Msg
| MsgForSilenceForm SilenceFormMsg
| MsgForSilenceList SilenceListMsg
| MsgForStatus StatusMsg
| MsgForSettings SettingsMsg
| NavigateToAlerts Filter
| NavigateToNotFound
| NavigateToSilenceView String
@ -43,6 +46,7 @@ type Msg
| NavigateToSilenceFormNew SilenceFormGetParams
| NavigateToSilenceList Filter
| NavigateToStatus
| NavigateToSettings
| NavigateToInternalUrl String
| NavigateToExternalUrl String
| RedirectAlerts
@ -62,3 +66,4 @@ type Route
| SilenceViewRoute String
| StatusRoute
| TopLevelRoute
| SettingsRoute

View File

@ -5,6 +5,7 @@ import Task
import Types exposing (Model, Msg(..), Route(..))
import Views.AlertList.Types exposing (AlertListMsg(..))
import Views.AlertList.Updates
import Views.Settings.Updates
import Views.SilenceForm.Types exposing (SilenceFormMsg(..))
import Views.SilenceForm.Updates
import Views.SilenceList.Types exposing (SilenceListMsg(..))
@ -66,6 +67,9 @@ update msg ({ basePath, apiUrl } as model) =
RedirectAlerts ->
( model, Navigation.pushUrl model.key (basePath ++ "#/alerts") )
NavigateToSettings ->
( { model | route = SettingsRoute }, Cmd.none )
MsgForStatus subMsg ->
Views.Status.Updates.update subMsg model
@ -83,6 +87,13 @@ update msg ({ basePath, apiUrl } as model) =
in
( { model | silenceList = silenceList }, Cmd.map MsgForSilenceList cmd )
MsgForSettings subMsg ->
let
( settingsView, cmd ) =
Views.Settings.Updates.update subMsg model.settings
in
( { model | settings = settingsView }, cmd )
MsgForSilenceView subMsg ->
let
( silenceView, cmd ) =

View File

@ -8,7 +8,7 @@ module Utils.DateTimePicker.Types exposing
)
import Time exposing (Posix)
import Utils.DateTimePicker.Utils exposing (floorMinute)
import Utils.DateTimePicker.Utils exposing (FirstDayOfWeek, floorMinute)
type alias DateTimePicker =
@ -18,6 +18,7 @@ type alias DateTimePicker =
, endDate : Maybe Posix
, startTime : Maybe Posix
, endTime : Maybe Posix
, firstDayOfWeek : FirstDayOfWeek
}
@ -41,19 +42,20 @@ type InputHourOrMinute
| InputMinute
initDateTimePicker : DateTimePicker
initDateTimePicker =
initDateTimePicker : FirstDayOfWeek -> DateTimePicker
initDateTimePicker firstDayOfWeek =
{ month = Nothing
, mouseOverDay = Nothing
, startDate = Nothing
, endDate = Nothing
, startTime = Nothing
, endTime = Nothing
, firstDayOfWeek = firstDayOfWeek
}
initFromStartAndEndTime : Maybe Posix -> Maybe Posix -> DateTimePicker
initFromStartAndEndTime start end =
initFromStartAndEndTime : Maybe Posix -> Maybe Posix -> FirstDayOfWeek -> DateTimePicker
initFromStartAndEndTime start end firstDayOfWeek =
let
startTime =
Maybe.map (\s -> floorMinute s) start
@ -67,4 +69,5 @@ initFromStartAndEndTime start end =
, endDate = end
, startTime = startTime
, endTime = endTime
, firstDayOfWeek = firstDayOfWeek
}

View File

@ -1,5 +1,6 @@
module Utils.DateTimePicker.Utils exposing
( addHour
( FirstDayOfWeek(..)
, addHour
, addMinute
, firstDayOfNextMonth
, firstDayOfPrevMonth
@ -21,8 +22,13 @@ import Time exposing (Month(..), Posix, Weekday(..), utc)
import Time.Extra as Time exposing (Interval(..))
listDaysOfMonth : Posix -> List Posix
listDaysOfMonth time =
type FirstDayOfWeek
= Monday
| Sunday
listDaysOfMonth : Posix -> FirstDayOfWeek -> List Posix
listDaysOfMonth time firstDayOfWeek =
let
firstOfMonth =
Time.floor Time.Month utc time
@ -33,17 +39,38 @@ listDaysOfMonth time =
padFront =
weekToInt (Time.toWeekday utc firstOfMonth)
|> (\wd ->
if wd == 7 then
0
case firstDayOfWeek of
Sunday ->
if wd == 7 then
0
else
wd
else
wd
Monday ->
if wd == 1 then
0
else
wd - 1
)
|> (\w -> Time.add Time.Day -w utc firstOfMonth)
|> (\d -> Time.range Time.Day 1 utc d firstOfMonth)
padBack =
weekToInt (Time.toWeekday utc firstOfNextMonth)
|> (\wd ->
case firstDayOfWeek of
Sunday ->
wd
Monday ->
if wd == 1 then
7
else
wd - 1
)
|> (\w -> Time.add Time.Day (7 - w) utc firstOfNextMonth)
|> Time.range Time.Day 1 utc firstOfNextMonth
in

View File

@ -9,7 +9,8 @@ import Time exposing (Posix, utc)
import Utils.DateTimePicker.Types exposing (DateTimePicker, InputHourOrMinute(..), Msg(..), StartOrEnd(..))
import Utils.DateTimePicker.Utils
exposing
( floorDate
( FirstDayOfWeek(..)
, floorDate
, floorMonth
, listDaysOfMonth
, monthToString
@ -80,14 +81,20 @@ viewMonth : DateTimePicker -> Posix -> Html Msg
viewMonth dateTimePicker justViewTime =
let
days =
listDaysOfMonth justViewTime
listDaysOfMonth justViewTime dateTimePicker.firstDayOfWeek
weeks =
splitWeek days []
in
div [ class "row justify-content-center" ]
[ div [ class "weekheader" ]
(List.map viewWeekHeader [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ])
(case dateTimePicker.firstDayOfWeek of
Sunday ->
List.map viewWeekHeader [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ]
Monday ->
List.map viewWeekHeader [ "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" ]
)
, div
[ class "date-container"
, onMouseOut ClearMouseOverDay

View File

@ -11,6 +11,7 @@ import Utils.Views
import Views.AlertList.Views as AlertList
import Views.NavBar.Views exposing (navBar)
import Views.NotFound.Views as NotFound
import Views.Settings.Views as SettingsView
import Views.SilenceForm.Views as SilenceForm
import Views.SilenceList.Views as SilenceList
import Views.SilenceView.Views as SilenceView
@ -74,6 +75,9 @@ cssNode url msg =
currentView : Model -> Html Msg
currentView model =
case model.route of
SettingsRoute ->
SettingsView.view model.settings |> Html.map MsgForSettings
StatusRoute ->
Status.view model.status

View File

@ -1,4 +1,4 @@
module Views.NavBar.Types exposing (Tab, alertsTab, noneTab, silencesTab, statusTab, tabs)
module Views.NavBar.Types exposing (Tab, alertsTab, noneTab, settingsTab, silencesTab, statusTab, tabs)
type alias Tab =
@ -22,6 +22,11 @@ statusTab =
{ link = "#/status", name = "Status" }
settingsTab : Tab
settingsTab =
{ link = "#/settings", name = "Settings" }
helpTab : Tab
helpTab =
{ link = "https://prometheus.io/docs/alerting/alertmanager/", name = "Help" }
@ -34,4 +39,4 @@ noneTab =
tabs : List Tab
tabs =
[ alertsTab, silencesTab, statusTab, helpTab ]
[ alertsTab, silencesTab, statusTab, settingsTab, helpTab ]

View File

@ -3,7 +3,7 @@ module Views.NavBar.Views exposing (navBar)
import Html exposing (Html, a, div, header, li, nav, text, ul)
import Html.Attributes exposing (class, href, style, title)
import Types exposing (Route(..))
import Views.NavBar.Types exposing (Tab, alertsTab, noneTab, silencesTab, statusTab, tabs)
import Views.NavBar.Types exposing (Tab, alertsTab, noneTab, settingsTab, silencesTab, statusTab, tabs)
navBar : Route -> Html msg
@ -82,3 +82,6 @@ routeToTab currentRoute =
TopLevelRoute ->
noneTab
SettingsRoute ->
settingsTab

View File

@ -0,0 +1,8 @@
module Views.Settings.Parsing exposing (settingsViewParser)
import Url.Parser exposing (Parser, s)
settingsViewParser : Parser a a
settingsViewParser =
s "settings"

View File

@ -0,0 +1,12 @@
module Views.Settings.Types exposing (..)
import Utils.DateTimePicker.Utils exposing (FirstDayOfWeek)
type alias Model =
{ firstDayOfWeek : FirstDayOfWeek
}
type SettingsMsg
= UpdateFirstDayOfWeek String

View File

@ -0,0 +1,49 @@
port module Views.Settings.Updates exposing (..)
import Task
import Types exposing (Msg(..))
import Utils.DateTimePicker.Utils exposing (FirstDayOfWeek(..))
import Views.Settings.Types exposing (..)
import Views.SilenceForm.Types
update : SettingsMsg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Views.Settings.Types.UpdateFirstDayOfWeek firstDayOfWeekString ->
let
firstDayOfWeek =
case firstDayOfWeekString of
"Monday" ->
Monday
"Sunday" ->
Sunday
_ ->
Monday
firstDayOfWeekString2 =
case firstDayOfWeek of
Monday ->
"Monday"
Sunday ->
"Sunday"
in
( { model | firstDayOfWeek = firstDayOfWeek }
, Cmd.batch
[ Task.perform identity
(Task.succeed
(MsgForSilenceForm
(Views.SilenceForm.Types.UpdateFirstDayOfWeek
firstDayOfWeek
)
)
)
, persistFirstDayOfWeek firstDayOfWeekString2
]
)
port persistFirstDayOfWeek : String -> Cmd msg

View File

@ -0,0 +1,41 @@
module Views.Settings.Views exposing (view)
import Html exposing (..)
import Html.Attributes exposing (checked, class, for, id, type_, value)
import Html.Events exposing (..)
import Utils.DateTimePicker.Utils exposing (FirstDayOfWeek(..))
import Views.Settings.Types exposing (Model, SettingsMsg(..))
view : Model -> Html SettingsMsg
view model =
div []
[ div [ class "no-gutters" ]
[ label
[ for "fieldset" ]
[ text "First day of the week:" ]
, fieldset [ id "fieldset" ]
[ radio "Monday" (model.firstDayOfWeek == Monday) UpdateFirstDayOfWeek
, radio "Sunday" (model.firstDayOfWeek == Sunday) UpdateFirstDayOfWeek
]
, small [ class "form-text text-muted" ]
[ text "Note: This setting is saved in local storage of your browser"
]
]
]
radio : String -> Bool -> (String -> msg) -> Html msg
radio radioValue isChecked msg =
label [ class "mt-1 ml-1 custom-control custom-radio" ]
[ input
[ type_ "checkbox"
, class "custom-control-input"
, checked isChecked
, value radioValue
, onInput msg
]
[]
, span [ class "custom-control-indicator" ] []
, span [ class "custom-control-description" ] [ text radioValue ]
]

View File

@ -23,6 +23,7 @@ import Silences.Types exposing (nullSilence)
import Time exposing (Posix)
import Utils.Date exposing (addDuration, durationFormat, parseDuration, timeDifference, timeFromString, timeToString)
import Utils.DateTimePicker.Types exposing (DateTimePicker, initDateTimePicker, initFromStartAndEndTime)
import Utils.DateTimePicker.Utils exposing (FirstDayOfWeek)
import Utils.Filter
import Utils.FormValidation
exposing
@ -44,6 +45,7 @@ type alias Model =
, alerts : ApiData (List GettableAlert)
, activeAlertId : Maybe String
, key : Key
, firstDayOfWeek : FirstDayOfWeek
}
@ -72,6 +74,7 @@ type SilenceFormMsg
| SilenceCreate (ApiData String)
| UpdateDateTimePicker Utils.DateTimePicker.Types.Msg
| MsgForFilterBar FilterBar.Msg
| UpdateFirstDayOfWeek FirstDayOfWeek
type SilenceFormFieldMsg
@ -88,15 +91,16 @@ type SilenceFormFieldMsg
| CloseDateTimePicker
initSilenceForm : Key -> Model
initSilenceForm key =
{ form = empty
initSilenceForm : Key -> FirstDayOfWeek -> Model
initSilenceForm key firstDayOfWeek =
{ form = empty firstDayOfWeek
, filterBar = FilterBar.initFilterBar []
, filterBarValid = Utils.FormValidation.Initial
, silenceId = Utils.Types.Initial
, alerts = Utils.Types.Initial
, activeAlertId = Nothing
, key = key
, firstDayOfWeek = firstDayOfWeek
}
@ -135,8 +139,8 @@ validMatchers { matchers, matcherText } =
Ok (List.map Utils.Filter.toApiMatcher nonEmptyMatchers)
fromSilence : GettableSilence -> SilenceForm
fromSilence { id, createdBy, comment, startsAt, endsAt } =
fromSilence : GettableSilence -> FirstDayOfWeek -> SilenceForm
fromSilence { id, createdBy, comment, startsAt, endsAt } firstDayOfWeek =
let
startsPosix =
Utils.Date.timeFromString (DateTime.toString startsAt)
@ -152,7 +156,7 @@ fromSilence { id, createdBy, comment, startsAt, endsAt } =
, startsAt = initialField (timeToString startsAt)
, endsAt = initialField (timeToString endsAt)
, duration = initialField (durationFormat (timeDifference startsAt endsAt) |> Maybe.withDefault "")
, dateTimePicker = initFromStartAndEndTime startsPosix endsPosix
, dateTimePicker = initFromStartAndEndTime startsPosix endsPosix firstDayOfWeek
, viewDateTimePicker = False
}
@ -194,15 +198,15 @@ parseEndsAt startsAt endsAt =
endsResult
empty : SilenceForm
empty =
empty : FirstDayOfWeek -> SilenceForm
empty firstDayOfWeek =
{ id = Nothing
, createdBy = initialField ""
, comment = initialField ""
, startsAt = initialField ""
, endsAt = initialField ""
, duration = initialField ""
, dateTimePicker = initDateTimePicker
, dateTimePicker = initDateTimePicker firstDayOfWeek
, viewDateTimePicker = False
}
@ -213,16 +217,16 @@ defaultDuration =
2 * 60 * 60 * 1000
fromMatchersAndCommentAndTime : String -> String -> Posix -> SilenceForm
fromMatchersAndCommentAndTime defaultCreator comment now =
{ empty
| startsAt = initialField (timeToString now)
, endsAt = initialField (timeToString (addDuration defaultDuration now))
, duration = initialField (durationFormat defaultDuration |> Maybe.withDefault "")
, createdBy = initialField defaultCreator
, comment = initialField comment
, dateTimePicker = initFromStartAndEndTime (Just now) (Just (addDuration defaultDuration now))
, viewDateTimePicker = False
fromMatchersAndCommentAndTime : String -> String -> Posix -> FirstDayOfWeek -> SilenceForm
fromMatchersAndCommentAndTime defaultCreator comment now firstDayOfWeek =
{ id = Nothing
, startsAt = initialField (timeToString now)
, endsAt = initialField (timeToString (addDuration defaultDuration now))
, duration = initialField (durationFormat defaultDuration |> Maybe.withDefault "")
, createdBy = initialField defaultCreator
, comment = initialField comment
, dateTimePicker = initFromStartAndEndTime (Just now) (Just (addDuration defaultDuration now)) firstDayOfWeek
, viewDateTimePicker = False
}

View File

@ -161,7 +161,7 @@ updateForm msg form =
in
{ form
| viewDateTimePicker = True
, dateTimePicker = initFromStartAndEndTime startsAtTime endsAtTime
, dateTimePicker = initFromStartAndEndTime startsAtTime endsAtTime form.dateTimePicker.firstDayOfWeek
}
CloseDateTimePicker ->
@ -209,13 +209,14 @@ update msg model basePath apiUrl =
( model, Task.perform (NewSilenceFromMatchersAndCommentAndTime defaultCreator params.matchers params.comment >> MsgForSilenceForm) Time.now )
NewSilenceFromMatchersAndCommentAndTime defaultCreator matchers comment time ->
( { form = fromMatchersAndCommentAndTime defaultCreator comment time
( { form = fromMatchersAndCommentAndTime defaultCreator comment time model.firstDayOfWeek
, alerts = Initial
, activeAlertId = Nothing
, silenceId = Initial
, filterBar = FilterBar.initFilterBar matchers
, filterBarValid = Utils.FormValidation.Initial
, key = model.key
, firstDayOfWeek = model.firstDayOfWeek
}
, Cmd.none
)
@ -224,13 +225,14 @@ update msg model basePath apiUrl =
( model, Silences.Api.getSilence apiUrl silenceId (SilenceFetch >> MsgForSilenceForm) )
SilenceFetch (Success silence) ->
( { form = fromSilence silence
( { form = fromSilence silence model.firstDayOfWeek
, filterBar = FilterBar.initFilterBar (List.map Utils.Filter.fromApiMatcher silence.matchers)
, filterBarValid = Utils.FormValidation.Initial
, silenceId = model.silenceId
, alerts = Initial
, activeAlertId = Nothing
, key = model.key
, firstDayOfWeek = model.firstDayOfWeek
}
, Task.perform identity (Task.succeed (MsgForSilenceForm PreviewSilence))
)
@ -296,5 +298,12 @@ update msg model basePath apiUrl =
, Cmd.map (MsgForFilterBar >> MsgForSilenceForm) subCmd
)
UpdateFirstDayOfWeek firstDayOfWeek ->
( { model
| firstDayOfWeek = firstDayOfWeek
}
, Cmd.none
)
port persistDefaultCreator : String -> Cmd msg