oneMoreStep.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. 'use client'
  2. import React, { useEffect, useReducer } from 'react'
  3. import { useTranslation } from 'react-i18next'
  4. import useSWR from 'swr'
  5. import { useRouter } from 'next/navigation'
  6. import Button from '@/app/components/base/button'
  7. import Tooltip from '@/app/components/base/tooltip/index'
  8. import { SimpleSelect } from '@/app/components/base/select'
  9. import { timezones } from '@/utils/timezone'
  10. import { languageMaps, languages } from '@/utils/language'
  11. import { oneMoreStep } from '@/service/common'
  12. import Toast from '@/app/components/base/toast'
  13. type IState = {
  14. formState: 'processing' | 'error' | 'success' | 'initial'
  15. invitation_code: string
  16. interface_language: string
  17. timezone: string
  18. }
  19. const reducer = (state: IState, action: any) => {
  20. switch (action.type) {
  21. case 'invitation_code':
  22. return { ...state, invitation_code: action.value }
  23. case 'interface_language':
  24. return { ...state, interface_language: action.value }
  25. case 'timezone':
  26. return { ...state, timezone: action.value }
  27. case 'formState':
  28. return { ...state, formState: action.value }
  29. case 'failed':
  30. return {
  31. formState: 'initial',
  32. invitation_code: '',
  33. interface_language: 'en-US',
  34. timezone: 'Asia/Shanghai',
  35. }
  36. default:
  37. throw new Error('Unknown action.')
  38. }
  39. }
  40. const OneMoreStep = () => {
  41. const { t } = useTranslation()
  42. const router = useRouter()
  43. const [state, dispatch] = useReducer(reducer, {
  44. formState: 'initial',
  45. invitation_code: '',
  46. interface_language: 'en-US',
  47. timezone: 'Asia/Shanghai',
  48. })
  49. const { data, error } = useSWR(state.formState === 'processing'
  50. ? {
  51. url: '/account/init',
  52. body: {
  53. invitation_code: state.invitation_code,
  54. interface_language: state.interface_language,
  55. timezone: state.timezone,
  56. },
  57. }
  58. : null, oneMoreStep)
  59. useEffect(() => {
  60. if (error && error.status === 400) {
  61. Toast.notify({ type: 'error', message: t('login.invalidInvitationCode') })
  62. dispatch({ type: 'failed', payload: null })
  63. }
  64. if (data)
  65. router.push('/apps')
  66. }, [data, error])
  67. return (
  68. <>
  69. <div className="w-full mx-auto">
  70. <h2 className="text-3xl font-normal text-gray-900">{t('login.oneMoreStep')}</h2>
  71. <p className='mt-2 text-sm text-gray-600 '>{t('login.createSample')}</p>
  72. </div>
  73. <div className="w-full mx-auto mt-8">
  74. <div className="space-y-6 bg-white">
  75. <div className="">
  76. <label className="flex items-center justify-between text-sm font-medium text-gray-900">
  77. {t('login.invitationCode')}
  78. <Tooltip
  79. clickable
  80. selector='dont-have'
  81. htmlContent={
  82. <div className='w-[256px] text-xs font-medium'>
  83. <div className='font-medium'>{t('login.sendUsMail')}</div>
  84. <div className='text-xs font-medium cursor-pointer text-primary-600'>
  85. <a href="mailto:request-invitation@langgenius.ai">request-invitation@langgenius.ai</a>
  86. </div>
  87. </div>
  88. }
  89. >
  90. <span className='cursor-pointer text-primary-600'>{t('login.donthave')}</span>
  91. </Tooltip>
  92. </label>
  93. <div className="mt-1">
  94. <input
  95. id="invitation_code"
  96. value={state.invitation_code}
  97. type="text"
  98. className={'appearance-none block w-full px-3 py-2 border border-gray-300 focus:outline-none focus:ring-primary-600 focus:border-primary-600 rounded-md shadow-sm placeholder-gray-400 sm:text-sm'}
  99. onChange={(e) => {
  100. dispatch({ type: 'invitation_code', value: e.target.value.trim() })
  101. }}
  102. />
  103. </div>
  104. </div>
  105. <div>
  106. <label htmlFor="name" className="block text-sm font-medium text-gray-700">
  107. {t('login.interfaceLanguage')}
  108. </label>
  109. <div className="relative mt-1 rounded-md shadow-sm">
  110. <SimpleSelect
  111. defaultValue={languageMaps.en}
  112. items={languages}
  113. onSelect={(item) => {
  114. dispatch({ type: 'interface_language', value: item.value })
  115. }}
  116. />
  117. </div>
  118. </div>
  119. <div>
  120. <label htmlFor="timezone" className="block text-sm font-medium text-gray-700">
  121. {t('login.timezone')}
  122. </label>
  123. <div className="relative mt-1 rounded-md shadow-sm">
  124. <SimpleSelect
  125. defaultValue={state.timezone}
  126. items={timezones}
  127. onSelect={(item) => {
  128. dispatch({ type: 'timezone', value: item.value })
  129. }}
  130. />
  131. </div>
  132. </div>
  133. <div>
  134. <Button
  135. type='primary'
  136. disabled={state.formState === 'processing'}
  137. onClick={() => {
  138. dispatch({ type: 'formState', value: 'processing' })
  139. }}
  140. >
  141. {t('login.go')}
  142. </Button>
  143. </div>
  144. </div>
  145. </div>
  146. </>
  147. )
  148. }
  149. export default OneMoreStep