aboutsummaryrefslogtreecommitdiff
path: root/front/src/Main.elm
diff options
context:
space:
mode:
Diffstat (limited to 'front/src/Main.elm')
-rw-r--r--front/src/Main.elm290
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 ]
+ ]