about summary refs log tree commit diff
path: root/website/habit-screens/src/Habits.elm
diff options
context:
space:
mode:
Diffstat (limited to 'website/habit-screens/src/Habits.elm')
-rw-r--r--website/habit-screens/src/Habits.elm465
1 files changed, 465 insertions, 0 deletions
diff --git a/website/habit-screens/src/Habits.elm b/website/habit-screens/src/Habits.elm
new file mode 100644
index 000000000000..bbd5887f8bd5
--- /dev/null
+++ b/website/habit-screens/src/Habits.elm
@@ -0,0 +1,465 @@
+module Habits exposing (render)
+
+import Browser
+import Date exposing (Date)
+import Html exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+import Set exposing (Set)
+import State exposing (HabitType(..))
+import Time exposing (Weekday(..))
+import UI
+import Utils exposing (Strategy(..))
+
+
+morning : List State.Habit
+morning =
+    List.map
+        (\( duration, x ) ->
+            { label = x
+            , habitType = State.Morning
+            , minutesDuration = duration
+            }
+        )
+        [ ( 1, "Make bed" )
+        , ( 2, "Brush teeth" )
+        , ( 10, "Shower" )
+        , ( 1, "Do push-ups" )
+        , ( 10, "Meditate" )
+        ]
+
+
+evening : List State.Habit
+evening =
+    List.map
+        (\( duration, x ) ->
+            { label = x
+            , habitType = State.Evening
+            , minutesDuration = duration
+            }
+        )
+        [ ( 30, "Read" )
+        , ( 1, "Record in habit Journal" )
+        ]
+
+
+monday : List ( Int, String )
+monday =
+    [ ( 90, "Bikram Yoga @ 17:00" )
+    ]
+
+
+tuesday : List ( Int, String )
+tuesday =
+    [ ( 90, "Bikram Yoga @ 18:00" )
+    ]
+
+
+wednesday : List ( Int, String )
+wednesday =
+    [ ( 5, "Shave" )
+    , ( 90, "Bikram Yoga @ 17:00" )
+    ]
+
+
+thursday : List ( Int, String )
+thursday =
+    []
+
+
+friday : List ( Int, String )
+friday =
+    [ ( 60, "Bikram Yoga @ 17:00" )
+    , ( 3, "Take-out trash" )
+    , ( 60, "Shop for groceries" )
+    ]
+
+
+saturday : List ( Int, String )
+saturday =
+    [ ( 60, "Warm Yin Yoga @ 15:00" )
+    ]
+
+
+sunday : List ( Int, String )
+sunday =
+    [ ( 1, "Shampoo" )
+    , ( 5, "Shave" )
+    , ( 1, "Trim nails" )
+    , ( 1, "Combine trash cans" )
+    , ( 10, "Mop tile and wood floors" )
+    , ( 10, "Laundry" )
+    , ( 5, "Vacuum bedroom" )
+    , ( 5, "Dust surfaces" )
+    , ( 5, "Clean mirrors" )
+    , ( 5, "Clean desk" )
+    ]
+
+
+payday : List State.Habit
+payday =
+    List.map
+        (\( duration, x ) ->
+            { label = x
+            , habitType = State.Payday
+            , minutesDuration = duration
+            }
+        )
+        [ ( 1, "Ensure \"Emergency\" fund has a balance of 1000 GBP" )
+        , ( 1, "Open \"finances_2020\" Google Sheet" )
+        , ( 1, "Settle up with Mimi on TransferWise" )
+        , ( 1, "Adjust GBP:USD exchange rate" )
+        , ( 1, "Adjust \"Stocks (after tax)\" to reflect amount Google sent" )
+        , ( 1, "Add remaining cash to \"Carryover (cash)\"" )
+        , ( 1, "Adjust \"Paycheck\" to reflect amount Google sent" )
+        , ( 5, "In the \"International Xfer\" table, send \"Xfer amount\" from Monzo to USAA" )
+        , ( 10, "Go to an ATM and extract the amount in \"ATM withdrawal\"" )
+        , ( 0, "Await the TransferWise transaction to complete and pay MyFedLoan in USD" )
+        ]
+
+
+firstOfTheMonth : List State.Habit
+firstOfTheMonth =
+    List.map
+        (\( duration, x ) ->
+            { label = x
+            , habitType = State.FirstOfTheMonth
+            , minutesDuration = duration
+            }
+        )
+        [ ( 10, "Create habit template in journal" )
+        , ( 30, "Assess previous month's performance" )
+        , ( 5, "Register for Bikram Yoga classes" )
+        ]
+
+
+firstOfTheYear : List State.Habit
+firstOfTheYear =
+    List.map
+        (\( duration, x ) ->
+            { label = x
+            , habitType = State.FirstOfTheYear
+            , minutesDuration = duration
+            }
+        )
+        [ ( 60, "Write a post mortem for the previous year" )
+        ]
+
+
+habitTypes :
+    { includeMorning : Bool
+    , includeEvening : Bool
+    , date : Date
+    }
+    -> List State.HabitType
+habitTypes { includeMorning, includeEvening, date } =
+    let
+        habitTypePredicates : List ( State.HabitType, Date -> Bool )
+        habitTypePredicates =
+            [ ( Morning, \_ -> includeMorning )
+            , ( DayOfWeek, \_ -> True )
+            , ( Payday, \x -> Date.day x == 25 )
+            , ( FirstOfTheMonth, \x -> Date.day x == 1 )
+            , ( FirstOfTheYear, \x -> Date.day x == 1 && Date.monthNumber x == 1 )
+            , ( Evening, \_ -> includeEvening )
+            ]
+    in
+    habitTypePredicates
+        |> List.filter (\( _, predicate ) -> predicate date)
+        |> List.map (\( habitType, _ ) -> habitType)
+
+
+habitsFor : State.HabitType -> Weekday -> List State.Habit
+habitsFor habitType weekday =
+    case habitType of
+        Morning ->
+            morning
+
+        Evening ->
+            evening
+
+        DayOfWeek ->
+            let
+                toHabit : List ( Int, String ) -> List State.Habit
+                toHabit =
+                    List.map
+                        (\( duration, x ) ->
+                            { label = x
+                            , habitType = State.DayOfWeek
+                            , minutesDuration = duration
+                            }
+                        )
+            in
+            case weekday of
+                Mon ->
+                    toHabit monday
+
+                Tue ->
+                    toHabit tuesday
+
+                Wed ->
+                    toHabit wednesday
+
+                Thu ->
+                    toHabit thursday
+
+                Fri ->
+                    toHabit friday
+
+                Sat ->
+                    toHabit saturday
+
+                Sun ->
+                    toHabit sunday
+
+        Payday ->
+            payday
+
+        FirstOfTheMonth ->
+            firstOfTheMonth
+
+        FirstOfTheYear ->
+            firstOfTheYear
+
+
+weekdayLabelFor : Weekday -> State.WeekdayLabel
+weekdayLabelFor weekday =
+    case weekday of
+        Mon ->
+            "Monday"
+
+        Tue ->
+            "Tuesday"
+
+        Wed ->
+            "Wednesday"
+
+        Thu ->
+            "Thursday"
+
+        Fri ->
+            "Friday"
+
+        Sat ->
+            "Saturday"
+
+        Sun ->
+            "Sunday"
+
+
+timeRemaining : State.WeekdayLabel -> State.CompletedHabits -> List State.Habit -> Int
+timeRemaining weekdayLabel completed habits =
+    habits
+        |> List.indexedMap
+            (\i { label, minutesDuration } ->
+                if Set.member ( weekdayLabel, label ) completed then
+                    0
+
+                else
+                    minutesDuration
+            )
+        |> List.sum
+
+
+render : State.Model -> Html State.Msg
+render { today, visibleDayOfWeek, completed, includeMorning, includeEvening } =
+    case ( today, visibleDayOfWeek ) of
+        ( Just todaysDate, Just visibleWeekday ) ->
+            let
+                todaysWeekday : Weekday
+                todaysWeekday =
+                    Date.weekday todaysDate
+
+                habits : List State.Habit
+                habits =
+                    habitTypes
+                        { includeMorning = includeMorning
+                        , includeEvening = includeEvening
+                        , date = todaysDate
+                        }
+                        |> List.map (\habitType -> habitsFor habitType todaysWeekday)
+                        |> List.concat
+            in
+            div
+                [ Utils.class
+                    [ Always "max-w-xl mx-auto py-6 px-6"
+                    , When (todaysWeekday /= visibleWeekday) "pt-20"
+                    ]
+                ]
+                [ header []
+                    [ if todaysWeekday /= visibleWeekday then
+                        div [ class "text-center w-full bg-blue-600 text-white fixed top-0 left-0 px-3 py-4" ]
+                            [ p [ class "py-2 inline pr-5" ]
+                                [ text "As you are not viewing today's habits, the UI is in read-only mode" ]
+                            , UI.button
+                                [ class "bg-blue-200 px-4 py-2 rounded text-blue-600 text-xs font-bold"
+                                , onClick State.ViewToday
+                                ]
+                                [ text "View Today's Habits" ]
+                            ]
+
+                      else
+                        text ""
+                    , div [ class "flex center" ]
+                        [ UI.button
+                            [ class "w-1/4 text-gray-500"
+                            , onClick State.ViewPrevious
+                            ]
+                            [ text "‹ previous" ]
+                        , h1 [ class "font-bold text-blue-500 text-3xl text-center w-full" ]
+                            [ text (weekdayLabelFor visibleWeekday) ]
+                        , UI.button
+                            [ class "w-1/4 text-gray-500"
+                            , onClick State.ViewNext
+                            ]
+                            [ text "next ›" ]
+                        ]
+                    ]
+                , if todaysWeekday == visibleWeekday then
+                    p [ class "text-center pt-1 pb-4" ]
+                        [ let
+                            t : Int
+                            t =
+                                timeRemaining (weekdayLabelFor todaysWeekday) completed habits
+                          in
+                          if t == 0 then
+                            text "Nothing to do!"
+
+                          else
+                            text
+                                ((habits
+                                    |> timeRemaining (weekdayLabelFor todaysWeekday) completed
+                                    |> String.fromInt
+                                 )
+                                    ++ " minutes remaining"
+                                )
+                        ]
+
+                  else
+                    text ""
+                , if todaysWeekday == visibleWeekday then
+                    div []
+                        [ UI.button
+                            [ onClick
+                                (if Set.size completed == 0 then
+                                    State.DoNothing
+
+                                 else
+                                    State.ClearAll
+                                )
+                            , Utils.class
+                                [ Always "ml-10 px-3"
+                                , If (Set.size completed == 0)
+                                    "text-gray-500 cursor-not-allowed"
+                                    "text-red-500 underline cursor-pointer"
+                                ]
+                            ]
+                            [ let
+                                numCompleted : Int
+                                numCompleted =
+                                    habits
+                                        |> List.indexedMap (\i { label } -> ( i, label ))
+                                        |> List.filter
+                                            (\( i, label ) ->
+                                                Set.member
+                                                    ( weekdayLabelFor todaysWeekday, label )
+                                                    completed
+                                            )
+                                        |> List.length
+                              in
+                              if numCompleted == 0 then
+                                text "Clear"
+
+                              else
+                                text ("Clear (" ++ String.fromInt numCompleted ++ ")")
+                            ]
+                        , UI.button
+                            [ onClick State.ToggleMorning
+                            , Utils.class
+                                [ Always "px-3 underline"
+                                , If includeMorning
+                                    "text-gray-600"
+                                    "text-blue-600"
+                                ]
+                            ]
+                            [ text
+                                (if includeMorning then
+                                    "Hide Morning"
+
+                                 else
+                                    "Show Morning"
+                                )
+                            ]
+                        , UI.button
+                            [ Utils.class
+                                [ Always "px-3 underline"
+                                , If includeEvening
+                                    "text-gray-600"
+                                    "text-blue-600"
+                                ]
+                            , onClick State.ToggleEvening
+                            ]
+                            [ text
+                                (if includeEvening then
+                                    "Hide Evening"
+
+                                 else
+                                    "Show Evening"
+                                )
+                            ]
+                        ]
+
+                  else
+                    text ""
+                , ul [ class "pb-10" ]
+                    (habits
+                        |> List.indexedMap
+                            (\i { label, minutesDuration } ->
+                                let
+                                    isCompleted : Bool
+                                    isCompleted =
+                                        Set.member ( weekdayLabelFor todaysWeekday, label ) completed
+                                in
+                                li [ class "text-xl list-disc ml-6" ]
+                                    [ if todaysWeekday == visibleWeekday then
+                                        UI.button
+                                            [ class "py-5 px-3"
+                                            , onClick
+                                                (State.ToggleHabit
+                                                    (weekdayLabelFor todaysWeekday)
+                                                    label
+                                                )
+                                            ]
+                                            [ span
+                                                [ Utils.class
+                                                    [ Always "text-white pt-1 px-2 rounded"
+                                                    , If isCompleted "bg-gray-400" "bg-blue-500"
+                                                    ]
+                                                ]
+                                                [ text (String.fromInt minutesDuration ++ " mins") ]
+                                            , p
+                                                [ Utils.class
+                                                    [ Always "inline pl-3"
+                                                    , When isCompleted "line-through text-gray-400"
+                                                    ]
+                                                ]
+                                                [ text label ]
+                                            ]
+
+                                      else
+                                        UI.button
+                                            [ class "py-5 px-3 cursor-not-allowed"
+                                            , onClick State.DoNothing
+                                            ]
+                                            [ text label ]
+                                    ]
+                            )
+                    )
+                , footer [ class "bg-white text-sm text-center text-gray-500 fixed bottom-0 left-0 w-full py-4" ]
+                    [ p [] [ text "This app is brought to you by William Carroll." ]
+                    , p [] [ text "Client: Elm; Server: n/a" ]
+                    ]
+                ]
+
+        ( _, _ ) ->
+            p [] [ text "Unable to display habits because we do not know what day of the week it is." ]