Selaa lähdekoodia

Move loan details into seperate component

Estimate creation process  will be more clear if the details entered by
the user is in a seperate page from the summary information shown before
PDF generation. It will also help keep the app looking minimal.
master
Immanuel Onyeka 1 vuosi sitten
vanhempi
commit
0d7428b276
5 muutettua tiedostoa jossa 218 lisäystä ja 148 poistoa
  1. +1
    -1
      components/app.vue
  2. +40
    -144
      components/new/details.vue
  3. +152
    -0
      components/new/new.vue
  4. +5
    -0
      components/new/summary.vue
  5. +20
    -3
      skouter.go

+ 1
- 1
components/app.vue Näytä tiedosto

@@ -28,7 +28,7 @@
import SideBar from "./sidebar.vue"
import Spinner from "./spinner.vue"
import Home from "./home.vue"
import NewEstimate from "./new.vue"
import NewEstimate from "./new/new.vue"
import Estimates from "./estimates.vue"
import Settings from "./settings.vue"
import SignOut from "./sign-out.vue"


components/new.vue → components/new/details.vue Näytä tiedosto

@@ -1,31 +1,10 @@
<template>
<div id="new" class="page">

<h2>New Loan</h2>

<section class="loans-list">

<h3 v-for="(l, indx) in loans"
:class="sel == indx ? 'sel' : ''"
@click="() => sel = indx"
>
{{l.title}}
</h3>

<div class="add">
<svg @click="create"
xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-plus" viewBox="0 0 16 16"> <path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0
0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"/> </svg>
</div>
</section>

<section class="form inputs">
<h3>Loan</h3>
<label>Name</label>
<input :value="loans[sel].title" required
@input="(e) => loans[sel].title = stripLetters(e)">
<button @click="del">Delete</button>
@input="(e) => $emit('update:name', stripLetters(e))">
<button @click="() => $emit('del')">Delete</button>
</section>

<section class="form inputs">
@@ -41,32 +20,38 @@ class="bi bi-plus" viewBox="0 0 16 16"> <path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0
<h3>Borrower</h3>
<label>Number of Borrowers</label>
<input :value="estimate.borrowers"
@input="(e) => estimate.borrowers = stripInt(e)">
@input="(e) => $emit('update:borrowers', stripInt(e))">
<label>Credit Score</label>
<input :value="estimate.creditScore"
@input="(e) => estimate.creditScore = stripInt(e)">
@input="(e) => $emit('update:creditScore', stripInt(e))">
<label>Monthly Income ($)</label>
<input :value="estimate.mIncome"
@input="(e) => estimate.mIncome = strip(e)">
@input="(e) => $emit('update:mIncome', strip(e))">

</section>

<section class="radios form">
<h3>Transaction Type</h3>
<input selected type="radio" name="transaction_type" value="0"
v-model="estimate.transaction" >
:value="estimate.transaction"
@input="() => $emit('update:transaction', 0)"
>
<label>Purchase</label>
<input type="radio" name="transaction_type" value="1"
v-model="estimate.transaction">
:value="estimate.transaction"
@input="() => $emit('update:transaction', 1)"
>
<label>Refinance</label>
</section>

<section class="form inputs">
<h3>Property Details</h3>
<label>Price ($)</label>
<input :value="estimate.price" @input="setPrice">
<input :value="estimate.price" @input="(e) => $emit('update:price', strip(e))">
<label>Type</label>
<select id="" name="" v-model="estimate.property">
<select id="" name=""
:value="estimate.property"
@change="(e) => $emit('update:property', e.target.value)">
<option value="attched">Single Family Attached</option>
<option value="detached">Single Family Detached</option>
<option value="lorise">Lo-rise (4 stories or less)</option>
@@ -159,13 +144,7 @@ v-model="estimate.transaction">

<section class="form radios">
<h3>Mortgage Insurance</h3>
<input type="radio">
<span>
<label>Custom %</label>
<input type="text" :value="estimate.loans[sel].mi"
@input="e => estimate.loans[sel].mi = strip(e)"
selected="estimate.transaction == 0">
</span>

</section>

<section class="form inputs">
@@ -178,81 +157,21 @@ v-model="estimate.transaction">

</section>

</div>
</template>

<script>
import Dialog from "./dialog.vue"
import FeeDialog from "./fee-dialog.vue"
import { stripLetters, strip, stripInt, stripPerc } from "../helpers.js"

