Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 
 

309 wiersze
7.7 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. <section class="form inputs">
  19. <h3>Loan</h3>
  20. <label>Name</label>
  21. <input :value="loans[sel].title"
  22. @input="(e) => loans[sel].title = stripLetters(e)">
  23. <button @click="del">Delete</button>
  24. </section>
  25. <section class="form inputs">
  26. <div class="hint">
  27. <img class="icon" src="/assets/image/icon/question-circle.svg" alt="">
  28. <div class="tooltip">
  29. <p>Assumes borrower is not self employed, not bankrupt in the past 7
  30. years, a citizen, and intends to occupy the property.</p>
  31. </div>
  32. </div>
  33. <h3>Borrower</h3>
  34. <label>Number of Borrowers</label>
  35. <input :value="estimate.borrowers"
  36. @input="(e) => estimate.borrowers = stripInt(e)">
  37. <label>Credit Score</label>
  38. <input :value="estimate.creditScore"
  39. @input="(e) => estimate.creditScore = stripInt(e)">
  40. <label>Monthly Income ($)</label>
  41. <input :value="estimate.mIncome"
  42. @input="(e) => estimate.mIncome = strip(e)">
  43. </section>
  44. <section class="radios form">
  45. <h3>Transaction Type</h3>
  46. <input type="radio" name="transaction_type" value="estimate.transaction"
  47. @input="e => estimate.transaction = 0"
  48. selected="estimate.transaction == 0">
  49. <label>Purchase</label>
  50. <input type="radio" name="transaction_type" value="estimate.refinance"
  51. @input="e => estimate.transaction = 1"
  52. selected="estimate.transaction == 1">
  53. <label>Refinance</label>
  54. </section>
  55. <section class="form inputs">
  56. <h3>Property Details</h3>
  57. <label>Price ($)</label>
  58. <input :value="estimate.price" @input="setPrice">
  59. <label>Type</label>
  60. <select id="" name="" v-model="estimate.property">
  61. <option value="attched">Single Family Attached</option>
  62. <option value="detached">Single Family Detached</option>
  63. <option value="lorise">Lo-rise (4 stories or less)</option>
  64. <option value="hirise">Hi-rise (over 4 stories)</option>
  65. </select>
  66. </section>
  67. <section class="radios form">
  68. <h3>Loan Type</h3>
  69. <input type="radio" name="loan_type" value="conv" v-model="loans[sel].type"
  70. >
  71. <label>Conventional</label>
  72. <input type="radio" name="loan_type" value="fha" v-model="loans[sel].type">
  73. <label>FHA</label>
  74. <input type="radio" name="loan_type" value="va" v-model="loans[sel].type">
  75. <label>VA</label>
  76. <input type="radio" name="loan_type" value="usda" v-model="loans[sel].type">
  77. <label>USDA</label>
  78. </section>
  79. <section class="form inputs">
  80. <h3>Loan Details</h3>
  81. <label>Loan Term (years)</label>
  82. <input :value="loans[sel].term"
  83. @input="(e) => loans[sel].term = strip(e)">
  84. <label>Loan Program</label>
  85. <select id="" name="" v-model="loans[sel].program">
  86. <option value="none">None</option>
  87. </select>
  88. <label>Loan to Value (%)</label>
  89. <input :value="loans[sel].ltv" @input="setLtv">
  90. <label>Loan Amount ($)</label>
  91. <input :value="loans[sel].amount"
  92. @input="setAmount">
  93. <label>Housing Expense DTI (%) - Optional</label>
  94. <input :value="loans[sel].housingDti" @input="setHousingDti">
  95. <label>Total DTI (%) - Optional</label>
  96. <input :value="loans[sel].dti" @input="setDti">
  97. <label>Home Owner's Association ($/month)</label>
  98. <input :value="loans[sel].hoa"
  99. @input="(e) => { loans[sel].hoa = strip(e) }">
  100. <label>Interest Rate (%)</label>
  101. <input :value="loans[sel].interest"
  102. @input="(e) => { loans[sel].interest = stripPerc(e) }">
  103. <label>Days of Interest</label>
  104. <input :value="loans[sel].interestDays"
  105. @input="(e) => {loans[sel].interestDays = stripInt(e)}">
  106. <label>Hazard Insurance Escrow (months)</label>
  107. <input :value="loans[sel].hazardEscrow"
  108. @input="(e) => {loans[sel].hazardEscrow = stripInt(e)}">
  109. <label>Hazard Insurance ($/month)</label>
  110. <input :value="loans[sel].hazard"
  111. @input="(e) => {loans[sel].hazard = strip(e)}">
  112. <label>Real Estate Tax Escrow (months)</label>
  113. <input :value="loans[sel].taxEscrow"
  114. @input="e => {loans[sel].taxEscrow = stripInt(e)}">
  115. <label>Real Estate Tax ($/month)</label>
  116. <input :value="loans[sel].tax"
  117. @input="(e) => {loans[sel].tax = strip(e)}">
  118. </section>
  119. <section class="form radios">
  120. <h3>Mortgage Insurance</h3>
  121. <input type="radio" name="transaction_type" value="transaction"
  122. @input="e => estimate.transaction = 0"
  123. selected="estimate.transaction == 0">
  124. <label>National MI</label>
  125. <input type="radio" name="transaction_type" value="refinance"
  126. @input="e => estimate.transaction = 1"
  127. selected="estimate.transaction == 1">
  128. <label>MGIC</label>
  129. </section>
  130. </div>
  131. </template>
  132. <script>
  133. const example = {
  134. title: "Example",
  135. type: "",
  136. term: 0,
  137. ltv: 0, // Loan to home value ratio
  138. dti: 0,
  139. housingDti: 0,
  140. amount: 0,
  141. interest: 0,
  142. interestDays: 0,
  143. hazard: 0, // Hazard insurance monthly payment
  144. hazardEscrow: 0, // Hazard insurance escrow in months (0 is none)
  145. tax: 0, // Real Estate taxes monthly payment
  146. taxEscrow: 0, // Months to escrow (0 is none)
  147. hoa: 0, // Home owner's association monthly fee
  148. program: "",
  149. pud: true, // Property under development
  150. zip: ''
  151. }
  152. const loans = [
  153. Object.assign({}, example),
  154. Object.assign(Object.assign({}, example), {title: "Another One"})
  155. ]
  156. const estimate = {
  157. property: "",
  158. transaction: 0,
  159. price: 0,
  160. borrowers: 0,
  161. creditScore: 0,
  162. mIncome: 0,
  163. loans: loans
  164. }
  165. // Clone loan from initial example as a new loan
  166. function create() {
  167. this.estimate.loans.push(Object.assign({}, loans[0]))
  168. }
  169. // Strips non-digits from an input box event and returns it's rounded integer.
  170. // It also preserves current valid entry (.)
  171. function strip(e) {
  172. let valid = e.target.value.match(/\d+\.?\d?\d?/)?.[0] ?? ""
  173. e.target.value = valid
  174. return Number(valid || 0)
  175. }
  176. function stripInt(e) {
  177. let value = parseInt(e.target.value.replace(/\D/g, '') || 0)
  178. e.target.value = value
  179. return value
  180. }
  181. function stripPerc(e) {
  182. let num = strip(e)
  183. if (num > 100) {
  184. num = 100
  185. e.target.value = num
  186. }
  187. if (num < 0) {
  188. num = 0
  189. e.target.value = num
  190. }
  191. return num
  192. }
  193. function stripLetters(e) {
  194. let value = (e.target.value.replace(/[^\w\s]/g, '').slice(0, 20) || '')
  195. e.target.value = value
  196. return value
  197. }
  198. function del() {
  199. if (this.loans.length > 1) {
  200. let x = this.sel
  201. this.sel = 0
  202. this.loans.splice(x, 1)
  203. }
  204. }
  205. // Changes loan.ltv's <input> and data() values, then syncs with data.amount
  206. function setLtv(e) {
  207. let ltv = strip(e)
  208. let loan = this.loans[this.sel]
  209. if (!this.estimate.price) return
  210. if (ltv > 100) ltv = 100
  211. if (ltv < 0) ltv = 0
  212. loan.ltv = ltv
  213. loan.amount = (ltv / 100 * this.estimate.price).toFixed(2)
  214. }
  215. // Changes loan.amount's <input> and data() values, then syncs with data.ltv
  216. function setAmount(e) {
  217. let amount = strip(e)
  218. let loan = this.loans[this.sel]
  219. if (!this.estimate.price) return
  220. if (amount > loan.price) amount = loan.price
  221. if (amount < 0) amount = 0
  222. loan.amount = amount
  223. loan.ltv = (amount / this.estimate.price * 100).toFixed(2)
  224. }
  225. // Updates the property price for all loans
  226. function setPrice(e) {
  227. let value = strip(e)
  228. this.estimate.price = value
  229. }
  230. function setDti(e) {
  231. let dti = strip(e)
  232. let loan = this.loans[this.sel]
  233. if (!loan.price) return
  234. if (dti > 100) dti = 100
  235. if (dti < 0) dti = 0
  236. e.target.value = dti
  237. loan.dti = dti
  238. }
  239. function setHousingDti(e) {
  240. let housingDti = strip(e)
  241. let loan = this.loans[this.sel]
  242. if (!loan.price) return
  243. if (housingDti > 100) housingDti = 100
  244. if (housingDti < 0) housingDti = 0
  245. e.target.value = housingDti
  246. loan.housingDti = housingDti
  247. }
  248. export default {
  249. methods: {
  250. setPrice, setLtv, setAmount, setDti, setHousingDti, strip, stripInt,
  251. stripLetters, stripPerc, del, create
  252. },
  253. props: ['user'],
  254. data() {
  255. return {
  256. estimate: estimate,
  257. loans: estimate.loans,
  258. sel: 0,
  259. }
  260. }
  261. }
  262. </script>