Add date picker to silence form views (#2262)

* add datepicker

Signed-off-by: m-masataka <m.mizukoshi.wakuwaku@gmail.com>

* fix import error

Signed-off-by: m-masataka <m.mizukoshi.wakuwaku@gmail.com>

* fix unnecessary import from DateTime package

Signed-off-by: m-masataka <m.mizukoshi.wakuwaku@gmail.com>

* fix unnecessary import utc

Signed-off-by: m-masataka <m.mizukoshi.wakuwaku@gmail.com>

* change datetime picker component

Signed-off-by: m-masataka <m.mizukoshi.wakuwaku@gmail.com>

* added datetime picker utils

Signed-off-by: m-masataka <m.mizukoshi.wakuwaku@gmail.com>

* added datetime picker utils

Signed-off-by: m-masataka <m.mizukoshi.wakuwaku@gmail.com>

* remove config

Signed-off-by: m-masataka <m.mizukoshi.wakuwaku@gmail.com>

* replace case expressions to Result.toMaybe

Signed-off-by: m-masataka <m.mizukoshi.wakuwaku@gmail.com>
This commit is contained in:
masataka 2020-07-24 21:52:29 +09:00 committed by GitHub
parent 277c9ed462
commit 73db78741f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1121 additions and 14 deletions

File diff suppressed because one or more lines are too long

View File

@ -16,11 +16,13 @@
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"rtfeldman/elm-iso8601-date-strings": "1.1.2",
"NoRedInk/elm-json-decode-pipeline": "1.0.0"
"NoRedInk/elm-json-decode-pipeline": "1.0.0",
"justinmimbs/time-extra": "1.1.0"
},
"indirect": {
"elm/virtual-dom": "1.0.0",
"elm/random": "1.0.0"
"elm/random": "1.0.0",
"justinmimbs/date": "3.2.0"
}
},
"test-dependencies": {

View File

@ -0,0 +1,140 @@
.cursor-pointer {cursor: pointer;}
.month {
height: 270px;
}
.calendar_ .date-container {
}
.calendar_ .weekheader {
margin-top: 5px;
}
.calendar_ .date {
color: #C0C0C0;
cursor: pointer;
height: 30px;
width: 40px;
display: inline-flex;
justify-content: center;
align-items: center;
font-size: .75rem;
background-color: #fff;
}
.calendar_ .date.thismonth {
color: #22292f;
}
.calendar_ .date.front {
background-color: rgba(0,0,0,0);
}
.calendar_ .date.back {
margin-bottom: 5px;
}
.calendar_ .date.front.mouseover {
background-color: #EEEEEE;
border-radius: 50%;
}
.calendar_ .date.front.start {
background-color: #b0c4de;
border-radius: 50%;
}
.calendar_ .date.front.end {
background-color: #b0c4de;
border-radius: 50%;
}
.calendar_ .date.back.start {
background-color: #b0c4de;
border-top-left-radius: 50%;
border-bottom-left-radius: 50%;
}
.calendar_ .date.back.end {
background-color: #b0c4de;
border-top-right-radius: 50%;
border-bottom-right-radius: 50%;
}
.calendar_ .date.back.between {
background-color: #b0c4de;
}
.timepicker {
height:60px;
width:80%;
margin-top: 10px;
}
.timepicker .subject {
padding:10px;
width:20%;
vertical-align:middle;
}
.timepicker .hour {
width:10%;
}
.timepicker .minute {
width:10%;
}
.timepicker .view {
width:100%;
height:50%;
text-align:center;
border: 0px none;
}
.timepicker .up-button {
width:100%;
height:25%;
border: 0px none;
}
.timepicker .down-button {
width:100%;
height:25%;
border: 0px none;
}
.timepicker .colon {
width: 5%;
}
.timepicker .timeview {
width:50%;
}
.month-header {
width:70%;
margin: 0 auto;
}
.month-header .prev-month {
width:20%;
}
.month-header .month-text {
width:60%;
font-weight: bold;
font-size: 12px;
text-align: center;
}
.month-header .next-month {
width:20%;
}
.d-flex-center {
display:flex;
align-items: center;
justify-content: space-around;
}

View File

@ -101,6 +101,7 @@ init flags url key =
libUrl
Loading
Loading
Loading
defaultCreator
groupExpandAll
key

View File

@ -23,6 +23,7 @@ type alias Model =
, libUrl : String
, bootstrapCSS : ApiData String
, fontAwesomeCSS : ApiData String
, elmDatepickerCSS : ApiData String
, defaultCreator : String
, expandAll : Bool
, key : Key
@ -49,6 +50,7 @@ type Msg
| UpdateFilter String
| BootstrapCSSLoaded (ApiData String)
| FontAwesomeCSSLoaded (ApiData String)
| ElmDatepickerCSSLoaded (ApiData String)
| SetDefaultCreator String
| SetGroupExpandAll Bool

View File

@ -121,6 +121,9 @@ update msg ({ basePath, apiUrl } as model) =
FontAwesomeCSSLoaded css ->
( { model | fontAwesomeCSS = css }, Cmd.none )
ElmDatepickerCSSLoaded css ->
( { model | elmDatepickerCSS = css }, Cmd.none )
SetDefaultCreator name ->
( { model | defaultCreator = name }, Cmd.none )

View File

@ -0,0 +1,70 @@
module Utils.DateTimePicker.Types exposing
( DateTimePicker
, InputHourOrMinute(..)
, Msg(..)
, StartOrEnd(..)
, initDateTimePicker
, initFromStartAndEndTime
)
import Time exposing (Posix)
import Utils.DateTimePicker.Utils exposing (floorMinute)
type alias DateTimePicker =
{ month : Maybe Posix
, mouseOverDay : Maybe Posix
, startDate : Maybe Posix
, endDate : Maybe Posix
, startTime : Maybe Posix
, endTime : Maybe Posix
}
type Msg
= NextMonth
| PrevMonth
| MouseOverDay Posix
| OnClickDay
| ClearMouseOverDay
| SetInputTime StartOrEnd InputHourOrMinute Int
| IncrementTime StartOrEnd InputHourOrMinute Int
type StartOrEnd
= Start
| End
type InputHourOrMinute
= InputHour
| InputMinute
initDateTimePicker : DateTimePicker
initDateTimePicker =
{ month = Nothing
, mouseOverDay = Nothing
, startDate = Nothing
, endDate = Nothing
, startTime = Nothing
, endTime = Nothing
}
initFromStartAndEndTime : Maybe Posix -> Maybe Posix -> DateTimePicker
initFromStartAndEndTime start end =
let
startTime =
Maybe.map (\s -> floorMinute s) start
endTime =
Maybe.map (\e -> floorMinute e) end
in
{ month = start
, mouseOverDay = Nothing
, startDate = start
, endDate = end
, startTime = startTime
, endTime = endTime
}

View File

@ -0,0 +1,183 @@
module Utils.DateTimePicker.Updates exposing (update)
import Time exposing (Posix)
import Utils.DateTimePicker.Types
exposing
( DateTimePicker
, InputHourOrMinute(..)
, Msg(..)
, StartOrEnd(..)
)
import Utils.DateTimePicker.Utils
exposing
( addHour
, addMinute
, firstDayOfNextMonth
, firstDayOfPrevMonth
, floorDate
, trimTime
, updateHour
, updateMinute
)
update : Msg -> DateTimePicker -> DateTimePicker
update msg dateTimePicker =
let
justMonth =
dateTimePicker.month
|> Maybe.withDefault (Time.millisToPosix 0)
setTime_ : StartOrEnd -> InputHourOrMinute -> (InputHourOrMinute -> Posix -> Posix) -> ( Maybe Posix, Maybe Posix )
setTime_ soe ihom updateTime =
let
set_ : Maybe Posix -> Maybe Posix
set_ a =
Maybe.map (\b -> updateTime ihom b) a
in
case soe of
Start ->
( set_ dateTimePicker.startTime, dateTimePicker.endTime )
End ->
( dateTimePicker.startTime, set_ dateTimePicker.endTime )
in
case msg of
NextMonth ->
{ dateTimePicker | month = Just (firstDayOfNextMonth justMonth) }
PrevMonth ->
{ dateTimePicker | month = Just (firstDayOfPrevMonth justMonth) }
MouseOverDay time ->
{ dateTimePicker | mouseOverDay = Just time }
ClearMouseOverDay ->
{ dateTimePicker | mouseOverDay = Nothing }
OnClickDay ->
let
addDateTime_ : Posix -> Maybe Posix -> Posix
addDateTime_ date maybeTime =
case maybeTime of
Just time ->
floorDate date
|> Time.posixToMillis
|> (\d ->
trimTime time
|> Time.posixToMillis
|> (\t -> d + t)
)
|> Time.millisToPosix
Nothing ->
floorDate date
updateTime_ : Maybe Posix -> Maybe Posix -> Maybe Posix
updateTime_ maybeDate maybeTime =
case maybeDate of
Just date ->
Just <| addDateTime_ date maybeTime
Nothing ->
maybeTime
( startDate, endDate ) =
case dateTimePicker.mouseOverDay of
Just m ->
case ( dateTimePicker.startDate, dateTimePicker.endDate ) of
( Nothing, Nothing ) ->
( Just m
, Nothing
)
( Just start, Nothing ) ->
case
compare (floorDate m |> Time.posixToMillis)
(floorDate start |> Time.posixToMillis)
of
LT ->
( Just m
, Just start
)
_ ->
( Just start
, Just m
)
( Nothing, Just end ) ->
( Just m
, Just end
)
( Just start, Just end ) ->
( Just m
, Nothing
)
_ ->
( dateTimePicker.startDate
, dateTimePicker.endDate
)
in
{ dateTimePicker
| startDate = startDate
, endDate = endDate
, startTime = updateTime_ startDate dateTimePicker.startTime
, endTime = updateTime_ endDate dateTimePicker.endTime
}
SetInputTime startOrEnd inputHourOrMinute num ->
let
limit_ : Int -> Int -> Int
limit_ limit n =
if n < 0 then
0
else
modBy limit n
updateHourOrMinute_ : InputHourOrMinute -> Posix -> Posix
updateHourOrMinute_ ihom s =
case ihom of
InputHour ->
updateHour (limit_ 24 num) s
InputMinute ->
updateMinute (limit_ 60 num) s
( startTime, endTime ) =
setTime_ startOrEnd inputHourOrMinute updateHourOrMinute_
in
{ dateTimePicker | startTime = startTime, endTime = endTime }
IncrementTime startOrEnd inputHourOrMinute num ->
let
updateHourOrMinute_ : InputHourOrMinute -> Posix -> Posix
updateHourOrMinute_ ihom s =
let
compare_ : Posix -> Posix
compare_ a =
if
(floorDate s |> Time.posixToMillis)
== (floorDate a |> Time.posixToMillis)
then
a
else
s
in
case ihom of
InputHour ->
addHour num s
|> compare_
InputMinute ->
addMinute num s
|> compare_
( startTime, endTime ) =
setTime_ startOrEnd inputHourOrMinute updateHourOrMinute_
in
{ dateTimePicker | startTime = startTime, endTime = endTime }

View File

@ -0,0 +1,217 @@
module Utils.DateTimePicker.Utils exposing
( addHour
, addMinute
, firstDayOfNextMonth
, firstDayOfPrevMonth
, floorDate
, floorMinute
, floorMonth
, listDaysOfMonth
, monthToString
, splitWeek
, targetValueIntParse
, trimTime
, updateHour
, updateMinute
)
import Html.Events exposing (targetValue)
import Json.Decode as Decode
import Time exposing (Month(..), Posix, Weekday(..), Zone, utc)
import Time.Extra as Time exposing (Interval(..))
listDaysOfMonth : Posix -> List Posix
listDaysOfMonth time =
let
firstOfMonth =
Time.floor Time.Month utc time
firstOfNextMonth =
firstDayOfNextMonth time
padFront =
weekToInt (Time.toWeekday utc firstOfMonth)
|> (\wd ->
if wd == 7 then
0
else
wd
)
|> (\w -> Time.add Time.Day -w utc firstOfMonth)
|> (\d -> Time.range Time.Day 1 utc d firstOfMonth)
padBack =
weekToInt (Time.toWeekday utc firstOfNextMonth)
|> (\w -> Time.add Time.Day (7 - w) utc firstOfNextMonth)
|> Time.range Time.Day 1 utc firstOfNextMonth
in
Time.range Time.Day 1 utc firstOfMonth firstOfNextMonth
|> (\m -> padFront ++ m ++ padBack)
firstDayOfNextMonth : Posix -> Posix
firstDayOfNextMonth time =
Time.floor Time.Month utc time
|> Time.add Time.Day 1 utc
|> Time.ceiling Time.Month utc
firstDayOfPrevMonth : Posix -> Posix
firstDayOfPrevMonth time =
Time.floor Time.Month utc time
|> Time.add Time.Day -1 utc
|> Time.floor Time.Month utc
splitWeek : List Posix -> List (List Posix) -> List (List Posix)
splitWeek days weeks =
if List.length days < 7 then
weeks
else
List.append weeks [ List.take 7 days ]
|> splitWeek (List.drop 7 days)
floorDate : Posix -> Posix
floorDate time =
Time.floor Time.Day utc time
floorMonth : Posix -> Posix
floorMonth time =
Time.floor Time.Month utc time
floorMinute : Posix -> Posix
floorMinute time =
Time.floor Time.Minute utc time
trimTime : Posix -> Posix
trimTime time =
Time.floor Time.Day utc time
|> Time.posixToMillis
|> (\d ->
Time.posixToMillis time - d
)
|> Time.millisToPosix
updateHour : Int -> Posix -> Posix
updateHour n time =
let
diff =
n - Time.toHour utc time
in
Time.add Hour diff utc time
updateMinute : Int -> Posix -> Posix
updateMinute n time =
let
diff =
n - Time.toMinute utc time
in
Time.add Minute diff utc time
addHour : Int -> Posix -> Posix
addHour n time =
Time.add Hour n utc time
addMinute : Int -> Posix -> Posix
addMinute n time =
Time.add Minute n utc time
weekToInt : Weekday -> Int
weekToInt weekday =
case weekday of
Mon ->
1
Tue ->
2
Wed ->
3
Thu ->
4
Fri ->
5
Sat ->
6
Sun ->
7
monthToString : Month -> String
monthToString month =
case month of
Jan ->
"January"
Feb ->
"February"
Mar ->
"March"
Apr ->
"April"
May ->
"May"
Jun ->
"June"
Jul ->
"July"
Aug ->
"August"
Sep ->
"September"
Oct ->
"October"
Nov ->
"November"
Dec ->
"December"
targetValueIntParse : Decode.Decoder Int
targetValueIntParse =
customDecoder targetValue (String.toInt >> maybeStringToResult)
maybeStringToResult : Maybe a -> Result String a
maybeStringToResult =
Result.fromMaybe "could not convert string"
customDecoder : Decode.Decoder a -> (a -> Result String b) -> Decode.Decoder b
customDecoder d f =
let
resultDecoder x =
case x of
Ok a ->
Decode.succeed a
Err e ->
Decode.fail e
in
Decode.map f d |> Decode.andThen resultDecoder

View File

@ -0,0 +1,305 @@
module Utils.DateTimePicker.Views exposing (viewDateTimePicker)
import Html exposing (Html, br, button, div, i, input, p, strong, text)
import Html.Attributes exposing (class, maxlength, value)
import Html.Events exposing (on, onClick, onMouseOut, onMouseOver)
import Iso8601
import Json.Decode as Decode exposing (Decoder)
import Time exposing (Posix, utc)
import Utils.DateTimePicker.Types exposing (DateTimePicker, InputHourOrMinute(..), Msg(..), StartOrEnd(..))
import Utils.DateTimePicker.Updates exposing (update)
import Utils.DateTimePicker.Utils
exposing
( floorDate
, floorMonth
, listDaysOfMonth
, monthToString
, splitWeek
, targetValueIntParse
)
viewDateTimePicker : DateTimePicker -> Html Msg
viewDateTimePicker dateTimePicker =
div [ class "w-100 container" ]
[ viewCalendar dateTimePicker
, div [ class "pt-4 row justify-content-center" ]
[ viewTimePicker dateTimePicker Start
, viewTimePicker dateTimePicker End
]
]
viewCalendar : DateTimePicker -> Html Msg
viewCalendar dateTimePicker =
let
justViewTime =
dateTimePicker.month
|> Maybe.withDefault (Time.millisToPosix 0)
in
div [ class "calendar_ month" ]
[ viewMonthHeader dateTimePicker justViewTime
, viewMonth dateTimePicker justViewTime
]
viewMonthHeader : DateTimePicker -> Posix -> Html Msg
viewMonthHeader dateTimePicker justViewTime =
div [ class "row month-header" ]
[ div
[ class "prev-month d-flex-center"
, onClick PrevMonth
]
[ p
[ class "arrow" ]
[ i
[ class "fa fa-angle-left fa-3x cursor-pointer" ]
[]
]
]
, div
[ class "month-text d-flex-center" ]
[ text (Time.toYear utc justViewTime |> String.fromInt)
, br [] []
, text (Time.toMonth utc justViewTime |> monthToString)
]
, div
[ class "next-month d-flex-center"
, onClick NextMonth
]
[ p
[ class "arrow" ]
[ i
[ class "fa fa-angle-right fa-3x cursor-pointer" ]
[]
]
]
]
viewMonth : DateTimePicker -> Posix -> Html Msg
viewMonth dateTimePicker justViewTime =
let
days =
listDaysOfMonth justViewTime
weeks =
splitWeek days []
in
div [ class "row justify-content-center" ]
[ div [ class "weekheader" ]
(List.map viewWeekHeader [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ])
, div
[ class "date-container"
, onMouseOut ClearMouseOverDay
]
(List.map (viewWeek dateTimePicker justViewTime) weeks)
]
viewWeekHeader : String -> Html Msg
viewWeekHeader weekday =
div [ class "date text-muted" ]
[ text weekday ]
viewWeek : DateTimePicker -> Posix -> List Posix -> Html Msg
viewWeek dateTimePicker justViewTime days =
div []
[ div [] (List.map (viewDay dateTimePicker justViewTime) days) ]
viewDay : DateTimePicker -> Posix -> Posix -> Html Msg
viewDay dateTimePicker justViewTime day =
let
compareDate_ : Posix -> Posix -> Order
compareDate_ a b =
compare (floorDate a |> Time.posixToMillis)
(floorDate b |> Time.posixToMillis)
setClass_ : Maybe Posix -> String -> String
setClass_ d s =
case d of
Just m ->
case compareDate_ m day of
EQ ->
s
_ ->
""
Nothing ->
""
thisMonthClass =
if floorMonth justViewTime == floorMonth day then
" thismonth"
else
""
mouseoverClass =
setClass_ dateTimePicker.mouseOverDay " mouseover"
startClass =
setClass_ dateTimePicker.startDate " start"
endClass =
setClass_ dateTimePicker.endDate " end"
( startClassBack, endClassBack ) =
Maybe.map2 (\sd ed -> ( startClass, endClass )) dateTimePicker.startDate dateTimePicker.endDate
|> Maybe.withDefault ( "", "" )
betweenClass =
case ( dateTimePicker.startDate, dateTimePicker.endDate ) of
( Just start, Just end ) ->
case ( compareDate_ start day, compareDate_ end day ) of
( LT, GT ) ->
" between"
_ ->
""
_ ->
""
in
div [ class ("date back" ++ startClassBack ++ endClassBack ++ betweenClass) ]
[ div
[ class ("date front" ++ mouseoverClass ++ startClass ++ endClass ++ thisMonthClass)
, onMouseOver <| MouseOverDay day
, onClick OnClickDay
]
[ text (Time.toDay utc day |> String.fromInt) ]
]
viewTimePicker : DateTimePicker -> StartOrEnd -> Html Msg
viewTimePicker dateTimePicker startOrEnd =
div
[ class "row timepicker" ]
[ strong [ class "subject" ]
[ text
(case startOrEnd of
Start ->
"Start"
End ->
"End"
)
]
, div [ class "hour" ]
[ button
[ class "up-button d-flex-center"
, onClick <| IncrementTime startOrEnd InputHour 1
]
[ i
[ class "fa fa-angle-up" ]
[]
]
, input
[ on "blur" <| Decode.map (SetInputTime startOrEnd InputHour) targetValueIntParse
, value
(case startOrEnd of
Start ->
case dateTimePicker.startTime of
Just t ->
Time.toHour utc t |> String.fromInt
Nothing ->
"0"
End ->
case dateTimePicker.endTime of
Just t ->
Time.toHour utc t |> String.fromInt
Nothing ->
"0"
)
, maxlength 2
, class "view d-flex-center"
]
[]
, button
[ class "down-button d-flex-center"
, onClick <| IncrementTime startOrEnd InputHour -1
]
[ i
[ class "fa fa-angle-down" ]
[]
]
]
, div [ class "colon d-flex-center" ] [ text ":" ]
, div [ class "minute" ]
[ button
[ class "up-button d-flex-center"
, onClick <| IncrementTime startOrEnd InputMinute 1
]
[ i
[ class "fa fa-angle-up" ]
[]
]
, input
[ on "blur" <| Decode.map (SetInputTime startOrEnd InputMinute) targetValueIntParse
, value
(case startOrEnd of
Start ->
case dateTimePicker.startTime of
Just t ->
Time.toMinute utc t |> String.fromInt
Nothing ->
"0"
End ->
case dateTimePicker.endTime of
Just t ->
Time.toMinute utc t |> String.fromInt
Nothing ->
"0"
)
, maxlength 2
, class "view"
]
[]
, button
[ class "down-button d-flex-center"
, onClick <| IncrementTime startOrEnd InputMinute -1
]
[ i
[ class "fa fa-angle-down" ]
[]
]
]
, div [ class "timeview d-flex-center" ]
[ text
(let
toString_ : Maybe Posix -> Maybe Posix -> String
toString_ maybeTime maybeDate =
Maybe.map
(\t ->
case maybeDate of
Just d ->
Iso8601.fromTime t
|> String.dropRight 8
Nothing ->
""
)
maybeTime
|> Maybe.withDefault ""
selectedTime =
case startOrEnd of
Start ->
toString_ dateTimePicker.startTime dateTimePicker.startDate
End ->
toString_ dateTimePicker.endTime dateTimePicker.endDate
in
selectedTime
)
]
]

View File

@ -21,17 +21,20 @@ view : Model -> Html Msg
view model =
div []
[ renderCSS model.libUrl
, case ( model.bootstrapCSS, model.fontAwesomeCSS ) of
( Success _, Success _ ) ->
, case ( model.bootstrapCSS, model.fontAwesomeCSS, model.elmDatepickerCSS ) of
( Success _, Success _, Success _ ) ->
div []
[ navBar model.route
, div [ class "container pb-4" ] [ currentView model ]
]
( Failure err, _ ) ->
( Failure err, _, _ ) ->
failureView model err
( _, Failure err ) ->
( _, Failure err, _ ) ->
failureView model err
( _, _, Failure err ) ->
failureView model err
_ ->
@ -53,6 +56,7 @@ renderCSS assetsUrl =
div []
[ cssNode (assetsUrl ++ "lib/bootstrap-4.0.0-alpha.6-dist/css/bootstrap.min.css") BootstrapCSSLoaded
, cssNode (assetsUrl ++ "lib/font-awesome-4.7.0/css/font-awesome.min.css") FontAwesomeCSSLoaded
, cssNode (assetsUrl ++ "lib/elm-datepicker/css/elm-datepicker.css") ElmDatepickerCSSLoaded
]

View File

@ -5,6 +5,7 @@ module Views.SilenceForm.Types exposing
, SilenceFormFieldMsg(..)
, SilenceFormMsg(..)
, emptyMatcher
, fromDateTimePicker
, fromMatchersAndCommentAndTime
, fromSilence
, initSilenceForm
@ -18,9 +19,11 @@ import Data.GettableAlert exposing (GettableAlert)
import Data.GettableSilence exposing (GettableSilence)
import Data.Matcher exposing (Matcher)
import Data.PostableSilence exposing (PostableSilence)
import DateTime
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.Filter
import Utils.FormValidation
exposing
@ -50,6 +53,8 @@ type alias SilenceForm =
, endsAt : ValidatedField
, duration : ValidatedField
, matchers : List MatcherForm
, dateTimePicker : DateTimePicker
, viewDateTimePicker : Bool
}
@ -71,6 +76,7 @@ type SilenceFormMsg
| NewSilenceFromMatchersAndCommentAndTime String (List Utils.Filter.Matcher) String Posix
| SilenceFetch (ApiData GettableSilence)
| SilenceCreate (ApiData String)
| UpdateDateTimePicker Utils.DateTimePicker.Types.Msg
type SilenceFormFieldMsg
@ -89,6 +95,9 @@ type SilenceFormFieldMsg
| UpdateMatcherValue Int String
| ValidateMatcherValue Int
| UpdateMatcherRegex Int Bool
| UpdateTimesFromPicker
| OpenDateTimePicker
| CloseDateTimePicker
initSilenceForm : Key -> Model
@ -124,6 +133,15 @@ toSilence { id, comment, matchers, createdBy, startsAt, endsAt } =
fromSilence : GettableSilence -> SilenceForm
fromSilence { id, createdBy, comment, startsAt, endsAt, matchers } =
let
startsPosix =
Utils.Date.timeFromString (DateTime.toString startsAt)
|> Result.toMaybe
endsPosix =
Utils.Date.timeFromString (DateTime.toString endsAt)
|> Result.toMaybe
in
{ id = Just id
, createdBy = initialField createdBy
, comment = initialField comment
@ -131,11 +149,13 @@ fromSilence { id, createdBy, comment, startsAt, endsAt, matchers } =
, endsAt = initialField (timeToString endsAt)
, duration = initialField (durationFormat (timeDifference startsAt endsAt) |> Maybe.withDefault "")
, matchers = List.map fromMatcher matchers
, dateTimePicker = initFromStartAndEndTime startsPosix endsPosix
, viewDateTimePicker = False
}
validateForm : SilenceForm -> SilenceForm
validateForm { id, createdBy, comment, startsAt, endsAt, duration, matchers } =
validateForm { id, createdBy, comment, startsAt, endsAt, duration, matchers, dateTimePicker } =
{ id = id
, createdBy = validate stringNotEmpty createdBy
, comment = validate stringNotEmpty comment
@ -143,6 +163,8 @@ validateForm { id, createdBy, comment, startsAt, endsAt, duration, matchers } =
, endsAt = validate (parseEndsAt startsAt.value) endsAt
, duration = validate parseDuration duration
, matchers = List.map validateMatcherForm matchers
, dateTimePicker = dateTimePicker
, viewDateTimePicker = False
}
@ -177,6 +199,8 @@ empty =
, endsAt = initialField ""
, duration = initialField ""
, matchers = []
, dateTimePicker = initDateTimePicker
, viewDateTimePicker = False
}
@ -209,6 +233,8 @@ fromMatchersAndCommentAndTime defaultCreator matchers comment now =
else
List.filterMap (filterMatcherToMatcher >> Maybe.map fromMatcher) matchers
, comment = initialField comment
, dateTimePicker = initFromStartAndEndTime (Just now) (Just (addDuration defaultDuration now))
, viewDateTimePicker = False
}
@ -239,3 +265,17 @@ fromMatcher { name, value, isRegex } =
, value = initialField value
, isRegex = isRegex
}
fromDateTimePicker : SilenceForm -> DateTimePicker -> SilenceForm
fromDateTimePicker { id, createdBy, comment, startsAt, endsAt, duration, matchers, dateTimePicker } newPicker =
{ id = id
, createdBy = createdBy
, comment = comment
, startsAt = startsAt
, endsAt = endsAt
, duration = duration
, matchers = matchers
, dateTimePicker = newPicker
, viewDateTimePicker = True
}