// The default values of a new estimate
const example = {
title: "Example",
type: "",
term: 0,
ltv: 0, // Loan to home value ratio
dti: 0,
housingDti: 0,
amount: 0,
interest: 0,
interestDays: 0,
hazard: 0, // Hazard insurance monthly payment
hazardEscrow: 0, // Hazard insurance escrow in months (0 is none)
tax: 0, // Real Estate taxes monthly payment
taxEscrow: 0, // Months to escrow (0 is none)
hoa: 100, // Home owner's association monthly fee
program: "",
pud: true, // Property under development
zip: '',
fees: [],
mi: 0
}

// The default loans on a new estimate
const loans = [
Object.assign({}, example,),
Object.assign(
Object.assign({}, example),
{title: "Another One",}
),
]

// Default estimate fields
const estimate = {
property: "",
transaction: 0,
price: 0,
borrowers: 0,
creditScore: 0,
mIncome: 0,
loans: loans,
}
import FeeDialog from "../fee-dialog.vue"
import { stripLetters, strip, stripInt, stripPerc } from "../../helpers.js"

const newFee = {
name: '', type: '', amount: 0, perc: 0
}

// Clone loan from initial example as a new loan
function create() {
this.estimate.loans.push(
Object.assign({}, example, {fees: this.createFees()})
)
}

function createFees() {
return this.fees.map(f => Object.assign({}, f))
}

// Setup this.newFee for the creation dialog
function createFee() {
this.newFee = Object.assign({}, newFee)
}

function resetFees() {
this.estimate.loans[this.sel].fees = this.createFees()
}

