TwoFactorConfigurationWizard.vue 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. <!-- Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. import { ref, computed, useTemplateRef } from 'vue'
  4. import { useTwoFactorPlugins } from '#shared/entities/two-factor/composables/useTwoFactorPlugins.ts'
  5. import type { ObjectLike } from '#shared/types/utils.ts'
  6. import TwoFactorConfigurationMethodList from './TwoFactorConfiguration/TwoFactorConfigurationMethodList.vue'
  7. import TwoFactorConfigurationPasswordCheck from './TwoFactorConfiguration/TwoFactorConfigurationPasswordCheck.vue'
  8. import TwoFactorConfigurationRecoveryCodes from './TwoFactorConfiguration/TwoFactorConfigurationRecoveryCodes.vue'
  9. import TwoFactorConfigurationWizardFooterActions from './TwoFactorConfigurationWizard/TwoFactorConfigurationWizardFooterActions.vue'
  10. import type {
  11. TwoFactorConfigurationActionPayload,
  12. TwoFactorConfigurationComponentInstance,
  13. TwoFactorConfigurationType,
  14. } from './types.ts'
  15. const props = defineProps<{
  16. token?: string
  17. }>()
  18. const activeComponentInstance =
  19. useTemplateRef<TwoFactorConfigurationComponentInstance>('active-component')
  20. const emit = defineEmits<{
  21. redirect: [url: string]
  22. }>()
  23. const state = ref<TwoFactorConfigurationType>('method_list')
  24. const componentOptions = ref<ObjectLike>()
  25. const localToken = ref(props.token)
  26. const { twoFactorMethodLookup } = useTwoFactorPlugins()
  27. const activeComponent = computed(() => {
  28. switch (state.value) {
  29. case 'recovery_codes':
  30. return TwoFactorConfigurationRecoveryCodes
  31. case 'password_check':
  32. return TwoFactorConfigurationPasswordCheck
  33. case 'method_list':
  34. if (!localToken.value) return TwoFactorConfigurationPasswordCheck
  35. return TwoFactorConfigurationMethodList
  36. default:
  37. return twoFactorMethodLookup[state.value].configurationOptions?.component
  38. }
  39. })
  40. const footerActionOptions = computed(() => ({
  41. hideActionButton:
  42. activeComponentInstance.value?.footerActionOptions?.hideActionButton,
  43. actionLabel: activeComponentInstance.value?.footerActionOptions?.actionLabel,
  44. actionButton:
  45. activeComponentInstance.value?.footerActionOptions?.actionButton,
  46. hideCancelButton:
  47. activeComponentInstance.value?.footerActionOptions?.hideCancelButton,
  48. cancelLabel:
  49. activeComponentInstance.value?.footerActionOptions?.cancelLabel ||
  50. __('Go Back'),
  51. cancelButton:
  52. activeComponentInstance.value?.footerActionOptions?.cancelButton,
  53. form: activeComponentInstance.value?.footerActionOptions?.form,
  54. }))
  55. const handleActionPayload = (payload: TwoFactorConfigurationActionPayload) => {
  56. if (!payload?.nextState) {
  57. emit('redirect', '/')
  58. return
  59. }
  60. state.value = payload.nextState
  61. localToken.value = payload.token ?? localToken.value
  62. componentOptions.value = payload.options
  63. }
  64. const onFooterButtonAction = () => {
  65. if (activeComponentInstance.value?.footerActionOptions?.form) return
  66. activeComponentInstance.value
  67. ?.executeAction?.()
  68. .then((payload) => handleActionPayload(payload))
  69. .catch(() => {})
  70. }
  71. const cancel = () => {
  72. if (state.value === 'method_list') {
  73. emit('redirect', '/logout')
  74. return
  75. }
  76. state.value = 'method_list'
  77. }
  78. </script>
  79. <template>
  80. <div class="mb-8">
  81. <component
  82. :is="activeComponent"
  83. ref="active-component"
  84. :type="state"
  85. :options="componentOptions"
  86. :token="localToken"
  87. :form-submit-callback="handleActionPayload"
  88. />
  89. </div>
  90. <div class="flex flex-col gap-3">
  91. <TwoFactorConfigurationWizardFooterActions
  92. v-bind="footerActionOptions"
  93. @action="onFooterButtonAction()"
  94. @cancel="cancel()"
  95. />
  96. </div>
  97. </template>