View File

@ -7,8 +7,10 @@ import Task
import Time
import Types exposing (Msg(..))
import Utils.Date exposing (timeFromString)
import Utils.DateTimePicker.Types exposing (initFromStartAndEndTime)
import Utils.DateTimePicker.Updates as DateTimePickerUpdates
import Utils.Filter exposing (silencePreviewFilter)
import Utils.FormValidation exposing (fromResult, stringNotEmpty, updateValue, validate)
import Utils.FormValidation exposing (fromResult, initialField, stringNotEmpty, updateValue, validate)
import Utils.List
import Utils.Types exposing (ApiData(..))
import Views.SilenceForm.Types
@ -18,6 +20,7 @@ import Views.SilenceForm.Types
, SilenceFormFieldMsg(..)
, SilenceFormMsg(..)
, emptyMatcher
, fromDateTimePicker
, fromMatchersAndCommentAndTime
, fromSilence
, parseEndsAt
@ -172,6 +175,50 @@ updateForm msg form =
in
{ form | matchers = matchers }
UpdateTimesFromPicker ->
let
( startsAt, endsAt, duration ) =
case ( form.dateTimePicker.startTime, form.dateTimePicker.endTime ) of
( Just start, Just end ) ->
( validate timeFromString (initialField (Utils.Date.timeToString start))
, validate (parseEndsAt (Utils.Date.timeToString start)) (initialField (Utils.Date.timeToString end))
, initialField (Utils.Date.durationFormat (Utils.Date.timeDifference start end) |> Maybe.withDefault "")
|> validate Utils.Date.parseDuration
)
_ ->
( form.startsAt, form.endsAt, form.duration )
in
{ form
| startsAt = startsAt
, endsAt = endsAt
, duration = duration
, viewDateTimePicker = False
}
OpenDateTimePicker ->
let
startsAtTime =
case timeFromString form.startsAt.value of
Ok time ->
Just time
_ ->
form.dateTimePicker.startTime
endsAtTime =
timeFromString form.endsAt.value |> Result.toMaybe
in
{ form
| viewDateTimePicker = True
, dateTimePicker = initFromStartAndEndTime startsAtTime endsAtTime
}
CloseDateTimePicker ->
{ form
| viewDateTimePicker = False
}
update : SilenceFormMsg -> Model -> String -> String -> ( Model, Cmd Msg )
update msg model basePath apiUrl =
@ -269,5 +316,16 @@ update msg model basePath apiUrl =
, Cmd.none
)
UpdateDateTimePicker subMsg ->
let
newPicker =
DateTimePickerUpdates.update subMsg model.form.dateTimePicker
in
( { model
| form = fromDateTimePicker model.form newPicker
}
, Cmd.none
)
port persistDefaultCreator : String -> Cmd msg

