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.
 
 
 
 
 
 

267 lines
5.7 KiB

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