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

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