Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 
 
 
 

253 satır
6.3 KiB

  1. <template>
  2. <div id="new" class="page">
  3. <h2>New Loan</h2>
  4. <section class="loans-list">
  5. <h3 v-for="(l, indx) in loans"
  6. :class="sel == indx ? 'sel' : ''"
  7. @click="() => sel = indx"
  8. >
  9. {{l.title}}
  10. </h3>
  11. <div class="add">
  12. <svg @click="create"
  13. xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor"
  14. class="bi bi-plus" viewBox="0 0 16 16"> <path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0
  15. 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>
  16. </div>
  17. </section>
  18. <loan-details v-if="hash == '#new'"
  19. :estimate="estimate"
  20. :loans="estimate.loans"
  21. :sel="sel"
  22. :loan="loan"
  23. :token="token"
  24. @update:name="(name) => loan.title = name"
  25. @del="del"
  26. @update:borrowerNum="(b) => estimate.borrower.num = b"
  27. @update:borrowerCredit="(c) => estimate.borrower.credit = c"
  28. @update:borrowerIncome="(m) => estimate.borrower.income = Math.round(m*100)"
  29. @update:transaction="(t) => estimate.transaction = t"
  30. @update:occupancy="(t) => estimate.occupancy = t"
  31. @update:price="setPrice"
  32. @update:property="(p) => estimate.property = p"
  33. @update:loanType="changeLoanType"
  34. @update:term="(lt) => loan.term = lt"
  35. @update:program="(p) => loan.program = p"
  36. @update:ltv="setLtv"
  37. @update:amount="setAmount"
  38. @update:housingDti="setHousingDti"
  39. @update:dti="setDti"
  40. @update:hoi="(hoi) => loan.hoi = Math.round(hoi*100)"
  41. @update:interest="(i) => loan.interest = i"
  42. @update:interestDays="(d) => loans[sel].interestDays = d"
  43. @update:hazardEscrow="(h) => loans[sel].hazardEscrow = h"
  44. @update:hazard="(h) => loan.hazard = Math.round(h * 100)"
  45. @update:taxEscrow="(t) => loans[sel].taxEscrow = t"
  46. @update:tax="(t) => loan.tax = Math.round(t*100)"
  47. @update:manualMI="perc => loan.mi.rate = perc"
  48. @toggle:manualMIMonthly=
  49. "() => loans[sel].mi.monthly = !loans[sel].mi.monthly"
  50. @continue="generate"
  51. />
  52. <loan-summary v-if="hash == '#new/summary'"
  53. :loan="loan"
  54. :downpayment="estimate.price - loan.amount"
  55. :token="token"
  56. :estimate="estimate"/>
  57. </div>
  58. </template>
  59. <script>
  60. import { stripLetters, strip, stripInt, stripPerc } from "../../helpers.js"
  61. import LoanDetails from "./details.vue"
  62. import LoanSummary from "./summary.vue"
  63. // The default values of a new loan
  64. const example = {
  65. title: "Example",
  66. type: {},
  67. term: 0,
  68. ltv: 0, // Loan to home value ratio
  69. dti: 0,
  70. housingDti: 0,
  71. amount: 0,
  72. interest: 0,
  73. interestDays: 0,
  74. hazard: 0, // Hazard insurance monthly payment
  75. hazardEscrow: 0, // Hazard insurance escrow in months (0 is none)
  76. tax: 0, // Real Estate taxes monthly payment
  77. taxEscrow: 0, // Months to escrow (0 is none)
  78. hoi: 10000, // Home owner's association monthly fee
  79. program: "",
  80. pud: true, // Property under development
  81. zip: '',
  82. fees: [],
  83. mi: { monthly: false, rate: 0 }
  84. }
  85. // The default loans on a new estimate
  86. const loans = [
  87. Object.assign({mi: { monthly: false, rate: 0 }, type: {}}, example)
  88. ]
  89. // Default estimate fields
  90. const estimate = {
  91. property: "",
  92. transaction: "",
  93. occupancy: "",
  94. price: 0,
  95. borrower: {num: 0, credit: 0, income: 0},
  96. loans: loans,
  97. }
  98. function loan() {
  99. return this.estimate.loans[this.sel]
  100. }
  101. // Clone loan from initial example as a new loan
  102. function create() {
  103. this.estimate.loans.push(
  104. Object.assign(
  105. {mi: { monthly: false, rate: 0 }, type: {}},
  106. example, {fees: this.createFees()}
  107. )
  108. )
  109. }
  110. function createFees() {
  111. return this.fees.map(f => Object.assign({}, f))
  112. }
  113. function del() {
  114. if (this.loans.length > 1) {
  115. let x = this.sel
  116. this.sel = 0
  117. this.loans.splice(x, 1)
  118. }
  119. }
  120. // Updates the property price for all loans and their fee amounts.
  121. function setPrice(value) {
  122. this.estimate.price = Math.round(value*100)
  123. this.estimate.loans[this.sel].fees.forEach(fee => {
  124. if (fee.perc) fee.amount = Math.round(fee.perc * value)
  125. })
  126. this.estimate.loans.forEach(l => {l.ltv = 0; l.amount = 0})
  127. }
  128. // Changes loan.ltv's <input> and data() values, then syncs with data.amount
  129. function setLtv(e) {
  130. let ltv = strip(e)
  131. if (!this.estimate.price) return
  132. if (ltv > 100) ltv = 100
  133. if (ltv < 0) ltv = 0
  134. this.loan.ltv = ltv
  135. let num = ltv / 100 * this.estimate.price
  136. this.loan.amount = Math.round(num)
  137. }
  138. // Changes loan.amount\'s <input> and data() values, then syncs with data.ltv
  139. // Loan amount is in cents but LTV is in decimals so some rounding needs to be done.
  140. function setAmount(e) {
  141. let amount = strip(e) * 100
  142. if (!this.estimate.price) return
  143. if (amount > this.estimate.price) amount = this.estimate.price
  144. if (amount < 0) amount = 0
  145. this.loan.amount = Math.round(amount)
  146. let num = amount / this.estimate.price
  147. this.loan.ltv = Math.round(num*100)
  148. }
  149. function setDti(e) {
  150. let dti = strip(e)
  151. let loan = this.loans[this.sel]
  152. if (!loan.price) return
  153. if (dti > 100) dti = 100
  154. if (dti < 0) dti = 0
  155. e.target.value = dti
  156. loan.dti = dti
  157. }
  158. function setHousingDti(e) {
  159. let housingDti = strip(e)
  160. let loan = this.loans[this.sel]
  161. if (!loan.price) return
  162. if (housingDti > 100) housingDti = 100
  163. if (housingDti < 0) housingDti = 0
  164. e.target.value = housingDti
  165. loan.housingDti = housingDti
  166. }
  167. function changeLoanType(id) {
  168. if (id == this.loan.type.id) return
  169. // Set mandatory upfront MIP in fees if type is FHA
  170. let i = this.loan.fees.findIndex(
  171. l => l.required && l.name == "FHA Upfront MIP"
  172. )
  173. if (id == 2) {
  174. this.loan.fees.push({
  175. amount: Math.round(this.estimate.price*1.75/100),
  176. perc: 1.75,
  177. name: "FHA Upfront MIP",
  178. type: "Required",
  179. id: 0,
  180. required: true
  181. })
  182. } else if (i >= 0) {
  183. this.loan.fees.splice(i, 1)
  184. }
  185. this.loan.type.id = id
  186. }
  187. function generate() {
  188. window.location.hash = 'new/summary'
  189. }
  190. // Percentage values of fees always take precedent over amounts. The conversion
  191. // happens in setPrice()
  192. export default {
  193. components: { LoanSummary, LoanDetails },
  194. methods: {
  195. generate, createFees, del, create, setPrice, setLtv, setAmount,
  196. setDti, setHousingDti, changeLoanType
  197. },
  198. computed: { loan },
  199. props: ['user', 'fees', 'token'],
  200. data() {
  201. return {
  202. estimate: estimate,
  203. loans: estimate.loans,
  204. sel: 0,
  205. errors: [],
  206. hash: window.location.hash
  207. }
  208. },
  209. created() {
  210. this.estimate.loans.forEach(l => l.fees = this.createFees())
  211. window.addEventListener("hashchange",
  212. () => this.hash = window.location.hash)
  213. }
  214. }
  215. </script>