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.
 
 
 
 
 
 

230 line
5.8 KiB

  1. <template>
  2. <div class="page settings">
  3. <h2>Settings</h2>
  4. <section class="form inputs">
  5. <h3>Avatar</h3>
  6. <canvas class="displayer" width="200" height="200" ref="avatar"></canvas>
  7. <input type="file"
  8. @change="e => changeAvatar(e.target.files[0])"
  9. />
  10. <button @click="uploadAvatar">Upload</button>
  11. <label class="error">{{avatarError}}</label>
  12. </section>
  13. <section class="form inputs letterhead">
  14. <h3>Letterhead</h3>
  15. <canvas class="displayer" height="200" ref="letterhead"></canvas>
  16. <input type="file"
  17. @change="e => {setLetterhead(e.target.files[0])}"
  18. />
  19. <button @click="uploadLetterhead">Upload</button>
  20. <label class="error">{{letterheadError}}</label>
  21. </section>
  22. <section class="form inputs special">
  23. <h3>Profile</h3>
  24. <label for="">First Name</label>
  25. <input type="text" v-model="user.firstName">
  26. <label for="">Last Name</label>
  27. <input type="text" :value="user.lastName">
  28. <label for="">NMLS ID</label>
  29. <input type="text">
  30. <label for="">Branch ID</label>
  31. <input type="text" :value="user.branchId">
  32. <select id="" name="" :value="user.country">
  33. <option value="USA">USA</option>
  34. <option value="Canada">Canada</option>
  35. </select>
  36. <button @click="saveProfile">Save</button>
  37. </section>
  38. <section class="form inputs special">
  39. <h3>Reset Password</h3>
  40. <label for="">Old Password</label>
  41. <input type="password" v-model="oldPass">
  42. <label for="">New Password</label>
  43. <input type="password" v-model="newPass">
  44. <label for="">Confirm Password</label>
  45. <input type="password" v-model="confirmPass">
  46. <button
  47. :disabled="!(oldPass && newPass && confirmPass)"
  48. @click="changePassword">Save</button>
  49. <label class="error">{{passError}}</label>
  50. </section>
  51. <Dialog v-if="ready" @close="() => ready = false">
  52. <h3>Confirm your current password to save changes.</h3>
  53. <input type="text">
  54. <button>Confirm</button>
  55. </Dialog>
  56. </div>
  57. </template>
  58. <script setup>
  59. import { ref, watch, onMounted } from "vue"
  60. import Dialog from "./dialog.vue"
  61. let avatar = ref(null) // the canvas element
  62. let letterhead = ref(null) // the canvas element
  63. let ready = ref(false)
  64. let avatarChanged = ref(false)
  65. let avatarError = ref('')
  66. let letterheadError = ref('')
  67. let passError = ref('')
  68. let oldPass = ref('')
  69. let newPass = ref('')
  70. let confirmPass = ref('')
  71. const props = defineProps(['user', 'token'])
  72. const emit = defineEmits(['updateAvatar', 'updateLetterhead'])
  73. let user = Object.assign({}, props.user)
  74. function save() {
  75. }
  76. function check() {
  77. ready.value = true
  78. }
  79. function uploadAvatar() {
  80. avatar.value.toBlob(b => {
  81. fetch(`/api/user/avatar`,
  82. {method: 'POST',
  83. body: b,
  84. headers: {
  85. "Accept": "application/json",
  86. "Authorization": `Bearer ${props.token}`,
  87. },
  88. }).then(resp => {
  89. if (resp.ok) {emit('updateAvatar')}
  90. })
  91. })
  92. }
  93. function uploadLetterhead() {
  94. letterhead.value.toBlob(b => {
  95. fetch(`/api/user/letterhead`,
  96. {method: 'POST',
  97. body: b,
  98. headers: {
  99. "Accept": "application/json",
  100. "Authorization": `Bearer ${props.token}`,
  101. },
  102. }).then(resp => {
  103. if (resp.ok) {emit('updateLetterhead')}
  104. })
  105. })
  106. }
  107. function setLetterhead(f) {
  108. letterheadError.value = ""
  109. const validTypes = ['image/jpeg', 'image/png']
  110. if (!validTypes.includes(f.type)) {
  111. letterheadError.value = 'Image must be JPEG of PNG format'
  112. return
  113. }
  114. fetch(`/api/letterhead`,
  115. {method: 'POST',
  116. body: f,
  117. headers: {
  118. "Accept": "application/json",
  119. "Authorization": `Bearer ${props.token}`,
  120. },
  121. }).then(resp => {
  122. if (resp.ok) {
  123. resp.blob().then(b => changeLetterhead(b))
  124. } else {
  125. resp.text().then(e => letterheadError.value = e)
  126. }
  127. })
  128. }
  129. function changeAvatar(blob) {
  130. const validTypes = ['image/jpeg', 'image/png']
  131. if (!validTypes.includes(blob?.type)) {
  132. avatarError.value = 'Image must be JPEG of PNG format'
  133. return
  134. }
  135. avatarError.value = ''
  136. createImageBitmap(blob,
  137. {resizeWidth: 200, resizeHeight: 200, resizeQuality: 'medium'}).
  138. then((img) => {
  139. avatar.value.getContext("2d").drawImage(img, 0, 0, 200, 200)
  140. })
  141. }
  142. function changeLetterhead(blob) {
  143. const validTypes = ['image/jpeg', 'image/png']
  144. if (!validTypes.includes(blob.type)) {
  145. letterheadError.value = 'Image must be JPEG of PNG format'
  146. return
  147. }
  148. createImageBitmap(blob).
  149. then((img) => {
  150. let ctx = letterhead.value.getContext("2d")
  151. ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
  152. ctx.drawImage(img, 0, 0)
  153. })
  154. }
  155. function saveProfile() {
  156. console.log(user.firstName)
  157. fetch(`/api/user`,
  158. {method: 'PATCH',
  159. body: JSON.stringify(user),
  160. headers: {
  161. "Accept": "application/json",
  162. "Authorization": `Bearer ${props.token}`,
  163. },
  164. }).then(resp => {
  165. if (resp.ok) {}
  166. })
  167. }
  168. function changePassword(f) {
  169. fetch(`/api/user/password`,
  170. {method: 'POST',
  171. body: JSON.stringify({
  172. old: oldPass.value,
  173. new: newPass.value,
  174. confirm: confirmPass.value,
  175. }),
  176. headers: {
  177. "Accept": "application/json",
  178. "Authorization": `Bearer ${props.token}`,
  179. },
  180. }).then(resp => {
  181. if (resp.ok) {
  182. resp.blob().then(b => {
  183. oldPass.value = ""
  184. newPass.value = ""
  185. confirmPass.value = ""
  186. passError.value = ""
  187. })
  188. } else {
  189. resp.text().then(e => passError.value = e)
  190. }
  191. })
  192. }
  193. watch(props.user, (u) => {
  194. if (props.user.avatar) {
  195. changeAvatar(props.user.avatar)
  196. }
  197. if (props.user.letterhead) {
  198. changeLetterhead(props.user.letterhead)
  199. }
  200. }, {immediate: true})
  201. </script>