education-apply-page.tsx 6.6 KB

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