Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 
 

229 行
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. const validTypes = ['image/jpeg', 'image/png']
  109. if (!validTypes.includes(f.type)) {
  110. letterheadError.value = 'Image must be JPEG of PNG format'
  111. return
  112. }
  113. fetch(`/api/letterhead`,
  114. {method: 'POST',
  115. body: f,
  116. headers: {
  117. "Accept": "application/json",
  118. "Authorization": `Bearer ${props.token}`,
  119. },
  120. }).then(resp => {
  121. if (resp.ok) {
  122. resp.blob().then(b => changeLetterhead(b))
  123. } else {
  124. resp.text().then(e => letterheadError.value = e)
  125. }
  126. })
  127. }
  128. function changeAvatar(blob) {
  129. const validTypes = ['image/jpeg', 'image/png']
  130. if (!validTypes.includes(blob.type)) {
  131. avatarError.value = 'Image must be JPEG of PNG format'
  132. return
  133. }
  134. avatarError.value = ''
  135. createImageBitmap(blob,
  136. {resizeWidth: 200, resizeHeight: 200, resizeQuality: 'medium'}).
  137. then((img) => {
  138. avatar.value.getContext("2d").drawImage(img, 0, 0, 200, 200)
  139. })
  140. }
  141. function changeLetterhead(blob) {
  142. const validTypes = ['image/jpeg', 'image/png']
  143. if (!validTypes.includes(blob.type)) {
  144. letterheadError.value = 'Image must be JPEG of PNG format'
  145. return
  146. }
  147. createImageBitmap(blob).
  148. then((img) => {
  149. let ctx = letterhead.value.getContext("2d")
  150. ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
  151. ctx.drawImage(img, 0, 0)
  152. })
  153. }
  154. function saveProfile() {
  155. console.log(user.firstName)
  156. fetch(`/api/user`,
  157. {method: 'PATCH',
  158. body: JSON.stringify(user),
  159. headers: {
  160. "Accept": "application/json",
  161. "Authorization": `Bearer ${props.token}`,
  162. },
  163. }).then(resp => {
  164. if (resp.ok) {}
  165. })
  166. }
  167. function changePassword(f) {
  168. fetch(`/api/user/password`,
  169. {method: 'POST',
  170. body: JSON.stringify({
  171. old: oldPass.value,
  172. new: newPass.value,
  173. confirm: confirmPass.value,
  174. }),
  175. headers: {
  176. "Accept": "application/json",
  177. "Authorization": `Bearer ${props.token}`,
  178. },
  179. }).then(resp => {
  180. if (resp.ok) {
  181. resp.blob().then(b => {
  182. oldPass.value = ""
  183. newPass.value = ""
  184. confirmPass.value = ""
  185. passError.value = ""
  186. })
  187. } else {
  188. resp.text().then(e => passError.value = e)
  189. }
  190. })
  191. }
  192. watch(props.user, (u) => {
  193. if (props.user.avatar) {
  194. changeAvatar(props.user.avatar)
  195. }
  196. if (props.user.letterhead) {
  197. changeLetterhead(props.user.letterhead)
  198. }
  199. }, {immediate: true})
  200. </script>