Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 
 

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