Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

172 lines
4.1 KiB

  1. <template>
  2. <div class="page">
  3. <h2>Estimates</h2>
  4. <section class="form inputs">
  5. <h3>Default Fees</h3>
  6. <div
  7. v-for="(fee, indx) in fees"
  8. :key="fee.name + indx" class="fee"
  9. >
  10. <label @click="() => edit = fee">
  11. ${{fee.amount/100}}{{ fee.perc ? ` ${fee.perc}%` : ''}} - {{fee.name}}<br>
  12. {{fee.type}}
  13. </label>
  14. <img width="21" height="21" src="/assets/image/icon/x-red.svg"
  15. @click="() => remove(fee)">
  16. </div>
  17. <button @click="() => edit = {}">New Fee</button>
  18. </section>
  19. <fee-dialog v-if="edit"
  20. :heading="'Fee'"
  21. :initial="edit"
  22. :price="0"
  23. @close="() => edit = null"
  24. @save="newFee"
  25. />
  26. <section class="inputs estimates">
  27. <h3>Saved Estimates</h3>
  28. <div class="entry" v-for="e in estimates" v-if="!estimate"
  29. @click="() => estimate = e" key="e.id">
  30. <span>
  31. {{e.id}} - {{e.property}} - ${{(e.price/100).toLocaleString()}}
  32. </span>
  33. </div>
  34. <div class="details" v-if="estimate">
  35. <label>
  36. #{{estimate.id}} -
  37. {{estimate.transaction}} -
  38. {{estimate.property}} -
  39. ${{(estimate.price / 100).toLocaleString()}}
  40. </label>
  41. <label>Borrowers: {{estimate.borrower.num}}</label>
  42. <label>Credit: {{estimate.borrower.credit}}</label>
  43. <label>Income: {{(estimate.borrower.income/100).toLocaleString()}}</label>
  44. <div v-for="l in estimate.loans" class="details">
  45. <h4>{{l.title}}</h4>
  46. <label>{{l.type.name}}</label>
  47. <label>Total monthly: ${{format(l.result.totalMonthly)}}</label>
  48. <label>Cash to close: ${{format(l.result.cashToClose)}}</label>
  49. </div>
  50. <button @click="() => download(estimate)">Generate PDF</button>
  51. <button @click="() => estimate = null">Cancel</button>
  52. </div>
  53. </section>
  54. <DDialog v-if="dlink" @close="() => dlink = ''"
  55. :fileName="`estimate-${estimate.id}.pdf`" :url="dlink">
  56. </DDialog>
  57. </div>
  58. </template>
  59. <script setup>
  60. import { ref, computed, onMounted } from 'vue'
  61. import FeeDialog from "./fee-dialog.vue"
  62. import DDialog from "./download-dialog.vue"
  63. import { format } from "../helpers.js"
  64. const props = defineProps(['user', 'fees', 'token'])
  65. const emit = defineEmits(['addFeeTemp', 'removeFeeTemp', 'preview'])
  66. let edit = ref(null)
  67. let estimates = ref([])
  68. let estimate = ref()
  69. let dlink = ref("")
  70. function newFee(fee, isDebit) {
  71. if (!isDebit) {
  72. fee.amount = -1 * fee.amount || 0
  73. fee.perc = -1 * fee.perc || 0
  74. }
  75. fetch(`/api/fee`,
  76. {method: 'POST',
  77. body: JSON.stringify(fee),
  78. headers: {
  79. "Accept": "application/json",
  80. "Authorization": `Bearer ${props.token}`,
  81. },
  82. }).then(resp => {
  83. if (resp.ok && resp.status == 200) {
  84. resp.json().then(r => emit('addFeeTemp', r))
  85. } else {
  86. // resp.text().then(t => this.errors = [t])
  87. // window.location.hash = 'new'
  88. resp.text().then(t => console.log(t))
  89. }
  90. })
  91. edit.value = null
  92. }
  93. function newType() {
  94. }
  95. function remove(fee) {
  96. fetch(`/api/fee`,
  97. {method: 'DELETE',
  98. headers: {
  99. "Accept": "application/json",
  100. "Authorization": `Bearer ${props.token}`,
  101. },
  102. body: JSON.stringify(fee)
  103. }).then(response => {
  104. if (response.ok) { emit('removeFeeTemp', fee) } else {
  105. response.text().then(t => console.log(t))
  106. }
  107. })
  108. }
  109. function getEstimates() {
  110. fetch(`/api/estimates`,
  111. {method: 'GET',
  112. headers: {
  113. "Accept": "application/json",
  114. "Authorization": `Bearer ${props.token}`,
  115. },
  116. }).then(response => {
  117. if (response.ok) { return response.json() } else {
  118. response.text().then(t => console.log(t))
  119. }
  120. }).then (result => {
  121. if (!result || !result.length) return // Exit if token is invalid or no fees are saved
  122. estimates.value = result
  123. // console.log(result)
  124. })
  125. }
  126. function download(estimate) {
  127. fetch(`/api/pdf`,
  128. {method: 'POST',
  129. body: JSON.stringify(estimate),
  130. headers: {
  131. "Accept": "application/json",
  132. "Authorization": `Bearer ${props.token}`,
  133. },
  134. }).then(response => {
  135. if (response.ok) { return response.blob() }
  136. }).then (result => {
  137. if (!result) return // Exit if token is invalid or blank file returned
  138. dlink.value = URL.createObjectURL(result)
  139. })
  140. }
  141. onMounted(() => {
  142. getEstimates()
  143. })
  144. </script>
  145. <style scoped>
  146. .modal a.button {
  147. margin: auto;
  148. margin-top: 30px;
  149. }
  150. </style>