2017-07-17 10:53:38 +00:00
|
|
|
module Views.DayView exposing (dayView)
|
|
|
|
|
|
|
|
-- Local modules
|
|
|
|
|
|
|
|
import Messages exposing (Msg(..))
|
2017-07-19 14:12:12 +00:00
|
|
|
import Models exposing (Model, Day, EventInstance)
|
|
|
|
|
|
|
|
|
|
|
|
-- Core modules
|
|
|
|
|
|
|
|
import Date exposing (Date)
|
2017-07-17 10:53:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- External modules
|
|
|
|
|
2017-07-19 16:30:50 +00:00
|
|
|
import Html exposing (Html, text, div, ul, li, span, i, h4, table, p, a)
|
|
|
|
import Html.Attributes exposing (classList, style, href)
|
2017-07-19 14:12:12 +00:00
|
|
|
import Date.Extra
|
2017-07-19 23:30:54 +00:00
|
|
|
import List.Extra
|
2017-07-19 14:12:12 +00:00
|
|
|
|
|
|
|
|
|
|
|
blockHeight : Int
|
|
|
|
blockHeight =
|
|
|
|
15
|
|
|
|
|
|
|
|
|
|
|
|
headerHeight : Int
|
|
|
|
headerHeight =
|
|
|
|
50
|
|
|
|
|
|
|
|
|
|
|
|
px : Int -> String
|
|
|
|
px value =
|
|
|
|
(toString value) ++ "px"
|
|
|
|
|
|
|
|
|
|
|
|
dayView : Day -> Model -> Html Msg
|
|
|
|
dayView day model =
|
|
|
|
let
|
|
|
|
start =
|
|
|
|
Date.Extra.add Date.Extra.Hour model.flags.schedule_midnight_offset_hours day.date
|
|
|
|
|
|
|
|
lastHour =
|
|
|
|
Date.Extra.add Date.Extra.Day 1 start
|
|
|
|
|
|
|
|
minutes =
|
|
|
|
Date.Extra.range Date.Extra.Minute 15 start lastHour
|
2017-07-17 10:53:38 +00:00
|
|
|
|
2017-07-19 14:12:12 +00:00
|
|
|
filteredEventInstances =
|
|
|
|
List.filter (\x -> Date.Extra.equalBy Date.Extra.Day x.from day.date) model.eventInstances
|
|
|
|
in
|
|
|
|
div
|
|
|
|
[ classList [ ( "row", True ) ] ]
|
|
|
|
[ gutter minutes
|
2017-07-19 23:30:54 +00:00
|
|
|
, locationColumns filteredEventInstances model.eventLocations model.flags.schedule_midnight_offset_hours
|
2017-07-19 14:12:12 +00:00
|
|
|
]
|
2017-07-17 10:53:38 +00:00
|
|
|
|
2017-07-17 19:48:56 +00:00
|
|
|
|
2017-07-19 23:30:54 +00:00
|
|
|
locationColumns eventInstances eventLocations offset =
|
2017-07-19 14:12:12 +00:00
|
|
|
let
|
|
|
|
columnWidth =
|
|
|
|
100.0 / toFloat (List.length eventLocations)
|
|
|
|
in
|
|
|
|
div
|
|
|
|
[ style
|
|
|
|
[ ( "display", "flex" )
|
|
|
|
, ( "justify-content", "space-around" )
|
|
|
|
]
|
2017-07-19 16:55:22 +00:00
|
|
|
, classList
|
|
|
|
[ ( "col-sm-11", True )
|
|
|
|
]
|
2017-07-19 14:12:12 +00:00
|
|
|
]
|
2017-07-19 23:30:54 +00:00
|
|
|
(List.map (\location -> locationColumn columnWidth eventInstances offset location) eventLocations)
|
2017-07-17 19:48:56 +00:00
|
|
|
|
2017-07-19 14:12:12 +00:00
|
|
|
|
2017-07-19 23:30:54 +00:00
|
|
|
locationColumn columnWidth eventInstances offset location =
|
2017-07-19 14:12:12 +00:00
|
|
|
let
|
|
|
|
locationInstances =
|
2017-07-19 23:30:54 +00:00
|
|
|
List.filter (\instance -> instance.location == location.slug) eventInstances
|
|
|
|
|
|
|
|
overlappingGroups =
|
|
|
|
List.Extra.groupWhile
|
|
|
|
(\instanceA instanceB -> Date.Extra.isBetween instanceA.from instanceA.to instanceB.from)
|
|
|
|
locationInstances
|
2017-07-19 14:12:12 +00:00
|
|
|
in
|
|
|
|
div
|
|
|
|
[ style
|
|
|
|
[ ( "width", (toString columnWidth) ++ "%" )
|
|
|
|
]
|
|
|
|
, classList
|
|
|
|
[ ( "location-column", True ) ]
|
|
|
|
]
|
|
|
|
([ div
|
|
|
|
[ style
|
|
|
|
[ ( "height", px headerHeight )
|
|
|
|
]
|
|
|
|
, classList
|
|
|
|
[ ( "location-column-header", True )
|
|
|
|
]
|
|
|
|
]
|
|
|
|
[ text location.name ]
|
|
|
|
]
|
2017-07-19 23:30:54 +00:00
|
|
|
++ (List.map (\group -> renderGroup offset group) overlappingGroups)
|
2017-07-19 14:12:12 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2017-07-19 23:30:54 +00:00
|
|
|
renderGroup offset group =
|
2017-07-19 14:12:12 +00:00
|
|
|
let
|
2017-07-19 23:30:54 +00:00
|
|
|
sortedGroup =
|
|
|
|
List.sortWith
|
|
|
|
(\x y ->
|
|
|
|
case Date.Extra.compare x.from y.from of
|
|
|
|
z ->
|
|
|
|
z
|
|
|
|
)
|
|
|
|
group
|
|
|
|
|
|
|
|
findLefts instanceA =
|
|
|
|
( instanceA
|
|
|
|
, List.foldl
|
|
|
|
(+)
|
|
|
|
0
|
|
|
|
(List.map
|
|
|
|
(\instanceB ->
|
|
|
|
if instanceA == instanceB then
|
|
|
|
0
|
|
|
|
else if (Date.Extra.equal instanceB.from instanceA.from) && (Date.Extra.equal instanceB.to instanceA.to) then
|
|
|
|
-- Set to 0 and then fix it further down in the code
|
|
|
|
0
|
|
|
|
else if (Date.Extra.equal instanceB.from instanceA.from) && not (Date.Extra.equal instanceB.to instanceA.to) then
|
|
|
|
-- Set to 0 and then fix it further down in the code
|
|
|
|
0
|
|
|
|
else if Date.Extra.isBetween instanceB.from instanceB.to instanceA.from then
|
|
|
|
1
|
|
|
|
else
|
|
|
|
0
|
|
|
|
)
|
|
|
|
sortedGroup
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
lefts =
|
|
|
|
List.map findLefts sortedGroup
|
|
|
|
|
|
|
|
numberInGroup =
|
|
|
|
case List.maximum (List.map (\( _, left ) -> left) lefts) of
|
|
|
|
Just num ->
|
|
|
|
num
|
|
|
|
|
|
|
|
Nothing ->
|
|
|
|
1
|
|
|
|
|
|
|
|
fixedLefts =
|
|
|
|
if numberInGroup == 0 then
|
|
|
|
List.map
|
|
|
|
(\( instance, x ) ->
|
|
|
|
( instance
|
|
|
|
, case List.Extra.elemIndex ( instance, x ) lefts of
|
|
|
|
Just index ->
|
|
|
|
index
|
|
|
|
|
|
|
|
Nothing ->
|
|
|
|
0
|
|
|
|
)
|
|
|
|
)
|
|
|
|
lefts
|
|
|
|
else
|
|
|
|
lefts
|
|
|
|
|
|
|
|
fixedNumberInGroup =
|
|
|
|
case List.maximum (List.map (\( _, left ) -> left) fixedLefts) of
|
|
|
|
Just num ->
|
|
|
|
num
|
|
|
|
|
|
|
|
Nothing ->
|
|
|
|
1
|
2017-07-19 14:12:12 +00:00
|
|
|
in
|
|
|
|
div
|
|
|
|
[ style
|
|
|
|
[ ( "display", "flex" )
|
|
|
|
]
|
|
|
|
]
|
2017-07-19 23:30:54 +00:00
|
|
|
(List.map (\instance -> eventInstanceBlock offset fixedNumberInGroup instance) fixedLefts)
|
2017-07-19 14:12:12 +00:00
|
|
|
|
|
|
|
|
2017-07-19 23:30:54 +00:00
|
|
|
eventInstanceBlock : Int -> Int -> ( EventInstance, Int ) -> Html Msg
|
|
|
|
eventInstanceBlock offset numberInGroup ( eventInstance, lefts ) =
|
2017-07-19 14:12:12 +00:00
|
|
|
let
|
|
|
|
length =
|
|
|
|
(toFloat (Date.Extra.diff Date.Extra.Minute eventInstance.from eventInstance.to)) / 15
|
|
|
|
|
|
|
|
height =
|
|
|
|
(toString (length * toFloat blockHeight)) ++ "px"
|
2017-07-19 23:30:54 +00:00
|
|
|
|
|
|
|
hourInMinutes =
|
|
|
|
(Date.hour eventInstance.from) * 60
|
|
|
|
|
|
|
|
minutes =
|
|
|
|
Date.minute eventInstance.from
|
|
|
|
|
|
|
|
topOffset =
|
|
|
|
((((toFloat (hourInMinutes + minutes)) / 60)
|
|
|
|
- (toFloat offset)
|
|
|
|
)
|
|
|
|
* 4.0
|
|
|
|
* (toFloat blockHeight)
|
|
|
|
)
|
|
|
|
+ (toFloat headerHeight)
|
|
|
|
|
|
|
|
width =
|
|
|
|
100 / (toFloat (numberInGroup + 1))
|
2017-07-19 14:12:12 +00:00
|
|
|
in
|
2017-07-19 16:30:50 +00:00
|
|
|
a
|
2017-07-19 14:12:12 +00:00
|
|
|
[ classList
|
|
|
|
[ ( "event", True )
|
|
|
|
, ( "event-in-dayview", True )
|
|
|
|
]
|
|
|
|
, style
|
|
|
|
[ ( "height", height )
|
2017-07-19 23:30:54 +00:00
|
|
|
, ( "width", (toString width) ++ "%" )
|
|
|
|
, ( "top", (toString topOffset) ++ "px" )
|
|
|
|
, ( "left", (toString (toFloat (lefts) * width)) ++ "%" )
|
2017-07-19 14:12:12 +00:00
|
|
|
, ( "background-color", eventInstance.backgroundColor )
|
2017-07-19 16:30:50 +00:00
|
|
|
, ( "color", eventInstance.forgroundColor )
|
2017-07-19 14:12:12 +00:00
|
|
|
]
|
2017-07-19 16:30:50 +00:00
|
|
|
, href ("#event/" ++ eventInstance.eventSlug)
|
2017-07-19 14:12:12 +00:00
|
|
|
]
|
|
|
|
[ p [] [ text ((Date.Extra.toFormattedString "HH:mm" eventInstance.from) ++ " " ++ eventInstance.title) ]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
gutter : List Date -> Html Msg
|
|
|
|
gutter hours =
|
|
|
|
div
|
|
|
|
[ classList
|
|
|
|
[ ( "col-sm-1", True )
|
|
|
|
, ( "day-view-gutter", True )
|
|
|
|
]
|
2017-07-17 10:53:38 +00:00
|
|
|
]
|
2017-07-19 14:12:12 +00:00
|
|
|
([ div [ style [ ( "height", px headerHeight ) ] ]
|
|
|
|
[ text ""
|
|
|
|
]
|
|
|
|
]
|
|
|
|
++ (List.map gutterHour hours)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
gutterHour : Date -> Html Msg
|
|
|
|
gutterHour date =
|
|
|
|
let
|
|
|
|
textToShow =
|
|
|
|
case Date.minute date of
|
|
|
|
0 ->
|
|
|
|
(Date.Extra.toFormattedString "HH:mm" date)
|
|
|
|
|
|
|
|
30 ->
|
|
|
|
(Date.Extra.toFormattedString "HH:mm" date)
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
""
|
|
|
|
in
|
|
|
|
div [ style [ ( "height", px blockHeight ) ] ]
|
|
|
|
[ text textToShow
|
|
|
|
]
|