// If valid, add the current this.newFee to the array of fees and reset
// this.newFee to null
function addFee(fee, isDebit) {
@@ -262,13 +181,6 @@ function addFee(fee, isDebit) {
this.newFee = null
}

function del() {
if (this.loans.length > 1) {
let x = this.sel
this.sel = 0
this.loans.splice(x, 1)
}
}

// Changes loan.ltv's <input> and data() values, then syncs with data.amount
function setLtv(e) {
@@ -296,15 +208,6 @@ function setAmount(e) {
loan.ltv = (amount / this.estimate.price * 100).toFixed(2)
}

// Updates the property price for all loans and their fee amounts.
function setPrice(e) {
let value = strip(e)
this.estimate.price = value
this.estimate.loans[this.sel].fees.forEach(fee => {
if (fee.perc) fee.amount = (fee.perc / 100 * value).toFixed(2)
})
}

function setDti(e) {
let dti = strip(e)
let loan = this.loans[this.sel]
@@ -329,22 +232,6 @@ function setHousingDti(e) {
loan.housingDti = housingDti
}

function generate() {
this.errors = this.validate()
if (this.errors.length) return
fetch(`/api/estimate`,
{
method: 'POST',
body: JSON.stringify( this.estimate ),
headers: {
"Accept": "application/json",
"Authorization": `Bearer ${token}`,
},
}
)
}

function validate() {
let errors = []
const estimate = this.estimate
@@ -380,31 +267,40 @@ function validate() {
errors.push("Loan term cannot be zero")
}
})

return errors
}

// Percentage values of fees always takek precedent over amounts. The conversion
// happens in setPrice()
export default {
components: { Dialog, FeeDialog },
components: { FeeDialog },
methods: {
setPrice, setLtv, setAmount, setDti, setHousingDti, strip, stripInt,
stripLetters, del, create, createFees, createFee, resetFees,
addFee, generate, validate
setLtv, setAmount, setDti, setHousingDti, strip, stripInt,
stripLetters, stripPerc, createFee, addFee, validate
},
props: ['user', 'fees'],
props: ['estimate', 'loans', 'sel'],
// Loan updates assume the currently selected loan is being modified, and
// $emit has no need to clarify.
emits: [
'del',
'update:name',
'update:borrowers',
'update:creditScore',
'update:mIncome',
'update:transaction',
'update:price',
'update:propertyType',
'update:loanType',
'update:loanTerm',
'update:loanProgram',
'update:ltv'
],
data() {
return {
estimate: estimate,
loans: estimate.loans,
sel: 0,
newFee: null,
errors: [],
hash: window.location.hash
}
},
created() {
this.estimate.loans.forEach(l => l.fees = this.createFees())
}
}
</script>

+ 152
- 0
components/new/new.vue Näytä tiedosto

@@ -0,0 +1,152 @@
<template>
<div id="new" class="page">

<h2>New Loan</h2>

<section class="loans-list">

<h3 v-for="(l, indx) in loans"
:class="sel == indx ? 'sel' : ''"
@click="() => sel = indx"
>
{{l.title}}
</h3>

<div class="add">
<svg @click="create"
xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
class="bi bi-plus" viewBox="0 0 16 16"> <path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0
0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"/> </svg>
</div>
</section>

<loan-details v-if="hash == '#new'"
:estimate="estimate"
:loans="estimate.loans"
:sel="sel"
@update:name="(name) => loans[sel].title = name"
@del="del"
/>
<summary v-if="hash == '#new/summary'"/>

</div>
</template>

<script>
import LoanDetails from "./details.vue"
import Summary from "./summary.vue"

// The default values of a new estimate
const example = {
title: "Example",
type: "",
term: 0,
ltv: 0, // Loan to home value ratio
dti: 0,
housingDti: 0,
amount: 0,
interest: 0,
interestDays: 0,
hazard: 0, // Hazard insurance monthly payment
hazardEscrow: 0, // Hazard insurance escrow in months (0 is none)
tax: 0, // Real Estate taxes monthly payment
taxEscrow: 0, // Months to escrow (0 is none)
hoa: 100, // Home owner's association monthly fee
program: "",
pud: true, // Property under development
zip: '',
fees: [],
mi: {}
}

// The default loans on a new estimate
const loans = [
Object.assign({}, example,),
Object.assign(
Object.assign({}, example),
{title: "Another One",}
),
]

// Default estimate fields
const estimate = {
property: "",
transaction: 0,
price: 0,
borrowers: 0,
creditScore: 0,
mIncome: 0,
loans: loans,
}

// Clone loan from initial example as a new loan
function create() {
this.estimate.loans.push(
Object.assign({}, example, {fees: this.createFees()})
)
}


function createFees() {
return this.fees.map(f => Object.assign({}, f))
}


function del() {
if (this.loans.length > 1) {
let x = this.sel
this.sel = 0
this.loans.splice(x, 1)
}
}

// Updates the property price for all loans and their fee amounts.
function setPrice(value) {
this.estimate.price = value
this.estimate.loans[this.sel].fees.forEach(fee => {
if (fee.perc) fee.amount = (fee.perc / 100 * value).toFixed(2)
})
}

function generate() {
this.errors = this.validate()
if (this.errors.length) return
fetch(`/api/estimate`,
{
method: 'POST',
body: JSON.stringify( this.estimate ),
headers: {
"Accept": "application/json",
"Authorization": `Bearer ${token}`,
},
}
)

return errors
}
// Percentage values of fees always takek precedent over amounts. The conversion
// happens in setPrice()
export default {
components: { Summary, LoanDetails },
methods: {
generate, createFees, del, create, setPrice
},
props: ['user', 'fees'],
data() {
return {
estimate: estimate,
loans: estimate.loans,
sel: 0,
errors: [],
hash: window.location.hash
}
},
created() {
this.estimate.loans.forEach(l => l.fees = this.createFees())
window.addEventListener("hashchange", () => this.hash = window.location.hash)
}
}
</script>

+ 5
- 0
components/new/summary.vue Näytä tiedosto

@@ -0,0 +1,5 @@
<template>
</template>

<script>
</script>

+ 20
- 3
skouter.go Näytä tiedosto

@@ -85,10 +85,11 @@ type LoanType struct {
}

type Loan struct {
Id int `json:id`
EstimateId int `json:estimate_id`
Type LoanType `json:"loanType"`
Id int `json:id`
EstimateId int `json:estimate_id`
Type LoanType `json:"loanType"`
Amount int `json:"loanAmount"`
Amortization string `json:"loanAmount"`
Term int `json:"term"`
Ltv float32 `json:"ltv"`
Dti float32 `json:"dti"`
@@ -112,6 +113,18 @@ type MI struct {
InitialAmount float32
}

type Result struct {
User int `json:"user"`
Borrower Borrower `json:"borrower"`
Transaction string `json:"transaction"`
Price int `json:"price"`
Property string `json:"property"`
Occupancy string `json:"occupancy"`
Zip string `json:"zip"`
Pud bool `json:"pud"`
Loans []Loan `json:"loans"`
}

type Estimate struct {
Id int `json:"id"`
User int `json:"user"`
@@ -547,6 +560,10 @@ func fetchMi(db *sql.DB, estimate *Estimate, pos int) []MI {
return result
}

// Make comparison PDF
func generatePDF(w http.ResponseWriter, db *sql.DB, r *http.Request) {
}

func login(w http.ResponseWriter, db *sql.DB, r *http.Request) {
var id int
var role string


Loading…
Peruuta
Tallenna