Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
 
 
 
 
 
 

286 wiersze
6.2 KiB

  1. <template>
  2. <div class="panel">
  3. <template v-if="user">
  4. <side-bar v-if="user"
  5. :role="user && user.status"
  6. :avatar="user.avatar"
  7. :active="active">
  8. </side-bar>
  9. <div v-if="loading" class="page loading">
  10. <span class="error" >{{loadingError}}</span>
  11. <spinner></spinner>
  12. </div>
  13. <home :user="user" v-else-if="active == 1" />
  14. <new-estimate
  15. :user="user"
  16. :fees="fees"
  17. :token="token"
  18. v-else-if="active == 2" />
  19. <estimates
  20. :user="user"
  21. :fees="fees"
  22. v-else-if="active == 3"
  23. :token="token"
  24. @addFeeTemp="(f) => fees.push(f)"
  25. @removeFeeTemp="(fee) => fees = fees.filter(f => f.id != fee.id)"
  26. @download="downloadEstimate"
  27. />
  28. <settings
  29. :user="user"
  30. :token="token"
  31. @updateAvatar="updateAvatar"
  32. @updateLetterhead="updateLetterhead"
  33. v-else-if="active == 4" />
  34. <sign-out :user="user" v-else-if="active == 5" />
  35. </template>
  36. <template v-if="!user && active == 6">
  37. <login @login="start" />
  38. </template>
  39. </div>
  40. </template>
  41. <script>
  42. import SideBar from "./sidebar.vue"
  43. import Spinner from "./spinner.vue"
  44. import Home from "./home.vue"
  45. import NewEstimate from "./new/new.vue"
  46. import Estimates from "./estimates.vue"
  47. import Settings from "./settings.vue"
  48. import SignOut from "./sign-out.vue"
  49. import Login from "./login.vue"
  50. function getCookie(name) {
  51. var re = new RegExp(name + "=([^;]+)")
  52. var value = re.exec(document.cookie)
  53. return (value != null) ? unescape(value[1]) : null
  54. }
  55. function refreshToken() {
  56. const token = getCookie("skouter")
  57. const timeout = setTimeout(this.refreshToken, 1000*60*25)
  58. fetch(`/api/token`,
  59. {method: 'GET',
  60. headers: {
  61. "Accept": "application/json",
  62. "Authorization": `Bearer ${token}`,
  63. },
  64. }).then(response => {
  65. if (!response.ok) {
  66. console.log("Error refreshing token.")
  67. clearTimeout(timeout)
  68. window.location.hash = '#login'
  69. } else {
  70. this.token = getCookie("skouter")
  71. }
  72. })
  73. }
  74. function getUser() {
  75. const token = getCookie("skouter")
  76. this.token = token
  77. return fetch(`/api/user`,
  78. {method: 'GET',
  79. headers: {
  80. "Accept": "application/json",
  81. "Authorization": `Bearer ${token}`,
  82. },
  83. }).then(response => {
  84. if (response.ok) {
  85. return response.json()
  86. } else {
  87. // Redirect to login if starting token is invalid
  88. window.location.hash = '#login'
  89. }
  90. }).then (result => {
  91. if (!result) return // Exit if token is invalid
  92. this.user = result
  93. if (this.user.avatar) return
  94. return getAvatar(token)
  95. }).then(b => {
  96. if (!b) return // Exit if token is invalid
  97. const validTypes = ['image/jpeg', 'image/png']
  98. if (!b || !validTypes.includes(b.type) || b.size <= 1) {
  99. return fetch("/assets/image/default-avatar.jpg").
  100. then(r => r.blob()).then( a => this.user.avatar = a )
  101. }
  102. this.user.avatar = b
  103. return getLetterhead(token)
  104. }).then(b => {
  105. if (!b) return // Exit if token is invalid
  106. const validTypes = ['image/jpeg', 'image/png']
  107. this.user.letterhead = b
  108. })
  109. }
  110. function getAvatar(t) {
  111. return fetch("/api/user/avatar",
  112. {method: 'GET',
  113. headers: {
  114. "Accept": "application/json",
  115. "Authorization": `Bearer ${t || this.token}`,
  116. }
  117. }).then(r => r.blob())
  118. }
  119. function getLetterhead(t) {
  120. return fetch("/api/user/letterhead",
  121. {method: 'GET',
  122. headers: {
  123. "Accept": "application/json",
  124. "Authorization": `Bearer ${t || this.token}`,
  125. }
  126. }).then(r => r.blob())
  127. }
  128. function updateAvatar() {
  129. const token = getCookie("skouter")
  130. getAvatar(token).then(b => this.user.avatar = b)
  131. }
  132. function updateLetterhead() {
  133. const token = getCookie("skouter")
  134. getLetterhead(token).then(b => this.user.letterhead = b)
  135. }
  136. function getFees() {
  137. const token = getCookie("skouter")
  138. return fetch(`/api/fees`,
  139. {method: 'GET',
  140. headers: {
  141. "Accept": "application/json",
  142. "Authorization": `Bearer ${token}`,
  143. },
  144. }).then(response => {
  145. if (response.ok) { return response.json() }
  146. }).then (result => {
  147. if (!result || !result.length) return // Exit if token is invalid or no fees are saved
  148. this.fees = result
  149. })
  150. }
  151. // Used to check the current section of the app generally without a regex match
  152. // each time.
  153. function active() {
  154. if (this.hash == '' || this.hash == '#') {
  155. return 1
  156. } else if (/^#new\/?/.exec(this.hash)) {
  157. return 2
  158. } else if (/^#estimates\/?/.exec(this.hash)) {
  159. return 3
  160. } else if (/^#settings\/?/.exec(this.hash)) {
  161. return 4
  162. } else if (/^#sign-out\/?/.exec(this.hash)) {
  163. return 5
  164. } else if (/^#login\/?/.exec(this.hash)) {
  165. return 6
  166. } else if (/^#estimate\/?/.exec(this.hash)) {
  167. return 7
  168. } else {
  169. return 0
  170. }
  171. }
  172. // Fetch data before showing UI. If requests fail, assume token is expired.
  173. function start() {
  174. this.loading = true
  175. let loaders = []
  176. loaders.push(this.getUser())
  177. loaders.push(this.getFees())
  178. Promise.all(loaders).then((a, b) => {
  179. this.loading = false
  180. if (!b) {
  181. // Time untill token expiration may have elapsed before the page
  182. // reloaded
  183. this.refreshToken()
  184. }
  185. }).catch(error => {
  186. console.log("An error occured %O", error)
  187. this.loadingError = "Could not initialize app."
  188. window.location.hash = 'login'
  189. })
  190. }
  191. function downloadEstimate(estimate) {
  192. fetch(`/api/pdf`,
  193. {method: 'POST',
  194. body: JSON.stringify(estimate),
  195. headers: {
  196. "Accept": "application/json",
  197. "Authorization": `Bearer ${this.token}`,
  198. },
  199. }).then(response => {
  200. if (response.ok) { return response.blob() }
  201. }).then (result => {
  202. console.log(result)
  203. if (!result) return // Exit if token is invalid or blank file returned
  204. })
  205. }
  206. export default {
  207. components: {
  208. SideBar,
  209. Spinner,
  210. Home,
  211. NewEstimate,
  212. Estimates,
  213. Settings,
  214. SignOut,
  215. Login
  216. },
  217. computed: { active },
  218. methods: {
  219. getCookie,
  220. start,
  221. getUser,
  222. getFees,
  223. refreshToken,
  224. updateAvatar,
  225. getAvatar,
  226. updateLetterhead,
  227. getLetterhead,
  228. downloadEstimate,
  229. },
  230. data() {
  231. return {
  232. loading: true,
  233. user: null,
  234. hash: window.location.hash,
  235. fees: [],
  236. loadingError: "",
  237. token: '',
  238. }
  239. },
  240. created() {
  241. window.onhashchange = () => this.hash = window.location.hash
  242. this.token = this.getCookie("skouter")
  243. if (!this.token) {
  244. window.location.hash = 'login'
  245. this.loading = false
  246. return
  247. }
  248. this.start()
  249. }
  250. }
  251. </script>