<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> </section> <section class="form inputs"> <div class="hint"> <img class="icon" src="/assets/image/icon/question-circle.svg" alt=""> <div class="tooltip"> <p>Assumes borrower is not self employed, not bankrupt in the past 7 years, a citizen, and intends to occupy the property.</p> </div> </div> <h3>Borrower</h3> <label>Number of Borrowers</label> <input :value="estimate.borrowers" @input="(e) => estimate.borrowers = stripInt(e)"> <label>Credit Score</label> <input :value="estimate.creditScore" @input="(e) => estimate.creditScore = stripInt(e)"> <label>Monthly Income ($)</label> <input :value="estimate.mIncome" @input="(e) => estimate.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" > <label>Purchase</label> <input type="radio" name="transaction_type" value="1" v-model="estimate.transaction"> <label>Refinance</label> </section> <section class="form inputs"> <h3>Property Details</h3> <label>Price ($)</label> <input :value="estimate.price" @input="setPrice"> <label>Type</label> <select id="" name="" v-model="estimate.property"> <option value="attched">Single Family Attached</option> <option value="detached">Single Family Detached</option> <option value="lorise">Lo-rise (4 stories or less)</option> <option value="hirise">Hi-rise (over 4 stories)</option> </select> </section> <section class="radios form"> <h3>Loan Type</h3> <input type="radio" name="loan_type" value="conv" v-model="loans[sel].type" > <label>Conventional</label> <input type="radio" name="loan_type" value="fha" v-model="loans[sel].type"> <label>FHA</label> <input type="radio" name="loan_type" value="va" v-model="loans[sel].type"> <label>VA</label> <input type="radio" name="loan_type" value="usda" v-model="loans[sel].type"> <label>USDA</label> </section> <section class="form inputs"> <h3>Loan Details</h3> <label>Loan Term (years)</label> <input :value="loans[sel].term" @input="(e) => loans[sel].term = strip(e)"> <label>Loan Program</label> <select id="" name="" v-model="loans[sel].program"> <option value="none">None</option> </select> <label>Loan to Value (%)</label> <input :value="loans[sel].ltv" @input="setLtv"> <label>Loan Amount ($)</label> <input :value="loans[sel].amount" @input="setAmount"> <label>Housing Expense DTI (%) - Optional</label> <input :value="loans[sel].housingDti" @input="setHousingDti"> <label>Total DTI (%) - Optional</label> <input :value="loans[sel].dti" @input="setDti"> <label>Home Owner's Association ($/month)</label> <input :value="loans[sel].hoa" @input="(e) => { loans[sel].hoa = strip(e) }"> <label>Interest Rate (%)</label> <input :value="loans[sel].interest" @input="(e) => { loans[sel].interest = stripPerc(e) }"> <label>Days of Interest</label> <input :value="loans[sel].interestDays" @input="(e) => {loans[sel].interestDays = stripInt(e)}"> <label>Hazard Insurance Escrow (months)</label> <input :value="loans[sel].hazardEscrow" @input="(e) => {loans[sel].hazardEscrow = stripInt(e)}"> <label>Hazard Insurance ($/month)</label> <input :value="loans[sel].hazard" @input="(e) => {loans[sel].hazard = strip(e)}"> <label>Real Estate Tax Escrow (months)</label> <input :value="loans[sel].taxEscrow" @input="e => {loans[sel].taxEscrow = stripInt(e)}"> <label>Real Estate Tax ($/month)</label> <input :value="loans[sel].tax" @input="(e) => {loans[sel].tax = strip(e)}"> </section> <section class="form inputs"> <h3>Fees</h3> <div v-for="(fee, indx) in estimate.loans[sel].fees" :key="fee.name + indx" class="fee" > <label> ${{fee.amount}}{{fee.perc && ` (${fee.perc}%)`}} - {{fee.name}}<br> {{fee.type}} </label> <img width="21" height="21" src="/assets/image/icon/x-red.svg" @click="() => estimate.loans[sel].fees.splice(indx, 1)"> </div> <button @click="resetFees">Reset</button> <button @click="createFee">New</button> </section> <fee-dialog v-if="newFee" :heading="'New Fee'" :initial="{}" :price="estimate.price" @close="() => newFee = null" @save="addFee" /> <section class="form radios"> <h3>Mortgage Insurance</h3> <input type="radio" name="transaction_type" value="transaction" @input="e => estimate.transaction = 0" selected="estimate.transaction == 0"> <label>1.43% - National MI</label> <input type="radio" name="transaction_type" value="refinance" @input="e => estimate.transaction = 1" selected="estimate.transaction == 1"> <label>0.73% - MGIC</label> </section> <section class="form inputs"> <button @click="generate">Generate</button> <div class="errors"> <span v-for="e in errors">{{e}}</span> </div> </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: [], } // 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, } 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: 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) { if (!isDebit) fee.amount = fee.amount * -1 this.estimate.loans[this.sel].fees.push(fee) 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) { let ltv = strip(e) let loan = this.loans[this.sel] if (!this.estimate.price) return if (ltv > 100) ltv = 100 if (ltv < 0) ltv = 0 loan.ltv = ltv loan.amount = (ltv / 100 * this.estimate.price).toFixed(2) } // Changes loan.amount's <input> and data() values, then syncs with data.ltv function setAmount(e) { let amount = strip(e) let loan = this.loans[this.sel] if (!this.estimate.price) return if (amount > loan.price) amount = loan.price if (amount < 0) amount = 0 loan.amount = amount 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] if (!loan.price) return if (dti > 100) dti = 100 if (dti < 0) dti = 0 e.target.value = dti loan.dti = dti } function setHousingDti(e) { let housingDti = strip(e) let loan = this.loans[this.sel] if (!loan.price) return if (housingDti > 100) housingDti = 100 if (housingDti < 0) housingDti = 0 e.target.value = housingDti loan.housingDti = housingDti } function generate() { this.errors = this.validate() } function validate() { let errors = [] const estimate = this.estimate // Alternative attribute names for error messages const names = { term: "loan term", ltv: "loan to value", hazard: "hazard insurance", hazardEscrow: "hazard insurance escrow", } if (!estimate.property) { errors.push("Missing property type.") } else if (!estimate.price) { errors.push("Missing property price.") } else if (!estimate.borrowers) { errors.push("Missing number of borrowers.") } else if (!estimate.creditScore) { errors.push("Missing credit score.") } else if (!estimate.mIncome) { errors.push("Missing monthly income.") } return errors } // Percentage values of fees always takek precedent over amounts. The conversion // happens in setPrice() export default { components: { Dialog, FeeDialog }, methods: { setPrice, setLtv, setAmount, setDti, setHousingDti, strip, stripInt, stripLetters, del, create, createFees, createFee, resetFees, addFee, generate, validate }, props: ['user', 'fees'], data() { return { estimate: estimate, loans: estimate.loans, sel: 0, newFee: null, errors: [], } }, created() { this.estimate.loans.forEach(l => l.fees = this.createFees()) } } </script>