Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

app.vue 6.1 KiB

il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
il y a 2 ans
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  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>