diff --git a/web/app/account/account-page/index.tsx b/web/app/account/account-page/index.tsx
index 30b781c606..f94087b0f0 100644
--- a/web/app/account/account-page/index.tsx
+++ b/web/app/account/account-page/index.tsx
@@ -14,6 +14,7 @@ import Modal from '@/app/components/base/modal'
import Button from '@/app/components/base/button'
import { updateUserProfile } from '@/service/common'
import { useAppContext } from '@/context/app-context'
+import { useProviderContext } from '@/context/provider-context'
import { ToastContext } from '@/app/components/base/toast'
import AppIcon from '@/app/components/base/app-icon'
import { IS_CE_EDITION } from '@/config'
@@ -33,6 +34,7 @@ export default function AccountPage() {
const { t } = useTranslation()
const { systemFeatures } = useAppContext()
const { mutateUserProfile, userProfile, apps } = useAppContext()
+ const { isEducationAccount } = useProviderContext()
const { notify } = useContext(ToastContext)
const [editNameModalVisible, setEditNameModalVisible] = useState(false)
const [editName, setEditName] = useState('')
@@ -140,10 +142,12 @@ export default function AccountPage() {
{userProfile.name}
-
-
- EDU
-
+ {isEducationAccount && (
+
+
+ EDU
+
+ )}
{userProfile.email}
diff --git a/web/app/account/avatar.tsx b/web/app/account/avatar.tsx
index c3568a66a5..bf2cb71b7f 100644
--- a/web/app/account/avatar.tsx
+++ b/web/app/account/avatar.tsx
@@ -9,6 +9,7 @@ import { Menu, Transition } from '@headlessui/react'
import Avatar from '@/app/components/base/avatar'
import { logout } from '@/service/common'
import { useAppContext } from '@/context/app-context'
+import { useProviderContext } from '@/context/provider-context'
import { LogOut01 } from '@/app/components/base/icons/src/vender/line/general'
import PremiumBadge from '@/app/components/base/premium-badge'
@@ -20,6 +21,7 @@ export default function AppSelector() {
const router = useRouter()
const { t } = useTranslation()
const { userProfile } = useAppContext()
+ const { isEducationAccount } = useProviderContext()
const handleLogout = async () => {
await logout({
@@ -74,10 +76,12 @@ export default function AppSelector() {
{userProfile.name}
-
-
- EDU
-
+ {isEducationAccount && (
+
+
+ EDU
+
+ )}
{userProfile.email}
diff --git a/web/app/components/billing/plan/index.tsx b/web/app/components/billing/plan/index.tsx
index a5454f3b11..78569b0044 100644
--- a/web/app/components/billing/plan/index.tsx
+++ b/web/app/components/billing/plan/index.tsx
@@ -2,6 +2,7 @@
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
+import { useRouter } from 'next/navigation'
import {
RiBook2Line,
RiBox3Line,
@@ -29,8 +30,9 @@ const PlanComp: FC = ({
loc,
}) => {
const { t } = useTranslation()
+ const router = useRouter()
const { userProfile } = useAppContext()
- const { plan } = useProviderContext()
+ const { plan, enableEducationPlan, isEducationAccount } = useProviderContext()
const {
type,
} = plan
@@ -41,6 +43,12 @@ const PlanComp: FC = ({
} = plan
const [showModal, setShowModal] = React.useState(false)
+ const handleVerify = () => {
+ if (userProfile.email.endsWith('.edu'))
+ router.push('/education-apply')
+ else
+ setShowModal(true)
+ }
return (
@@ -65,12 +73,12 @@ const PlanComp: FC
= ({
{t(`billing.plans.${type}.for`)}
- {/* {(plan.type === Plan.sandbox || plan.type === Plan.professional) && ( */}
-
- {/* )} */}
+ {enableEducationPlan && !isEducationAccount && (
+
+ )}
{(plan.type as any) !== SelfHostedPlan.enterprise && (
{
@@ -90,10 +92,12 @@ export default function AppSelector() {
{userProfile.name}
-
-
- EDU
-
+ {isEducationAccount && (
+
+
+ EDU
+
+ )}
{userProfile.email}
diff --git a/web/app/components/header/index.tsx b/web/app/components/header/index.tsx
index c2568f9219..e7d1f3c7cb 100644
--- a/web/app/components/header/index.tsx
+++ b/web/app/components/header/index.tsx
@@ -68,7 +68,7 @@ const Header = () => {
- {enableBilling ? : }
+ {enableBilling ? : }
}
@@ -79,7 +79,7 @@ const Header = () => {
/
- {enableBilling ? : }
+ {enableBilling ? : }
)}
{
diff --git a/web/app/components/header/plan-badge/index.tsx b/web/app/components/header/plan-badge/index.tsx
index d6dad3012d..b8dd094cfd 100644
--- a/web/app/components/header/plan-badge/index.tsx
+++ b/web/app/components/header/plan-badge/index.tsx
@@ -1,9 +1,9 @@
import { useProviderContext } from '@/context/provider-context'
import type { FC } from 'react'
import { useTranslation } from 'react-i18next'
-// import {
-// RiGraduationCapFill,
-// } from '@remixicon/react'
+import {
+ RiGraduationCapFill,
+} from '@remixicon/react'
import { SparklesSoft } from '../../base/icons/src/public/common'
import PremiumBadge from '../../base/premium-badge'
import { Plan } from '../../billing/type'
@@ -16,7 +16,7 @@ type PlanBadgeProps = {
}
const PlanBadge: FC = ({ plan, allowHover, sandboxAsUpgrade = false, onClick }) => {
- const { isFetchedPlan } = useProviderContext()
+ const { isFetchedPlan, isEducationWorkspace } = useProviderContext()
const { t } = useTranslation()
if (!isFetchedPlan) return null
@@ -42,7 +42,8 @@ const PlanBadge: FC = ({ plan, allowHover, sandboxAsUpgrade = fa
if (plan === Plan.professional) {
return
-
+
+ {isEducationWorkspace && }
pro
diff --git a/web/app/education-apply/components/education-apply-page.tsx b/web/app/education-apply/components/education-apply-page.tsx
index f8c041bc82..cce0401f18 100644
--- a/web/app/education-apply/components/education-apply-page.tsx
+++ b/web/app/education-apply/components/education-apply-page.tsx
@@ -13,7 +13,9 @@ import Checkbox from '@/app/components/base/checkbox'
import {
useEducationAdd,
useEducationVerify,
+ useInvalidateEducationStatus,
} from '@/service/use-education'
+import { useProviderContext } from '@/context/provider-context'
const EducationApplyAge = () => {
const { t } = useTranslation()
@@ -25,8 +27,16 @@ const EducationApplyAge = () => {
isPending,
mutateAsync: educationAdd,
} = useEducationAdd({ onSuccess: () => {} })
- const [modalShow, setShowModal] = useState(undefined)
+ const [modalShow, setShowModal] = useState void }>(undefined)
const { data } = useEducationVerify()
+ const { onPlanInfoChanged } = useProviderContext()
+ const updateEducationStatus = useInvalidateEducationStatus()
+
+ const handleModalConfirm = () => {
+ setShowModal(undefined)
+ onPlanInfoChanged()
+ updateEducationStatus()
+ }
const handleSubmit = () => {
educationAdd({
@@ -38,6 +48,7 @@ const EducationApplyAge = () => {
setShowModal({
title: t('education.successTitle'),
desc: t('education.successContent'),
+ onConfirm: handleModalConfirm,
})
}
else {
@@ -139,7 +150,7 @@ const EducationApplyAge = () => {
isShow={!!modalShow}
title={modalShow?.title || ''}
content={modalShow?.desc}
- onConfirm={() => setShowModal(undefined)}
+ onConfirm={modalShow?.onConfirm || (() => {})}
onCancel={() => setShowModal(undefined)}
/>
diff --git a/web/app/education-apply/page.tsx b/web/app/education-apply/page.tsx
index 2b89f5874e..7f55fd3f31 100644
--- a/web/app/education-apply/page.tsx
+++ b/web/app/education-apply/page.tsx
@@ -6,16 +6,14 @@ import {
} from 'react'
import { useRouter } from 'next/navigation'
import EducationApplyAge from './components/education-apply-page'
-import { IS_CE_EDITION } from '@/config'
-import { useAppContext } from '@/context/app-context'
-import { LicenseStatus } from '@/types/feature'
+import { useProviderContext } from '@/context/provider-context'
export default function EducationApply() {
const router = useRouter()
- const { systemFeatures } = useAppContext()
+ const { enableEducationPlan, isEducationAccount } = useProviderContext()
const hiddenEducationApply = useMemo(() => {
- return IS_CE_EDITION || (systemFeatures.license.status !== LicenseStatus.NONE)
- }, [systemFeatures.license.status])
+ return enableEducationPlan && isEducationAccount
+ }, [enableEducationPlan, isEducationAccount])
useEffect(() => {
if (hiddenEducationApply)
diff --git a/web/context/provider-context.tsx b/web/context/provider-context.tsx
index 11340f6acd..67c389167d 100644
--- a/web/context/provider-context.tsx
+++ b/web/context/provider-context.tsx
@@ -22,6 +22,9 @@ import { fetchCurrentPlanInfo } from '@/service/billing'
import { parseCurrentPlan } from '@/app/components/billing/utils'
import { defaultPlan } from '@/app/components/billing/config'
import Toast from '@/app/components/base/toast'
+import {
+ useEducationStatus,
+} from '@/service/use-education'
type ProviderContextState = {
modelProviders: ModelProvider[]
@@ -40,6 +43,9 @@ type ProviderContextState = {
enableReplaceWebAppLogo: boolean
modelLoadBalancingEnabled: boolean
datasetOperatorEnabled: boolean
+ enableEducationPlan: boolean
+ isEducationWorkspace: boolean
+ isEducationAccount: boolean
}
const ProviderContext = createContext({
modelProviders: [],
@@ -70,6 +76,9 @@ const ProviderContext = createContext({
enableReplaceWebAppLogo: false,
modelLoadBalancingEnabled: false,
datasetOperatorEnabled: false,
+ enableEducationPlan: false,
+ isEducationWorkspace: false,
+ isEducationAccount: false,
})
export const useProviderContext = () => useContext(ProviderContext)
@@ -97,13 +106,19 @@ export const ProviderContextProvider = ({
const [modelLoadBalancingEnabled, setModelLoadBalancingEnabled] = useState(false)
const [datasetOperatorEnabled, setDatasetOperatorEnabled] = useState(false)
+ const [enableEducationPlan, setEnableEducationPlan] = useState(false)
+ const [isEducationWorkspace, setIsEducationWorkspace] = useState(false)
+ const { data: isEducationAccount } = useEducationStatus(!enableEducationPlan)
+
const fetchPlan = async () => {
const data = await fetchCurrentPlanInfo()
const enabled = data.billing.enabled
setEnableBilling(enabled)
+ setEnableEducationPlan(data.education.enabled)
+ setIsEducationWorkspace(data.education.activated)
setEnableReplaceWebAppLogo(data.can_replace_logo)
if (enabled) {
- setPlan(parseCurrentPlan(data))
+ setPlan(parseCurrentPlan(data) as any)
setIsFetchedPlan(true)
}
if (data.model_load_balancing_enabled)
@@ -155,6 +170,9 @@ export const ProviderContextProvider = ({
enableReplaceWebAppLogo,
modelLoadBalancingEnabled,
datasetOperatorEnabled,
+ enableEducationPlan,
+ isEducationWorkspace,
+ isEducationAccount: isEducationAccount?.result || false,
}}>
{children}
diff --git a/web/service/use-education.ts b/web/service/use-education.ts
index 225ee38207..314813d7b9 100644
--- a/web/service/use-education.ts
+++ b/web/service/use-education.ts
@@ -3,6 +3,7 @@ import {
useMutation,
useQuery,
} from '@tanstack/react-query'
+import { useInvalid } from './use-base'
import type { EducationAddParams } from '@/app/education-apply/components/types'
const NAME_SPACE = 'education'
@@ -50,3 +51,18 @@ export const useEducationAutocomplete = () => {
},
})
}
+
+export const useEducationStatus = (disable?: boolean) => {
+ return useQuery({
+ enabled: !disable,
+ queryKey: [NAME_SPACE, 'education-status'],
+ queryFn: () => {
+ return get<{ result: boolean }>('/account/education')
+ },
+ retry: false,
+ })
+}
+
+export const useInvalidateEducationStatus = () => {
+ return useInvalid([NAME_SPACE, 'education-status'])
+}