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.

1077 lines
46 KiB

  1. <template lang='pug'>
  2. v-container(fluid, grid-list-lg)
  3. v-layout(row, wrap)
  4. v-flex(xs12)
  5. .admin-header
  6. img.animated.fadeInUp(src='/_assets/svg/icon-male-user.svg', :alt='$t(`admin:users.edit`)', style='width: 80px;')
  7. .admin-header-title
  8. .headline.blue--text.text--darken-2.animated.fadeInLeft {{$t('admin:users.edit')}}
  9. .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s {{user.name}}
  10. v-spacer
  11. i18next.pr-4.caption.grey--text.animated.fadeInDown(path='admin:users.id', tag='div')
  12. strong(place='id') {{user.id}}
  13. template(v-if='user.isActive')
  14. status-indicator.mr-3(positive, pulse)
  15. .caption.green--text {{$t('admin:users.active')}}
  16. template(v-else)
  17. status-indicator.mr-3(negative, pulse)
  18. .caption.red--text {{$t('admin:users.inactive')}}
  19. template(v-if='user.isVerified')
  20. status-indicator.mr-3.ml-4(active, pulse)
  21. .caption.blue--text {{$t('admin:users.verified')}}
  22. template(v-else)
  23. status-indicator.mr-3.ml-4(intermediary, pulse)
  24. .caption.deep-orange--text {{$t('admin:users.unverified')}}
  25. v-spacer
  26. v-btn.ml-3.animated.fadeInDown.wait-p3s(color='grey', icon, outlined, to='/users')
  27. v-icon mdi-arrow-left
  28. v-menu(offset-y, origin='top right')
  29. template(v-slot:activator='{ on }')
  30. v-btn.ml-3.animated.fadeInDown.wait-p2s(color='black', v-on='on', depressed, dark)
  31. span Actions
  32. v-icon(right) mdi-chevron-down
  33. v-list(dense, nav)
  34. v-list-item(v-if='!user.isActive', @click='activateUser')
  35. v-list-item-icon
  36. v-icon(color='purple') mdi-account-key
  37. v-list-item-title Activate
  38. v-list-item(v-else, @click='deactivateUser', :disabled='user.id == currentUserId || user.isSystem')
  39. v-list-item-icon
  40. v-icon(color='purple') mdi-account-cancel
  41. v-list-item-title Deactivate
  42. v-list-item(@click='verifyUser', :disabled='user.isVerified')
  43. v-list-item-icon
  44. v-icon(color='blue') mdi-account-check
  45. v-list-item-title Set as Verified
  46. v-list-item(@click='deleteUserConfirm', :disabled='user.id == currentUserId || user.isSystem')
  47. v-list-item-icon
  48. v-icon(color='red') mdi-trash-can-outline
  49. v-list-item-title Delete
  50. v-btn.ml-3.animated.fadeInDown(color='primary', large, depressed, @click='updateUser')
  51. v-icon(left) mdi-check
  52. span {{$t('admin:users.updateUser')}}
  53. v-flex(xs6)
  54. v-card.animated.fadeInUp
  55. v-toolbar(color='primary', dense, dark, flat)
  56. v-icon.mr-2 mdi-information-variant
  57. span {{$t('admin:users.basicInfo')}}
  58. v-list.py-0(two-line, dense)
  59. v-list-item
  60. v-list-item-avatar(size='32')
  61. v-icon mdi-email-variant
  62. v-list-item-content
  63. v-list-item-title {{$t('admin:users.email')}}
  64. v-list-item-subtitle {{ user.email }}
  65. v-list-item-action(v-if='!user.isSystem && user.providerKey === `local`')
  66. v-menu(
  67. v-model='editPop.email'
  68. :close-on-content-click='false'
  69. min-width='350'
  70. left
  71. )
  72. template(v-slot:activator='{ on }')
  73. v-btn(icon, color='grey', x-small, v-on='on', @click='focusField(`iptEmail`)')
  74. v-icon mdi-pencil
  75. v-card
  76. v-text-field(
  77. ref='iptEmail'
  78. v-model='user.email'
  79. :label='$t(`admin:users.email`)'
  80. solo
  81. hide-details
  82. append-icon='mdi-check'
  83. @click:append='editPop.email = false'
  84. @keydown.enter='editPop.email = false'
  85. @keydown.esc='editPop.email = false'
  86. )
  87. v-divider
  88. v-list-item
  89. v-list-item-avatar(size='32')
  90. v-icon mdi-account
  91. v-list-item-content
  92. v-list-item-title {{$t('admin:users.displayName')}}
  93. v-list-item-subtitle {{ user.name }}
  94. v-list-item-action
  95. v-menu(
  96. v-model='editPop.name'
  97. :close-on-content-click='false'
  98. min-width='350'
  99. left
  100. )
  101. template(v-slot:activator='{ on }')
  102. v-btn(icon, color='grey', x-small, v-on='on', @click='focusField(`iptDisplayName`)')
  103. v-icon mdi-pencil
  104. v-card
  105. v-text-field(
  106. ref='iptDisplayName'
  107. v-model='user.name'
  108. :label='$t(`admin:users.displayName`)'
  109. solo
  110. hide-details
  111. append-icon='mdi-check'
  112. @click:append='editPop.name = false'
  113. @keydown.enter='editPop.name = false'
  114. @keydown.esc='editPop.name = false'
  115. )
  116. v-card.mt-3.animated.fadeInUp.wait-p2s(v-if='!user.isSystem')
  117. v-toolbar(color='primary', dense, dark, flat)
  118. v-icon.mr-2 mdi-lock-outline
  119. span {{$t('admin:users.authentication')}}
  120. v-list.py-0(two-line, dense)
  121. v-list-item
  122. v-list-item-avatar(size='32')
  123. v-icon mdi-domain
  124. v-list-item-content
  125. v-list-item-title {{$t('admin:users.authProvider')}}
  126. v-list-item-subtitle {{ user.providerName }} #[em.caption ({{ user.providerKey }})]
  127. template(v-if='user.providerKey === `local`')
  128. v-divider
  129. v-list-item
  130. v-list-item-avatar(size='32')
  131. v-icon mdi-form-textbox-password
  132. v-list-item-content
  133. v-list-item-title {{$t('admin:users.password')}}
  134. v-list-item-subtitle &bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;
  135. v-list-item-action
  136. v-menu(
  137. v-model='editPop.newPassword'
  138. :close-on-content-click='false'
  139. min-width='350'
  140. left
  141. )
  142. template(v-slot:activator='{ on: menu }')
  143. v-tooltip(top)
  144. template(v-slot:activator='{ on: tooltip }')
  145. v-btn(icon, color='grey', x-small, v-on='{ ...menu, ...tooltip }', @click='focusField(`iptNewPassword`)')
  146. v-icon mdi-pencil
  147. span {{$t('admin:users.changePassword')}}
  148. v-card
  149. v-text-field(
  150. ref='iptNewPassword'
  151. v-model='newPassword'
  152. :label='$t(`admin:users.newPassword`)'
  153. solo
  154. hide-details
  155. append-icon='mdi-check'
  156. type='password'
  157. @click:append='editPop.newPassword = false'
  158. @keydown.enter='editPop.newPassword = false'
  159. @keydown.esc='editPop.newPassword = false'
  160. )
  161. v-list-item-action
  162. v-tooltip(top)
  163. template(v-slot:activator='{ on }')
  164. v-btn(icon, color='grey', x-small, v-on='on', disabled)
  165. v-icon mdi-email
  166. span Send Password Reset Email
  167. template(v-if='user.providerIs2FACapable')
  168. v-divider
  169. v-list-item
  170. v-list-item-avatar(size='32')
  171. v-icon mdi-two-factor-authentication
  172. v-list-item-content
  173. v-list-item-title {{$t('admin:users.tfa')}}
  174. v-list-item-subtitle.green--text(v-if='user.tfaIsActive') Active
  175. v-list-item-subtitle.red--text(v-else) Inactive
  176. v-list-item-action
  177. v-tooltip(top)
  178. template(v-slot:activator='{ on }')
  179. v-btn(icon, color='grey', x-small, v-on='on', @click='toggle2FA')
  180. v-icon mdi-power
  181. span {{$t('admin:users.toggle2FA')}}
  182. template(v-if='user.providerId')
  183. v-divider
  184. v-list-item
  185. v-list-item-avatar(size='32')
  186. v-icon mdi-music-accidental-sharp
  187. v-list-item-content
  188. v-list-item-title {{$t('admin:users.authProviderId')}}
  189. v-list-item-subtitle {{ user.providerId }}
  190. v-card.mt-3.animated.fadeInUp.wait-p4s
  191. v-toolbar(color='primary', dense, dark, flat)
  192. v-icon.mr-2 mdi-account-group
  193. span {{$t('admin:users.groups')}}
  194. v-list(dense)
  195. template(v-for='(group, idx) in user.groups')
  196. v-list-item(:key='`group-` + group.id')
  197. v-list-item-avatar(size='32')
  198. v-icon mdi-account-group-outline
  199. v-list-item-content
  200. v-list-item-title {{group.name}}
  201. v-list-item-action(v-if='!user.isSystem')
  202. v-btn(icon, color='red', x-small, @click='unassignGroup(group.id)')
  203. v-icon mdi-close
  204. v-divider(v-if='idx < user.groups.length - 1')
  205. v-alert.mx-3(v-if='user.groups.length < 1', outlined, color='grey darken-1', icon='mdi-alert')
  206. .caption {{$t('admin:users.noGroupAssigned')}}
  207. v-card-chin(v-if='!user.isSystem')
  208. v-spacer
  209. v-select(
  210. ref='iptAssignGroup'
  211. :items='groups'
  212. v-model='newGroup'
  213. :label='$t(`admin:users.selectGroup`)'
  214. item-value='id'
  215. item-text='name'
  216. item-disabled='isSystem'
  217. solo
  218. flat
  219. hide-details
  220. @keydown.esc='editPop.assignGroup = false'
  221. style='max-width: 300px;'
  222. dense
  223. )
  224. v-btn.ml-2.px-4(depressed, color='primary', @click='assignGroup', :disabled='newGroup === 0')
  225. v-icon(left) mdi-clipboard-account-outline
  226. span {{$t('admin:users.groupAssign')}}
  227. v-system-bar(window, :color='$vuetify.theme.dark ? `grey darken-4-l3` : `grey lighten-3`')
  228. v-spacer
  229. .caption {{$t('admin:users.groupAssignNotice')}}
  230. v-flex(xs6)
  231. v-card.animated.fadeInUp.wait-p2s
  232. v-toolbar(color='primary', dense, dark, flat)
  233. v-icon.mr-2 mdi-account-badge-outline
  234. span {{$t('admin:users.extendedMetadata')}}
  235. v-list.py-0(two-line, dense)
  236. v-list-item
  237. v-list-item-avatar(size='32')
  238. v-icon mdi-map-marker
  239. v-list-item-content
  240. v-list-item-title {{$t('admin:users.location')}}
  241. v-list-item-subtitle {{ user.location }}
  242. v-list-item-action
  243. v-menu(
  244. v-model='editPop.location'
  245. :close-on-content-click='false'
  246. min-width='350'
  247. left
  248. )
  249. template(v-slot:activator='{ on }')
  250. v-btn(icon, color='grey', x-small, v-on='on', @click='focusField(`iptLocation`)')
  251. v-icon mdi-pencil
  252. v-card
  253. v-text-field(
  254. ref='iptLocation'
  255. v-model='user.location'
  256. :label='$t(`admin:users.location`)'
  257. solo
  258. hide-details
  259. append-icon='mdi-check'
  260. @click:append='editPop.location = false'
  261. @keydown.enter='editPop.location = false'
  262. @keydown.esc='editPop.location = false'
  263. )
  264. v-divider
  265. v-list-item
  266. v-list-item-avatar(size='32')
  267. v-icon mdi-briefcase
  268. v-list-item-content
  269. v-list-item-title {{$t('admin:users.jobTitle')}}
  270. v-list-item-subtitle {{ user.jobTitle }}
  271. v-list-item-action
  272. v-menu(
  273. v-model='editPop.jobTitle'
  274. :close-on-content-click='false'
  275. min-width='350'
  276. left
  277. )
  278. template(v-slot:activator='{ on }')
  279. v-btn(icon, color='grey', x-small, v-on='on', @click='focusField(`iptJobTitle`)')
  280. v-icon mdi-pencil
  281. v-card
  282. v-text-field(
  283. ref='iptJobTitle'
  284. v-model='user.jobTitle'
  285. :label='$t(`admin:users.jobTitle`)'
  286. solo
  287. hide-details
  288. append-icon='mdi-check'
  289. @click:append='editPop.jobTitle = false'
  290. @keydown.enter='editPop.jobTitle = false'
  291. @keydown.esc='editPop.jobTitle = false'
  292. )
  293. v-divider
  294. v-list-item
  295. v-list-item-avatar(size='32')
  296. v-icon mdi-map-clock-outline
  297. v-list-item-content
  298. v-list-item-title {{$t('admin:users.timezone')}}
  299. v-list-item-subtitle {{ user.timezone }}
  300. v-list-item-action
  301. v-menu(
  302. v-model='editPop.timezone'
  303. :close-on-content-click='false'
  304. min-width='350'
  305. left
  306. )
  307. template(v-slot:activator='{ on }')
  308. v-btn(icon, color='grey', x-small, v-on='on', @click='focusField(`iptTimezone`)')
  309. v-icon mdi-pencil
  310. v-card
  311. v-select(
  312. ref='iptTimezone'
  313. :items='timezones'
  314. v-model='user.timezone'
  315. :label='$t(`admin:users.timezone`)'
  316. solo
  317. dense
  318. hide-details
  319. append-icon='mdi-check'
  320. @click:append='editPop.timezone = false'
  321. @keydown.enter='editPop.timezone = false'
  322. @keydown.esc='editPop.timezone = false'
  323. )
  324. v-card.mt-3.animated.fadeInUp.wait-p4s
  325. v-toolbar(color='teal', dark, dense, flat)
  326. v-toolbar-title
  327. .subtitle-1 {{$t('profile:activity.title')}}
  328. v-card-text.grey--text.text--darken-2
  329. .caption.grey--text {{$t('profile:activity.joinedOn')}}
  330. .body-2: strong {{ user.createdAt | moment('LLLL') }}
  331. .caption.grey--text.mt-3 {{$t('profile:activity.lastUpdatedOn')}}
  332. .body-2: strong {{ user.updatedAt | moment('LLLL') }}
  333. .caption.grey--text.mt-3 {{$t('profile:activity.lastLoginOn')}}
  334. .body-2: strong {{ user.lastLoginAt | moment('LLLL') }}
  335. v-card.mt-3.animated.fadeInUp.wait-p6s
  336. v-toolbar(color='teal', dense, dark, flat)
  337. v-icon.mr-2 mdi-file-document-box-multiple-outline
  338. span Content
  339. v-card-text
  340. em.caption.grey--text Coming soon
  341. v-dialog(v-model='deleteUserDialog', max-width='500')
  342. v-card
  343. .dialog-header.is-red {{$t('admin:users.deleteConfirmTitle')}}
  344. v-card-text.pt-5
  345. i18next(path='admin:users.deleteConfirmText', tag='span')
  346. strong(place='username') {{ user.email }}
  347. .mt-3 {{$t('admin:users.deleteConfirmReplaceWarn')}}
  348. v-divider.my-3
  349. .d-flex.align-center.mt-3
  350. v-btn.text-none(color='primary', depressed, @click='deleteSearchUserDialog = true')
  351. v-icon(left) mdi-clipboard-account
  352. | Select User...
  353. .caption.pl-3
  354. strong ID {{deleteReplaceUser.id}}
  355. .caption {{deleteReplaceUser.name}}
  356. em {{deleteReplaceUser.email}}
  357. v-card-chin
  358. v-spacer
  359. v-btn(text, @click='deleteUserDialog = false') {{$t('common:actions.cancel')}}
  360. v-btn(color='red', dark, @click='deleteUser') {{$t('common:actions.delete')}}
  361. user-search(v-model='deleteSearchUserDialog', @select='assignDeleteUser')
  362. </template>
  363. <script>
  364. import _ from 'lodash'
  365. import { get } from 'vuex-pathify'
  366. import gql from 'graphql-tag'
  367. import { StatusIndicator } from 'vue-status-indicator'
  368. import UserSearch from '../common/user-search.vue'
  369. import groupsQuery from 'gql/admin/users/users-query-groups.gql'
  370. export default {
  371. i18nOptions: {
  372. namespaces: ['admin', 'profile']
  373. },
  374. components: {
  375. StatusIndicator,
  376. UserSearch
  377. },
  378. data () {
  379. return {
  380. deleteUserDialog: false,
  381. deleteSearchUserDialog: false,
  382. deleteReplaceUser: {
  383. id: 1,
  384. name: '',
  385. email: ''
  386. },
  387. editPop: {
  388. email: false,
  389. name: false,
  390. pwd: false,
  391. location: false,
  392. jobTitle: false,
  393. timezone: false,
  394. newPassword: false,
  395. assignGroup: false
  396. },
  397. newGroup: 0,
  398. newPassword: '',
  399. user: {
  400. email: '',
  401. name: '',
  402. location: '',
  403. jobTitle: '',
  404. timezone: '',
  405. groups: [],
  406. isActive: false,
  407. isVerified: false
  408. },
  409. timezones: [
  410. { text: '(GMT-11:00) Niue', value: 'Pacific/Niue' },
  411. { text: '(GMT-11:00) Pago Pago', value: 'Pacific/Pago_Pago' },
  412. { text: '(GMT-10:00) Hawaii Time', value: 'Pacific/Honolulu' },
  413. { text: '(GMT-10:00) Rarotonga', value: 'Pacific/Rarotonga' },
  414. { text: '(GMT-10:00) Tahiti', value: 'Pacific/Tahiti' },
  415. { text: '(GMT-09:30) Marquesas', value: 'Pacific/Marquesas' },
  416. { text: '(GMT-09:00) Alaska Time', value: 'America/Anchorage' },
  417. { text: '(GMT-09:00) Gambier', value: 'Pacific/Gambier' },
  418. { text: '(GMT-08:00) Pacific Time', value: 'America/Los_Angeles' },
  419. { text: '(GMT-08:00) Pacific Time - Tijuana', value: 'America/Tijuana' },
  420. { text: '(GMT-08:00) Pacific Time - Vancouver', value: 'America/Vancouver' },
  421. { text: '(GMT-08:00) Pacific Time - Whitehorse', value: 'America/Whitehorse' },
  422. { text: '(GMT-08:00) Pitcairn', value: 'Pacific/Pitcairn' },
  423. { text: '(GMT-07:00) Mountain Time', value: 'America/Denver' },
  424. { text: '(GMT-07:00) Mountain Time - Arizona', value: 'America/Phoenix' },
  425. { text: '(GMT-07:00) Mountain Time - Chihuahua, Mazatlan', value: 'America/Mazatlan' },
  426. { text: '(GMT-07:00) Mountain Time - Dawson Creek', value: 'America/Dawson_Creek' },
  427. { text: '(GMT-07:00) Mountain Time - Edmonton', value: 'America/Edmonton' },
  428. { text: '(GMT-07:00) Mountain Time - Hermosillo', value: 'America/Hermosillo' },
  429. { text: '(GMT-07:00) Mountain Time - Yellowknife', value: 'America/Yellowknife' },
  430. { text: '(GMT-06:00) Belize', value: 'America/Belize' },
  431. { text: '(GMT-06:00) Central Time', value: 'America/Chicago' },
  432. { text: '(GMT-06:00) Central Time - Mexico City', value: 'America/Mexico_City' },
  433. { text: '(GMT-06:00) Central Time - Regina', value: 'America/Regina' },
  434. { text: '(GMT-06:00) Central Time - Tegucigalpa', value: 'America/Tegucigalpa' },
  435. { text: '(GMT-06:00) Central Time - Winnipeg', value: 'America/Winnipeg' },
  436. { text: '(GMT-06:00) Costa Rica', value: 'America/Costa_Rica' },
  437. { text: '(GMT-06:00) El Salvador', value: 'America/El_Salvador' },
  438. { text: '(GMT-06:00) Galapagos', value: 'Pacific/Galapagos' },
  439. { text: '(GMT-06:00) Guatemala', value: 'America/Guatemala' },
  440. { text: '(GMT-06:00) Managua', value: 'America/Managua' },
  441. { text: '(GMT-05:00) America Cancun', value: 'America/Cancun' },
  442. { text: '(GMT-05:00) Bogota', value: 'America/Bogota' },
  443. { text: '(GMT-05:00) Easter Island', value: 'Pacific/Easter' },
  444. { text: '(GMT-05:00) Eastern Time', value: 'America/New_York' },
  445. { text: '(GMT-05:00) Eastern Time - Iqaluit', value: 'America/Iqaluit' },
  446. { text: '(GMT-05:00) Eastern Time - Toronto', value: 'America/Toronto' },
  447. { text: '(GMT-05:00) Guayaquil', value: 'America/Guayaquil' },
  448. { text: '(GMT-05:00) Havana', value: 'America/Havana' },
  449. { text: '(GMT-05:00) Jamaica', value: 'America/Jamaica' },
  450. { text: '(GMT-05:00) Lima', value: 'America/Lima' },
  451. { text: '(GMT-05:00) Nassau', value: 'America/Nassau' },
  452. { text: '(GMT-05:00) Panama', value: 'America/Panama' },
  453. { text: '(GMT-05:00) Port-au-Prince', value: 'America/Port-au-Prince' },
  454. { text: '(GMT-05:00) Rio Branco', value: 'America/Rio_Branco' },
  455. { text: '(GMT-04:00) Atlantic Time - Halifax', value: 'America/Halifax' },
  456. { text: '(GMT-04:00) Barbados', value: 'America/Barbados' },
  457. { text: '(GMT-04:00) Bermuda', value: 'Atlantic/Bermuda' },
  458. { text: '(GMT-04:00) Boa Vista', value: 'America/Boa_Vista' },
  459. { text: '(GMT-04:00) Caracas', value: 'America/Caracas' },
  460. { text: '(GMT-04:00) Curacao', value: 'America/Curacao' },
  461. { text: '(GMT-04:00) Grand Turk', value: 'America/Grand_Turk' },
  462. { text: '(GMT-04:00) Guyana', value: 'America/Guyana' },
  463. { text: '(GMT-04:00) La Paz', value: 'America/La_Paz' },
  464. { text: '(GMT-04:00) Manaus', value: 'America/Manaus' },
  465. { text: '(GMT-04:00) Martinique', value: 'America/Martinique' },
  466. { text: '(GMT-04:00) Port of Spain', value: 'America/Port_of_Spain' },
  467. { text: '(GMT-04:00) Porto Velho', value: 'America/Porto_Velho' },
  468. { text: '(GMT-04:00) Puerto Rico', value: 'America/Puerto_Rico' },
  469. { text: '(GMT-04:00) Santo Domingo', value: 'America/Santo_Domingo' },
  470. { text: '(GMT-04:00) Thule', value: 'America/Thule' },
  471. { text: '(GMT-03:30) Newfoundland Time - St. Johns', value: 'America/St_Johns' },
  472. { text: '(GMT-03:00) Araguaina', value: 'America/Araguaina' },
  473. { text: '(GMT-03:00) Asuncion', value: 'America/Asuncion' },
  474. { text: '(GMT-03:00) Belem', value: 'America/Belem' },
  475. { text: '(GMT-03:00) Buenos Aires', value: 'America/Argentina/Buenos_Aires' },
  476. { text: '(GMT-03:00) Campo Grande', value: 'America/Campo_Grande' },
  477. { text: '(GMT-03:00) Cayenne', value: 'America/Cayenne' },
  478. { text: '(GMT-03:00) Cuiaba', value: 'America/Cuiaba' },
  479. { text: '(GMT-03:00) Fortaleza', value: 'America/Fortaleza' },
  480. { text: '(GMT-03:00) Godthab', value: 'America/Godthab' },
  481. { text: '(GMT-03:00) Maceio', value: 'America/Maceio' },
  482. { text: '(GMT-03:00) Miquelon', value: 'America/Miquelon' },
  483. { text: '(GMT-03:00) Montevideo', value: 'America/Montevideo' },
  484. { text: '(GMT-03:00) Palmer', value: 'Antarctica/Palmer' },
  485. { text: '(GMT-03:00) Paramaribo', value: 'America/Paramaribo' },
  486. { text: '(GMT-03:00) Punta Arenas', value: 'America/Punta_Arenas' },
  487. { text: '(GMT-03:00) Recife', value: 'America/Recife' },
  488. { text: '(GMT-03:00) Rothera', value: 'Antarctica/Rothera' },
  489. { text: '(GMT-03:00) Salvador', value: 'America/Bahia' },
  490. { text: '(GMT-03:00) Santiago', value: 'America/Santiago' },
  491. { text: '(GMT-03:00) Stanley', value: 'Atlantic/Stanley' },
  492. { text: '(GMT-02:00) Noronha', value: 'America/Noronha' },
  493. { text: '(GMT-02:00) Sao Paulo', value: 'America/Sao_Paulo' },
  494. { text: '(GMT-02:00) South Georgia', value: 'Atlantic/South_Georgia' },
  495. { text: '(GMT-01:00) Azores', value: 'Atlantic/Azores' },
  496. { text: '(GMT-01:00) Cape Verde', value: 'Atlantic/Cape_Verde' },
  497. { text: '(GMT-01:00) Scoresbysund', value: 'America/Scoresbysund' },
  498. { text: '(GMT+00:00) Abidjan', value: 'Africa/Abidjan' },
  499. { text: '(GMT+00:00) Accra', value: 'Africa/Accra' },
  500. { text: '(GMT+00:00) Bissau', value: 'Africa/Bissau' },
  501. { text: '(GMT+00:00) Canary Islands', value: 'Atlantic/Canary' },
  502. { text: '(GMT+00:00) Casablanca', value: 'Africa/Casablanca' },
  503. { text: '(GMT+00:00) Danmarkshavn', value: 'America/Danmarkshavn' },
  504. { text: '(GMT+00:00) Dublin', value: 'Europe/Dublin' },
  505. { text: '(GMT+00:00) El Aaiun', value: 'Africa/El_Aaiun' },
  506. { text: '(GMT+00:00) Faeroe', value: 'Atlantic/Faroe' },
  507. { text: '(GMT+00:00) GMT (no daylight saving)', value: 'Etc/GMT' },
  508. { text: '(GMT+00:00) Lisbon', value: 'Europe/Lisbon' },
  509. { text: '(GMT+00:00) London', value: 'Europe/London' },
  510. { text: '(GMT+00:00) Monrovia', value: 'Africa/Monrovia' },
  511. { text: '(GMT+00:00) Reykjavik', value: 'Atlantic/Reykjavik' },
  512. { text: '(GMT+01:00) Algiers', value: 'Africa/Algiers' },
  513. { text: '(GMT+01:00) Amsterdam', value: 'Europe/Amsterdam' },
  514. { text: '(GMT+01:00) Andorra', value: 'Europe/Andorra' },
  515. { text: '(GMT+01:00) Berlin', value: 'Europe/Berlin' },
  516. { text: '(GMT+01:00) Brussels', value: 'Europe/Brussels' },
  517. { text: '(GMT+01:00) Budapest', value: 'Europe/Budapest' },
  518. { text: '(GMT+01:00) Central European Time - Belgrade', value: 'Europe/Belgrade' },
  519. { text: '(GMT+01:00) Central European Time - Prague', value: 'Europe/Prague' },
  520. { text: '(GMT+01:00) Ceuta', value: 'Africa/Ceuta' },
  521. { text: '(GMT+01:00) Copenhagen', value: 'Europe/Copenhagen' },
  522. { text: '(GMT+01:00) Gibraltar', value: 'Europe/Gibraltar' },
  523. { text: '(GMT+01:00) Lagos', value: 'Africa/Lagos' },
  524. { text: '(GMT+01:00) Luxembourg', value: 'Europe/Luxembourg' },
  525. { text: '(GMT+01:00) Madrid', value: 'Europe/Madrid' },
  526. { text: '(GMT+01:00) Malta', value: 'Europe/Malta' },
  527. { text: '(GMT+01:00) Monaco', value: 'Europe/Monaco' },
  528. { text: '(GMT+01:00) Ndjamena', value: 'Africa/Ndjamena' },
  529. { text: '(GMT+01:00) Oslo', value: 'Europe/Oslo' },
  530. { text: '(GMT+01:00) Paris', value: 'Europe/Paris' },
  531. { text: '(GMT+01:00) Rome', value: 'Europe/Rome' },
  532. { text: '(GMT+01:00) Stockholm', value: 'Europe/Stockholm' },
  533. { text: '(GMT+01:00) Tirane', value: 'Europe/Tirane' },
  534. { text: '(GMT+01:00) Tunis', value: 'Africa/Tunis' },
  535. { text: '(GMT+01:00) Vienna', value: 'Europe/Vienna' },
  536. { text: '(GMT+01:00) Warsaw', value: 'Europe/Warsaw' },
  537. { text: '(GMT+01:00) Zurich', value: 'Europe/Zurich' },
  538. { text: '(GMT+02:00) Amman', value: 'Asia/Amman' },
  539. { text: '(GMT+02:00) Athens', value: 'Europe/Athens' },
  540. { text: '(GMT+02:00) Beirut', value: 'Asia/Beirut' },
  541. { text: '(GMT+02:00) Bucharest', value: 'Europe/Bucharest' },
  542. { text: '(GMT+02:00) Cairo', value: 'Africa/Cairo' },
  543. { text: '(GMT+02:00) Chisinau', value: 'Europe/Chisinau' },
  544. { text: '(GMT+02:00) Damascus', value: 'Asia/Damascus' },
  545. { text: '(GMT+02:00) Gaza', value: 'Asia/Gaza' },
  546. { text: '(GMT+02:00) Helsinki', value: 'Europe/Helsinki' },
  547. { text: '(GMT+02:00) Jerusalem', value: 'Asia/Jerusalem' },
  548. { text: '(GMT+02:00) Johannesburg', value: 'Africa/Johannesburg' },
  549. { text: '(GMT+02:00) Khartoum', value: 'Africa/Khartoum' },
  550. { text: '(GMT+02:00) Kiev', value: 'Europe/Kiev' },
  551. { text: '(GMT+02:00) Maputo', value: 'Africa/Maputo' },
  552. { text: '(GMT+02:00) Moscow-01 - Kaliningrad', value: 'Europe/Kaliningrad' },
  553. { text: '(GMT+02:00) Nicosia', value: 'Asia/Nicosia' },
  554. { text: '(GMT+02:00) Riga', value: 'Europe/Riga' },
  555. { text: '(GMT+02:00) Sofia', value: 'Europe/Sofia' },
  556. { text: '(GMT+02:00) Tallinn', value: 'Europe/Tallinn' },
  557. { text: '(GMT+02:00) Tripoli', value: 'Africa/Tripoli' },
  558. { text: '(GMT+02:00) Vilnius', value: 'Europe/Vilnius' },
  559. { text: '(GMT+02:00) Windhoek', value: 'Africa/Windhoek' },
  560. { text: '(GMT+03:00) Baghdad', value: 'Asia/Baghdad' },
  561. { text: '(GMT+03:00) Istanbul', value: 'Europe/Istanbul' },
  562. { text: '(GMT+03:00) Minsk', value: 'Europe/Minsk' },
  563. { text: '(GMT+03:00) Moscow+00 - Moscow', value: 'Europe/Moscow' },
  564. { text: '(GMT+03:00) Nairobi', value: 'Africa/Nairobi' },
  565. { text: '(GMT+03:00) Qatar', value: 'Asia/Qatar' },
  566. { text: '(GMT+03:00) Riyadh', value: 'Asia/Riyadh' },
  567. { text: '(GMT+03:00) Syowa', value: 'Antarctica/Syowa' },
  568. { text: '(GMT+03:30) Tehran', value: 'Asia/Tehran' },
  569. { text: '(GMT+04:00) Baku', value: 'Asia/Baku' },
  570. { text: '(GMT+04:00) Dubai', value: 'Asia/Dubai' },
  571. { text: '(GMT+04:00) Mahe', value: 'Indian/Mahe' },
  572. { text: '(GMT+04:00) Mauritius', value: 'Indian/Mauritius' },
  573. { text: '(GMT+04:00) Moscow+01 - Samara', value: 'Europe/Samara' },
  574. { text: '(GMT+04:00) Reunion', value: 'Indian/Reunion' },
  575. { text: '(GMT+04:00) Tbilisi', value: 'Asia/Tbilisi' },
  576. { text: '(GMT+04:00) Yerevan', value: 'Asia/Yerevan' },
  577. { text: '(GMT+04:30) Kabul', value: 'Asia/Kabul' },
  578. { text: '(GMT+05:00) Aqtau', value: 'Asia/Aqtau' },
  579. { text: '(GMT+05:00) Aqtobe', value: 'Asia/Aqtobe' },
  580. { text: '(GMT+05:00) Ashgabat', value: 'Asia/Ashgabat' },
  581. { text: '(GMT+05:00) Dushanbe', value: 'Asia/Dushanbe' },
  582. { text: '(GMT+05:00) Karachi', value: 'Asia/Karachi' },
  583. { text: '(GMT+05:00) Kerguelen', value: 'Indian/Kerguelen' },
  584. { text: '(GMT+05:00) Maldives', value: 'Indian/Maldives' },
  585. { text: '(GMT+05:00) Mawson', value: 'Antarctica/Mawson' },
  586. { text: '(GMT+05:00) Moscow+02 - Yekaterinburg', value: 'Asia/Yekaterinburg' },
  587. { text: '(GMT+05:00) Tashkent', value: 'Asia/Tashkent' },
  588. { text: '(GMT+05:30) Colombo', value: 'Asia/Colombo' },
  589. { text: '(GMT+05:30) India Standard Time', value: 'Asia/Kolkata' },
  590. { text: '(GMT+05:45) Kathmandu', value: 'Asia/Kathmandu' },
  591. { text: '(GMT+06:00) Almaty', value: 'Asia/Almaty' },
  592. { text: '(GMT+06:00) Bishkek', value: 'Asia/Bishkek' },
  593. { text: '(GMT+06:00) Chagos', value: 'Indian/Chagos' },
  594. { text: '(GMT+06:00) Dhaka', value: 'Asia/Dhaka' },
  595. { text: '(GMT+06:00) Moscow+03 - Omsk', value: 'Asia/Omsk' },
  596. { text: '(GMT+06:00) Thimphu', value: 'Asia/Thimphu' },
  597. { text: '(GMT+06:00) Vostok', value: 'Antarctica/Vostok' },
  598. { text: '(GMT+06:30) Cocos', value: 'Indian/Cocos' },
  599. { text: '(GMT+06:30) Rangoon', value: 'Asia/Yangon' },
  600. { text: '(GMT+07:00) Bangkok', value: 'Asia/Bangkok' },
  601. { text: '(GMT+07:00) Christmas', value: 'Indian/Christmas' },
  602. { text: '(GMT+07:00) Davis', value: 'Antarctica/Davis' },
  603. { text: '(GMT+07:00) Hanoi', value: 'Asia/Saigon' },
  604. { text: '(GMT+07:00) Hovd', value: 'Asia/Hovd' },
  605. { text: '(GMT+07:00) Jakarta', value: 'Asia/Jakarta' },
  606. { text: '(GMT+07:00) Moscow+04 - Krasnoyarsk', value: 'Asia/Krasnoyarsk' },
  607. { text: '(GMT+08:00) Brunei', value: 'Asia/Brunei' },
  608. { text: '(GMT+08:00) China Time - Beijing', value: 'Asia/Shanghai' },
  609. { text: '(GMT+08:00) Choibalsan', value: 'Asia/Choibalsan' },
  610. { text: '(GMT+08:00) Hong Kong', value: 'Asia/Hong_Kong' },
  611. { text: '(GMT+08:00) Kuala Lumpur', value: 'Asia/Kuala_Lumpur' },
  612. { text: '(GMT+08:00) Macau', value: 'Asia/Macau' },
  613. { text: '(GMT+08:00) Makassar', value: 'Asia/Makassar' },
  614. { text: '(GMT+08:00) Manila', value: 'Asia/Manila' },
  615. { text: '(GMT+08:00) Moscow+05 - Irkutsk', value: 'Asia/Irkutsk' },
  616. { text: '(GMT+08:00) Singapore', value: 'Asia/Singapore' },
  617. { text: '(GMT+08:00) Taipei', value: 'Asia/Taipei' },
  618. { text: '(GMT+08:00) Ulaanbaatar', value: 'Asia/Ulaanbaatar' },
  619. { text: '(GMT+08:00) Western Time - Perth', value: 'Australia/Perth' },
  620. { text: '(GMT+08:30) Pyongyang', value: 'Asia/Pyongyang' },
  621. { text: '(GMT+09:00) Dili', value: 'Asia/Dili' },
  622. { text: '(GMT+09:00) Jayapura', value: 'Asia/Jayapura' },
  623. { text: '(GMT+09:00) Moscow+06 - Yakutsk', value: 'Asia/Yakutsk' },
  624. { text: '(GMT+09:00) Palau', value: 'Pacific/Palau' },
  625. { text: '(GMT+09:00) Seoul', value: 'Asia/Seoul' },
  626. { text: '(GMT+09:00) Tokyo', value: 'Asia/Tokyo' },
  627. { text: '(GMT+09:30) Central Time - Darwin', value: 'Australia/Darwin' },
  628. { text: '(GMT+10:00) Dumont D\'Urville', value: 'Antarctica/DumontDUrville' },
  629. { text: '(GMT+10:00) Eastern Time - Brisbane', value: 'Australia/Brisbane' },
  630. { text: '(GMT+10:00) Guam', value: 'Pacific/Guam' },
  631. { text: '(GMT+10:00) Moscow+07 - Vladivostok', value: 'Asia/Vladivostok' },
  632. { text: '(GMT+10:00) Port Moresby', value: 'Pacific/Port_Moresby' },
  633. { text: '(GMT+10:00) Truk', value: 'Pacific/Chuuk' },
  634. { text: '(GMT+10:30) Central Time - Adelaide', value: 'Australia/Adelaide' },
  635. { text: '(GMT+11:00) Casey', value: 'Antarctica/Casey' },
  636. { text: '(GMT+11:00) Eastern Time - Hobart', value: 'Australia/Hobart' },
  637. { text: '(GMT+11:00) Eastern Time - Melbourne, Sydney', value: 'Australia/Sydney' },
  638. { text: '(GMT+11:00) Efate', value: 'Pacific/Efate' },
  639. { text: '(GMT+11:00) Guadalcanal', value: 'Pacific/Guadalcanal' },
  640. { text: '(GMT+11:00) Kosrae', value: 'Pacific/Kosrae' },
  641. { text: '(GMT+11:00) Moscow+08 - Magadan', value: 'Asia/Magadan' },
  642. { text: '(GMT+11:00) Norfolk', value: 'Pacific/Norfolk' },
  643. { text: '(GMT+11:00) Noumea', value: 'Pacific/Noumea' },
  644. { text: '(GMT+11:00) Ponape', value: 'Pacific/Pohnpei' },
  645. { text: '(GMT+12:00) Funafuti', value: 'Pacific/Funafuti' },
  646. { text: '(GMT+12:00) Kwajalein', value: 'Pacific/Kwajalein' },
  647. { text: '(GMT+12:00) Majuro', value: 'Pacific/Majuro' },
  648. { text: '(GMT+12:00) Moscow+09 - Petropavlovsk-Kamchatskiy', value: 'Asia/Kamchatka' },
  649. { text: '(GMT+12:00) Nauru', value: 'Pacific/Nauru' },
  650. { text: '(GMT+12:00) Tarawa', value: 'Pacific/Tarawa' },
  651. { text: '(GMT+12:00) Wake', value: 'Pacific/Wake' },
  652. { text: '(GMT+12:00) Wallis', value: 'Pacific/Wallis' },
  653. { text: '(GMT+13:00) Auckland', value: 'Pacific/Auckland' },
  654. { text: '(GMT+13:00) Enderbury', value: 'Pacific/Enderbury' },
  655. { text: '(GMT+13:00) Fakaofo', value: 'Pacific/Fakaofo' },
  656. { text: '(GMT+13:00) Fiji', value: 'Pacific/Fiji' },
  657. { text: '(GMT+13:00) Tongatapu', value: 'Pacific/Tongatapu' },
  658. { text: '(GMT+14:00) Apia', value: 'Pacific/Apia' },
  659. { text: '(GMT+14:00) Kiritimati', value: 'Pacific/Kiritimati' }
  660. ]
  661. }
  662. },
  663. computed: {
  664. currentUserId: get('user/id')
  665. },
  666. methods: {
  667. /**
  668. * Activate a user (if previously deactivated)
  669. */
  670. async activateUser () {
  671. this.$store.commit(`loadingStart`, 'admin-users-activate')
  672. const resp = await this.$apollo.mutate({
  673. mutation: gql`
  674. mutation ($id: Int!) {
  675. users {
  676. activate(id: $id) {
  677. responseResult {
  678. succeeded
  679. errorCode
  680. slug
  681. message
  682. }
  683. }
  684. }
  685. }
  686. `,
  687. variables: {
  688. id: this.user.id
  689. }
  690. })
  691. if (_.get(resp, 'data.users.activate.responseResult.succeeded', false)) {
  692. this.$store.commit('showNotification', {
  693. style: 'success',
  694. message: this.$t('admin:users.userActivateSuccess'),
  695. icon: 'check'
  696. })
  697. this.user.isActive = true
  698. } else {
  699. this.$store.commit('showNotification', {
  700. style: 'red',
  701. message: _.get(resp, 'data.users.activate.responseResult.message', 'An unexpected error occurred.'),
  702. icon: 'warning'
  703. })
  704. }
  705. this.$store.commit(`loadingStop`, 'admin-users-activate')
  706. },
  707. /**
  708. * Deactivate a currently active user
  709. */
  710. async deactivateUser () {
  711. this.$store.commit(`loadingStart`, 'admin-users-deactivate')
  712. const resp = await this.$apollo.mutate({
  713. mutation: gql`
  714. mutation ($id: Int!) {
  715. users {
  716. deactivate(id: $id) {
  717. responseResult {
  718. succeeded
  719. errorCode
  720. slug
  721. message
  722. }
  723. }
  724. }
  725. }
  726. `,
  727. variables: {
  728. id: this.user.id
  729. }
  730. })
  731. if (_.get(resp, 'data.users.deactivate.responseResult.succeeded', false)) {
  732. this.$store.commit('showNotification', {
  733. style: 'success',
  734. message: this.$t('admin:users.userDeactivateSuccess'),
  735. icon: 'check'
  736. })
  737. this.user.isActive = false
  738. } else {
  739. this.$store.commit('showNotification', {
  740. style: 'red',
  741. message: _.get(resp, 'data.users.deactivate.responseResult.message', 'An unexpected error occurred.'),
  742. icon: 'warning'
  743. })
  744. }
  745. this.$store.commit(`loadingStop`, 'admin-users-deactivate')
  746. },
  747. /**
  748. * Delete a user
  749. */
  750. deleteUserConfirm () {
  751. this.deleteUserDialog = true
  752. this.deleteReplaceUser = {
  753. id: this.currentUserId,
  754. name: this.$store.get('user/name'),
  755. email: this.$store.get('user/email')
  756. }
  757. },
  758. async deleteUser () {
  759. this.$store.commit(`loadingStart`, 'admin-users-delete')
  760. const resp = await this.$apollo.mutate({
  761. mutation: gql`
  762. mutation ($id: Int!, $replaceId: Int!) {
  763. users {
  764. delete(id: $id, replaceId: $replaceId) {
  765. responseResult {
  766. succeeded
  767. errorCode
  768. slug
  769. message
  770. }
  771. }
  772. }
  773. }
  774. `,
  775. variables: {
  776. id: this.user.id,
  777. replaceId: this.deleteReplaceUser.id
  778. }
  779. })
  780. if (_.get(resp, 'data.users.delete.responseResult.succeeded', false)) {
  781. this.$store.commit('showNotification', {
  782. style: 'success',
  783. message: this.$t('admin:users.userDeleteSuccess'),
  784. icon: 'check'
  785. })
  786. this.$router.push('/users')
  787. } else {
  788. this.$store.commit('showNotification', {
  789. style: 'red',
  790. message: _.get(resp, 'data.users.delete.responseResult.message', 'An unexpected error occurred.'),
  791. icon: 'warning'
  792. })
  793. }
  794. this.deleteUserDialog = false
  795. this.$store.commit(`loadingStop`, 'admin-users-delete')
  796. },
  797. assignDeleteUser (selUsr) {
  798. if (selUsr.id === this.user.id) {
  799. this.$store.commit('showNotification', {
  800. style: 'red',
  801. message: 'You cannot select the account you\'re about to delete!',
  802. icon: 'warning'
  803. })
  804. } else if (selUsr.id === 2) {
  805. this.$store.commit('showNotification', {
  806. style: 'red',
  807. message: 'You cannot use the guest account for this operation.',
  808. icon: 'warning'
  809. })
  810. } else {
  811. this.deleteReplaceUser = selUsr
  812. }
  813. },
  814. /**
  815. * Update a user
  816. */
  817. async updateUser() {
  818. this.$store.commit(`loadingStart`, 'admin-users-update')
  819. const resp = await this.$apollo.mutate({
  820. mutation: gql`
  821. mutation ($id: Int!, $email: String, $name: String, $newPassword: String, $groups: [Int], $location: String, $jobTitle: String, $timezone: String) {
  822. users {
  823. update(id: $id, email: $email, name: $name, newPassword: $newPassword, groups: $groups, location: $location, jobTitle: $jobTitle, timezone: $timezone) {
  824. responseResult {
  825. succeeded
  826. errorCode
  827. slug
  828. message
  829. }
  830. }
  831. }
  832. }
  833. `,
  834. variables: {
  835. id: this.user.id,
  836. email: this.user.email,
  837. name: this.user.name,
  838. newPassword: this.newPassword,
  839. groups: _.map(this.user.groups, 'id'),
  840. location: this.user.location,
  841. jobTitle: this.user.jobTitle,
  842. timezone: this.user.timezone
  843. }
  844. })
  845. this.newPassword = ''
  846. if (_.get(resp, 'data.users.update.responseResult.succeeded', false)) {
  847. this.$store.commit('showNotification', {
  848. style: 'success',
  849. message: this.$t('admin:users.userUpdateSuccess'),
  850. icon: 'check'
  851. })
  852. this.$router.push('/users')
  853. } else {
  854. this.$store.commit('showNotification', {
  855. style: 'red',
  856. message: _.get(resp, 'data.users.update.responseResult.message', 'An unexpected error occurred.'),
  857. icon: 'warning'
  858. })
  859. }
  860. this.$store.commit(`loadingStop`, 'admin-users-update')
  861. },
  862. /**
  863. * Focus an input after delay
  864. */
  865. focusField (ipt) {
  866. this.$nextTick(() => {
  867. _.delay(() => {
  868. this.$refs[ipt].focus()
  869. }, 200)
  870. })
  871. },
  872. /**
  873. * Assign group to user
  874. */
  875. assignGroup() {
  876. if (_.some(this.user.groups, ['id', this.newGroup])) {
  877. this.$store.commit('showNotification', {
  878. message: this.$t('admin:users.userAlreadyAssignedToGroup'),
  879. style: 'error',
  880. icon: 'alert'
  881. })
  882. } else {
  883. this.user.groups.push(_.find(this.groups, ['id', this.newGroup]))
  884. this.newGroup = 0
  885. }
  886. },
  887. /**
  888. * Unassign group from user
  889. */
  890. unassignGroup(gid) {
  891. this.user.groups = _.reject(this.user.groups, ['id', gid])
  892. },
  893. /**
  894. * Manually set user as verified
  895. */
  896. async verifyUser () {
  897. this.$store.commit(`loadingStart`, 'admin-users-verify')
  898. const resp = await this.$apollo.mutate({
  899. mutation: gql`
  900. mutation ($id: Int!) {
  901. users {
  902. verify(id: $id) {
  903. responseResult {
  904. succeeded
  905. errorCode
  906. slug
  907. message
  908. }
  909. }
  910. }
  911. }
  912. `,
  913. variables: {
  914. id: this.user.id
  915. }
  916. })
  917. if (_.get(resp, 'data.users.verify.responseResult.succeeded', false)) {
  918. this.$store.commit('showNotification', {
  919. style: 'success',
  920. message: this.$t('admin:users.userVerifySuccess'),
  921. icon: 'check'
  922. })
  923. this.user.isVerified = true
  924. } else {
  925. this.$store.commit('showNotification', {
  926. style: 'red',
  927. message: _.get(resp, 'data.users.verify.responseResult.message', 'An unexpected error occurred.'),
  928. icon: 'warning'
  929. })
  930. }
  931. this.$store.commit(`loadingStop`, 'admin-users-verify')
  932. },
  933. /**
  934. * Toggle 2FA State
  935. */
  936. async toggle2FA () {
  937. this.$store.commit(`loadingStart`, 'admin-users-toggle2fa')
  938. if (this.user.tfaIsActive) {
  939. const resp = await this.$apollo.mutate({
  940. mutation: gql`
  941. mutation ($id: Int!) {
  942. users {
  943. disableTFA(id: $id) {
  944. responseResult {
  945. succeeded
  946. errorCode
  947. slug
  948. message
  949. }
  950. }
  951. }
  952. }
  953. `,
  954. variables: {
  955. id: this.user.id
  956. }
  957. })
  958. if (_.get(resp, 'data.users.disableTFA.responseResult.succeeded', false)) {
  959. this.$store.commit('showNotification', {
  960. style: 'success',
  961. message: this.$t('admin:users.userTFADisableSuccess'),
  962. icon: 'check'
  963. })
  964. this.user.tfaIsActive = false
  965. } else {
  966. this.$store.commit('showNotification', {
  967. style: 'red',
  968. message: _.get(resp, 'data.users.disableTFA.responseResult.message', 'An unexpected error occurred.'),
  969. icon: 'warning'
  970. })
  971. }
  972. } else {
  973. const resp = await this.$apollo.mutate({
  974. mutation: gql`
  975. mutation ($id: Int!) {
  976. users {
  977. enableTFA(id: $id) {
  978. responseResult {
  979. succeeded
  980. errorCode
  981. slug
  982. message
  983. }
  984. }
  985. }
  986. }
  987. `,
  988. variables: {
  989. id: this.user.id
  990. }
  991. })
  992. if (_.get(resp, 'data.users.enableTFA.responseResult.succeeded', false)) {
  993. this.$store.commit('showNotification', {
  994. style: 'success',
  995. message: this.$t('admin:users.userTFAEnableSuccess'),
  996. icon: 'check'
  997. })
  998. this.user.tfaIsActive = true
  999. } else {
  1000. this.$store.commit('showNotification', {
  1001. style: 'red',
  1002. message: _.get(resp, 'data.users.enableTFA.responseResult.message', 'An unexpected error occurred.'),
  1003. icon: 'warning'
  1004. })
  1005. }
  1006. }
  1007. this.$store.commit(`loadingStop`, 'admin-users-toggle2fa')
  1008. }
  1009. },
  1010. apollo: {
  1011. user: {
  1012. query: gql`
  1013. query ($id: Int!) {
  1014. users {
  1015. single(id: $id) {
  1016. id
  1017. name
  1018. email
  1019. providerKey
  1020. providerName
  1021. providerId
  1022. providerIs2FACapable
  1023. location
  1024. jobTitle
  1025. timezone
  1026. isSystem
  1027. isActive
  1028. isVerified
  1029. createdAt
  1030. updatedAt
  1031. lastLoginAt
  1032. tfaIsActive
  1033. groups {
  1034. id
  1035. name
  1036. }
  1037. }
  1038. }
  1039. }
  1040. `,
  1041. variables() {
  1042. return {
  1043. id: _.toSafeInteger(this.$route.params.id)
  1044. }
  1045. },
  1046. fetchPolicy: 'network-only',
  1047. update: (data) => data.users.single,
  1048. watchLoading (isLoading) {
  1049. this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-users-refresh')
  1050. }
  1051. },
  1052. groups: {
  1053. query: groupsQuery,
  1054. fetchPolicy: 'network-only',
  1055. update: (data) => data.groups.list,
  1056. watchLoading (isLoading) {
  1057. this.$store.commit(`loading${isLoading ? 'Start' : 'Stop'}`, 'admin-groups-refresh')
  1058. }
  1059. }
  1060. }
  1061. }
  1062. </script>
  1063. <style lang='scss'>
  1064. </style>