Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

267 lines
5.8 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. const timeout = setTimeout(this.refreshToken, 1000*60*25)
  54. fetch(`/api/token`,
  55. {method: 'GET',
  56. headers: {
  57. "Accept": "application/json",
  58. "Authorization": `Bearer ${token}`,
  59. },
  60. }).then(response => {
  61. if (!response.ok) {
  62. console.log("Error refreshing token.")
  63. clearTimeout(timeout)
  64. window.location.hash = '#login'
  65. } else {
  66. this.token = getCookie("skouter")
  67. }
  68. })
  69. }
  70. function getUser() {
  71. const token = getCookie("skouter")
  72. this.token = token
  73. return fetch(`/api/user`,
  74. {method: 'GET',
  75. headers: {
  76. "Accept": "application/json",
  77. "Authorization": `Bearer ${token}`,
  78. },
  79. }).then(response => {
  80. if (response.ok) {
  81. return response.json()
  82. } else {
  83. // Redirect to login if starting token is invalid
  84. window.location.hash = '#login'
  85. }
  86. }).then (result => {
  87. if (!result || !result.length) return // Exit if token is invalid
  88. this.user = result[0]
  89. if (this.user.avatar) return
  90. return getAvatar(token)
  91. }).then(b => {
  92. const validTypes = ['image/jpeg', 'image/png']
  93. if (!validTypes.includes(b.type) || b.size <= 1) {
  94. fetch("/assets/image/empty-avatar.jpg").
  95. then(r => r.blob()).then( a => this.user.avatar = a )
  96. return
  97. }
  98. this.user.avatar = b
  99. return getLetterhead(token)
  100. }).then(b => {
  101. const validTypes = ['image/jpeg', 'image/png']
  102. if (!validTypes.includes(b.type) || b.size <= 1) {
  103. fetch("/assets/image/empty-letterhead.jpg").
  104. then(r => r.blob()).then( a => this.user.letterhead = a )
  105. return
  106. }
  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 {
  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. export default {
  190. components: {
  191. SideBar,
  192. Spinner,
  193. Home,
  194. NewEstimate,
  195. Estimates,
  196. Settings,
  197. SignOut,
  198. Login
  199. },
  200. computed: { active },
  201. methods: {
  202. getCookie,
  203. start,
  204. getUser,
  205. getFees,
  206. refreshToken,
  207. updateAvatar,
  208. getAvatar,
  209. updateLetterhead,
  210. getLetterhead,
  211. },
  212. data() {
  213. return {
  214. loading: true,
  215. user: null,
  216. hash: window.location.hash,
  217. fees: [],
  218. loadingError: "",
  219. token: '',
  220. }
  221. },
  222. created() {
  223. window.onhashchange = () => this.hash = window.location.hash
  224. this.token = this.getCookie("skouter")
  225. if (!this.token) {
  226. window.location.hash = 'login'
  227. this.loading = false
  228. return
  229. }
  230. this.start()
  231. }
  232. }
  233. </script>