feat: add education apply

This commit is contained in:
zxhlyh 2025-03-12 17:36:44 +08:00
parent bed9407045
commit f17a76a00e
17 changed files with 286 additions and 105 deletions

View File

@ -0,0 +1,3 @@
<svg width="16" height="22" viewBox="0 0 16 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path id="Rectangle 979" d="M0 0H16L9.91493 16.7339C8.76529 19.8955 5.76063 22 2.39658 22H0V0Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 214 B

View File

@ -0,0 +1,5 @@
<svg width="18" height="16" viewBox="0 0 18 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="loop">
<path id="Vector" fill-rule="evenodd" clip-rule="evenodd" d="M2.02915 5.34506C3.50752 3.88498 5.9006 3.88498 7.37896 5.34506L8.99983 6.94588L10.6207 5.34506C12.0991 3.88499 14.4921 3.88498 15.9705 5.34506C17.454 6.81027 17.454 9.18971 15.9705 10.6549C14.4921 12.115 12.0991 12.115 10.6207 10.655L8.99983 9.05413L7.37896 10.655C5.9006 12.115 3.50753 12.115 2.02916 10.655C0.545627 9.18974 0.545611 6.81028 2.02915 5.34506ZM7.93251 8L6.32492 6.4123C5.4308 5.52924 3.97732 5.52923 3.08319 6.4123C2.19426 7.29026 2.19426 8.70975 3.0832 9.58772C3.97733 10.4708 5.4308 10.4707 6.32492 9.58771C6.32492 9.58772 6.32492 9.58771 6.32492 9.58771L7.93251 8ZM10.0671 8L11.6747 9.5877C11.6747 9.58769 11.6747 9.58771 11.6747 9.5877C12.5688 10.4707 14.0223 10.4707 14.9165 9.58773C15.8054 8.70975 15.8054 7.29024 14.9165 6.41229C14.0223 5.52923 12.5689 5.52924 11.6747 6.4123C11.6747 6.4123 11.6747 6.41229 11.6747 6.4123L10.0671 8Z" fill="white"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,27 @@
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "16",
"height": "22",
"viewBox": "0 0 16 22",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"id": "Rectangle 979",
"d": "M0 0H16L9.91493 16.7339C8.76529 19.8955 5.76063 22 2.39658 22H0V0Z",
"fill": "white"
},
"children": []
}
]
},
"name": "Triangle"
}

View File

@ -2,7 +2,7 @@
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './LoopStart.json'
import data from './Triangle.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
@ -11,6 +11,6 @@ const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseP
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
Icon.displayName = 'LoopStart'
Icon.displayName = 'Triangle'
export default Icon

View File

@ -0,0 +1 @@
export { default as Triangle } from './Triangle'

View File

