aboutsummaryrefslogtreecommitdiff
path: root/front/src
diff options
context:
space:
mode:
authorGuillermo Ramos2025-03-01 11:14:38 +0100
committerGuillermo Ramos2025-03-08 13:53:59 +0100
commit150734bb207fa99c4c74e8e07d79355abf5c7926 (patch)
tree1b03266b4df43e3cf5b79583522cc8a35d4cb847 /front/src
parent12879e066a55ef0d7567d9474f8b6ae815cfb08c (diff)
downloadhiccup-150734bb207fa99c4c74e8e07d79355abf5c7926.tar.gz
Additional data for initial payment (VAT + agency fee)
Diffstat (limited to 'front/src')
-rw-r--r--front/src/Main.elm175
1 files changed, 144 insertions, 31 deletions
diff --git a/front/src/Main.elm b/front/src/Main.elm
index c6f63f6..1fd3839 100644
--- a/front/src/Main.elm
+++ b/front/src/Main.elm
@@ -11,6 +11,7 @@ import Html
, div
, hr
, input
+ , pre
, span
, table
, tbody
@@ -48,6 +49,16 @@ import Url.Parser.Query as UQ
+-- CONSTANTS
+
+
+{-| TODO decide by locale
+-}
+amountSep =
+ { thousands = '.', decimal = ',' }
+
+
+
-- MAIN
@@ -94,6 +105,23 @@ capitalDecoder =
(field "interest" float)
+capitalSumView : Capital -> Html Msg
+capitalSumView { principal, interest } =
+ let
+ partsTitle =
+ String.concat
+ [ "Principal: "
+ , amountToString principal
+ , "\nInterest: "
+ , amountToString interest
+ , " ("
+ , Round.round 2 (100 * interest / (principal + interest))
+ , "% from total)"
+ ]
+ in
+ span [ class "underline", title partsTitle ] [ amountView (principal + interest) ]
+
+
type alias MortgageSim =
{ history : List Quota
, topay : Capital
@@ -118,6 +146,8 @@ type alias RawSpecs =
, rate : String
, i1 : String
, years : String
+ , vat : String
+ , fee : String
}
@@ -129,22 +159,26 @@ defaultRawSpecs =
, rate = "80"
, i1 = "1.621"
, years = "30"
+ , vat = "6"
+ , fee = "3"
}
rawSpecsParser : UQ.Parser RawSpecs
rawSpecsParser =
- UQ.map6 RawSpecs
+ UQ.map8 RawSpecs
(UQ.map (Maybe.withDefault defaultRawSpecs.title) <| UQ.string "title")
(UQ.map (Maybe.withDefault defaultRawSpecs.total) <| UQ.string "total")
(UQ.map (Maybe.withDefault defaultRawSpecs.initial) <| UQ.string "initial")
(UQ.map (Maybe.withDefault defaultRawSpecs.rate) <| UQ.string "rate")
(UQ.map (Maybe.withDefault defaultRawSpecs.i1) <| UQ.string "i1")
(UQ.map (Maybe.withDefault defaultRawSpecs.years) <| UQ.string "years")
+ (UQ.map (Maybe.withDefault defaultRawSpecs.vat) <| UQ.string "vat")
+ (UQ.map (Maybe.withDefault defaultRawSpecs.fee) <| UQ.string "fee")
rawSpecsToURL : RawSpecs -> String
-rawSpecsToURL { title, total, initial, rate, i1, years } =
+rawSpecsToURL { title, total, initial, rate, i1, years, vat, fee } =
UB.toQuery <|
[ UB.string "title" title
, UB.string "total" total
@@ -152,6 +186,8 @@ rawSpecsToURL { title, total, initial, rate, i1, years } =
, UB.string "rate" rate
, UB.string "i1" i1
, UB.string "years" years
+ , UB.string "vat" vat
+ , UB.string "fee" fee
]
@@ -297,6 +333,8 @@ type SpecField
| Initial
| I1
| Years
+ | VAT
+ | Fee
type Msg
@@ -382,6 +420,12 @@ update msg m =
Years ->
{ rawSpecs | years = val }
+
+ VAT ->
+ { rawSpecs | vat = val }
+
+ Fee ->
+ { rawSpecs | fee = val }
in
( { m | rawSpecs = newRawSpecs }, Cmd.none )
@@ -432,10 +476,48 @@ slider attributes onInputMsg valueTxt =
-- VIEW
+amountToString : Float -> String
+amountToString amount =
+ let
+ amountStr =
+ Round.round 2 amount
+
+ insertThousandsSep str =
+ let
+ l =
+ List.reverse <| String.toList str
+
+ indexed =
+ List.map2 Tuple.pair (List.range 0 (String.length str)) l
+
+ withCommas =
+ List.concatMap
+ (\( i, c ) ->
+ if i > 0 && modBy 3 i == 0 then
+ [ amountSep.thousands, c ]
+
+ else
+ [ c ]
+ )
+ indexed
+ in
+ String.fromList <| List.reverse withCommas
+ in
+ case String.split "." amountStr of
+ int :: float :: [] ->
+ String.join (String.fromChar amountSep.decimal)
+ [ insertThousandsSep int
+ , float
+ ]
+
+ _ ->
+ amountStr
+
+
specsView : RawSpecs -> Html Msg
specsView rawSpecs =
let
- { title, total, rate, initial, i1, years } =
+ { title, total, rate, initial, i1, years, vat, fee } =
rawSpecs
simButAttrs =
@@ -466,7 +548,7 @@ specsView rawSpecs =
total
]
, div [ class "flex my-1" ]
- [ div [ class "" ]
+ [ div []
[ text "Initial contribution: "
, txtInput [ class "w-[100px]", Html.Attributes.min "0", Html.Attributes.max total ]
(UpdateSpecs Initial)
@@ -487,14 +569,30 @@ specsView rawSpecs =
i1
, text " % (nominal)"
]
- , div [ class "flex my-1 mb-2" ]
+ , div [ class "flex my-1" ]
[ text "Years: "
, slider [ Html.Attributes.min "1", Html.Attributes.max "40", step "1" ]
(UpdateSpecs Years)
years
, text years
]
- , button (butAttrs ++ simButAttrs) [ text "Simulate" ]
+ , div [ class "flex my-1" ]
+ [ div []
+ [ text "VAT: "
+ , txtInput [ class "w-[55px] mx-1", Html.Attributes.min "0", Html.Attributes.max "50" ]
+ (UpdateSpecs VAT)
+ vat
+ , text "%"
+ ]
+ , div [ class "ml-6" ]
+ [ text "Agent fee: "
+ , txtInput [ class "w-[55px] mx-1", Html.Attributes.min "0", Html.Attributes.max "10" ]
+ (UpdateSpecs Fee)
+ fee
+ , text "%"
+ ]
+ ]
+ , button (butAttrs ++ simButAttrs ++ [ class "mt-2" ]) [ text "Simulate" ]
]
@@ -518,21 +616,22 @@ historyView m quotas =
]
-capitalSumView : Capital -> Html Msg
-capitalSumView { principal, interest } =
+amountView : Float -> Html Msg
+amountView amount =
let
- partsTitle =
- String.concat
- [ "Principal: "
- , Round.round 2 principal
- , "\nInterest: "
- , Round.round 2 interest
- , " ("
- , Round.round 2 (100 * interest / (principal + interest))
- , "% from total)"
- ]
+ amountStr =
+ amountToString amount
in
- span [ class "underline", title partsTitle ] [ text (Round.round 2 (principal + interest)) ]
+ case String.split (String.fromChar amountSep.decimal) amountStr of
+ int :: float :: [] ->
+ span []
+ [ text int
+ , text <| String.fromChar amountSep.decimal
+ , span [ class "text-xs" ] [ text float ]
+ ]
+
+ _ ->
+ text amountStr
periodToYear : Int -> Int
@@ -580,7 +679,7 @@ quotaView m { period, payed, pending_principal } =
, text
(String.fromInt period)
, capitalSumView payed
- , text (Round.round 2 pending_principal)
+ , amountView pending_principal
]
)
@@ -591,7 +690,7 @@ quotaView m { period, payed, pending_principal } =
, text
(String.fromInt period)
, capitalSumView payed
- , text (Round.round 2 pending_principal)
+ , amountView pending_principal
]
)
@@ -602,22 +701,36 @@ quotaView m { period, payed, pending_principal } =
simView : Model -> ( RawSpecs, MortgageSim ) -> Html Msg
simView m ( rawSpecs, { history, topay, payed } ) =
let
- initial =
- Maybe.withDefault 0 <| String.toFloat rawSpecs.initial
+ parseFloat =
+ Maybe.withDefault 0 << String.toFloat
total =
- topay.principal + topay.interest + initial
+ parseFloat rawSpecs.total
+
+ initial =
+ parseFloat rawSpecs.initial
+
+ vat =
+ total * parseFloat rawSpecs.vat / 100
+
+ fee =
+ total * parseFloat rawSpecs.fee / 100
in
div []
[ hr [ class "my-5" ] []
- , div []
- [ text "To pay: "
- , text <| Round.round 2 total
- , text " ("
- , text <| Round.round 2 initial
- , text " initial + "
+ , pre [ class "leading-none" ]
+ [ text "Total to pay: "
+ , amountView (topay.principal + topay.interest + initial + vat + fee)
+ , text "\n├ Initial payment: "
+ , amountView (initial + vat + fee)
+ , text "\n│ ├ Property: "
+ , amountView initial
+ , text "\n│ ├ Agent fee: "
+ , amountView fee
+ , text "\n│ └ VAT: "
+ , amountView vat
+ , text "\n└ Financed (mortgage): "
, capitalSumView topay
- , text " financed)"
]
-- , div [] [ text "payed: ", capitalSumView payed ]