aboutsummaryrefslogtreecommitdiff
path: root/src/client/elm/LoggedIn/Account
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/elm/LoggedIn/Account')
-rw-r--r--src/client/elm/LoggedIn/Account/Action.elm17
-rw-r--r--src/client/elm/LoggedIn/Account/Model.elm64
-rw-r--r--src/client/elm/LoggedIn/Account/Update.elm75
-rw-r--r--src/client/elm/LoggedIn/Account/View.elm131
4 files changed, 287 insertions, 0 deletions
diff --git a/src/client/elm/LoggedIn/Account/Action.elm b/src/client/elm/LoggedIn/Account/Action.elm
new file mode 100644
index 0000000..66ccfaa
--- /dev/null
+++ b/src/client/elm/LoggedIn/Account/Action.elm
@@ -0,0 +1,17 @@
+module LoggedIn.Account.Action
+ ( Action(..)
+ ) where
+
+import Time exposing (Time)
+
+import Model.User exposing (UserId)
+import Model.Income exposing (IncomeId)
+
+type Action =
+ NoOp
+ | ToggleDetail
+ | ToggleIncomeEdition
+ | UpdateIncomeEdition String
+ | UpdateEditionError String
+ | UpdateIncome Time Int
+ | ValidateUpdateIncome IncomeId Time Int
diff --git a/src/client/elm/LoggedIn/Account/Model.elm b/src/client/elm/LoggedIn/Account/Model.elm
new file mode 100644
index 0000000..2d0c4a3
--- /dev/null
+++ b/src/client/elm/LoggedIn/Account/Model.elm
@@ -0,0 +1,64 @@
+module LoggedIn.Account.Model
+ ( Model
+ , IncomeEdition
+ , init
+ , initIncomeEdition
+ , getCurrentIncome
+ , validateIncome
+ ) where
+
+import Result as Result exposing (Result(..))
+import Dict
+import String
+
+import Utils.Dict exposing (mapValues)
+
+import Model.Translations exposing (..)
+import Model.Income exposing (..)
+import Model.User exposing (UserId)
+
+type alias Model =
+ { me : UserId
+ , incomes : Incomes
+ , visibleDetail : Bool
+ , incomeEdition : Maybe IncomeEdition
+ }
+
+init : UserId -> Incomes -> Model
+init me incomes =
+ { me = me
+ , incomes = incomes
+ , visibleDetail = False
+ , incomeEdition = Nothing
+ }
+
+getCurrentIncome : Model -> Maybe Int
+getCurrentIncome account =
+ account.incomes
+ |> Dict.filter (\_ income -> income.userId == account.me)
+ |> Dict.values
+ |> List.sortBy .creation
+ |> List.reverse
+ |> List.head
+ |> Maybe.map .amount
+
+type alias IncomeEdition =
+ { income : String
+ , error : Maybe String
+ }
+
+initIncomeEdition : Int -> IncomeEdition
+initIncomeEdition income =
+ { income = toString income
+ , error = Nothing
+ }
+
+validateIncome : String -> Translations -> Result String Int
+validateIncome amount translations =
+ case String.toInt amount of
+ Ok number ->
+ if number > 0
+ then Ok number
+ else Err <| getMessage "IncomeMustBePositiveNumber" translations
+ Err _ ->
+ Err <| getMessage "IncomeRequired" translations
diff --git a/src/client/elm/LoggedIn/Account/Update.elm b/src/client/elm/LoggedIn/Account/Update.elm
new file mode 100644
index 0000000..a3d9745
--- /dev/null
+++ b/src/client/elm/LoggedIn/Account/Update.elm
@@ -0,0 +1,75 @@
+module LoggedIn.Account.Update
+ ( update
+ ) where
+
+import Maybe
+import Dict
+import Task
+
+import Effects exposing (Effects)
+
+import Server
+
+import LoggedIn.Account.Action as AccountAction
+import LoggedIn.Account.Model as AccountModel
+
+import Utils.Maybe exposing (isJust)
+
+update : AccountAction.Action -> AccountModel.Model -> (AccountModel.Model, Effects AccountAction.Action)
+update action account =
+ case action of
+
+ AccountAction.NoOp ->
+ (account, Effects.none)
+
+ AccountAction.ToggleDetail ->
+ ( { account | visibleDetail = not account.visibleDetail }
+ , Effects.none
+ )
+
+ AccountAction.ToggleIncomeEdition ->
+ ( { account | incomeEdition =
+ if isJust account.incomeEdition
+ then Nothing
+ else Just (AccountModel.initIncomeEdition (Maybe.withDefault 0 (AccountModel.getCurrentIncome account)))
+ }
+ , Effects.none
+ )
+
+ AccountAction.UpdateIncomeEdition income ->
+ case account.incomeEdition of
+ Just incomeEdition ->
+ ( { account | incomeEdition = Just { incomeEdition | income = income } }
+ , Effects.none
+ )
+ Nothing ->
+ ( account
+ , Effects.none
+ )
+
+ AccountAction.UpdateEditionError error ->
+ case account.incomeEdition of
+ Just incomeEdition ->
+ ( { account | incomeEdition = Just { incomeEdition | error = Just error } }
+ , Effects.none
+ )
+ Nothing ->
+ ( account
+ , Effects.none
+ )
+
+ AccountAction.UpdateIncome currentTime amount ->
+ ( account
+ , Server.setIncome currentTime amount
+ |> Task.map (\incomeId -> (AccountAction.ValidateUpdateIncome incomeId currentTime amount))
+ |> flip Task.onError (always <| Task.succeed AccountAction.NoOp)
+ |> Effects.task
+ )
+
+ AccountAction.ValidateUpdateIncome incomeId currentTime amount ->
+ ( { account
+ | incomes = Dict.insert incomeId { userId = account.me, creation = currentTime, amount = amount } account.incomes
+ , incomeEdition = Nothing
+ }
+ , Effects.none
+ )
diff --git a/src/client/elm/LoggedIn/Account/View.elm b/src/client/elm/LoggedIn/Account/View.elm
new file mode 100644
index 0000000..deee627
--- /dev/null
+++ b/src/client/elm/LoggedIn/Account/View.elm
@@ -0,0 +1,131 @@
+module LoggedIn.Account.View
+ ( view
+ ) where
+
+import List
+import Signal exposing (Address)
+
+import Html exposing (..)
+import Html as H exposing (..)
+import Html.Attributes exposing (..)
+import Html.Events exposing (..)
+
+import LoggedIn.Action as LoggedInAction
+import LoggedIn.Model as LoggedInModel
+
+import LoggedIn.Account.Action as AccountAction
+import LoggedIn.Account.Model as AccountModel
+
+import Model exposing (Model)
+import Model.User exposing (getUserName)
+import Model.Payer exposing (..)
+import Model.Translations exposing (getParamMessage, getMessage)
+import Model.Action exposing (..)
+
+import View.Expand exposing (..)
+import View.Price exposing (price)
+import View.Events exposing (onSubmitPrevDefault)
+
+import Utils.Either exposing (toMaybeError)
+
+view : Address Action -> Model -> LoggedInModel.Model -> Html
+view address model loggedInModel =
+ let account = loggedInModel.account
+ in div
+ [ classList
+ [ ("account", True)
+ , ("detail", account.visibleDetail)
+ ]
+ ]
+ [ exceedingPayers address model loggedInModel
+ , if account.visibleDetail
+ then income address model account
+ else text ""
+ ]
+
+exceedingPayers : Address Action -> Model -> LoggedInModel.Model -> Html
+exceedingPayers address model loggedInModel =
+ button
+ [ class "header"
+ , onClick address (UpdateLoggedIn << LoggedInAction.UpdateAccount <| AccountAction.ToggleDetail)
+ ]
+ ( (List.map (exceedingPayer model loggedInModel) (getOrderedExceedingPayers model.currentTime loggedInModel.users loggedInModel.account.incomes loggedInModel.payments))
+ ++ [ expand ExpandDown loggedInModel.account.visibleDetail ]
+ )
+
+exceedingPayer : Model -> LoggedInModel.Model -> ExceedingPayer -> Html
+exceedingPayer model loggedInModel payer =
+ div
+ [ class "exceedingPayer" ]
+ [ span
+ [ class "userName" ]
+ [ payer.userId
+ |> getUserName loggedInModel.users
+ |> Maybe.withDefault "−"
+ |> text
+ ]
+ , span
+ [ class "amount" ]
+ [ text ("+ " ++ (price model payer.amount)) ]
+ ]
+
+income : Address Action -> Model -> AccountModel.Model -> Html
+income address model account =
+ case account.incomeEdition of
+ Nothing ->
+ incomeRead address model account
+ Just edition ->
+ incomeEdition address model account edition
+
+incomeRead : Address Action -> Model -> AccountModel.Model -> Html
+incomeRead address model account =
+ div
+ [ class "income" ]
+ [ ( case AccountModel.getCurrentIncome account of
+ Nothing ->
+ text (getMessage "NoIncome" model.translations)
+ Just income ->
+ text (getParamMessage [price model income] "Income" model.translations)
+ )
+ , toggleIncomeEdition address "editIncomeEdition" (getMessage "Edit" model.translations)
+ ]
+
+incomeEdition : Address Action -> Model -> AccountModel.Model -> AccountModel.IncomeEdition -> Html
+incomeEdition address model account edition =
+ H.form
+ [ case AccountModel.validateIncome edition.income model.translations of
+ Ok validatedAmount ->
+ onSubmitPrevDefault address (UpdateLoggedIn << LoggedInAction.UpdateAccount <| AccountAction.UpdateIncome model.currentTime validatedAmount)
+ Err error ->
+ onSubmitPrevDefault address (UpdateLoggedIn << LoggedInAction.UpdateAccount << AccountAction.UpdateEditionError <| error)
+ , class "income"
+ ]
+ [ label
+ [ for "incomeInput" ]
+ [ text (getMessage "NewIncome" model.translations) ]
+ , input
+ [ id "incomeInput"
+ , value edition.income
+ , on "input" targetValue (Signal.message address << UpdateLoggedIn << LoggedInAction.UpdateAccount << AccountAction.UpdateIncomeEdition)
+ , maxlength 10
+ ]
+ []
+ , button
+ [ type' "submit"
+ , class "validateIncomeEdition"
+ ]
+ [ text (getMessage "Validate" model.translations) ]
+ , toggleIncomeEdition address "undoIncomeEdition" (getMessage "Undo" model.translations)
+ , case edition.error of
+ Just error -> div [ class "error" ] [ text error ]
+ Nothing -> text ""
+ ]
+
+toggleIncomeEdition : Address Action -> String -> String -> Html
+toggleIncomeEdition address className name =
+ button
+ [ type' "button"
+ , class className
+ , onClick address (UpdateLoggedIn << LoggedInAction.UpdateAccount <| AccountAction.ToggleIncomeEdition)
+ ]
+ [ text name ]