@ -4,9 +4,9 @@
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "40",
"height": "40",
"viewBox": "0 0 40 40",
"width": "18",
"height": "16",
"viewBox": "0 0 18 16",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
@ -15,46 +15,18 @@
"type": "element",
"name": "g",
"attributes": {
"filter": "url(#filter0_dd_10886_10012)",
"style": "transform: scale(2.5) translate(-12px, -8px)"
"id": "loop"
},
"children": [
{
"type": "element",
"name": "rect",
"attributes": {
"x": "8",
"y": "5",
"width": "24",
"height": "24",
"rx": "8",
"fill": "#06AED4"
},
"children": []
},
{
"type": "element",
"name": "rect",
"attributes": {
"x": "8.25",
"y": "5.25",
"width": "23.5",
"height": "23.5",
"rx": "7.75",
"stroke": "#101828",
"stroke-opacity": "0.04",
"stroke-width": "0.5"
},
"children": []
},
{
"type": "element",
"name": "path",
"attributes": {
"id": "Vector",
"fill-rule": "evenodd",
"clip-rule": "evenodd",
"d": "M13.0293 14.3451C14.5076 12.885 16.9007 12.885 18.3791 14.3451L19.9999 15.9459L21.6208 14.3451C23.0992 12.885 25.4922 12.885 26.9706 14.3451C28.4541 15.8103 28.4541 18.1897 26.9707 19.6549C25.4923 21.115 23.0992 21.115 21.6208 19.655L19.9999 18.0541L18.3791 19.655C16.9007 21.115 14.5076 21.115 13.0293 19.655C11.5457 18.1897 11.5457 15.8103 13.0293 14.3451ZM18.9326 17L17.325 15.4123C16.4309 14.5292 14.9774 14.5292 14.0833 15.4123C13.1944 16.2903 13.1944 17.7097 14.0833 18.5877C14.9774 19.4708 16.4309 19.4707 17.325 18.5877C17.325 18.5877 17.325 18.5877 17.325 18.5877L18.9326 17ZM21.0673 17L22.6748 18.5877C22.6748 18.5877 22.6748 18.5877 22.6748 18.5877C23.569 19.4707 25.0224 19.4707 25.9166 18.5877C26.8055 17.7098 26.8055 16.2902 25.9166 15.4123C25.0224 14.5292 23.569 14.5292 22.6748 15.4123C22.6748 15.4123 22.6748 15.4123 22.6748 15.4123L21.0673 17Z",
"fill": "white"
"d": "M2.02915 5.34506C3.50752 3.88498 5.9006 3.88498 7.37896 5.34506L8.99983 6.94588L10.6207 5.34506C12.0991 3.88499 14.4921 3.88498 15.9705 5.34506C17.454 6.81027 17.454 9.18971 15.9705 10.6549C14.4921 12.115 12.0991 12.115 10.6207 10.655L8.99983 9.05413L7.37896 10.655C5.9006 12.115 3.50753 12.115 2.02916 10.655C0.545627 9.18974 0.545611 6.81028 2.02915 5.34506ZM7.93251 8L6.32492 6.4123C5.4308 5.52924 3.97732 5.52923 3.08319 6.4123C2.19426 7.29026 2.19426 8.70975 3.0832 9.58772C3.97733 10.4708 5.4308 10.4707 6.32492 9.58771C6.32492 9.58772 6.32492 9.58771 6.32492 9.58771L7.93251 8ZM10.0671 8L11.6747 9.5877C11.6747 9.58769 11.6747 9.58771 11.6747 9.5877C12.5688 10.4707 14.0223 10.4707 14.9165 9.58773C15.8054 8.70975 15.8054 7.29024 14.9165 6.41229C14.0223 5.52923 12.5689 5.52924 11.6747 6.4123C11.6747 6.4123 11.6747 6.41229 11.6747 6.4123L10.0671 8Z",
"fill": "currentColor"
},
"children": []
}

View File

@ -1,36 +0,0 @@
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "12",
"height": "12",
"viewBox": "0 0 12 12",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "g",
"attributes": {
"id": "icons/block-start"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"id": "Vector",
"d": "M6.8498 1.72732C6.3379 1.3754 5.6621 1.3754 5.1502 1.72732L2.1502 3.78982C1.74317 4.06965 1.5 4.53193 1.5 5.02588V8.99983C1.5 9.82828 2.17158 10.4998 3 10.4998H4.25C4.52614 10.4998 4.75 10.276 4.75 9.99983V8.24983C4.75 7.55948 5.30965 6.99983 6 6.99983C6.69035 6.99983 7.25 7.55948 7.25 8.24983V9.99983C7.25 10.276 7.47385 10.4998 7.75 10.4998H9C9.82845 10.4998 10.5 9.82828 10.5 8.99983V5.02588C10.5 4.53193 10.2568 4.06965 9.8498 3.78982L6.8498 1.72732Z",
"fill": "red"
},
"children": []
}
]
}
]
},
"name": "LoopStart"
}

View File

@ -9,12 +9,11 @@ export { default as Http } from './Http'
export { default as IfElse } from './IfElse'
export { default as IterationStart } from './IterationStart'
export { default as Iteration } from './Iteration'
export { default as LoopStart } from './LoopStart'
export { default as Loop } from './Loop'
export { default as Jinja } from './Jinja'
export { default as KnowledgeRetrieval } from './KnowledgeRetrieval'
export { default as ListFilter } from './ListFilter'
export { default as Llm } from './Llm'
export { default as Loop } from './Loop'
export { default as ParameterExtractor } from './ParameterExtractor'
export { default as QuestionClassifier } from './QuestionClassifier'
export { default as TemplatingTransform } from './TemplatingTransform'

