Skip to content

Commit

Permalink
feat: sharable parameterized links
Browse files Browse the repository at this point in the history
  • Loading branch information
shanehull committed Oct 16, 2024
1 parent e242906 commit 0cb45eb
Show file tree
Hide file tree
Showing 17 changed files with 482 additions and 123 deletions.
8 changes: 4 additions & 4 deletions internal/calc/calc.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"strings"
)

type DebtRecyclingParameters struct {
type Parameters struct {
Salary float64
InitialInvestment float64
AnnualInvestment float64
Expand All @@ -20,7 +20,7 @@ type DebtRecyclingParameters struct {
ReinvestTaxRefunds bool
}

type DebtRecyclingData struct {
type Data struct {
DebtRecycled []float64
NonDeductibleInterest []float64
TaxDeductibleInterest []float64
Expand Down Expand Up @@ -122,8 +122,8 @@ func CAGR(initialValue, finalValue float64, numYears int) float64 {
return math.Pow(finalValue/initialValue, 1/float64(numYears)) - 1
}

func DebtRecycling(params DebtRecyclingParameters) (*DebtRecyclingData, error) {
data := &DebtRecyclingData{}
func DebtRecycling(params Parameters) (*Data, error) {
data := &Data{}

// Pre-allocate slices with the correct size
data.DebtRecycled = make([]float64, params.NumYears)
Expand Down
26 changes: 13 additions & 13 deletions internal/calc/calc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ func Test_CAGR(t *testing.T) {

func Test_DebtRecycling(t *testing.T) {
type drTests struct {
params DebtRecyclingParameters
expected *DebtRecyclingData
params Parameters
expected *Data
}

cases := []drTests{
{
params: DebtRecyclingParameters{
params: Parameters{
Salary: 150000,
InitialInvestment: 100000,
AnnualInvestment: 50000,
Expand All @@ -36,7 +36,7 @@ func Test_DebtRecycling(t *testing.T) {
ReinvestDividends: false,
ReinvestTaxRefunds: false,
},
expected: &DebtRecyclingData{
expected: &Data{
DebtRecycled: []float64{
100000,
150000,
Expand Down Expand Up @@ -151,7 +151,7 @@ func Test_DebtRecycling(t *testing.T) {
},
},
{
params: DebtRecyclingParameters{
params: Parameters{
Salary: 150000,
InitialInvestment: 100000,
AnnualInvestment: 50000,
Expand All @@ -164,7 +164,7 @@ func Test_DebtRecycling(t *testing.T) {
ReinvestDividends: true,
ReinvestTaxRefunds: false,
},
expected: &DebtRecyclingData{
expected: &Data{
PortfolioValue: []float64{
100000,
164160,
Expand Down Expand Up @@ -231,7 +231,7 @@ func Test_DebtRecycling(t *testing.T) {
},
},
// {
// params: DebtRecyclingParameters{
// params: Parameters{
// Salary: 150000,
// InitialInvestment: 100000,
// AnnualInvestment: 50000,
Expand All @@ -244,7 +244,7 @@ func Test_DebtRecycling(t *testing.T) {
// ReinvestDividends: false,
// ReinvestTaxRefunds: true,
// },
// expected: &DebtRecyclingData{
// expected: &Data{
// PortfolioValue: []float64{
// 100000,
// 166247.11052631578,
Expand Down Expand Up @@ -311,7 +311,7 @@ func Test_DebtRecycling(t *testing.T) {
// },
// },
// {
// params: DebtRecyclingParameters{
// params: Parameters{
// Salary: 150000,
// InitialInvestment: 100000,
// AnnualInvestment: 50000,
Expand All @@ -324,13 +324,13 @@ func Test_DebtRecycling(t *testing.T) {
// ReinvestDividends: true,
// ReinvestTaxRefunds: true,
// },
// expected: &DebtRecyclingData{
// expected: &Data{

// blah
//},
// },
// {
// params: DebtRecyclingParameters{
// params: Parameters{
// Salary: 150000,
// InitialInvestment: 100000,
// AnnualInvestment: 50000,
Expand All @@ -343,7 +343,7 @@ func Test_DebtRecycling(t *testing.T) {
// ReinvestDividends: false,
// ReinvestTaxRefunds: false,
// },
// expected: &DebtRecyclingData{},
// expected: &Data{},
// },
}
for i, c := range cases {
Expand Down Expand Up @@ -422,7 +422,7 @@ func compareAllFloat64Values(
got, want []float64,
fieldName string,
testIndex int,
params DebtRecyclingParameters,
params Parameters,
) {
if len(got) != len(want) {
t.Errorf(
Expand Down
6 changes: 3 additions & 3 deletions internal/charts/charts.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func ChartToTemplComponent(chart Renderable) templ.Component {
}

func Positions(
data *calc.DebtRecyclingData,
data *calc.Data,
years int,
ctx context.Context,
) (*echarts.Line, error) {
Expand Down Expand Up @@ -184,7 +184,7 @@ func Positions(
return line, nil
}

func Income(data *calc.DebtRecyclingData, years int, ctx context.Context) (*echarts.Bar, error) {
func Income(data *calc.Data, years int, ctx context.Context) (*echarts.Bar, error) {
bar := echarts.NewBar()

styleNonce := middleware.GetInlineStyleNonce(ctx)
Expand Down Expand Up @@ -259,7 +259,7 @@ func Income(data *calc.DebtRecyclingData, years int, ctx context.Context) (*echa
return bar, nil
}

func Interest(data *calc.DebtRecyclingData, years int, ctx context.Context) (*echarts.Bar, error) {
func Interest(data *calc.Data, years int, ctx context.Context) (*echarts.Bar, error) {
bar := echarts.NewBar()

styleNonce := middleware.GetInlineStyleNonce(ctx)
Expand Down
92 changes: 28 additions & 64 deletions internal/handlers/calc.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package handlers

import (
"fmt"
"net/http"
"strconv"

"debtrecyclingcalc.com/internal/calc"
"debtrecyclingcalc.com/internal/charts"
Expand All @@ -21,73 +21,12 @@ func CalcHandler(w http.ResponseWriter, r *http.Request) {
return
}

salary, err := strconv.ParseFloat(r.Form.Get("salary"), 64)
params, err := getFormParams(r)
if err != nil {
http.Error(w, "error parsing salary", http.StatusBadRequest)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

inititalInvestmentAmount, err := strconv.ParseFloat(r.Form.Get("initial_investment"), 64)
if err != nil {
http.Error(w, "error parsing initial investment amount", http.StatusBadRequest)
return
}

annualInvestmentAmount, err := strconv.ParseFloat(r.Form.Get("annual_investment"), 64)
if err != nil {
http.Error(w, "error parsing annual investment amount", http.StatusBadRequest)
return
}

mortgageSize, err := strconv.ParseFloat(r.Form.Get("mortgage_size"), 64)
if err != nil {
http.Error(w, "error parsing mortgage size", http.StatusBadRequest)
return
}

mortgageInterestRate, err := strconv.ParseFloat(r.Form.Get("mortgage_interest_rate"), 64)
if err != nil {
http.Error(w, "error parsing mortgage interest rate", http.StatusBadRequest)
return
}

dividendReturnRate, err := strconv.ParseFloat(r.Form.Get("dividend_return_rate"), 64)
if err != nil {
http.Error(w, "error parsing dividend return rate", http.StatusBadRequest)
return
}

capitalGrowthRate, err := strconv.ParseFloat(r.Form.Get("capital_growth_rate"), 64)
if err != nil {
http.Error(w, "error parsing capital growth rate", http.StatusBadRequest)
return
}

years, err := strconv.Atoi(r.Form.Get("years"))
if err != nil {
http.Error(w, "error parsing years", http.StatusBadRequest)
return
}

country := r.Form.Get("country")

reinvestDividends := r.Form.Get("reinvest_dividends") == "on"
reinvestTaxRefunds := r.Form.Get("reinvest_tax_refunds") == "on"

params := &calc.DebtRecyclingParameters{
Salary: salary,
InitialInvestment: inititalInvestmentAmount,
AnnualInvestment: annualInvestmentAmount,
MortgageSize: mortgageSize,
MortgageInterestRate: mortgageInterestRate / 100,
DividendReturnRate: dividendReturnRate / 100,
CapitalGrowthRate: capitalGrowthRate / 100,
NumYears: years,
Country: country,
ReinvestDividends: reinvestDividends,
ReinvestTaxRefunds: reinvestTaxRefunds,
}

// if params is empty respond with error
data, err := calc.DebtRecycling(*params)
if err != nil {
Expand Down Expand Up @@ -115,6 +54,31 @@ func CalcHandler(w http.ResponseWriter, r *http.Request) {

results := templates.Results(data, params, positionsChart, incomeChart, interestChart)

w.Header().
Set("HX-Push-Url", fmt.Sprintf("/?salary=%.2f"+
"&initial_investment=%.2f"+
"&annual_investment=%.2f"+
"&mortgage_size=%.2f"+
"&mortgage_interest_rate=%.2f"+
"&dividend_return_rate=%.2f"+
"&capital_growth_rate=%.2f"+
"&years=%d"+
"&country=%s"+
"&reinvest_dividends=%t"+
"&reinvest_tax_refunds=%t",
params.Salary,
params.InitialInvestment,
params.AnnualInvestment,
params.MortgageSize,
params.MortgageInterestRate*100,
params.DividendReturnRate*100,
params.CapitalGrowthRate*100,
params.NumYears,
params.Country,
params.ReinvestDividends,
params.ReinvestTaxRefunds,
))

err = results.Render(r.Context(), w)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
Expand Down
22 changes: 16 additions & 6 deletions internal/handlers/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)

c := templates.NotFound()
err := templates.Layout(c, "Not Fountempl.WithStatus(http.StatusNotFound)d", buildinfo.GitTag, buildinfo.BuildDate).
err := templates.Layout(c, "Not Found", buildinfo.GitTag, buildinfo.BuildDate).
Render(r.Context(), w)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
Expand All @@ -28,20 +28,30 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
return
}

params := &calc.DebtRecyclingParameters{
params := &calc.Parameters{
Salary: 150000,
InitialInvestment: 100000,
AnnualInvestment: 50000,
MortgageSize: 600000,
MortgageInterestRate: 0.05,
DividendReturnRate: 0.02,
CapitalGrowthRate: 0.08,
MortgageInterestRate: 0.0500,
DividendReturnRate: 0.0200,
CapitalGrowthRate: 0.0800,
NumYears: 10,
Country: "au",
ReinvestDividends: true,
ReinvestTaxRefunds: true,
}

query := r.URL.Query()
var err error
if len(query) != 0 {
params, err = getQueryParams(query)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}

data, err := calc.DebtRecycling(*params)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
Expand All @@ -68,7 +78,7 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {

index := templates.Index(
templates.Hero(),
templates.Form(),
templates.Form(params),
templates.Results(data, params, positionsChart, incomeChart, interestChart),
)

Expand Down
Loading

0 comments on commit 0cb45eb

Please sign in to comment.