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.

774 lines
35 KiB

  1. <template lang='pug'>
  2. v-container(fluid, grid-list-lg)
  3. v-layout(row wrap)
  4. v-flex(xs12)
  5. .profile-header
  6. img.animated.fadeInUp(src='/svg/icon-profile.svg', alt='Users', style='width: 80px;')
  7. .profile-header-title
  8. .headline.primary--text.animated.fadeInLeft {{$t('profile:title')}}
  9. .subheading.grey--text.animated.fadeInLeft {{$t('profile:subtitle')}}
  10. v-spacer
  11. //- v-btn.animated.fadeInDown(outlined, color='primary', disabled).mr-0
  12. //- v-icon(left) mdi-earth
  13. //- span {{$t('profile:viewPublicProfile')}}
  14. v-flex(lg6 xs12)
  15. v-card.animated.fadeInUp
  16. v-toolbar(color='primary', dark, dense, flat)
  17. v-toolbar-title.subtitle-1 {{$t('profile:myInfo')}}
  18. v-list(two-line, dense)
  19. v-list-item
  20. v-list-item-avatar(size='32')
  21. v-icon mdi-account
  22. v-list-item-content
  23. v-list-item-title {{$t('profile:displayName')}}
  24. v-list-item-subtitle {{ user.name }}
  25. v-list-item-action
  26. v-menu(
  27. v-model='editPop.name'
  28. :close-on-content-click='false'
  29. min-width='350'
  30. left
  31. )
  32. template(v-slot:activator='{ on }')
  33. v-btn(text, color='grey', small, v-on='on', @click='focusField(`iptDisplayName`)')
  34. v-icon(left) mdi-pencil
  35. span {{ $t('common:actions:edit') }}
  36. v-card
  37. v-text-field(
  38. ref='iptDisplayName'
  39. v-model='user.name'
  40. :label='$t(`profile:displayName`)'
  41. solo
  42. hide-details
  43. append-icon='mdi-check'
  44. @click:append='editPop.name = false'
  45. @keydown.enter='editPop.name = false'
  46. @keydown.esc='editPop.name = false'
  47. )
  48. v-divider
  49. v-list-item
  50. v-list-item-avatar(size='32')
  51. v-icon mdi-map-marker
  52. v-list-item-content
  53. v-list-item-title {{$t('profile:location')}}
  54. v-list-item-subtitle {{ user.location }}
  55. v-list-item-action
  56. v-menu(
  57. v-model='editPop.location'
  58. :close-on-content-click='false'
  59. min-width='350'
  60. left
  61. )
  62. template(v-slot:activator='{ on }')
  63. v-btn(text, color='grey', small, v-on='on', @click='focusField(`iptLocation`)')
  64. v-icon(left) mdi-pencil
  65. span {{ $t('common:actions:edit') }}
  66. v-card
  67. v-text-field(
  68. ref='iptLocation'
  69. v-model='user.location'
  70. :label='$t(`profile:location`)'
  71. solo
  72. hide-details
  73. append-icon='mdi-check'
  74. @click:append='editPop.location = false'
  75. @keydown.enter='editPop.location = false'
  76. @keydown.esc='editPop.location = false'
  77. )
  78. v-divider
  79. v-list-item
  80. v-list-item-avatar(size='32')
  81. v-icon mdi-briefcase
  82. v-list-item-content
  83. v-list-item-title {{$t('profile:jobTitle')}}
  84. v-list-item-subtitle {{ user.jobTitle }}
  85. v-list-item-action
  86. v-menu(
  87. v-model='editPop.jobTitle'
  88. :close-on-content-click='false'
  89. min-width='350'
  90. left
  91. )
  92. template(v-slot:activator='{ on }')
  93. v-btn(text, color='grey', small, v-on='on', @click='focusField(`iptJobTitle`)')
  94. v-icon(left) mdi-pencil
  95. span {{ $t('common:actions:edit') }}
  96. v-card
  97. v-text-field(
  98. ref='iptJobTitle'
  99. v-model='user.jobTitle'
  100. :label='$t(`profile:jobTitle`)'
  101. solo
  102. hide-details
  103. append-icon='mdi-check'
  104. @click:append='editPop.jobTitle = false'
  105. @keydown.enter='editPop.jobTitle = false'
  106. @keydown.esc='editPop.jobTitle = false'
  107. )
  108. v-divider
  109. v-list-item
  110. v-list-item-avatar(size='32')
  111. v-icon mdi-map-clock-outline
  112. v-list-item-content
  113. v-list-item-title {{$t('profile:timezone')}}
  114. v-list-item-subtitle {{ user.timezone }}
  115. v-list-item-action
  116. v-menu(
  117. v-model='editPop.timezone'
  118. :close-on-content-click='false'
  119. min-width='350'
  120. max-width='350'
  121. left
  122. )
  123. template(v-slot:activator='{ on }')
  124. v-btn(text, color='grey', small, v-on='on', @click='focusField(`iptTimezone`)')
  125. v-icon(left) mdi-pencil
  126. span {{ $t('common:actions:edit') }}
  127. v-card(flat)
  128. v-select(
  129. ref='iptTimezone'
  130. :items='timezones'
  131. v-model='user.timezone'
  132. :label='$t(`profile:timezone`)'
  133. solo
  134. flat
  135. dense
  136. hide-details
  137. @keydown.enter='editPop.timezone = false'
  138. @keydown.esc='editPop.timezone = false'
  139. style='height: 44px;'
  140. )
  141. v-card-chin
  142. v-spacer
  143. v-btn(
  144. small
  145. text
  146. color='primary'
  147. @click='editPop.timezone = false'
  148. )
  149. v-icon(left) mdi-check
  150. span {{$t('common:actions.ok')}}
  151. v-card-chin
  152. v-spacer
  153. v-btn.px-4(color='success', depressed, @click='saveProfile', :loading='saveLoading')
  154. v-icon(left) mdi-content-save
  155. span {{$t('common:actions.save')}}
  156. v-card.mt-3.animated.fadeInUp.wait-p2s
  157. v-toolbar(color='primary', dark, dense, flat)
  158. v-toolbar-title
  159. .subtitle-1 {{$t('profile:auth.title')}}
  160. v-card-text.pt-0
  161. v-subheader.pl-0: span.subtitle-2 {{$t('profile:auth.provider')}}
  162. v-toolbar(
  163. flat
  164. :color='$vuetify.theme.dark ? "grey darken-2" : "purple lighten-5"'
  165. dense
  166. :class='$vuetify.theme.dark ? "grey--text text--lighten-1" : "purple--text text--darken-4"'
  167. )
  168. v-icon(:color='$vuetify.theme.dark ? "grey lighten-1" : "purple darken-4"') mdi-shield-lock
  169. .subheading.ml-3 {{ user.providerName }}
  170. //- v-divider.mt-3
  171. //- v-subheader.pl-0: span.subtitle-2 Two-Factor Authentication (2FA)
  172. //- .caption.mb-2 2FA adds an extra layer of security by requiring a unique code generated on your smartphone when signing in.
  173. //- v-btn(color='purple darken-4', disabled).ml-0 Enable 2FA
  174. //- v-btn(color='purple darken-4', dark, depressed, disabled).ml-0 Disable 2FA
  175. template(v-if='user.providerKey === `local`')
  176. v-divider.mt-3
  177. v-subheader.pl-0: span.subtitle-2 {{$t('profile:auth.changePassword')}}
  178. v-text-field(
  179. ref='iptCurrentPass'
  180. v-model='currentPass'
  181. outlined
  182. :label='$t(`profile:auth.currentPassword`)'
  183. type='password'
  184. prepend-inner-icon='mdi-textbox-password'
  185. )
  186. v-text-field(
  187. ref='iptNewPass'
  188. v-model='newPass'
  189. outlined
  190. :label='$t(`profile:auth.newPassword`)'
  191. type='password'
  192. prepend-inner-icon='mdi-textbox-password'
  193. counter='255'
  194. loading
  195. )
  196. password-strength(slot='progress', v-model='newPass')
  197. v-text-field(
  198. ref='iptVerifyPass'
  199. v-model='verifyPass'
  200. outlined
  201. :label='$t(`profile:auth.verifyPassword`)'
  202. type='password'
  203. prepend-inner-icon='mdi-textbox-password'
  204. hide-details
  205. )
  206. v-card-chin
  207. v-spacer
  208. v-btn.px-4(color='purple darken-4', dark, depressed, @click='changePassword', :loading='changePassLoading')
  209. v-icon(left) mdi-progress-check
  210. span {{$t('profile:auth.changePassword')}}
  211. v-flex(lg6 xs12)
  212. //- v-card
  213. //- v-toolbar(color='primary', dark, dense, flat)
  214. //- v-toolbar-title
  215. //- .subtitle-1 Picture
  216. //- v-card-title
  217. //- v-avatar.blue(v-if='picture.kind === `initials`', :size='40')
  218. //- span.white--text.subheading {{picture.initials}}
  219. //- v-avatar(v-else-if='picture.kind === `image`', :size='40')
  220. //- v-img(:src='picture.url')
  221. //- v-btn(outlined).mx-4 Upload Picture
  222. //- v-btn(outlined, disabled) Remove Picture
  223. v-card.animated.fadeInUp.wait-p2s
  224. v-toolbar(color='primary', dark, dense, flat)
  225. v-toolbar-title
  226. .subtitle-1 {{$t('profile:groups.title')}}
  227. v-list(dense)
  228. template(v-for='(grp, idx) of user.groups')
  229. v-list-item(:key='`grp-id-` + grp')
  230. v-list-item-avatar(size='32')
  231. v-icon mdi-account-group
  232. v-list-item-content
  233. v-list-item-title.body-2 {{grp}}
  234. v-divider(v-if='idx < user.groups.length - 1')
  235. v-card.mt-3.animated.fadeInUp.wait-p3s
  236. v-toolbar(color='teal', dark, dense, flat)
  237. v-toolbar-title
  238. .subtitle-1 {{$t('profile:activity.title')}}
  239. v-card-text.grey--text.text--darken-2
  240. .caption.grey--text {{$t('profile:activity.joinedOn')}}
  241. .body-2: strong {{ user.createdAt | moment('LLLL') }}
  242. .caption.grey--text.mt-3 {{$t('profile:activity.lastUpdatedOn')}}
  243. .body-2: strong {{ user.updatedAt | moment('LLLL') }}
  244. .caption.grey--text.mt-3 {{$t('profile:activity.lastLoginOn')}}
  245. .body-2: strong {{ user.lastLoginAt | moment('LLLL') }}
  246. v-divider.mt-3
  247. .caption.grey--text.mt-3 {{$t('profile:activity.pagesCreated')}}
  248. .body-2: strong {{ user.pagesTotal }}
  249. .caption.grey--text.mt-3 {{$t('profile:activity.commentsPosted')}}
  250. .body-2: strong 0
  251. </template>
  252. <script>
  253. import { get } from 'vuex-pathify'
  254. import gql from 'graphql-tag'
  255. import _ from 'lodash'
  256. import Cookies from 'js-cookie'
  257. import validate from 'validate.js'
  258. import PasswordStrength from '../common/password-strength.vue'
  259. export default {
  260. i18nOptions: {
  261. namespaces: ['profile', 'auth']
  262. },
  263. components: {
  264. PasswordStrength
  265. },
  266. data() {
  267. return {
  268. saveLoading: false,
  269. changePassLoading: false,
  270. user: {
  271. name: 'unknown',
  272. location: '',
  273. jobTitle: '',
  274. timezone: '',
  275. createdAt: '1970-01-01',
  276. updatedAt: '1970-01-01',
  277. lastLoginAt: '1970-01-01',
  278. groups: []
  279. },
  280. currentPass: '',
  281. newPass: '',
  282. verifyPass: '',
  283. editPop: {
  284. name: false,
  285. location: false,
  286. jobTitle: false,
  287. timezone: false
  288. },
  289. timezones: [
  290. { text: '(GMT-11:00) Niue', value: 'Pacific/Niue' },
  291. { text: '(GMT-11:00) Pago Pago', value: 'Pacific/Pago_Pago' },
  292. { text: '(GMT-10:00) Hawaii Time', value: 'Pacific/Honolulu' },
  293. { text: '(GMT-10:00) Rarotonga', value: 'Pacific/Rarotonga' },
  294. { text: '(GMT-10:00) Tahiti', value: 'Pacific/Tahiti' },
  295. { text: '(GMT-09:30) Marquesas', value: 'Pacific/Marquesas' },
  296. { text: '(GMT-09:00) Alaska Time', value: 'America/Anchorage' },
  297. { text: '(GMT-09:00) Gambier', value: 'Pacific/Gambier' },
  298. { text: '(GMT-08:00) Pacific Time', value: 'America/Los_Angeles' },
  299. { text: '(GMT-08:00) Pacific Time - Tijuana', value: 'America/Tijuana' },
  300. { text: '(GMT-08:00) Pacific Time - Vancouver', value: 'America/Vancouver' },
  301. { text: '(GMT-08:00) Pacific Time - Whitehorse', value: 'America/Whitehorse' },
  302. { text: '(GMT-08:00) Pitcairn', value: 'Pacific/Pitcairn' },
  303. { text: '(GMT-07:00) Mountain Time', value: 'America/Denver' },
  304. { text: '(GMT-07:00) Mountain Time - Arizona', value: 'America/Phoenix' },
  305. { text: '(GMT-07:00) Mountain Time - Chihuahua, Mazatlan', value: 'America/Mazatlan' },
  306. { text: '(GMT-07:00) Mountain Time - Dawson Creek', value: 'America/Dawson_Creek' },
  307. { text: '(GMT-07:00) Mountain Time - Edmonton', value: 'America/Edmonton' },
  308. { text: '(GMT-07:00) Mountain Time - Hermosillo', value: 'America/Hermosillo' },
  309. { text: '(GMT-07:00) Mountain Time - Yellowknife', value: 'America/Yellowknife' },
  310. { text: '(GMT-06:00) Belize', value: 'America/Belize' },
  311. { text: '(GMT-06:00) Central Time', value: 'America/Chicago' },
  312. { text: '(GMT-06:00) Central Time - Mexico City', value: 'America/Mexico_City' },
  313. { text: '(GMT-06:00) Central Time - Regina', value: 'America/Regina' },
  314. { text: '(GMT-06:00) Central Time - Tegucigalpa', value: 'America/Tegucigalpa' },
  315. { text: '(GMT-06:00) Central Time - Winnipeg', value: 'America/Winnipeg' },
  316. { text: '(GMT-06:00) Costa Rica', value: 'America/Costa_Rica' },
  317. { text: '(GMT-06:00) El Salvador', value: 'America/El_Salvador' },
  318. { text: '(GMT-06:00) Galapagos', value: 'Pacific/Galapagos' },
  319. { text: '(GMT-06:00) Guatemala', value: 'America/Guatemala' },
  320. { text: '(GMT-06:00) Managua', value: 'America/Managua' },
  321. { text: '(GMT-05:00) America Cancun', value: 'America/Cancun' },
  322. { text: '(GMT-05:00) Bogota', value: 'America/Bogota' },
  323. { text: '(GMT-05:00) Easter Island', value: 'Pacific/Easter' },
  324. { text: '(GMT-05:00) Eastern Time', value: 'America/New_York' },
  325. { text: '(GMT-05:00) Eastern Time - Iqaluit', value: 'America/Iqaluit' },
  326. { text: '(GMT-05:00) Eastern Time - Toronto', value: 'America/Toronto' },
  327. { text: '(GMT-05:00) Guayaquil', value: 'America/Guayaquil' },
  328. { text: '(GMT-05:00) Havana', value: 'America/Havana' },
  329. { text: '(GMT-05:00) Jamaica', value: 'America/Jamaica' },
  330. { text: '(GMT-05:00) Lima', value: 'America/Lima' },
  331. { text: '(GMT-05:00) Nassau', value: 'America/Nassau' },
  332. { text: '(GMT-05:00) Panama', value: 'America/Panama' },
  333. { text: '(GMT-05:00) Port-au-Prince', value: 'America/Port-au-Prince' },
  334. { text: '(GMT-05:00) Rio Branco', value: 'America/Rio_Branco' },
  335. { text: '(GMT-04:00) Atlantic Time - Halifax', value: 'America/Halifax' },
  336. { text: '(GMT-04:00) Barbados', value: 'America/Barbados' },
  337. { text: '(GMT-04:00) Bermuda', value: 'Atlantic/Bermuda' },
  338. { text: '(GMT-04:00) Boa Vista', value: 'America/Boa_Vista' },
  339. { text: '(GMT-04:00) Caracas', value: 'America/Caracas' },
  340. { text: '(GMT-04:00) Curacao', value: 'America/Curacao' },
  341. { text: '(GMT-04:00) Grand Turk', value: 'America/Grand_Turk' },
  342. { text: '(GMT-04:00) Guyana', value: 'America/Guyana' },
  343. { text: '(GMT-04:00) La Paz', value: 'America/La_Paz' },
  344. { text: '(GMT-04:00) Manaus', value: 'America/Manaus' },
  345. { text: '(GMT-04:00) Martinique', value: 'America/Martinique' },
  346. { text: '(GMT-04:00) Port of Spain', value: 'America/Port_of_Spain' },
  347. { text: '(GMT-04:00) Porto Velho', value: 'America/Porto_Velho' },
  348. { text: '(GMT-04:00) Puerto Rico', value: 'America/Puerto_Rico' },
  349. { text: '(GMT-04:00) Santo Domingo', value: 'America/Santo_Domingo' },
  350. { text: '(GMT-04:00) Thule', value: 'America/Thule' },
  351. { text: '(GMT-03:30) Newfoundland Time - St. Johns', value: 'America/St_Johns' },
  352. { text: '(GMT-03:00) Araguaina', value: 'America/Araguaina' },
  353. { text: '(GMT-03:00) Asuncion', value: 'America/Asuncion' },
  354. { text: '(GMT-03:00) Belem', value: 'America/Belem' },
  355. { text: '(GMT-03:00) Buenos Aires', value: 'America/Argentina/Buenos_Aires' },
  356. { text: '(GMT-03:00) Campo Grande', value: 'America/Campo_Grande' },
  357. { text: '(GMT-03:00) Cayenne', value: 'America/Cayenne' },
  358. { text: '(GMT-03:00) Cuiaba', value: 'America/Cuiaba' },
  359. { text: '(GMT-03:00) Fortaleza', value: 'America/Fortaleza' },
  360. { text: '(GMT-03:00) Godthab', value: 'America/Godthab' },
  361. { text: '(GMT-03:00) Maceio', value: 'America/Maceio' },
  362. { text: '(GMT-03:00) Miquelon', value: 'America/Miquelon' },
  363. { text: '(GMT-03:00) Montevideo', value: 'America/Montevideo' },
  364. { text: '(GMT-03:00) Palmer', value: 'Antarctica/Palmer' },
  365. { text: '(GMT-03:00) Paramaribo', value: 'America/Paramaribo' },
  366. { text: '(GMT-03:00) Punta Arenas', value: 'America/Punta_Arenas' },
  367. { text: '(GMT-03:00) Recife', value: 'America/Recife' },
  368. { text: '(GMT-03:00) Rothera', value: 'Antarctica/Rothera' },
  369. { text: '(GMT-03:00) Salvador', value: 'America/Bahia' },
  370. { text: '(GMT-03:00) Santiago', value: 'America/Santiago' },
  371. { text: '(GMT-03:00) Stanley', value: 'Atlantic/Stanley' },
  372. { text: '(GMT-02:00) Noronha', value: 'America/Noronha' },
  373. { text: '(GMT-02:00) Sao Paulo', value: 'America/Sao_Paulo' },
  374. { text: '(GMT-02:00) South Georgia', value: 'Atlantic/South_Georgia' },
  375. { text: '(GMT-01:00) Azores', value: 'Atlantic/Azores' },
  376. { text: '(GMT-01:00) Cape Verde', value: 'Atlantic/Cape_Verde' },
  377. { text: '(GMT-01:00) Scoresbysund', value: 'America/Scoresbysund' },
  378. { text: '(GMT+00:00) Abidjan', value: 'Africa/Abidjan' },
  379. { text: '(GMT+00:00) Accra', value: 'Africa/Accra' },
  380. { text: '(GMT+00:00) Bissau', value: 'Africa/Bissau' },
  381. { text: '(GMT+00:00) Canary Islands', value: 'Atlantic/Canary' },
  382. { text: '(GMT+00:00) Casablanca', value: 'Africa/Casablanca' },
  383. { text: '(GMT+00:00) Danmarkshavn', value: 'America/Danmarkshavn' },
  384. { text: '(GMT+00:00) Dublin', value: 'Europe/Dublin' },
  385. { text: '(GMT+00:00) El Aaiun', value: 'Africa/El_Aaiun' },
  386. { text: '(GMT+00:00) Faeroe', value: 'Atlantic/Faroe' },
  387. { text: '(GMT+00:00) GMT (no daylight saving)', value: 'Etc/GMT' },
  388. { text: '(GMT+00:00) Lisbon', value: 'Europe/Lisbon' },
  389. { text: '(GMT+00:00) London', value: 'Europe/London' },
  390. { text: '(GMT+00:00) Monrovia', value: 'Africa/Monrovia' },
  391. { text: '(GMT+00:00) Reykjavik', value: 'Atlantic/Reykjavik' },
  392. { text: '(GMT+01:00) Algiers', value: 'Africa/Algiers' },
  393. { text: '(GMT+01:00) Amsterdam', value: 'Europe/Amsterdam' },
  394. { text: '(GMT+01:00) Andorra', value: 'Europe/Andorra' },
  395. { text: '(GMT+01:00) Berlin', value: 'Europe/Berlin' },
  396. { text: '(GMT+01:00) Brussels', value: 'Europe/Brussels' },
  397. { text: '(GMT+01:00) Budapest', value: 'Europe/Budapest' },
  398. { text: '(GMT+01:00) Central European Time - Belgrade', value: 'Europe/Belgrade' },
  399. { text: '(GMT+01:00) Central European Time - Prague', value: 'Europe/Prague' },
  400. { text: '(GMT+01:00) Ceuta', value: 'Africa/Ceuta' },
  401. { text: '(GMT+01:00) Copenhagen', value: 'Europe/Copenhagen' },
  402. { text: '(GMT+01:00) Gibraltar', value: 'Europe/Gibraltar' },
  403. { text: '(GMT+01:00) Lagos', value: 'Africa/Lagos' },
  404. { text: '(GMT+01:00) Luxembourg', value: 'Europe/Luxembourg' },
  405. { text: '(GMT+01:00) Madrid', value: 'Europe/Madrid' },
  406. { text: '(GMT+01:00) Malta', value: 'Europe/Malta' },
  407. { text: '(GMT+01:00) Monaco', value: 'Europe/Monaco' },
  408. { text: '(GMT+01:00) Ndjamena', value: 'Africa/Ndjamena' },
  409. { text: '(GMT+01:00) Oslo', value: 'Europe/Oslo' },
  410. { text: '(GMT+01:00) Paris', value: 'Europe/Paris' },
  411. { text: '(GMT+01:00) Rome', value: 'Europe/Rome' },
  412. { text: '(GMT+01:00) Stockholm', value: 'Europe/Stockholm' },
  413. { text: '(GMT+01:00) Tirane', value: 'Europe/Tirane' },
  414. { text: '(GMT+01:00) Tunis', value: 'Africa/Tunis' },
  415. { text: '(GMT+01:00) Vienna', value: 'Europe/Vienna' },
  416. { text: '(GMT+01:00) Warsaw', value: 'Europe/Warsaw' },
  417. { text: '(GMT+01:00) Zurich', value: 'Europe/Zurich' },
  418. { text: '(GMT+02:00) Amman', value: 'Asia/Amman' },
  419. { text: '(GMT+02:00) Athens', value: 'Europe/Athens' },
  420. { text: '(GMT+02:00) Beirut', value: 'Asia/Beirut' },
  421. { text: '(GMT+02:00) Bucharest', value: 'Europe/Bucharest' },
  422. { text: '(GMT+02:00) Cairo', value: 'Africa/Cairo' },
  423. { text: '(GMT+02:00) Chisinau', value: 'Europe/Chisinau' },
  424. { text: '(GMT+02:00) Damascus', value: 'Asia/Damascus' },
  425. { text: '(GMT+02:00) Gaza', value: 'Asia/Gaza' },
  426. { text: '(GMT+02:00) Helsinki', value: 'Europe/Helsinki' },
  427. { text: '(GMT+02:00) Jerusalem', value: 'Asia/Jerusalem' },
  428. { text: '(GMT+02:00) Johannesburg', value: 'Africa/Johannesburg' },
  429. { text: '(GMT+02:00) Khartoum', value: 'Africa/Khartoum' },
  430. { text: '(GMT+02:00) Kiev', value: 'Europe/Kiev' },
  431. { text: '(GMT+02:00) Maputo', value: 'Africa/Maputo' },
  432. { text: '(GMT+02:00) Moscow-01 - Kaliningrad', value: 'Europe/Kaliningrad' },
  433. { text: '(GMT+02:00) Nicosia', value: 'Asia/Nicosia' },
  434. { text: '(GMT+02:00) Riga', value: 'Europe/Riga' },
  435. { text: '(GMT+02:00) Sofia', value: 'Europe/Sofia' },
  436. { text: '(GMT+02:00) Tallinn', value: 'Europe/Tallinn' },
  437. { text: '(GMT+02:00) Tripoli', value: 'Africa/Tripoli' },
  438. { text: '(GMT+02:00) Vilnius', value: 'Europe/Vilnius' },
  439. { text: '(GMT+02:00) Windhoek', value: 'Africa/Windhoek' },
  440. { text: '(GMT+03:00) Baghdad', value: 'Asia/Baghdad' },
  441. { text: '(GMT+03:00) Istanbul', value: 'Europe/Istanbul' },
  442. { text: '(GMT+03:00) Minsk', value: 'Europe/Minsk' },
  443. { text: '(GMT+03:00) Moscow+00 - Moscow', value: 'Europe/Moscow' },
  444. { text: '(GMT+03:00) Nairobi', value: 'Africa/Nairobi' },
  445. { text: '(GMT+03:00) Qatar', value: 'Asia/Qatar' },
  446. { text: '(GMT+03:00) Riyadh', value: 'Asia/Riyadh' },
  447. { text: '(GMT+03:00) Syowa', value: 'Antarctica/Syowa' },
  448. { text: '(GMT+03:30) Tehran', value: 'Asia/Tehran' },
  449. { text: '(GMT+04:00) Baku', value: 'Asia/Baku' },
  450. { text: '(GMT+04:00) Dubai', value: 'Asia/Dubai' },
  451. { text: '(GMT+04:00) Mahe', value: 'Indian/Mahe' },
  452. { text: '(GMT+04:00) Mauritius', value: 'Indian/Mauritius' },
  453. { text: '(GMT+04:00) Moscow+01 - Samara', value: 'Europe/Samara' },
  454. { text: '(GMT+04:00) Reunion', value: 'Indian/Reunion' },
  455. { text: '(GMT+04:00) Tbilisi', value: 'Asia/Tbilisi' },
  456. { text: '(GMT+04:00) Yerevan', value: 'Asia/Yerevan' },
  457. { text: '(GMT+04:30) Kabul', value: 'Asia/Kabul' },
  458. { text: '(GMT+05:00) Aqtau', value: 'Asia/Aqtau' },
  459. { text: '(GMT+05:00) Aqtobe', value: 'Asia/Aqtobe' },
  460. { text: '(GMT+05:00) Ashgabat', value: 'Asia/Ashgabat' },
  461. { text: '(GMT+05:00) Dushanbe', value: 'Asia/Dushanbe' },
  462. { text: '(GMT+05:00) Karachi', value: 'Asia/Karachi' },
  463. { text: '(GMT+05:00) Kerguelen', value: 'Indian/Kerguelen' },
  464. { text: '(GMT+05:00) Maldives', value: 'Indian/Maldives' },
  465. { text: '(GMT+05:00) Mawson', value: 'Antarctica/Mawson' },
  466. { text: '(GMT+05:00) Moscow+02 - Yekaterinburg', value: 'Asia/Yekaterinburg' },
  467. { text: '(GMT+05:00) Tashkent', value: 'Asia/Tashkent' },
  468. { text: '(GMT+05:30) Colombo', value: 'Asia/Colombo' },
  469. { text: '(GMT+05:30) India Standard Time', value: 'Asia/Kolkata' },
  470. { text: '(GMT+05:45) Kathmandu', value: 'Asia/Kathmandu' },
  471. { text: '(GMT+06:00) Almaty', value: 'Asia/Almaty' },
  472. { text: '(GMT+06:00) Bishkek', value: 'Asia/Bishkek' },
  473. { text: '(GMT+06:00) Chagos', value: 'Indian/Chagos' },
  474. { text: '(GMT+06:00) Dhaka', value: 'Asia/Dhaka' },
  475. { text: '(GMT+06:00) Moscow+03 - Omsk', value: 'Asia/Omsk' },
  476. { text: '(GMT+06:00) Thimphu', value: 'Asia/Thimphu' },
  477. { text: '(GMT+06:00) Vostok', value: 'Antarctica/Vostok' },
  478. { text: '(GMT+06:30) Cocos', value: 'Indian/Cocos' },
  479. { text: '(GMT+06:30) Rangoon', value: 'Asia/Yangon' },
  480. { text: '(GMT+07:00) Bangkok', value: 'Asia/Bangkok' },
  481. { text: '(GMT+07:00) Christmas', value: 'Indian/Christmas' },
  482. { text: '(GMT+07:00) Davis', value: 'Antarctica/Davis' },
  483. { text: '(GMT+07:00) Hanoi', value: 'Asia/Saigon' },
  484. { text: '(GMT+07:00) Hovd', value: 'Asia/Hovd' },
  485. { text: '(GMT+07:00) Jakarta', value: 'Asia/Jakarta' },
  486. { text: '(GMT+07:00) Moscow+04 - Krasnoyarsk', value: 'Asia/Krasnoyarsk' },
  487. { text: '(GMT+08:00) Brunei', value: 'Asia/Brunei' },
  488. { text: '(GMT+08:00) China Time - Beijing', value: 'Asia/Shanghai' },
  489. { text: '(GMT+08:00) Choibalsan', value: 'Asia/Choibalsan' },
  490. { text: '(GMT+08:00) Hong Kong', value: 'Asia/Hong_Kong' },
  491. { text: '(GMT+08:00) Kuala Lumpur', value: 'Asia/Kuala_Lumpur' },
  492. { text: '(GMT+08:00) Macau', value: 'Asia/Macau' },
  493. { text: '(GMT+08:00) Makassar', value: 'Asia/Makassar' },
  494. { text: '(GMT+08:00) Manila', value: 'Asia/Manila' },
  495. { text: '(GMT+08:00) Moscow+05 - Irkutsk', value: 'Asia/Irkutsk' },
  496. { text: '(GMT+08:00) Singapore', value: 'Asia/Singapore' },
  497. { text: '(GMT+08:00) Taipei', value: 'Asia/Taipei' },
  498. { text: '(GMT+08:00) Ulaanbaatar', value: 'Asia/Ulaanbaatar' },
  499. { text: '(GMT+08:00) Western Time - Perth', value: 'Australia/Perth' },
  500. { text: '(GMT+08:30) Pyongyang', value: 'Asia/Pyongyang' },
  501. { text: '(GMT+09:00) Dili', value: 'Asia/Dili' },
  502. { text: '(GMT+09:00) Jayapura', value: 'Asia/Jayapura' },
  503. { text: '(GMT+09:00) Moscow+06 - Yakutsk', value: 'Asia/Yakutsk' },
  504. { text: '(GMT+09:00) Palau', value: 'Pacific/Palau' },
  505. { text: '(GMT+09:00) Seoul', value: 'Asia/Seoul' },
  506. { text: '(GMT+09:00) Tokyo', value: 'Asia/Tokyo' },
  507. { text: '(GMT+09:30) Central Time - Darwin', value: 'Australia/Darwin' },
  508. { text: '(GMT+10:00) Dumont D\'Urville', value: 'Antarctica/DumontDUrville' },
  509. { text: '(GMT+10:00) Eastern Time - Brisbane', value: 'Australia/Brisbane' },
  510. { text: '(GMT+10:00) Guam', value: 'Pacific/Guam' },
  511. { text: '(GMT+10:00) Moscow+07 - Vladivostok', value: 'Asia/Vladivostok' },
  512. { text: '(GMT+10:00) Port Moresby', value: 'Pacific/Port_Moresby' },
  513. { text: '(GMT+10:00) Truk', value: 'Pacific/Chuuk' },
  514. { text: '(GMT+10:30) Central Time - Adelaide', value: 'Australia/Adelaide' },
  515. { text: '(GMT+11:00) Casey', value: 'Antarctica/Casey' },
  516. { text: '(GMT+11:00) Eastern Time - Hobart', value: 'Australia/Hobart' },
  517. { text: '(GMT+11:00) Eastern Time - Melbourne, Sydney', value: 'Australia/Sydney' },
  518. { text: '(GMT+11:00) Efate', value: 'Pacific/Efate' },
  519. { text: '(GMT+11:00) Guadalcanal', value: 'Pacific/Guadalcanal' },
  520. { text: '(GMT+11:00) Kosrae', value: 'Pacific/Kosrae' },
  521. { text: '(GMT+11:00) Moscow+08 - Magadan', value: 'Asia/Magadan' },
  522. { text: '(GMT+11:00) Norfolk', value: 'Pacific/Norfolk' },
  523. { text: '(GMT+11:00) Noumea', value: 'Pacific/Noumea' },
  524. { text: '(GMT+11:00) Ponape', value: 'Pacific/Pohnpei' },
  525. { text: '(GMT+12:00) Funafuti', value: 'Pacific/Funafuti' },
  526. { text: '(GMT+12:00) Kwajalein', value: 'Pacific/Kwajalein' },
  527. { text: '(GMT+12:00) Majuro', value: 'Pacific/Majuro' },
  528. { text: '(GMT+12:00) Moscow+09 - Petropavlovsk-Kamchatskiy', value: 'Asia/Kamchatka' },
  529. { text: '(GMT+12:00) Nauru', value: 'Pacific/Nauru' },
  530. { text: '(GMT+12:00) Tarawa', value: 'Pacific/Tarawa' },
  531. { text: '(GMT+12:00) Wake', value: 'Pacific/Wake' },
  532. { text: '(GMT+12:00) Wallis', value: 'Pacific/Wallis' },
  533. { text: '(GMT+13:00) Auckland', value: 'Pacific/Auckland' },
  534. { text: '(GMT+13:00) Enderbury', value: 'Pacific/Enderbury' },
  535. { text: '(GMT+13:00) Fakaofo', value: 'Pacific/Fakaofo' },
  536. { text: '(GMT+13:00) Fiji', value: 'Pacific/Fiji' },
  537. { text: '(GMT+13:00) Tongatapu', value: 'Pacific/Tongatapu' },
  538. { text: '(GMT+14:00) Apia', value: 'Pacific/Apia' },
  539. { text: '(GMT+14:00) Kiritimati', value: 'Pacific/Kiritimati' }
  540. ]
  541. }
  542. },
  543. computed: {
  544. pictureUrl: get('user/pictureUrl'),
  545. picture () {
  546. if (this.pictureUrl && this.pictureUrl.length > 1) {
  547. return {
  548. kind: 'image',
  549. url: this.pictureUrl
  550. }
  551. } else {
  552. const nameParts = this.user.name.toUpperCase().split(' ')
  553. let initials = _.head(nameParts).charAt(0)
  554. if (nameParts.length > 1) {
  555. initials += _.last(nameParts).charAt(0)
  556. }
  557. return {
  558. kind: 'initials',
  559. initials
  560. }
  561. }
  562. }
  563. },
  564. methods: {
  565. /**
  566. * Focus an input after delay
  567. */
  568. focusField (ipt) {
  569. this.$nextTick(() => {
  570. _.delay(() => {
  571. this.$refs[ipt].focus()
  572. }, 200)
  573. })
  574. },
  575. /**
  576. * Save User Profile
  577. */
  578. async saveProfile () {
  579. this.saveLoading = true
  580. this.$store.commit(`loadingStart`, 'profile-save')
  581. try {
  582. const respRaw = await this.$apollo.mutate({
  583. mutation: gql`
  584. mutation ($name: String!, $location: String!, $jobTitle: String!, $timezone: String!) {
  585. users {
  586. updateProfile(name: $name, location: $location, jobTitle: $jobTitle, timezone: $timezone) {
  587. responseResult {
  588. succeeded
  589. errorCode
  590. slug
  591. message
  592. }
  593. jwt
  594. }
  595. }
  596. }
  597. `,
  598. variables: {
  599. name: this.user.name,
  600. location: this.user.location,
  601. jobTitle: this.user.jobTitle,
  602. timezone: this.user.timezone
  603. }
  604. })
  605. const resp = _.get(respRaw, 'data.users.updateProfile.responseResult', {})
  606. if (resp.succeeded) {
  607. Cookies.set('jwt', _.get(respRaw, 'data.users.updateProfile.jwt', ''), { expires: 365 })
  608. this.$store.set('user/name', this.user.name)
  609. this.$store.commit('showNotification', {
  610. message: this.$t('profile:save.success'),
  611. style: 'success',
  612. icon: 'check'
  613. })
  614. } else {
  615. throw new Error(resp.message)
  616. }
  617. } catch (err) {
  618. this.$store.commit('pushGraphError', err)
  619. }
  620. this.$store.commit(`loadingStop`, 'profile-save')
  621. this.saveLoading = false
  622. },
  623. /**
  624. * Change Password
  625. */
  626. async changePassword () {
  627. const validation = validate({
  628. current: this.currentPass,
  629. password: this.newPass,
  630. verifyPassword: this.verifyPass
  631. }, {
  632. current: {
  633. presence: {
  634. message: this.$t('auth:missingPassword'),
  635. allowEmpty: false
  636. },
  637. length: {
  638. minimum: 6,
  639. tooShort: this.$t('auth:passwordTooShort')
  640. }
  641. },
  642. password: {
  643. presence: {
  644. message: this.$t('auth:missingPassword'),
  645. allowEmpty: false
  646. },
  647. length: {
  648. minimum: 6,
  649. tooShort: this.$t('auth:passwordTooShort')
  650. }
  651. },
  652. verifyPassword: {
  653. equality: {
  654. attribute: 'password',
  655. message: this.$t('auth:passwordNotMatch')
  656. }
  657. }
  658. }, { fullMessages: false })
  659. if (validation) {
  660. if (validation.current) {
  661. this.$store.commit('showNotification', {
  662. style: 'red',
  663. message: validation.current[0],
  664. icon: 'warning'
  665. })
  666. this.$refs.iptCurrentPass.focus()
  667. } else if (validation.password) {
  668. this.$store.commit('showNotification', {
  669. style: 'red',
  670. message: validation.password[0],
  671. icon: 'warning'
  672. })
  673. this.$refs.iptNewPass.focus()
  674. } else if (validation.verifyPassword) {
  675. this.$store.commit('showNotification', {
  676. style: 'red',
  677. message: validation.verifyPassword[0],
  678. icon: 'warning'
  679. })
  680. this.$refs.iptVerifyPass.focus()
  681. }
  682. } else {
  683. this.changePassLoading = true
  684. this.$store.commit(`loadingStart`, 'profile-changepassword')
  685. try {
  686. const respRaw = await this.$apollo.mutate({
  687. mutation: gql`
  688. mutation ($current: String!, $new: String!) {
  689. users {
  690. changePassword(current: $current, new: $new) {
  691. responseResult {
  692. succeeded
  693. errorCode
  694. slug
  695. message
  696. }
  697. jwt
  698. }
  699. }
  700. }
  701. `,
  702. variables: {
  703. current: this.currentPass,
  704. new: this.newPass
  705. }
  706. })
  707. const resp = _.get(respRaw, 'data.users.changePassword.responseResult', {})
  708. if (resp.succeeded) {
  709. this.currentPass = ''
  710. this.newPass = ''
  711. this.verifyPass = ''
  712. Cookies.set('jwt', _.get(respRaw, 'data.users.changePassword.jwt', ''), { expires: 365 })
  713. this.$store.commit('showNotification', {
  714. message: this.$t('profile:auth.changePassSuccess'),
  715. style: 'success',
  716. icon: 'check'
  717. })
  718. } else {
  719. throw new Error(resp.message)
  720. }
  721. } catch (err) {
  722. this.$store.commit('pushGraphError', err)
  723. }
  724. this.$store.commit(`loadingStop`, 'profile-changepassword')
  725. this.changePassLoading = false
  726. }
  727. }
  728. },
  729. apollo: {
  730. user: {
  731. query: gql`
  732. {
  733. users {
  734. profile {
  735. id
  736. name
  737. email
  738. providerKey
  739. providerName
  740. isSystem
  741. isVerified
  742. location
  743. jobTitle
  744. timezone
  745. createdAt
  746. updatedAt
  747. lastLoginAt
  748. groups
  749. pagesTotal
  750. }
  751. }
  752. }
  753. `,
  754. fetchPolicy: 'network-only',
  755. update: (data) => _.cloneDeep(data.users.profile),
  756. watchLoading (isLoading) {
  757. this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'profile-refresh')
  758. }
  759. }
  760. }
  761. }
  762. </script>
  763. <style lang='scss'>
  764. </style>