Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 
 
 

255 rindas
6.4 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 estimate.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) => estimate.loans[sel].interestDays = d"
  43. @update:hazardEscrow="(h) => estimate.loans[sel].hazardEscrow = h"
  44. @update:hazard="(h) => loan.hazard = Math.round(h * 100)"
  45. @update:taxEscrow="(t) => estimate.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. "() => estimate.loans[sel].mi.monthly = !estimate.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. @update="(e) => estimate = e" />
  58. </div>
  59. </template>
  60. <script>
  61. import { stripLetters, strip, stripInt, stripPerc } from "../../helpers.js"
  62. import LoanDetails from "./details.vue"
  63. import LoanSummary from "./summary.vue"
  64. // The default values of a new loan
  65. const example = {
  66. title: "Example",
  67. type: {},
  68. term: 0,
  69. ltv: 0, // Loan to home value ratio
  70. dti: 0,
  71. housingDti: 0,
  72. amount: 0,
  73. interest: 0,
  74. interestDays: 0,
  75. hazard: 0, // Hazard insurance monthly payment
  76. hazardEscrow: 0, // Hazard insurance escrow in months (0 is none)
  77. tax: 0, // Real Estate taxes monthly payment
  78. taxEscrow: 0, // Months to escrow (0 is none)
  79. hoi: 10000, // Home owner's association monthly fee
  80. program: "",
  81. pud: true, // Property under development
  82. zip: '',
  83. fees: [],
  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. let 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. // Move current loan to last inserted
  110. this.sel = this.estimate.loans.length - 1
  111. }
  112. function createFees() {
  113. return this.fees.map(f => Object.assign({}, f))
  114. }
  115. function del() {
  116. if (this.estimate.loans.length > 1) {
  117. let x = this.sel
  118. this.sel = 0
  119. this.estimate.loans.splice(x, 1)
  120. }
  121. }
  122. // Updates the property price for all loans and their fee amounts.
  123. function setPrice(value) {
  124. this.estimate.price = Math.round(value*100)
  125. this.estimate.loans[this.sel].fees.forEach(fee => {
  126. if (fee.perc) fee.amount = Math.round(fee.perc * value)
  127. })
  128. this.estimate.loans.forEach(l => {l.ltv = 0; l.amount = 0})
  129. }
  130. // Changes loan.ltv's <input> and data() values, then syncs with data.amount
  131. function setLtv(e) {
  132. let ltv = strip(e)
  133. if (!this.estimate.price) return
  134. if (ltv > 100) ltv = 100
  135. if (ltv < 0) ltv = 0
  136. this.loan.ltv = ltv
  137. let num = ltv / 100 * this.estimate.price
  138. this.loan.amount = Math.round(num)
  139. }
  140. // Changes loan.amount\'s <input> and data() values, then syncs with data.ltv
  141. // Loan amount is in cents but LTV is in decimals so some rounding needs to be done.
  142. function setAmount(e) {
  143. let amount = strip(e) * 100
  144. if (!this.estimate.price) return
  145. if (amount > this.estimate.price) amount = this.estimate.price
  146. if (amount < 0) amount = 0
  147. this.loan.amount = Math.round(amount)
  148. let num = amount / this.estimate.price
  149. this.loan.ltv = Math.round(num*100)
  150. }
  151. function setDti(e) {
  152. let dti = strip(e)
  153. let loan = this.estimate.loans[this.sel]
  154. if (!loan.price) return
  155. if (dti > 100) dti = 100
  156. if (dti < 0) dti = 0
  157. e.target.value = dti
  158. loan.dti = dti
  159. }
  160. function setHousingDti(e) {
  161. let housingDti = strip(e)
  162. let loan = this.estimate.loans[this.sel]
  163. if (!loan.price) return
  164. if (housingDti > 100) housingDti = 100
  165. if (housingDti < 0) housingDti = 0
  166. e.target.value = housingDti
  167. loan.housingDti = housingDti
  168. }
  169. function changeLoanType(id) {
  170. if (id == this.loan.type.id) return
  171. // Set mandatory upfront MIP in fees if type is FHA
  172. let i = this.loan.fees.findIndex(
  173. l => l.required && l.name == "FHA Upfront MIP"
  174. )
  175. if (id == 2) {
  176. this.loan.fees.push({
  177. amount: Math.round(this.estimate.price*1.75/100),
  178. perc: 1.75,
  179. name: "FHA Upfront MIP",
  180. type: "Required",
  181. id: 0,
  182. required: true
  183. })
  184. } else if (i >= 0) {
  185. this.loan.fees.splice(i, 1)
  186. }
  187. this.loan.type.id = id
  188. }
  189. function generate() {
  190. window.location.hash = 'new/summary'
  191. }
  192. // Percentage values of fees always take precedent over amounts. The conversion
  193. // happens in setPrice()
  194. export default {
  195. components: { LoanSummary, LoanDetails },
  196. methods: {
  197. generate, createFees, del, create, setPrice, setLtv, setAmount,
  198. setDti, setHousingDti, changeLoanType
  199. },
  200. computed: { loan },
  201. props: ['user', 'fees', 'token'],
  202. data() {
  203. return {
  204. estimate: estimate,
  205. sel: 0,
  206. errors: [],
  207. hash: window.location.hash
  208. }
  209. },
  210. created() {
  211. this.estimate.loans.forEach(l => l.fees = this.createFees())
  212. window.addEventListener("hashchange",
  213. () => this.hash = window.location.hash)
  214. }
  215. }
  216. </script>