diff options
Diffstat (limited to 'src/client/elm/LoggedIn/Account')
| -rw-r--r-- | src/client/elm/LoggedIn/Account/Action.elm | 17 | ||||
| -rw-r--r-- | src/client/elm/LoggedIn/Account/Model.elm | 64 | ||||
| -rw-r--r-- | src/client/elm/LoggedIn/Account/Update.elm | 75 | ||||
| -rw-r--r-- | src/client/elm/LoggedIn/Account/View.elm | 131 | 
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 ] | 
