Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

education-apply-page.tsx 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. 'use client'
  2. import {
  3. useMemo,
  4. useState,
  5. } from 'react'
  6. import { useTranslation } from 'react-i18next'
  7. import { RiExternalLinkLine } from '@remixicon/react'
  8. import {
  9. useRouter,
  10. useSearchParams,
  11. } from 'next/navigation'
  12. import UserInfo from './user-info'
  13. import SearchInput from './search-input'
  14. import RoleSelector from './role-selector'
  15. import Confirm from './verify-state-modal'
  16. import Button from '@/app/components/base/button'
  17. import Checkbox from '@/app/components/base/checkbox'
  18. import {
  19. useEducationAdd,
  20. useInvalidateEducationStatus,
  21. } from '@/service/use-education'
  22. import { useProviderContext } from '@/context/provider-context'
  23. import { useToastContext } from '@/app/components/base/toast'
  24. import { EDUCATION_VERIFYING_LOCALSTORAGE_ITEM } from '@/app/education-apply/constants'
  25. import { getLocaleOnClient } from '@/i18n'
  26. import { noop } from 'lodash-es'
  27. const EducationApplyAge = () => {
  28. const { t } = useTranslation()
  29. const locale = getLocaleOnClient()
  30. const [schoolName, setSchoolName] = useState('')
  31. const [role, setRole] = useState('Student')
  32. const [ageChecked, setAgeChecked] = useState(false)
  33. const [inSchoolChecked, setInSchoolChecked] = useState(false)
  34. const {
  35. isPending,
  36. mutateAsync: educationAdd,
  37. } = useEducationAdd({ onSuccess: noop })
  38. const [modalShow, setShowModal] = useState<undefined | { title: string; desc: string; onConfirm?: () => void }>(undefined)
  39. const { onPlanInfoChanged } = useProviderContext()
  40. const updateEducationStatus = useInvalidateEducationStatus()
  41. const { notify } = useToastContext()
  42. const router = useRouter()
  43. const docLink = useMemo(() => {
  44. if (locale === 'zh-Hans')
  45. return 'https://docs.dify.ai/zh-hans/getting-started/dify-for-education'
  46. if (locale === 'ja-JP')
  47. return 'https://docs.dify.ai/ja-jp/getting-started/dify-for-education'
  48. return 'https://docs.dify.ai/getting-started/dify-for-education'
  49. }, [locale])
  50. const handleModalConfirm = () => {
  51. setShowModal(undefined)
  52. onPlanInfoChanged()
  53. updateEducationStatus()
  54. localStorage.removeItem(EDUCATION_VERIFYING_LOCALSTORAGE_ITEM)
  55. router.replace('/')
  56. }
  57. const searchParams = useSearchParams()
  58. const token = searchParams.get('token')
  59. const handleSubmit = () => {
  60. educationAdd({
  61. token: token || '',
  62. role,
  63. institution: schoolName,
  64. }).then((res) => {
  65. if (res.message === 'success') {
  66. setShowModal({
  67. title: t('education.successTitle'),
  68. desc: t('education.successContent'),
  69. onConfirm: handleModalConfirm,
  70. })
  71. }
  72. else {
  73. notify({
  74. type: 'error',
  75. message: t('education.submitError'),
  76. })
  77. }
  78. })
  79. }
  80. return (
  81. <div className='fixed inset-0 z-[31] overflow-y-auto bg-background-body p-6'>
  82. <div className='mx-auto w-full max-w-[1408px] rounded-2xl border border-effects-highlight bg-background-default-subtle'>
  83. <div
  84. className="h-[349px] w-full overflow-hidden rounded-t-2xl bg-cover bg-center bg-no-repeat"
  85. style={{
  86. backgroundImage: 'url(/education/bg.png)',
  87. }}
  88. >
  89. </div>
  90. <div className='mt-[-349px] flex h-[88px] items-center justify-between px-8 py-6'>
  91. <img
  92. src='/logo/logo-site-dark.png'
  93. alt='dify logo'
  94. className='h-10'
  95. />
  96. </div>
  97. <div className='mx-auto max-w-[720px] px-8 pb-[180px]'>
  98. <div className='mb-2 flex h-[192px] flex-col justify-end pb-4 pt-3 text-text-primary-on-surface'>
  99. <div className='title-5xl-bold mb-2 shadow-xs'>{t('education.toVerified')}</div>
  100. <div className='system-md-medium shadow-xs'>
  101. {t('education.toVerifiedTip.front')}&nbsp;
  102. <span className='system-md-semibold underline'>{t('education.toVerifiedTip.coupon')}</span>&nbsp;
  103. {t('education.toVerifiedTip.end')}
  104. </div>
  105. </div>
  106. <div className='mb-7'>
  107. <UserInfo />
  108. </div>
  109. <div className='mb-7'>
  110. <div className='system-md-semibold mb-1 flex h-6 items-center text-text-secondary'>
  111. {t('education.form.schoolName.title')}
  112. </div>
  113. <SearchInput
  114. value={schoolName}
  115. onChange={setSchoolName}
  116. />
  117. </div>
  118. <div className='mb-7'>
  119. <div className='system-md-semibold mb-1 flex h-6 items-center text-text-secondary'>
  120. {t('education.form.schoolRole.title')}
  121. </div>
  122. <RoleSelector
  123. value={role}
  124. onChange={setRole}
  125. />
  126. </div>
  127. <div className='mb-7'>
  128. <div className='system-md-semibold mb-1 flex h-6 items-center text-text-secondary'>
  129. {t('education.form.terms.title')}
  130. </div>
  131. <div className='system-md-regular mb-1 text-text-tertiary'>
  132. {t('education.form.terms.desc.front')}&nbsp;
  133. <a href='https://dify.ai/terms' target='_blank' className='text-text-secondary hover:underline'>{t('education.form.terms.desc.termsOfService')}</a>&nbsp;
  134. {t('education.form.terms.desc.and')}&nbsp;
  135. <a href='https://dify.ai/privacy' target='_blank' className='text-text-secondary hover:underline'>{t('education.form.terms.desc.privacyPolicy')}</a>
  136. {t('education.form.terms.desc.end')}
  137. </div>
  138. <div className='system-md-regular py-2 text-text-primary'>
  139. <div className='mb-2 flex'>
  140. <Checkbox
  141. className='mr-2 shrink-0'
  142. checked={ageChecked}
  143. onCheck={() => setAgeChecked(!ageChecked)}
  144. />
  145. {t('education.form.terms.option.age')}
  146. </div>
  147. <div className='flex'>
  148. <Checkbox
  149. className='mr-2 shrink-0'
  150. checked={inSchoolChecked}
  151. onCheck={() => setInSchoolChecked(!inSchoolChecked)}
  152. />
  153. {t('education.form.terms.option.inSchool')}
  154. </div>
  155. </div>
  156. </div>
  157. <Button
  158. variant='primary'
  159. disabled={!ageChecked || !inSchoolChecked || !schoolName || !role || isPending}
  160. onClick={handleSubmit}
  161. >
  162. {t('education.submit')}
  163. </Button>
  164. <div className='mb-4 mt-5 h-[1px] bg-gradient-to-r from-[rgba(16,24,40,0.08)]'></div>
  165. <a
  166. className='system-xs-regular flex items-center text-text-accent'
  167. href={docLink}
  168. target='_blank'
  169. >
  170. {t('education.learn')}
  171. <RiExternalLinkLine className='ml-1 h-3 w-3' />
  172. </a>
  173. </div>
  174. </div>
  175. <Confirm
  176. isShow={!!modalShow}
  177. title={modalShow?.title || ''}
  178. content={modalShow?.desc}
  179. onConfirm={modalShow?.onConfirm || noop}
  180. onCancel={modalShow?.onConfirm || noop}
  181. />
  182. </div>
  183. )
  184. }
  185. export default EducationApplyAge