Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 
 
 
 

284 wiersze
6.1 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. const validTypes = ['image/jpeg', 'image/png']
  97. if (!b || !validTypes.includes(b.type) || b.size <= 1) {
  98. return fetch("/assets/image/default-avatar.jpg").
  99. then(r => r.blob()).then( a => this.user.avatar = a )
  100. }
  101. this.user.avatar = b
  102. return getLetterhead(token)
  103. }).then(b => {
  104. const validTypes = ['image/jpeg', 'image/png']
  105. this.user.letterhead = b
  106. })
  107. }
  108. function getAvatar(t) {
  109. return fetch("/api/user/avatar",
  110. {method: 'GET',
  111. headers: {
  112. "Accept": "application/json",
  113. "Authorization": `Bearer ${t || this.token}`,
  114. }
  115. }).then(r => r.blob())
  116. }
  117. function getLetterhead(t) {
  118. return fetch("/api/user/letterhead",
  119. {method: 'GET',
  120. headers: {
  121. "Accept": "application/json",
  122. "Authorization": `Bearer ${t || this.token}`,
  123. }
  124. }).then(r => r.blob())
  125. }
  126. function updateAvatar() {
  127. const token = getCookie("skouter")
  128. getAvatar(token).then(b => this.user.avatar = b)
  129. }
  130. function updateLetterhead() {
  131. const token = getCookie("skouter")
  132. getLetterhead(token).then(b => this.user.letterhead = b)
  133. }
  134. function getFees() {
  135. const token = getCookie("skouter")
  136. return fetch(`/api/fees`,
  137. {method: 'GET',
  138. headers: {
  139. "Accept": "application/json",
  140. "Authorization": `Bearer ${token}`,
  141. },
  142. }).then(response => {
  143. if (response.ok) { return response.json() }
  144. }).then (result => {
  145. if (!result || !result.length) return // Exit if token is invalid or no fees are saved
  146. this.fees = result
  147. })
  148. }
  149. // Used to check the current section of the app generally without a regex match
  150. // each time.
  151. function active() {
  152. if (this.hash == '' || this.hash == '#') {
  153. return 1
  154. } else if (/^#new\/?/.exec(this.hash)) {
  155. return 2
  156. } else if (/^#estimates\/?/.exec(this.hash)) {
  157. return 3
  158. } else if (/^#settings\/?/.exec(this.hash)) {
  159. return 4
  160. } else if (/^#sign-out\/?/.exec(this.hash)) {
  161. return 5
  162. } else if (/^#login\/?/.exec(this.hash)) {
  163. return 6
  164. } else if (/^#estimate\/?/.exec(this.hash)) {
  165. return 7
  166. } else {
  167. return 0
  168. }
  169. }
  170. // Fetch data before showing UI. If requests fail, assume token is expired.
  171. function start() {
  172. this.loading = true
  173. let loaders = []
  174. loaders.push(this.getUser())
  175. loaders.push(this.getFees())
  176. Promise.all(loaders).then((a, b) => {
  177. this.loading = false
  178. if (!b) {
  179. // Time untill token expiration may have elapsed before the page
  180. // reloaded
  181. this.refreshToken()
  182. }
  183. }).catch(error => {
  184. console.log("An error occured %O", error)
  185. this.loadingError = "Could not initialize app."
  186. window.location.hash = 'login'
  187. })
  188. }
  189. function downloadEstimate(estimate) {
  190. fetch(`/api/pdf`,
  191. {method: 'POST',
  192. body: JSON.stringify(estimate),
  193. headers: {
  194. "Accept": "application/json",
  195. "Authorization": `Bearer ${this.token}`,
  196. },
  197. }).then(response => {
  198. if (response.ok) { return response.blob() }
  199. }).then (result => {
  200. console.log(result)
  201. if (!result) return // Exit if token is invalid or blank file returned
  202. })
  203. }
  204. export default {
  205. components: {
  206. SideBar,
  207. Spinner,
  208. Home,
  209. NewEstimate,
  210. Estimates,
  211. Settings,
  212. SignOut,
  213. Login
  214. },
  215. computed: { active },
  216. methods: {
  217. getCookie,
  218. start,
  219. getUser,
  220. getFees,
  221. refreshToken,
  222. updateAvatar,
  223. getAvatar,
  224. updateLetterhead,
  225. getLetterhead,
  226. downloadEstimate,
  227. },
  228. data() {
  229. return {
  230. loading: true,
  231. user: null,
  232. hash: window.location.hash,
  233. fees: [],
  234. loadingError: "",
  235. token: '',
  236. }
  237. },
  238. created() {
  239. window.onhashchange = () => this.hash = window.location.hash
  240. this.token = this.getCookie("skouter")
  241. if (!this.token) {
  242. window.location.hash = 'login'
  243. this.loading = false
  244. return
  245. }
  246. this.start()
  247. }
  248. }
  249. </script>