diff options
Diffstat (limited to 'front/src/Main.elm')
| -rw-r--r-- | front/src/Main.elm | 290 | 
1 files changed, 263 insertions, 27 deletions
| diff --git a/front/src/Main.elm b/front/src/Main.elm index fdf2fc4..47edd9d 100644 --- a/front/src/Main.elm +++ b/front/src/Main.elm @@ -1,15 +1,12 @@  module Main exposing (..) --- Press buttons to increment and decrement a counter. --- --- Read how it works: ---   https://guide.elm-lang.org/architecture/buttons.html --- - -  import Browser -import Html exposing (Html, button, div, text) -import Html.Events exposing (onClick) +import Html exposing (Html, button, div, input, text) +import Html.Attributes exposing (max, min, step, type_, value) +import Html.Events exposing (onClick, onInput) +import Http +import Json.Decode exposing (Decoder, field, float, int, list, map, map2, map3, map4) +import Json.Encode as Encode exposing (Value, object) @@ -17,48 +14,287 @@ import Html.Events exposing (onClick)  main = -  Browser.sandbox { init = init, update = update, view = view } +    Browser.element { init = init, update = update, view = view, subscriptions = \_ -> Sub.none }  -- MODEL -type alias Model = Int +type alias Capital = +    { principal : Float +    , interest : Float +    } + + +capitalDecoder : Decoder Capital +capitalDecoder = +    map2 Capital +        (field "principal" float) +        (field "interest" float) + + +capitalStr : Capital -> String +capitalStr { principal, interest } = +    String.concat [ "{principal=", String.fromFloat principal, ", interest=", String.fromFloat interest, "}" ] + + +type alias Quota = +    { period : Int +    , payed : Capital +    , pending_principal : Float +    } + + +quotaDecoder : Decoder Quota +quotaDecoder = +    map3 Quota +        (field "period" int) +        (field "payed" capitalDecoder) +        (field "pending_principal" float) + + +type alias Simulation = +    { history : List Quota +    , topay : Capital +    , payed : Capital +    , payed_amortized : Float +    } + +simDecoder : Decoder Simulation +simDecoder = +    map4 Simulation +        (field "history" (list quotaDecoder)) +        (field "topay" capitalDecoder) +        (field "payed" capitalDecoder) +        (field "payed_amortized" float) -init : Model -init = -  0 + +type alias SimSpecs = +    { principal : Float +    , i1 : Float +    , years : Int +    } + + +type alias Model = +    { error : String +    , simSpecs : SimSpecs +    , simulation : Maybe Simulation +    } + + +qs : List ( String, String ) -> String +qs ss = +    String.join "&" (List.map (\( s, t ) -> String.join "=" [ s, t ]) ss) + + +simSpecsToURL : SimSpecs -> String +simSpecsToURL { principal, i1, years } = +    let +        base = +            "/api/simulate" +    in +    String.concat +        [ base +        , "?" +        , qs +            [ ( "principal", String.fromFloat principal ) +            , ( "i1" +              , String.fromFloat (i1 / 100) +              ) +            , ( "years", String.fromInt years ) +            ] +        ] + + +runSimulation : SimSpecs -> Cmd Msg +runSimulation simSpecs = +    Http.get +        { url = simSpecsToURL simSpecs +        , expect = Http.expectJson GotSimulation simDecoder +        } + + +init : () -> ( Model, Cmd Msg ) +init () = +    let +        simSpecs = +            { principal = 300000.0, i1 = 2.1, years = 30 } + +        req = +            runSimulation simSpecs +    in +    ( { error = "", simSpecs = simSpecs, simulation = Nothing }, req )  -- UPDATE +type SimSpecUpdate +    = Principal +    | I1 +    | Years + +  type Msg -  = Increment -  | Decrement +    = GotSimulation (Result Http.Error Simulation) +    | UpdateSimSpecs SimSpecUpdate String +    | RunSimulation -update : Msg -> Model -> Model +update : Msg -> Model -> ( Model, Cmd Msg )  update msg model = -  case msg of -    Increment -> -      model + 1 +    let +        _ = +            Debug.log "UPDATE!" msg +    in +    case msg of +        GotSimulation (Ok simulation) -> +            ( { model | simulation = Just simulation }, Cmd.none ) + +        GotSimulation (Err err) -> +            ( { model | error = errorToString err }, Cmd.none ) + +        RunSimulation -> +            ( model, runSimulation model.simSpecs ) + +        UpdateSimSpecs u val -> +            let +                simSpecs = +                    model.simSpecs + +                m = +                    case u of +                        Principal -> +                            case String.toFloat val of +                                Just p -> +                                    { model | simSpecs = { simSpecs | principal = p } } + +                                Nothing -> +                                    { model | error = "Error parsing principal" } + +                        I1 -> +                            case String.toFloat val of +                                Just i -> +                                    { model | simSpecs = { simSpecs | i1 = i } } -    Decrement -> -      model - 1 +                                Nothing -> +                                    { model | error = "Error parsing interest" } + +                        Years -> +                            case String.toInt val of +                                Just i -> +                                    { model | simSpecs = { simSpecs | years = i } } + +                                Nothing -> +                                    { model | error = "Error parsing years" } +            in +            ( m, Cmd.none )  -- VIEW +errorToString : Http.Error -> String +errorToString error = +    case error of +        Http.BadUrl url -> +            "The URL " ++ url ++ " was invalid" + +        Http.Timeout -> +            "Unable to reach the server, try again" + +        Http.NetworkError -> +            "Unable to reach the server, check your network connection" + +        Http.BadStatus 500 -> +            "The server had a problem, try again later" + +        Http.BadStatus 400 -> +            "Verify your information and try again" + +        Http.BadStatus _ -> +            "Unknown error" + +        Http.BadBody errorMessage -> +            errorMessage + + +specsView : SimSpecs -> Html Msg +specsView { principal, i1, years } = +    div [] +        [ div [] +            [ text "Principal: " +            , input +                [ type_ "range" +                , Html.Attributes.min "0" +                , Html.Attributes.max "1000000" +                , step "10000" +                , value (String.fromFloat principal) +                , onInput (UpdateSimSpecs Principal) +                ] +                [] +            , text (String.fromFloat principal) +            ] +        , div [] +            [ text "Interest rate: " +            , input +                [ Html.Attributes.min "0" +                , Html.Attributes.max "100" +                , value (String.fromFloat i1) +                , onInput (UpdateSimSpecs I1) +                ] +                [] +            ] +        , div [] +            [ text "Years: " +            , input +                [ type_ "range" +                , Html.Attributes.min "1" +                , Html.Attributes.max "50" +                , step "1" +                , value (String.fromInt years) +                , onInput (UpdateSimSpecs Years) +                ] +                [] +            , text (String.fromInt years) +            ] +        , button [ onClick RunSimulation ] [ text "Simulate" ] +        ] + + +quotaView : Quota -> Html Msg +quotaView { period, payed, pending_principal } = +    div [] [ text (String.join "\t" [ String.fromInt period, capitalStr payed, String.fromFloat pending_principal ]) ] + + +historyView : List Quota -> Html Msg +historyView quotas = +    div [] (List.map quotaView quotas) + + +simView : Simulation -> Html Msg +simView { history, topay, payed } = +    div [] +        [ historyView history +        , div [] [ text (String.concat [ "to pay: ", capitalStr topay ]) ] +        , div [] [ text (String.concat [ "payed: ", capitalStr payed ]) ] +        ] + +  view : Model -> Html Msg  view model = -  div [] -    [ button [ onClick Decrement ] [ text "-" ] -    , div [] [ text (String.fromInt model) ] -    , button [ onClick Increment ] [ text "+" ] -    ] +    div [] +        [ specsView model.simSpecs +        , case model.simulation of +            Nothing -> +                text "" + +            Just sim -> +                div [] [ simView sim ] +        , div [] [ text model.error ] +        ] | 
