Login.vue 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <!-- Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/ -->
  2. <script setup lang="ts">
  3. import useNotifications from '@common/composables/useNotifications'
  4. import useAuthenticationStore from '@common/stores/authenticated'
  5. import { useRouter } from 'vue-router'
  6. import { NotificationTypes } from '@common/types/notification'
  7. import CommonLogo from '@common/components/common/CommonLogo.vue'
  8. import useApplicationConfigStore from '@common/stores/application/config'
  9. import { i18n } from '@common/i18n'
  10. import Form from '@common/components/form/Form.vue'
  11. import { FormData } from '@common/types/form'
  12. import UserError from '@common/errors/UserError'
  13. interface Props {
  14. invalidatedSession?: string
  15. }
  16. const props = defineProps<Props>()
  17. // Output a hint when the session is longer valid.
  18. // This could happen because because the session was deleted on the server.
  19. if (props.invalidatedSession === '1') {
  20. const { notify } = useNotifications()
  21. notify({
  22. message: __('The session is no longer valid. Please log in again.'),
  23. type: NotificationTypes.WARN,
  24. })
  25. }
  26. const authentication = useAuthenticationStore()
  27. const router = useRouter()
  28. const formSchema = [
  29. {
  30. type: 'text',
  31. name: 'login',
  32. label: __('Username / Email'),
  33. placeholder: __('Username / Email'),
  34. validation: 'required',
  35. },
  36. {
  37. type: 'password',
  38. label: __('Password'),
  39. name: 'password',
  40. placeholder: __('Password'),
  41. validation: 'required',
  42. },
  43. {
  44. isLayout: true,
  45. element: 'div',
  46. attrs: {
  47. class: 'mt-2.5 flex grow justify-between text-white',
  48. },
  49. children: [
  50. {
  51. type: 'checkbox',
  52. label: __('Remember me'),
  53. name: 'remember_me',
  54. },
  55. {
  56. isLayout: true,
  57. component: 'CommonLink',
  58. props: {
  59. class: 'text-right !text-white',
  60. link: 'TODO',
  61. },
  62. children: i18n.t('Forgot password?'),
  63. },
  64. ],
  65. },
  66. ]
  67. interface LoginFormData {
  68. login?: string
  69. password?: string
  70. remember_me?: boolean
  71. }
  72. const login = (formData: FormData<LoginFormData>) => {
  73. authentication
  74. .login(formData.login as string, formData.password as string)
  75. .then(() => {
  76. router.replace('/')
  77. })
  78. .catch((errors: UserError) => {
  79. const { notify } = useNotifications()
  80. notify({
  81. message: errors.generalErrors[0],
  82. type: NotificationTypes.ERROR,
  83. })
  84. })
  85. }
  86. const applicationConfig = useApplicationConfigStore()
  87. </script>
  88. <template>
  89. <!-- TODO: Only a "first" dummy implementation for the login... -->
  90. <div class="flex h-full min-h-screen flex-col items-center px-7 pt-7 pb-4">
  91. <div class="m-auto w-full max-w-md">
  92. <div class="flex grow flex-col justify-center">
  93. <div class="my-5 grow">
  94. <div class="flex justify-center p-2">
  95. <CommonLogo />
  96. </div>
  97. <div class="mb-6 flex justify-center p-2 text-2xl font-extrabold">
  98. {{ applicationConfig.value.product_name }}
  99. </div>
  100. <template v-if="applicationConfig.value.maintenance_login">
  101. <!-- eslint-disable vue/no-v-html -->
  102. <div
  103. class="my-1 flex items-center rounded-xl bg-green py-2 px-4 text-white"
  104. v-html="applicationConfig.value.maintenance_login_message"
  105. ></div>
  106. </template>
  107. <Form
  108. ref="form"
  109. v-bind:schema="formSchema"
  110. class="text-left"
  111. v-on:submit="login"
  112. >
  113. <template v-slot:after-fields>
  114. <div class="mt-4 flex grow items-center justify-center">
  115. <span class="ltr:mr-1 rtl:ml-1">{{ i18n.t('New user?') }}</span>
  116. <CommonLink
  117. v-bind:link="'TODO'"
  118. class="cursor-pointer select-none !text-yellow underline"
  119. >{{ i18n.t('Register') }}</CommonLink
  120. >
  121. </div>
  122. <FormKit
  123. wrapper-class="mx-8 mt-8 flex grow justify-center items-center"
  124. input-class="py-2 px-4 w-full h-14 text-xl font-semibold text-black bg-yellow rounded-xl select-none"
  125. type="submit"
  126. >
  127. {{ i18n.t('Sign in') }}
  128. </FormKit>
  129. </template>
  130. </Form>
  131. </div>
  132. </div>
  133. </div>
  134. <div class="mb-6 flex items-center justify-center">
  135. <CommonLink link="TODO" class="!text-gray underline">
  136. {{ i18n.t('Continue to desktop app') }}
  137. </CommonLink>
  138. </div>
  139. <div class="flex items-center justify-center align-middle text-gray-200">
  140. <CommonLink
  141. link="https://zammad.org"
  142. is-external
  143. open-in-new-tab
  144. class="ltr:mr-1 rtl:ml-1"
  145. >
  146. <CommonIcon name="logo" v-bind:fixed-size="{ width: 24, height: 24 }" />
  147. </CommonLink>
  148. <span class="ltr:mr-1 rtl:ml-1">{{ i18n.t('Powered by') }}</span>
  149. <CommonLink
  150. link="https://zammad.org"
  151. is-external
  152. open-in-new-tab
  153. class="font-semibold !text-gray-200"
  154. >
  155. Zammad
  156. </CommonLink>
  157. </div>
  158. </div>
  159. </template>