View File

@ -1,67 +1,78 @@
'use client'
import { useTranslation } from 'react-i18next'
import UserInfo from './user-info'
import SearchInput from './search-input'
import Radio from '@/app/components/base/radio'
import RoleSelector from './role-selector'
import Button from '@/app/components/base/button'
import Checkbox from '@/app/components/base/checkbox'
const EducationApplyAge = () => {
const { t } = useTranslation()
return (
<div className='flex justify-center p-6 w-full h-full'>
<div className='relative max-w-[1408px] w-full border border-effects-highlight bg-background-default-subtle rounded-2xl'>
<div className='absolute top-0 w-full h-[349px] rounded-t-2xl overflow-hidden'>
<img
src={'/education/bg.png'}
className='absolute top-0 left-1/2 translate-x-[-50%] w-[1464px] h-[480px]'
alt='education background image'
/>
<div
className="absolute top-0 w-full h-[349px] rounded-t-2xl overflow-hidden bg-no-repeat bg-cover bg-center"
style={{
backgroundImage: 'url(/education/bg.png)',
}}
>
</div>
<div className='relative flex items-center justify-between px-8 py-6 h-[88px] z-10'>
<div></div>
<div></div>
<img
src='/logo/logo-site-dark.png'
alt='dify logo'
className='h-10'
/>
</div>
<div className='relative m-auto px-8 pt-20 max-w-[720px] z-10'>
<div className='mb-2 pt-3 pb-4 text-text-primary-on-surface'>
<div className='mb-2 title-5xl-bold shadow-xs'>Get Education Verified </div>
<div className='system-md-medium shadow-xs'>You are now eligible for Education Verified status. Please enter your education information below to complete the process and receive an exclusive 50% coupon for the Dify Professional Plan.</div>
<div className='relative m-auto px-8 max-w-[720px] z-10'>
<div className='flex flex-col justify-end mb-2 pt-3 pb-4 h-[192px] text-text-primary-on-surface'>
<div className='mb-2 title-5xl-bold shadow-xs'>{t('education.toVerified')}</div>
<div className='system-md-medium shadow-xs'>
{t('education.toVerifiedTip.front')}
<span className='system-md-semibold underline'>{t('education.toVerifiedTip.coupon')}</span>
{t('education.toVerifiedTip.end')}
</div>
</div>
<div className='mb-7'>
<UserInfo />
</div>
<div className='mb-7'>
<div className='flex items-center mb-1 h-6 system-md-semibold text-text-secondary'>
Your School Name
{t('education.form.schoolName.title')}
</div>
<SearchInput />
</div>
<div className='mb-7'>
<div className='flex items-center mb-1 h-6 system-md-semibold text-text-secondary'>
Your School Role
{t('education.form.schoolRole.title')}
</div>
<Radio.Group
className='flex items-center'
>
<Radio value={1}>Student</Radio>
<Radio value={2}>Teacher</Radio>
<Radio value={3}>School Administrator</Radio>
</Radio.Group>
<RoleSelector
value='student'
onChange={() => {}}
/>
</div>
<div className='mb-7'>
<div className='flex items-center mb-1 h-6 system-md-semibold text-text-secondary'>
Terms & Agreements
{t('education.form.terms.title')}
</div>
<div className='mb-1 system-md-regular text-text-tertiary'>
Your information and use of education verified status are subject to our Terms of Service and Privacy Policy. By submitting:
{t('education.form.terms.desc.front')}
<a href='' className='text-text-secondary'>{t('education.form.terms.desc.termsOfService')}</a>
{t('education.form.terms.desc.and')}
<a href='' className='text-text-secondary'>{t('education.form.terms.desc.privacyPolicy')}</a>
{t('education.form.terms.desc.end')}
</div>
<div className='py-2 system-md-regular text-text-primary'>
<div className='flex mb-2'>
<Checkbox className='shrink-0 mr-2' />
I confirm I am at least 18 years old
{t('education.form.terms.option.age')}
</div>
<div className='flex'>
<Checkbox className='shrink-0 mr-2' />
I confirm I am enrolled or employed at the institution provided. Dify may request proof of enrollment/employment. If I misrepresent my eligibility, I agree to pay any fees initially waived based on my education status.
{t('education.form.terms.option.inSchool')}
</div>
</div>
</div>
@ -69,7 +80,7 @@ const EducationApplyAge = () => {
variant='primary'
disabled={false}
>
Send request
{t('education.submit')}
</Button>
</div>
</div>

View File

@ -0,0 +1,53 @@
import { useTranslation } from 'react-i18next'
import cn from '@/utils/classnames'
type RoleSelectorProps = {
onChange: (value: string) => void
value: string
}
const RoleSelector = ({
onChange,
value,
}: RoleSelectorProps) => {
const { t } = useTranslation()
const options = [
{
key: 'student',
value: t('education.form.schoolRole.option.student'),
},
{
key: 'teacher',
value: t('education.form.schoolRole.option.teacher'),
},
{
key: 'school-administrator',
value: t('education.form.schoolRole.option.administrator'),
},
]
return (
<div className='flex'>
{
options.map(option => (
<div
key={option.key}
className='flex items-center mr-6 h-5 cursor-pointer'
onClick={() => onChange(option.key)}
>
<div
className={cn(
'mr-2 w-4 h-4 bg-components-radio-bg rounded-full border border-components-radio-border shadow-xs system-md-regular text-text-primary',
option.key === value && 'border-[5px] border-components-radio-border-checked ',
)}
>
</div>
{option.value}
</div>
))
}
</div>
)
}
export default RoleSelector

View File

@ -1,3 +1,4 @@
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
import {
PortalToFollowElem,
@ -6,12 +7,14 @@ import {
} from '@/app/components/base/portal-to-follow-elem'
const SearchInput = () => {
const { t } = useTranslation()
return (
<PortalToFollowElem>
<PortalToFollowElemTrigger className='block w-full'>
<Input
className='w-full'
placeholder='Enter the official, unabbreviated name of your school'
placeholder={t('education.form.schoolName.placeholder')}
/>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent>

View File

@ -4,6 +4,7 @@ import Button from '@/app/components/base/button'
import { useAppContext } from '@/context/app-context'
import { logout } from '@/service/common'
import Avatar from '@/app/components/base/avatar'
import { Triangle } from '@/app/components/base/icons/src/public/education'
const UserInfo = () => {
const router = useRouter()
@ -24,7 +25,13 @@ const UserInfo = () => {
}
return (
<div className='flex items-center justify-between pl-6 pt-9 pr-8 pb-6 bg-gradient-to-r from-background-gradient-bg-fill-chat-bg-2 to-background-gradient-bg-fill-chat-bg-1 border-[4px] border-components-panel-on-panel-item-bg rounded-xl shadow-shadow-shadow-5'>
<div className='relative flex items-center justify-between pl-6 pt-9 pr-8 pb-6 bg-gradient-to-r from-background-gradient-bg-fill-chat-bg-2 to-background-gradient-bg-fill-chat-bg-1 border-[4px] border-components-panel-on-panel-item-bg rounded-xl shadow-shadow-shadow-5'>
<div className='absolute top-0 left-0 flex items-center'>
<div className='flex items-center pl-2 pt-1 h-[22px] bg-components-panel-on-panel-item-bg system-2xs-semibold-uppercase text-text-accent-light-mode-only'>
{t('education.currentSigned')}
</div>
<Triangle className='w-4 h-[22px] text-components-panel-on-panel-item-bg' />
</div>
<div className='flex items-center'>
<Avatar
className='mr-4'

View File

@ -0,0 +1,41 @@
const translation = {
toVerified: 'Get Education Verified',
toVerifiedTip: {
front: 'You are now eligible for Education Verified status. Please enter your education information below to complete the process and receive an',
coupon: 'exclusive 50% coupon',
end: ' for the Dify Professional Plan.',
},
currentSigned: 'CURRENTLY SIGNED IN AS',
form: {
schoolName: {
title: 'Your School Name',
placeholder: 'Enter the official, unabbreviated name of your school',
},
schoolRole: {
title: 'Your School Role',
option: {
student: 'Student',
teacher: 'Teacher',
administrator: 'School Administrator',
},
},
terms: {
title: 'Terms & Agreements',
desc: {
front: 'Your information and use of Education Verified status are subject to our',
and: 'and',
end: '. By submitting',
termsOfService: 'Terms of Service',
privacyPolicy: 'Privacy Policy',
},
option: {
age: 'I confirm I am at least 18 years old',
inSchool: 'I confirm I am enrolled or employed at the institution provided. Dify may request proof of enrollment/employment. If I misrepresent my eligibility, I agree to pay any fees initially waived based on my education status.',
},
},
},
submit: 'Submit',
learn: 'Learn how to get education verified',
}
export default translation

View File

@ -4,6 +4,18 @@ import { initReactI18next } from 'react-i18next'
import { LanguagesSupported } from '@/i18n/language'
const requireSilent = (lang: string) => {
let res
try {
res = require(`./${lang}/education`).default
}
catch {
res = require('./en-US/education').default
}
return res
}
const loadLangResources = (lang: string) => ({
translation: {
common: require(`./${lang}/common`).default,
@ -31,6 +43,7 @@ const loadLangResources = (lang: string) => ({
plugin: require(`./${lang}/plugin`).default,
pluginTags: require(`./${lang}/plugin-tags`).default,
time: require(`./${lang}/time`).default,
education: requireSilent(lang),
},
})

View File

@ -0,0 +1,41 @@
const translation = {
toVerified: '教育認証を取得',
toVerifiedTip: {
front: '現在、教育認証ステータスを取得する資格があります。以下に教育情報を入力し、認証プロセスを完了すると、Difyプロフェッショナルプランの',
coupon: '50割引クーポン',
end: 'を受け取ることができます。',
},
currentSigned: '現在ログイン中のアカウントは',
form: {
schoolName: {
title: '学校名',
placeholder: '学校の正式名称(省略不可)を入力してください。',
},
schoolRole: {
title: '学校での役割',
option: {
student: '学生',
teacher: '教師',
administrator: '学校管理者',
},
},
terms: {
title: '利用規約と同意事項',
desc: {
front: 'お客様の情報および 教育認証ステータス の利用は、当社の ',
and: 'および',
end: 'に従うものとします。送信することで以下を確認します:',
termsOfService: '利用規約',
privacyPolicy: 'プライバシーポリシー',
},
option: {
age: '18歳以上であることを確認します。',
inSchool: '提供した教育機関に在籍または勤務している ことを確認します。Difyは在籍/雇用証明の提出を求める場合があります。不正な情報を申告した場合、教育認証に基づき免除された費用を支払うことに同意します。',
},
},
},
submit: '送信',
learn: '教育認証の取得方法はこちら',
}
export default translation

View File

@ -0,0 +1,41 @@
const translation = {
toVerified: '获取教育版认证',
toVerifiedTip: {
front: '您现在符合教育版认证的资格。请在下方输入您的教育信息,以完成认证流程,并领取 Dify Professional 版 的',
coupon: '50% 独家优惠券',
end: '。',
},
currentSigned: '您当前登录的账户是',
form: {
schoolName: {
title: '您的学校名称',
placeholder: '请输入您的学校的官方全称(不得缩写)',
},
schoolRole: {
title: '您在学校的身份',
option: {
student: '学生',
teacher: '教师',
administrator: '学校管理员',
},
},
terms: {
title: '条款与协议',
desc: {
front: '您的信息和教育版认证资格的使用需遵守我们的',
and: '和',
end: '。提交即表示:',
termsOfService: '服务条款',
privacyPolicy: '隐私政策',
},
option: {
age: '我确认我已年满 18 周岁。',
inSchool: '我确认我目前已在提供的学校入学或受雇。Dify 可能会要求提供入学/雇佣证明。如我虚报资格,我同意支付因教育版认证而被减免的费用。',
},
},
},
submit: '提交',
learn: '了解如何获取教育版认证',
}
export default translation

Binary file not shown.

Before

Width:  |  Height:  |  Size: 646 KiB

After

Width:  |  Height:  |  Size: 170 KiB