education verify redirection
This commit is contained in:
parent
9f673dab0d
commit
05e25096ba
@ -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() {
|
||||
<div className='ml-4'>
|
||||
<p className='system-xl-semibold text-text-primary'>
|
||||
{userProfile.name}
|
||||
<PremiumBadge size='s' color='blue' className='ml-1 !px-2'>
|
||||
<RiGraduationCapFill className='w-3 h-3 mr-1' />
|
||||
<span className='system-2xs-medium'>EDU</span>
|
||||
</PremiumBadge>
|
||||
{isEducationAccount && (
|
||||
<PremiumBadge size='s' color='blue' className='ml-1 !px-2'>
|
||||
<RiGraduationCapFill className='w-3 h-3 mr-1' />
|
||||
<span className='system-2xs-medium'>EDU</span>
|
||||
</PremiumBadge>
|
||||
)}
|
||||
</p>
|
||||
<p className='system-xs-regular text-text-tertiary'>{userProfile.email}</p>
|
||||
</div>
|
||||
|
@ -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() {
|
||||
<div className='grow'>
|
||||
<div className='system-md-medium text-text-primary break-all'>
|
||||
{userProfile.name}
|
||||
<PremiumBadge size='s' color='blue' className='ml-1 !px-2'>
|
||||
<RiGraduationCapFill className='w-3 h-3 mr-1' />
|
||||
<span className='system-2xs-medium'>EDU</span>
|
||||
</PremiumBadge>
|
||||
{isEducationAccount && (
|
||||
<PremiumBadge size='s' color='blue' className='ml-1 !px-2'>
|
||||
<RiGraduationCapFill className='w-3 h-3 mr-1' />
|
||||
<span className='system-2xs-medium'>EDU</span>
|
||||
</PremiumBadge>
|
||||
)}
|
||||
</div>
|
||||
<div className='system-xs-regular text-text-tertiary break-all'>{userProfile.email}</div>
|
||||
</div>
|
||||
|
@ -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<Props> = ({
|
||||
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<Props> = ({
|
||||
} = plan
|
||||
|
||||
const [showModal, setShowModal] = React.useState(false)
|
||||
const handleVerify = () => {
|
||||
if (userProfile.email.endsWith('.edu'))
|
||||
router.push('/education-apply')
|
||||
else
|
||||
setShowModal(true)
|
||||
}
|
||||
return (
|
||||
<div className='bg-background-section-burn rounded-2xl border-[0.5px] border-effects-highlight-lightmode-off'>
|
||||
<div className='p-6 pb-2'>
|
||||
@ -65,12 +73,12 @@ const PlanComp: FC<Props> = ({
|
||||
<div className='system-xs-regular text-util-colors-gray-gray-600'>{t(`billing.plans.${type}.for`)}</div>
|
||||
</div>
|
||||
<div className='shrink-0 flex items-center gap-1'>
|
||||
{/* {(plan.type === Plan.sandbox || plan.type === Plan.professional) && ( */}
|
||||
<Button variant='ghost' onClick={() => setShowModal(true)}>
|
||||
<RiGraduationCapLine className='w-4 h-4 mr-1'/>
|
||||
{t('education.toVerified')}
|
||||
</Button>
|
||||
{/* )} */}
|
||||
{enableEducationPlan && !isEducationAccount && (
|
||||
<Button variant='ghost' onClick={handleVerify}>
|
||||
<RiGraduationCapLine className='w-4 h-4 mr-1'/>
|
||||
{t('education.toVerified')}
|
||||
</Button>
|
||||
)}
|
||||
{(plan.type as any) !== SelfHostedPlan.enterprise && (
|
||||
<UpgradeBtn
|
||||
className='shrink-0'
|
||||
|
@ -86,6 +86,10 @@ export type CurrentPlanInfoBackend = {
|
||||
can_replace_logo: boolean
|
||||
model_load_balancing_enabled: boolean
|
||||
dataset_operator_enabled: boolean
|
||||
education: {
|
||||
enabled: boolean
|
||||
activated: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export type SubscriptionItem = {
|
||||
|
@ -27,6 +27,7 @@ import I18n from '@/context/i18n'
|
||||
import Avatar from '@/app/components/base/avatar'
|
||||
import { logout } from '@/service/common'
|
||||
import AppContext, { useAppContext } from '@/context/app-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { LanguagesSupported } from '@/i18n/language'
|
||||
import { LicenseStatus } from '@/types/feature'
|
||||
@ -45,6 +46,7 @@ export default function AppSelector() {
|
||||
const { locale } = useContext(I18n)
|
||||
const { t } = useTranslation()
|
||||
const { userProfile, langeniusVersionInfo, isCurrentWorkspaceOwner } = useAppContext()
|
||||
const { isEducationAccount } = useProviderContext()
|
||||
const { setShowAccountSettingModal } = useModalContext()
|
||||
|
||||
const handleLogout = async () => {
|
||||
@ -90,10 +92,12 @@ export default function AppSelector() {
|
||||
<div className='grow'>
|
||||
<div className='system-md-medium text-text-primary break-all'>
|
||||
{userProfile.name}
|
||||
<PremiumBadge size='s' color='blue' className='ml-1 !px-2'>
|
||||
<RiGraduationCapFill className='w-3 h-3 mr-1' />
|
||||
<span className='system-2xs-medium'>EDU</span>
|
||||
</PremiumBadge>
|
||||
{isEducationAccount && (
|
||||
<PremiumBadge size='s' color='blue' className='ml-1 !px-2'>
|
||||
<RiGraduationCapFill className='w-3 h-3 mr-1' />
|
||||
<span className='system-2xs-medium'>EDU</span>
|
||||
</PremiumBadge>
|
||||
)}
|
||||
</div>
|
||||
<div className='system-xs-regular text-text-tertiary break-all'>{userProfile.email}</div>
|
||||
</div>
|
||||
|
@ -68,7 +68,7 @@ const Header = () => {
|
||||
<WorkspaceProvider>
|
||||
<WorkplaceSelector />
|
||||
</WorkspaceProvider>
|
||||
{enableBilling ? <PlanBadge size='s' allowHover sandboxAsUpgrade plan={plan.type} onClick={handlePlanClick} /> : <LicenseNav />}
|
||||
{enableBilling ? <PlanBadge allowHover sandboxAsUpgrade plan={plan.type} onClick={handlePlanClick} /> : <LicenseNav />}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@ -79,7 +79,7 @@ const Header = () => {
|
||||
<LogoSite />
|
||||
</Link>
|
||||
<div className='font-light text-divider-deep'>/</div>
|
||||
{enableBilling ? <PlanBadge size='s' allowHover sandboxAsUpgrade plan={plan.type} onClick={handlePlanClick} /> : <LicenseNav />}
|
||||
{enableBilling ? <PlanBadge allowHover sandboxAsUpgrade plan={plan.type} onClick={handlePlanClick} /> : <LicenseNav />}
|
||||
</div >
|
||||
)}
|
||||
{
|
||||
|
@ -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<PlanBadgeProps> = ({ 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<PlanBadgeProps> = ({ plan, allowHover, sandboxAsUpgrade = fa
|
||||
if (plan === Plan.professional) {
|
||||
return <PremiumBadge className='select-none' size='s' color='blue' allowHover={allowHover} onClick={onClick}>
|
||||
<div className='system-2xs-medium-uppercase'>
|
||||
<span className='p-1'>
|
||||
<span className='p-1 inline-flex items-center gap-1'>
|
||||
{isEducationWorkspace && <RiGraduationCapFill className='w-3 h-3' />}
|
||||
pro
|
||||
</span>
|
||||
</div>
|
||||
|
@ -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 | { title: string; desc: string }>(undefined)
|
||||
const [modalShow, setShowModal] = useState<undefined | { title: string; desc: string; onConfirm?: () => 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)}
|
||||
/>
|
||||
</div>
|
||||
|
@ -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)
|
||||
|
@ -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<ProviderContextState>({
|
||||
modelProviders: [],
|
||||
@ -70,6 +76,9 @@ const ProviderContext = createContext<ProviderContextState>({
|
||||
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}
|
||||
</ProviderContext.Provider>
|
||||
|
@ -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'])
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user