Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

138 lines
3.3 KiB

  1. <template>
  2. <div v-if="valid">
  3. <section class="form inputs">
  4. <h3>Monthly Payment - ${{format(totalMonthly)}}</h3>
  5. <label>Loan payment: ${{format(loanPayment)}}</label>
  6. <label v-if="loan.mi.monthly">
  7. Mortgage insurance: ${{format(loan.amount*loan.mi.rate/100/12)}}
  8. </label>
  9. <label>Property taxes: ${{format(loan.tax)}}</label>
  10. <label>Homeowner's Insurance: ${{format(loan.hoi)}}</label>
  11. <label v-if="loan.hazard">
  12. Hazard insurance: ${{format(loan.hazard)}}
  13. </label>
  14. </section>
  15. <section class="form inputs">
  16. <h3>Cash to Close - ${{format(cashToClose)}}</h3>
  17. <label>Closing costs: ${{format(fees)}}</label>
  18. <label v-if="credits">Credits: ${{credits}}</label>
  19. <label>Down payment: ${{format(downpayment)}}</label>
  20. <label v-if="!loan.mi.monthly">
  21. Mortgage insurance: ${{format(loan.amount*loan.mi.rate/100)}}
  22. </label>
  23. </section>
  24. <section class="form inputs">
  25. <button :disabled="saved" @click="create">Save Estimate</button>
  26. <button>Generate PDF</button>
  27. </section>
  28. </div>
  29. </template>
  30. <script setup>
  31. import { ref, computed, onMounted } from 'vue'
  32. let valid = ref(false)
  33. let saved = ref(false)
  34. const props = defineProps(['downpayment', 'loan', 'token', 'estimate'])
  35. function amortize(principle, rate, periods) {
  36. return principle * rate*(1+rate)**periods / ((1+rate)**periods - 1)
  37. }
  38. const loanPayment = computed(() => {
  39. return amortize(props.loan.amount,
  40. props.loan.interest / 100 / 12,
  41. props.loan.term*12)
  42. })
  43. const totalMonthly = computed (() => {
  44. let total = loanPayment.value +
  45. props.loan.tax +
  46. props.loan.hoi +
  47. props.loan.hazard
  48. if (props.loan.mi.monthly) {
  49. total = total + props.loan.mi.rate/100*(props.loan.amount)
  50. }
  51. return total
  52. })
  53. // Closing costs
  54. const fees = computed(() => {
  55. return props.loan.fees.reduce((total, x) => {
  56. return x.amount > 0 ? total + x.amount : 0
  57. }, 0
  58. )
  59. })
  60. const credits = computed(() => {
  61. return props.loan.fees.reduce((total, x) => {
  62. return x.amount < 0 ? total + x.amount : 0
  63. }, 0
  64. )
  65. })
  66. const cashToClose = computed(() => {
  67. let total = fees.value + credits.value + props.downpayment
  68. if (!props.loan.mi.monthly) {
  69. total = total + props.loan.mi.rate/100*(props.loan.amount)
  70. }
  71. return total
  72. })
  73. function validate() {
  74. fetch(`/api/estimate/validate`,
  75. {method: 'POST',
  76. body: JSON.stringify(props.estimate),
  77. headers: {
  78. "Accept": "application/json",
  79. "Authorization": `Bearer ${props.token}`,
  80. },
  81. }).then(resp => {
  82. if (resp.ok && resp.status == 200) {
  83. valid.value = true
  84. return
  85. } else {
  86. // resp.text().then(t => this.errors = [t])
  87. // window.location.hash = 'new'
  88. }
  89. })
  90. }
  91. function create() {
  92. saved.value = true
  93. fetch(`/api/estimate`,
  94. {method: 'POST',
  95. body: JSON.stringify(props.estimate),
  96. headers: {
  97. "Accept": "application/json",
  98. "Authorization": `Bearer ${props.token}`,
  99. },
  100. }).then(resp => {
  101. if (resp.ok && resp.status == 200) {
  102. saved.value = true
  103. return
  104. } else {
  105. // resp.text().then(t => this.errors = [t])
  106. // window.location.hash = 'new'
  107. resp.text().then(t => console.log(t))
  108. saved.value = false
  109. }
  110. })
  111. }
  112. // Print number of cents as a nice string of dollars
  113. function format(num) {
  114. return (num/100).toFixed(2)
  115. }
  116. onMounted(() => {validate()})
  117. </script>