View File

@ -1,9 +1,10 @@
module Views.SilenceForm.Views exposing (view)
import Data.GettableAlert exposing (GettableAlert)
import Html exposing (Html, a, button, div, fieldset, h1, input, label, legend, span, strong, text, textarea)
import Html.Attributes exposing (class, href)
import Html exposing (Html, a, button, div, fieldset, h1, h5, i, input, label, legend, span, strong, text, textarea)
import Html.Attributes exposing (class, href, style)
import Html.Events exposing (onClick)
import Utils.DateTimePicker.Views exposing (viewDateTimePicker)
import Utils.Filter exposing (SilenceFormGetParams, emptySilenceFormGetParams)
import Utils.FormValidation exposing (ValidatedField, ValidationState(..))
import Utils.Types exposing (ApiData)
@ -44,9 +45,52 @@ view maybeId { matchers, comment } defaultCreator { form, silenceId, alerts, act
[ informationBlock activeAlertId silenceId alerts
, silenceActionButtons maybeId form resetClick
]
, dateTimePickerDialog form
]
dateTimePickerDialog : SilenceForm -> Html SilenceFormMsg
dateTimePickerDialog form =
case form.viewDateTimePicker of
True ->
div []
[ div [ class "modal fade show", style "display" "block" ]
[ div [ class "modal-dialog modal-dialog-centered" ]
[ div [ class "modal-content" ]
[ div [ class "modal-header" ]
[ button
[ class "close ml-auto"
, onClick (CloseDateTimePicker |> UpdateField)
]
[ text "x" ]
]
, div [ class "modal-body" ]
[ viewDateTimePicker form.dateTimePicker |> Html.map UpdateDateTimePicker ]
, div [ class "modal-footer" ]
[ button
[ class "ml-2 btn btn-outline-success mr-auto"
, onClick (CloseDateTimePicker |> UpdateField)
]
[ text "Cancel" ]
, button
[ class "ml-2 btn btn-primary"
, onClick (UpdateTimesFromPicker |> UpdateField)
]
[ text "Set Date/Time" ]
]
]
]
]
, div [ class "modal-backdrop fade show" ] []
]
False ->
div [ style "clip" "rect(0,0,0,0)", style "position" "fixed" ]
[ div [ class "modal fade" ] []
, div [ class "modal-backdrop fade" ] []
]
inputSectionPadding : String
inputSectionPadding =
"mt-5"
@ -57,7 +101,7 @@ timeInput startsAt endsAt duration =
div [ class <| "row " ++ inputSectionPadding ]
[ validatedField input
"Start"
"col-5"
"col-4"
(UpdateStartsAt >> UpdateField)
(ValidateTime |> UpdateField)
startsAt
@ -69,10 +113,26 @@ timeInput startsAt endsAt duration =
duration
, validatedField input
"End"
"col-5"
"col-4 pr-0"
(UpdateEndsAt >> UpdateField)
(ValidateTime |> UpdateField)
endsAt
, div
[ class "flex-column form-group"
]
[ label
[]
[ text "\u{00A0}" ]
, button
[ class "form-control cursor-pointer"
, onClick (OpenDateTimePicker |> UpdateField)
]
[ i
[ class "fa fa-calendar"
]
[]
]
]
]