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.

pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
pirms 2 gadiem
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. <template>
  2. <div>
  3. <section class="form inputs">
  4. <h3>Loan</h3>
  5. <label>Name</label>
  6. <input :value="loans[sel].title" required
  7. @input="(e) => $emit('update:name', stripLetters(e))">
  8. <button @click="() => $emit('del')">Delete</button>
  9. </section>
  10. <section class="form inputs">
  11. <div class="hint">
  12. <img class="icon" src="/assets/image/icon/question-circle.svg" alt="">
  13. <div class="tooltip">
  14. <p>Assumes borrower is not self employed, not bankrupt in the past 7
  15. years, a citizen, and intends to occupy the property.</p>
  16. </div>
  17. </div>
  18. <h3>Borrower</h3>
  19. <label>Number of Borrowers</label>
  20. <input :value="estimate.borrowers"
  21. @input="(e) => $emit('update:borrowers', stripInt(e))">
  22. <label>Credit Score</label>
  23. <input :value="estimate.creditScore"
  24. @input="(e) => $emit('update:creditScore', stripInt(e))">
  25. <label>Monthly Income ($)</label>
  26. <input :value="estimate.mIncome"
  27. @input="(e) => $emit('update:mIncome', strip(e))">
  28. </section>
  29. <section class="radios form">
  30. <h3>Transaction Type</h3>
  31. <input selected type="radio" name="transaction_type" value="0"
  32. :value="estimate.transaction"
  33. @input="() => $emit('update:transaction', 0)"
  34. >
  35. <label>Purchase</label>
  36. <input type="radio" name="transaction_type" value="1"
  37. :value="estimate.transaction"
  38. @input="() => $emit('update:transaction', 1)"
  39. >
  40. <label>Refinance</label>
  41. </section>
  42. <section class="form inputs">
  43. <h3>Property Details</h3>
  44. <label>Price ($)</label>
  45. <input :value="estimate.price" @input="(e) => $emit('update:price', strip(e))">
  46. <label>Type</label>
  47. <select id="" name=""
  48. :value="estimate.property"
  49. @change="(e) => $emit('update:property', e.target.value)">
  50. <option value="attched">Single Family Attached</option>
  51. <option value="detached">Single Family Detached</option>
  52. <option value="lorise">Lo-rise (4 stories or less)</option>
  53. <option value="hirise">Hi-rise (over 4 stories)</option>
  54. </select>
  55. </section>
  56. <section class="radios form">
  57. <h3>Loan Type</h3>
  58. <input type="radio"
  59. name="loan_type"
  60. value="conv"
  61. @change="(e) => $emit('update:loanType', e.target.value)"
  62. >
  63. <label>Conventional</label>
  64. <input type="radio"
  65. name="loan_type"
  66. value="fha"
  67. :checked="loans[sel].type == 'fha'"
  68. @change="(e) => $emit('update:loanType', e.target.value)">
  69. <label>FHA</label>
  70. <input type="radio"
  71. name="loan_type"
  72. value="va"
  73. :checked="loans[sel].type == 'va'"
  74. @change="(e) => $emit('update:loanType', e.target.value)">
  75. <label>VA</label>
  76. <input type="radio"
  77. name="loan_type"
  78. value="usda"
  79. :checked="loans[sel].type == 'usda'"
  80. @change="(e) => $emit('update:loanType', e.target.value)">
  81. <label>USDA</label>
  82. </section>
  83. <section class="form inputs">
  84. <h3>Loan Details</h3>
  85. <label>Loan Term (years)</label>
  86. <input :value="loans[sel].term"
  87. @input="(e) => $emit('update:term', stripInt(e))">
  88. <label>Loan Program</label>
  89. <select id="" name=""
  90. :value="loans[sel].program"
  91. @change="(e) => $emit('update:program', e.target.value)">
  92. <option value="none">None</option>
  93. </select>
  94. <label>Loan to Value (%)</label>
  95. <input :value="loans[sel].ltv" @input="(e) => $emit('update:ltv', e)">
  96. <label>Loan Amount ($)</label>
  97. <input :value="loans[sel].amount"
  98. @input="(e) => $emit('update:amount', e)">
  99. <label>Housing Expense DTI (%) - Optional</label>
  100. <input :value="loans[sel].housingDti"
  101. @input="(e) => $emit('update:housingDti', e)">
  102. <label>Total DTI (%) - Optional</label>
  103. <input :value="loans[sel].dti" @input="(e) => $emit('update:dti', e)">
  104. <label>Home Owner's Association ($/month)</label>
  105. <input :value="loans[sel].hoa"
  106. @input="(e) => { $emit('update:hoa', strip(e)) }">
  107. <label>Interest Rate (%)</label>
  108. <input :value="loans[sel].interest"
  109. @input="(e) => { $emit('update:interest', stripPerc(e)) }">
  110. <label>Days of Interest</label>
  111. <input :value="loans[sel].interestDays"
  112. @input="(e) => $emit('update:interestDays', stripInt(e))">
  113. <label>Hazard Insurance Escrow (months)</label>
  114. <input :value="loans[sel].hazardEscrow"
  115. @input="(e) => { $emit('update:hazardEscrow', stripInt(e)) }">
  116. <label>Hazard Insurance ($/month)</label>
  117. <input :value="loans[sel].hazard"
  118. @input="(e) => $emit('update:hazard', strip(e))">
  119. <label>Real Estate Tax Escrow (months)</label>
  120. <input :value="loans[sel].taxEscrow"
  121. @input="e => $emit('update:taxEscrow', stripInt(e))">
  122. <label>Real Estate Tax ($/month)</label>
  123. <input :value="loans[sel].tax"
  124. @input="(e) => $emit('update:tax', strip(e))">
  125. </section>
  126. <section class="form inputs">
  127. <h3>Fees</h3>
  128. <div v-for="(fee, indx) in estimate.loans[sel].fees"
  129. :key="fee.name + indx" class="fee"
  130. >
  131. <label>
  132. ${{fee.amount}}{{ fee.perc ? ` ${fee.perc}%` : ''}} - {{fee.name}}<br>
  133. {{fee.type}}
  134. </label>
  135. <img width="21" height="21" src="/assets/image/icon/x-red.svg"
  136. @click="() => estimate.loans[sel].fees.splice(indx, 1)">
  137. </div>
  138. <button @click="resetFees">Reset</button>
  139. <button @click="createFee">New</button>
  140. </section>
  141. <fee-dialog v-if="newFee"
  142. :heading="'New Fee'"
  143. :initial="{}"
  144. :price="estimate.price"
  145. @close="() => newFee = null"
  146. @save="addFee"
  147. />
  148. <section class="form radios">
  149. <h3>Mortgage Insurance</h3>
  150. <p>N/A</p>
  151. </section>
  152. <section class="form inputs">
  153. <button @click="() => validate() && $emit('continue')">Continue</button>
  154. <ul class="errors">
  155. <li v-for="e in errors">{{e}}</li>
  156. </ul>
  157. </section>
  158. </div>
  159. </template>
  160. <script>
  161. import FeeDialog from "../fee-dialog.vue"
  162. import { stripLetters, strip, stripInt, stripPerc } from "../../helpers.js"
  163. const newFee = {
  164. name: '', type: '', amount: 0, perc: 0
  165. }
  166. // Setup this.newFee for the creation dialog
  167. function createFee() {
  168. this.newFee = Object.assign({}, newFee)
  169. }
  170. // If valid, add the current this.newFee to the array of fees and reset
  171. // this.newFee to null
  172. function addFee(fee, isDebit) {
  173. if (!isDebit) fee.amount = fee.amount * -1
  174. this.estimate.loans[this.sel].fees.push(fee)
  175. this.newFee = null
  176. }
  177. function validate() {
  178. let errors = []
  179. const estimate = this.estimate
  180. // Alternative attribute names for error messages
  181. const names = {
  182. term: "loan term",
  183. ltv: "loan to value",
  184. hazard: "hazard insurance",
  185. hazardEscrow: "hazard insurance escrow",
  186. }
  187. if (!estimate.property) {
  188. errors.push("Missing property type.")
  189. } else if (!estimate.price) {
  190. errors.push("Missing property price.")
  191. } else if (!estimate.borrowers) {
  192. errors.push("Missing number of borrowers.")
  193. } else if (!estimate.creditScore) {
  194. errors.push("Missing credit score.")
  195. } else if (!estimate.mIncome) {
  196. errors.push("Missing monthly income.")
  197. }
  198. estimate.loans.forEach(l => {
  199. if (errors.length) return
  200. if (!l.amount) {
  201. errors.push(`${l.title} Loan amount cannot be zero`)
  202. } else if (!l.interest) {
  203. errors.push(`${l.title} Interest rate cannot be zero`)
  204. } else if (!l.term) {
  205. errors.push(`${l.title} Loan term cannot be zero`)
  206. }
  207. })
  208. if (errors.length) {
  209. this.errors = errors
  210. return false
  211. }
  212. return true
  213. }
  214. function generate() {
  215. const errors = this.validate()
  216. if (errors.length) {
  217. this.errors = errors
  218. return
  219. }
  220. window.location.hash = 'new/summary'
  221. }
  222. export default {
  223. components: { FeeDialog },
  224. methods: {
  225. strip, stripInt, stripLetters, stripPerc, createFee, addFee, validate,
  226. generate
  227. },
  228. props: ['estimate', 'loans', 'sel'],
  229. // Loan updates assume the currently selected loan is being modified, and
  230. // $emit has no need to clarify.
  231. emits: [
  232. 'del',
  233. 'update:name',
  234. 'update:borrowers',
  235. 'update:creditScore',
  236. 'update:mIncome',
  237. 'update:transaction',
  238. 'update:price',
  239. 'update:property',
  240. // Loan specific emits
  241. 'update:loanType',
  242. 'update:term',
  243. 'update:program',
  244. 'update:ltv',
  245. 'update:amount',
  246. 'update:housingDti',
  247. 'update:dti',
  248. 'update:hoa',
  249. 'update:interest',
  250. 'update:interestDays',
  251. 'update:hazardEscrow',
  252. 'update:hazard',
  253. 'update:taxEscrow',
  254. 'update:tax',
  255. 'continue'
  256. ],
  257. data() {
  258. return {
  259. newFee: null,
  260. errors: [],
  261. hash: window.location.hash
  262. }
  263. },
  264. created() {
  265. }
  266. }
  267. </script>