diff --git a/components/app.vue b/components/app.vue index 7b85f77..29c4aef 100644 --- a/components/app.vue +++ b/components/app.vue @@ -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" diff --git a/components/new.vue b/components/new/details.vue similarity index 64% rename from components/new.vue rename to components/new/details.vue index 45c1419..e36dbcf 100644 --- a/components/new.vue +++ b/components/new/details.vue @@ -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> diff --git a/components/new/new.vue b/components/new/new.vue new file mode 100644 index 0000000..2836d3d --- /dev/null +++ b/components/new/new.vue @@ -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> diff --git a/components/new/summary.vue b/components/new/summary.vue new file mode 100644 index 0000000..17a6529 --- /dev/null +++ b/components/new/summary.vue @@ -0,0 +1,5 @@ +<template> +</template> + +<script> +</script> diff --git a/skouter.go b/skouter.go index 82f3d6f..8e2ad7a 100644 --- a/skouter.go +++ b/skouter.go @@ -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